diff --git a/crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs b/crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs index f6f3b2a6e89e8b..b4cea92089ed64 100644 --- a/crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs +++ b/crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs @@ -22,7 +22,7 @@ pub struct MainPass2dNode { &'static RenderPhase, &'static ViewTarget, &'static Camera2d, - &'static PickingTextures, + Option<&'static PickingTextures>, ), With, >, @@ -64,24 +64,28 @@ impl Node for MainPass2dNode { { #[cfg(feature = "trace")] let _main_pass_2d = info_span!("main_pass_2d").entered(); + + let mut color_attachments = vec![Some(target.get_color_attachment(Operations { + load: match camera_2d.clear_color { + ClearColorConfig::Default => { + LoadOp::Clear(world.resource::().0.into()) + } + ClearColorConfig::Custom(color) => LoadOp::Clear(color.into()), + ClearColorConfig::None => LoadOp::Load, + }, + store: true, + }))]; + + if let Some(picking_textures) = picking_textures { + color_attachments.push(Some(picking_textures.get_color_attachment(Operations { + load: LoadOp::Clear(PickingTextures::clear_color()), + store: true, + }))) + } + let pass_descriptor = RenderPassDescriptor { label: Some("main_pass_2d"), - color_attachments: &[ - Some(target.get_color_attachment(Operations { - load: match camera_2d.clear_color { - ClearColorConfig::Default => { - LoadOp::Clear(world.resource::().0.into()) - } - ClearColorConfig::Custom(color) => LoadOp::Clear(color.into()), - ClearColorConfig::None => LoadOp::Load, - }, - store: true, - })), - Some(picking_textures.get_color_attachment(Operations { - load: LoadOp::Clear(PickingTextures::clear_color()), - store: true, - })), - ], + color_attachments: &color_attachments, depth_stencil_attachment: None, }; diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 068702e9ecf169..8f9ab3d5345afc 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -652,6 +652,7 @@ impl SpecializedMeshPipeline for MeshPipeline { true => ViewTarget::TEXTURE_FORMAT_HDR, false => TextureFormat::bevy_default(), }; + let mut targets = vec![Some(ColorTargetState { format, blend, diff --git a/crates/bevy_pbr/src/render/pbr.wgsl b/crates/bevy_pbr/src/render/pbr.wgsl index 564afaf51a3c5f..af0c44a2f9b7a5 100644 --- a/crates/bevy_pbr/src/render/pbr.wgsl +++ b/crates/bevy_pbr/src/render/pbr.wgsl @@ -15,11 +15,11 @@ struct FragmentInput { }; struct FragmentOutput { - @location(0) color: vec4, + @location(0) color: vec4, #ifdef PICKING - @location(1) picking: u32, + @location(1) picking: u32, #endif - } +} @fragment fn fragment(in: FragmentInput) -> FragmentOutput { diff --git a/crates/bevy_sprite/src/mesh2d/color_material.wgsl b/crates/bevy_sprite/src/mesh2d/color_material.wgsl index a2a24086e59a54..75f573c3641f1f 100644 --- a/crates/bevy_sprite/src/mesh2d/color_material.wgsl +++ b/crates/bevy_sprite/src/mesh2d/color_material.wgsl @@ -23,9 +23,11 @@ struct FragmentInput { }; struct FragmentOutput { - @location(0) color: vec4, - @location(1) picking: u32, - } + @location(0) color: vec4, +#ifdef PICKING + @location(1) picking: u32, +#endif +} @fragment fn fragment(in: FragmentInput) -> FragmentOutput { @@ -40,7 +42,10 @@ fn fragment(in: FragmentInput) -> FragmentOutput { var out: FragmentOutput; out.color = output_color; + +#ifdef PICKING out.picking = mesh.entity_index; +#endif return out; } diff --git a/crates/bevy_sprite/src/mesh2d/material.rs b/crates/bevy_sprite/src/mesh2d/material.rs index 0f3b86bf9e1d51..deaffa1a780e8c 100644 --- a/crates/bevy_sprite/src/mesh2d/material.rs +++ b/crates/bevy_sprite/src/mesh2d/material.rs @@ -18,6 +18,7 @@ use bevy_reflect::TypeUuid; use bevy_render::{ extract_component::ExtractComponentPlugin, mesh::{Mesh, MeshVertexBufferLayout}, + picking::Picking, prelude::Image, render_asset::{PrepareAssetLabel, RenderAssets}, render_phase::{ @@ -310,6 +311,7 @@ pub fn queue_material2d_meshes( &ExtractedView, &VisibleEntities, Option<&Tonemapping>, + Option<&Picking>, &mut RenderPhase, )>, ) where @@ -319,7 +321,7 @@ pub fn queue_material2d_meshes( return; } - for (view, visible_entities, tonemapping, mut transparent_phase) in &mut views { + for (view, visible_entities, tonemapping, picking, mut transparent_phase) in &mut views { let draw_transparent_pbr = transparent_draw_functions.read().id::>(); let mut view_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples) @@ -335,6 +337,10 @@ pub fn queue_material2d_meshes( } } + if picking.is_some() { + view_key |= Mesh2dPipelineKey::PICKING; + } + for visible_entity in &visible_entities.entities { if let Ok((material2d_handle, mesh2d_handle, mesh2d_uniform)) = material2d_meshes.get(*visible_entity) diff --git a/crates/bevy_sprite/src/mesh2d/mesh.rs b/crates/bevy_sprite/src/mesh2d/mesh.rs index 4fae1bf0a3c6d3..829b05c2a97d84 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh.rs +++ b/crates/bevy_sprite/src/mesh2d/mesh.rs @@ -291,6 +291,7 @@ bitflags::bitflags! { const HDR = (1 << 0); const TONEMAP_IN_SHADER = (1 << 1); const DEBAND_DITHER = (1 << 2); + const PICKING = (1 << 3); const MSAA_RESERVED_BITS = Self::MSAA_MASK_BITS << Self::MSAA_SHIFT_BITS; const PRIMITIVE_TOPOLOGY_RESERVED_BITS = Self::PRIMITIVE_TOPOLOGY_MASK_BITS << Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS; } @@ -393,6 +394,22 @@ impl SpecializedMeshPipeline for Mesh2dPipeline { false => TextureFormat::bevy_default(), }; + let mut targets = vec![Some(ColorTargetState { + format, + blend: Some(BlendState::ALPHA_BLENDING), + write_mask: ColorWrites::ALL, + })]; + + if key.contains(Mesh2dPipelineKey::PICKING) { + shader_defs.push("PICKING".into()); + + targets.push(Some(ColorTargetState { + format: TextureFormat::R32Uint, + blend: None, + write_mask: ColorWrites::ALL, + })) + } + Ok(RenderPipelineDescriptor { vertex: VertexState { shader: MESH2D_SHADER_HANDLE.typed::(), @@ -404,18 +421,7 @@ impl SpecializedMeshPipeline for Mesh2dPipeline { shader: MESH2D_SHADER_HANDLE.typed::(), shader_defs, entry_point: "fragment".into(), - targets: vec![ - Some(ColorTargetState { - format, - blend: Some(BlendState::ALPHA_BLENDING), - write_mask: ColorWrites::ALL, - }), - Some(ColorTargetState { - format: TextureFormat::R32Uint, - blend: None, - write_mask: ColorWrites::ALL, - }), - ], + targets, }), layout: Some(vec![self.view_layout.clone(), self.mesh_layout.clone()]), primitive: PrimitiveState { diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index 27f9860ddd6acd..5a8836479c7b87 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -14,6 +14,7 @@ use bevy_math::{Rect, Vec2}; use bevy_reflect::Uuid; use bevy_render::{ color::Color, + picking::Picking, render_asset::RenderAssets, render_phase::{ BatchedPhaseItem, DrawFunctions, EntityRenderCommand, RenderCommand, RenderCommandResult, @@ -152,6 +153,7 @@ bitflags::bitflags! { const HDR = (1 << 1); const TONEMAP_IN_SHADER = (1 << 2); const DEBAND_DITHER = (1 << 3); + const PICKING = (1 << 4); const MSAA_RESERVED_BITS = Self::MSAA_MASK_BITS << Self::MSAA_SHIFT_BITS; } } @@ -227,6 +229,22 @@ impl SpecializedRenderPipeline for SpritePipeline { false => TextureFormat::bevy_default(), }; + let mut targets = vec![Some(ColorTargetState { + format, + blend: Some(BlendState::ALPHA_BLENDING), + write_mask: ColorWrites::ALL, + })]; + + if key.contains(SpritePipelineKey::PICKING) { + shader_defs.push("PICKING".into()); + + targets.push(Some(ColorTargetState { + format: TextureFormat::R32Uint, + blend: None, + write_mask: ColorWrites::ALL, + })) + } + RenderPipelineDescriptor { vertex: VertexState { shader: SPRITE_SHADER_HANDLE.typed::(), @@ -238,18 +256,7 @@ impl SpecializedRenderPipeline for SpritePipeline { shader: SPRITE_SHADER_HANDLE.typed::(), shader_defs, entry_point: "fragment".into(), - targets: vec![ - Some(ColorTargetState { - format, - blend: Some(BlendState::ALPHA_BLENDING), - write_mask: ColorWrites::ALL, - }), - Some(ColorTargetState { - format: TextureFormat::R32Uint, - blend: None, - write_mask: ColorWrites::ALL, - }), - ], + targets, }), layout: Some(vec![self.view_layout.clone(), self.material_layout.clone()]), primitive: PrimitiveState { @@ -468,6 +475,7 @@ pub fn queue_sprites( &VisibleEntities, &ExtractedView, Option<&Tonemapping>, + Option<&Picking>, )>, events: Res, ) { @@ -523,7 +531,7 @@ pub fn queue_sprites( }); let image_bind_groups = &mut *image_bind_groups; - for (mut transparent_phase, visible_entities, view, tonemapping) in &mut views { + for (mut transparent_phase, visible_entities, view, tonemapping, picking) in &mut views { let mut view_key = SpritePipelineKey::from_hdr(view.hdr) | msaa_key; if let Some(Tonemapping::Enabled { deband_dither }) = tonemapping { if !view.hdr { @@ -534,6 +542,11 @@ pub fn queue_sprites( } } } + + if picking.is_some() { + view_key |= SpritePipelineKey::PICKING; + } + let pipeline = pipelines.specialize( &mut pipeline_cache, &sprite_pipeline, diff --git a/crates/bevy_sprite/src/render/sprite.wgsl b/crates/bevy_sprite/src/render/sprite.wgsl index 2086d45c2a364c..18db4d4d5cb74b 100644 --- a/crates/bevy_sprite/src/render/sprite.wgsl +++ b/crates/bevy_sprite/src/render/sprite.wgsl @@ -50,9 +50,11 @@ var sprite_texture: texture_2d; var sprite_sampler: sampler; struct FragmentOutput { - @location(0) color: vec4, - @location(1) picking: u32, - } + @location(0) color: vec4, +#ifdef PICKING + @location(1) picking: u32, +#endif +} @fragment fn fragment(in: VertexOutput) -> FragmentOutput { @@ -68,7 +70,10 @@ fn fragment(in: VertexOutput) -> FragmentOutput { var out: FragmentOutput; out.color = color; + +#ifdef PICKING out.picking = in.entity_index; +#endif return out; } diff --git a/crates/bevy_ui/Cargo.toml b/crates/bevy_ui/Cargo.toml index f0724e65d5e971..ca47dc26daddca 100644 --- a/crates/bevy_ui/Cargo.toml +++ b/crates/bevy_ui/Cargo.toml @@ -35,3 +35,4 @@ serde = { version = "1", features = ["derive"] } smallvec = { version = "1.6", features = ["union", "const_generics"] } bytemuck = { version = "1.5", features = ["derive"] } thiserror = "1.0.0" +bitflags = "1.2" diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 0834e3743efbc1..8962d4174054aa 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -2,6 +2,7 @@ mod pipeline; mod render_pass; use bevy_core_pipeline::{core_2d::Camera2d, core_3d::Camera3d}; +use bevy_render::picking::Picking; pub use pipeline::*; pub use render_pass::*; @@ -387,8 +388,8 @@ pub fn extract_text_uinodes( struct UiVertex { pub position: [f32; 3], pub uv: [f32; 2], - pub entity_index: u32, pub color: [f32; 4], + pub entity_index: u32, } #[derive(Resource)] @@ -579,7 +580,11 @@ pub fn queue_uinodes( mut image_bind_groups: ResMut, gpu_images: Res>, ui_batches: Query<(Entity, &UiBatch)>, - mut views: Query<(&ExtractedView, &mut RenderPhase)>, + mut views: Query<( + &ExtractedView, + Option<&Picking>, + &mut RenderPhase, + )>, events: Res, ) { // If an image has changed, the GpuImage has (probably) changed @@ -602,12 +607,14 @@ pub fn queue_uinodes( layout: &ui_pipeline.view_layout, })); let draw_ui_function = draw_functions.read().id::(); - for (view, mut transparent_phase) in &mut views { - let pipeline = pipelines.specialize( - &mut pipeline_cache, - &ui_pipeline, - UiPipelineKey { hdr: view.hdr }, - ); + for (view, picking, mut transparent_phase) in &mut views { + let mut key = UiPipelineKey::from_hdr(view.hdr); + + if picking.is_some() { + key |= UiPipelineKey::PICKING; + } + + let pipeline = pipelines.specialize(&mut pipeline_cache, &ui_pipeline, key); for (entity, batch) in &ui_batches { image_bind_groups .values diff --git a/crates/bevy_ui/src/render/pipeline.rs b/crates/bevy_ui/src/render/pipeline.rs index 676395aa48f61c..3c64f8176f7bd1 100644 --- a/crates/bevy_ui/src/render/pipeline.rs +++ b/crates/bevy_ui/src/render/pipeline.rs @@ -59,29 +59,70 @@ impl FromWorld for UiPipeline { } } -#[derive(Clone, Copy, Hash, PartialEq, Eq)] -pub struct UiPipelineKey { - pub hdr: bool, +bitflags::bitflags! { + #[repr(transparent)] + pub struct UiPipelineKey: u32 { + const NONE = 0; + const HDR = (1 << 0); + const PICKING = (1 << 1); + } +} + +impl UiPipelineKey { + pub fn from_hdr(hdr: bool) -> Self { + if hdr { + UiPipelineKey::HDR + } else { + UiPipelineKey::NONE + } + } } impl SpecializedRenderPipeline for UiPipeline { type Key = UiPipelineKey; fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { - let vertex_layout = VertexBufferLayout::from_vertex_formats( - VertexStepMode::Vertex, - vec![ - // position - VertexFormat::Float32x3, - // uv - VertexFormat::Float32x2, + let mut vertex_formats = vec![ + // position + VertexFormat::Float32x3, + // uv + VertexFormat::Float32x2, + // color + VertexFormat::Float32x4, + ]; + + // TODO: Find shader locs of color etc. + + let mut shader_defs = Vec::new(); + + if key.contains(UiPipelineKey::PICKING) { + vertex_formats.push( // entity index VertexFormat::Uint32, - // color - VertexFormat::Float32x4, - ], - ); - let shader_defs = Vec::new(); + ); + shader_defs.push("PICKING".into()); + } + + let vertex_layout = + VertexBufferLayout::from_vertex_formats(VertexStepMode::Vertex, vertex_formats); + + let mut targets = vec![Some(ColorTargetState { + format: if key.contains(UiPipelineKey::HDR) { + ViewTarget::TEXTURE_FORMAT_HDR + } else { + TextureFormat::bevy_default() + }, + blend: Some(BlendState::ALPHA_BLENDING), + write_mask: ColorWrites::ALL, + })]; + + if key.contains(UiPipelineKey::PICKING) { + targets.push(Some(ColorTargetState { + format: TextureFormat::R32Uint, + blend: None, + write_mask: ColorWrites::ALL, + })) + } RenderPipelineDescriptor { vertex: VertexState { @@ -94,22 +135,7 @@ impl SpecializedRenderPipeline for UiPipeline { shader: super::UI_SHADER_HANDLE.typed::(), shader_defs, entry_point: "fragment".into(), - targets: vec![ - Some(ColorTargetState { - format: if key.hdr { - ViewTarget::TEXTURE_FORMAT_HDR - } else { - TextureFormat::bevy_default() - }, - blend: Some(BlendState::ALPHA_BLENDING), - write_mask: ColorWrites::ALL, - }), - Some(ColorTargetState { - format: TextureFormat::R32Uint, - blend: None, - write_mask: ColorWrites::ALL, - }), - ], + targets, }), layout: Some(vec![self.view_layout.clone(), self.image_layout.clone()]), primitive: PrimitiveState { diff --git a/crates/bevy_ui/src/render/render_pass.rs b/crates/bevy_ui/src/render/render_pass.rs index 8561e3d76a2d7e..fca77f8a717b97 100644 --- a/crates/bevy_ui/src/render/render_pass.rs +++ b/crates/bevy_ui/src/render/render_pass.rs @@ -19,7 +19,7 @@ pub struct UiPassNode { ( &'static RenderPhase, &'static ViewTarget, - &'static PickingTextures, + Option<&'static PickingTextures>, Option<&'static UiCameraConfig>, ), With, @@ -84,12 +84,14 @@ impl Node for UiPassNode { store: true, }; + let mut color_attachments = vec![Some(target.get_unsampled_color_attachment(ops))]; + if let Some(picking_textures) = picking_textures { + color_attachments.push(Some(picking_textures.get_unsampled_color_attachment(ops))); + } + let pass_descriptor = RenderPassDescriptor { label: Some("ui_pass"), - color_attachments: &[ - Some(target.get_unsampled_color_attachment(ops)), - Some(picking_textures.get_unsampled_color_attachment(ops)), - ], + color_attachments: &color_attachments, depth_stencil_attachment: None, }; diff --git a/crates/bevy_ui/src/render/ui.wgsl b/crates/bevy_ui/src/render/ui.wgsl index 01598f59eaf704..523f01967876df 100644 --- a/crates/bevy_ui/src/render/ui.wgsl +++ b/crates/bevy_ui/src/render/ui.wgsl @@ -14,8 +14,8 @@ var view: View; struct VertexOutput { @location(0) uv: vec2, - @location(1) entity_index: u32, - @location(2) color: vec4, + @location(1) color: vec4, + @location(2) entity_index: u32, @builtin(position) position: vec4, }; @@ -23,14 +23,14 @@ struct VertexOutput { fn vertex( @location(0) vertex_position: vec3, @location(1) vertex_uv: vec2, - @location(2) entity_index: u32, - @location(3) vertex_color: vec4, + @location(2) vertex_color: vec4, + @location(3) entity_index: u32, ) -> VertexOutput { var out: VertexOutput; out.uv = vertex_uv; out.position = view.view_proj * vec4(vertex_position, 1.0); - out.entity_index = entity_index; out.color = vertex_color; + out.entity_index = entity_index; return out; } @@ -40,9 +40,11 @@ var sprite_texture: texture_2d; var sprite_sampler: sampler; struct FragmentOutput { - @location(0) color: vec4, - @location(1) picking: u32, - } + @location(0) color: vec4, +#ifdef PICKING + @location(1) picking: u32, +#endif +} @fragment fn fragment(in: VertexOutput) -> FragmentOutput { @@ -52,7 +54,10 @@ fn fragment(in: VertexOutput) -> FragmentOutput { var out: FragmentOutput; out.color = color; + +#ifdef PICKING out.picking = in.entity_index; +#endif return out; }