diff --git a/crates/bevy_pbr/src/meshlet/material_pipeline_prepare.rs b/crates/bevy_pbr/src/meshlet/material_pipeline_prepare.rs index 2351c7756d..9d56e71ede 100644 --- a/crates/bevy_pbr/src/meshlet/material_pipeline_prepare.rs +++ b/crates/bevy_pbr/src/meshlet/material_pipeline_prepare.rs @@ -336,9 +336,12 @@ pub fn prepare_material_meshlet_meshes_prepass( shader_defs.push("MESHLET_MESH_MATERIAL_PASS".into()); let view_layout = if view_key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) { - prepass_pipeline.view_layout_motion_vectors.clone() + prepass_pipeline.internal.view_layout_motion_vectors.clone() } else { - prepass_pipeline.view_layout_no_motion_vectors.clone() + prepass_pipeline + .internal + .view_layout_no_motion_vectors + .clone() }; let fragment_shader = if view_key.contains(MeshPipelineKey::DEFERRED_PREPASS) { @@ -357,7 +360,7 @@ pub fn prepare_material_meshlet_meshes_prepass( layout: vec![ view_layout, resource_manager.material_shade_bind_group_layout.clone(), - prepass_pipeline.material_layout.clone(), + prepass_pipeline.internal.material_layout.clone(), ], push_constant_ranges: vec![], vertex: VertexState { diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index 724e96b429..7b70df38aa 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -287,6 +287,13 @@ pub fn update_mesh_previous_global_transforms( #[derive(Resource)] pub struct PrepassPipeline { + pub internal: PrepassPipelineInternal, + pub material_pipeline: MaterialPipeline, +} + +/// Internal fields of the `PrepassPipeline` that don't need the generic bound +/// This is done as an optimization to not recompile the same code multiple time +pub struct PrepassPipelineInternal { pub view_layout_motion_vectors: BindGroupLayout, pub view_layout_no_motion_vectors: BindGroupLayout, pub mesh_layouts: MeshLayouts, @@ -295,7 +302,6 @@ pub struct PrepassPipeline { pub prepass_material_fragment_shader: Option>, pub deferred_material_vertex_shader: Option>, pub deferred_material_fragment_shader: Option>, - pub material_pipeline: MaterialPipeline, /// Whether skins will use uniform buffers on account of storage buffers /// being unavailable on this platform. @@ -306,8 +312,6 @@ pub struct PrepassPipeline { /// Whether binding arrays (a.k.a. bindless textures) are usable on the /// current render device. pub binding_arrays_are_usable: bool, - - _marker: PhantomData, } impl FromWorld for PrepassPipeline { @@ -372,8 +376,7 @@ impl FromWorld for PrepassPipeline { let depth_clip_control_supported = render_device .features() .contains(WgpuFeatures::DEPTH_CLIP_CONTROL); - - PrepassPipeline { + let internal = PrepassPipelineInternal { view_layout_motion_vectors, view_layout_no_motion_vectors, mesh_layouts: mesh_pipeline.mesh_layouts.clone(), @@ -398,11 +401,13 @@ impl FromWorld for PrepassPipeline { ShaderRef::Path(path) => Some(asset_server.load(path)), }, material_layout: M::bind_group_layout(render_device), - material_pipeline: world.resource::>().clone(), skins_use_uniform_buffers: skin::skins_use_uniform_buffers(render_device), depth_clip_control_supported, binding_arrays_are_usable: binding_arrays_are_usable(render_device, render_adapter), - _marker: PhantomData, + }; + PrepassPipeline { + internal, + material_pipeline: world.resource::>().clone(), } } } @@ -418,15 +423,38 @@ where key: Self::Key, layout: &MeshVertexBufferLayoutRef, ) -> Result { - let mut bind_group_layouts = vec![if key - .mesh_key + let mut shader_defs = Vec::new(); + if self.material_pipeline.bindless { + shader_defs.push("BINDLESS".into()); + } + let mut descriptor = self + .internal + .specialize(key.mesh_key, shader_defs, layout)?; + + // This is a bit risky because it's possible to change something that would + // break the prepass but be fine in the main pass. + // Since this api is pretty low-level it doesn't matter that much, but it is a potential issue. + M::specialize(&self.material_pipeline, &mut descriptor, layout, key)?; + + Ok(descriptor) + } +} + +impl PrepassPipelineInternal { + fn specialize( + &self, + mesh_key: MeshPipelineKey, + shader_defs: Vec, + layout: &MeshVertexBufferLayoutRef, + ) -> Result { + let mut shader_defs = shader_defs; + let mut bind_group_layouts = vec![if mesh_key .contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) { self.view_layout_motion_vectors.clone() } else { self.view_layout_no_motion_vectors.clone() }]; - let mut shader_defs = Vec::new(); let mut vertex_attributes = Vec::new(); // Let the shader code know that it's running in a prepass pipeline. @@ -437,40 +465,29 @@ where // NOTE: Eventually, it would be nice to only add this when the shaders are overloaded by the Material. // The main limitation right now is that bind group order is hardcoded in shaders. bind_group_layouts.push(self.material_layout.clone()); - #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] shader_defs.push("WEBGL2".into()); - shader_defs.push("VERTEX_OUTPUT_INSTANCE_INDEX".into()); - - if key.mesh_key.contains(MeshPipelineKey::DEPTH_PREPASS) { + if mesh_key.contains(MeshPipelineKey::DEPTH_PREPASS) { shader_defs.push("DEPTH_PREPASS".into()); } - - if key.mesh_key.contains(MeshPipelineKey::MAY_DISCARD) { + if mesh_key.contains(MeshPipelineKey::MAY_DISCARD) { shader_defs.push("MAY_DISCARD".into()); } - - let blend_key = key - .mesh_key - .intersection(MeshPipelineKey::BLEND_RESERVED_BITS); + let blend_key = mesh_key.intersection(MeshPipelineKey::BLEND_RESERVED_BITS); if blend_key == MeshPipelineKey::BLEND_PREMULTIPLIED_ALPHA { shader_defs.push("BLEND_PREMULTIPLIED_ALPHA".into()); } if blend_key == MeshPipelineKey::BLEND_ALPHA { shader_defs.push("BLEND_ALPHA".into()); } - if layout.0.contains(Mesh::ATTRIBUTE_POSITION) { shader_defs.push("VERTEX_POSITIONS".into()); vertex_attributes.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0)); } - // For directional light shadow map views, use unclipped depth via either the native GPU feature, // or emulated by setting depth in the fragment shader for GPUs that don't support it natively. - let emulate_unclipped_depth = key - .mesh_key - .contains(MeshPipelineKey::UNCLIPPED_DEPTH_ORTHO) + let emulate_unclipped_depth = mesh_key.contains(MeshPipelineKey::UNCLIPPED_DEPTH_ORTHO) && !self.depth_clip_control_supported; if emulate_unclipped_depth { shader_defs.push("UNCLIPPED_DEPTH_ORTHO_EMULATION".into()); @@ -482,36 +499,28 @@ where // https://github.com/bevyengine/bevy/pull/8877 shader_defs.push("PREPASS_FRAGMENT".into()); } - let unclipped_depth = key - .mesh_key - .contains(MeshPipelineKey::UNCLIPPED_DEPTH_ORTHO) + let unclipped_depth = mesh_key.contains(MeshPipelineKey::UNCLIPPED_DEPTH_ORTHO) && self.depth_clip_control_supported; - if layout.0.contains(Mesh::ATTRIBUTE_UV_0) { shader_defs.push("VERTEX_UVS".into()); shader_defs.push("VERTEX_UVS_A".into()); vertex_attributes.push(Mesh::ATTRIBUTE_UV_0.at_shader_location(1)); } - if layout.0.contains(Mesh::ATTRIBUTE_UV_1) { shader_defs.push("VERTEX_UVS".into()); shader_defs.push("VERTEX_UVS_B".into()); vertex_attributes.push(Mesh::ATTRIBUTE_UV_1.at_shader_location(2)); } - - if key.mesh_key.contains(MeshPipelineKey::NORMAL_PREPASS) { + if mesh_key.contains(MeshPipelineKey::NORMAL_PREPASS) { shader_defs.push("NORMAL_PREPASS".into()); } - - if key - .mesh_key - .intersects(MeshPipelineKey::NORMAL_PREPASS | MeshPipelineKey::DEFERRED_PREPASS) + if mesh_key.intersects(MeshPipelineKey::NORMAL_PREPASS | MeshPipelineKey::DEFERRED_PREPASS) { shader_defs.push("NORMAL_PREPASS_OR_DEFERRED_PREPASS".into()); if layout.0.contains(Mesh::ATTRIBUTE_NORMAL) { shader_defs.push("VERTEX_NORMALS".into()); vertex_attributes.push(Mesh::ATTRIBUTE_NORMAL.at_shader_location(3)); - } else if key.mesh_key.contains(MeshPipelineKey::NORMAL_PREPASS) { + } else if mesh_key.contains(MeshPipelineKey::NORMAL_PREPASS) { warn!( "The default normal prepass expects the mesh to have vertex normal attributes." ); @@ -521,91 +530,62 @@ where vertex_attributes.push(Mesh::ATTRIBUTE_TANGENT.at_shader_location(4)); } } - - if key - .mesh_key + if mesh_key .intersects(MeshPipelineKey::MOTION_VECTOR_PREPASS | MeshPipelineKey::DEFERRED_PREPASS) { shader_defs.push("MOTION_VECTOR_PREPASS_OR_DEFERRED_PREPASS".into()); } - - if key.mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS) { + if mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS) { shader_defs.push("DEFERRED_PREPASS".into()); } - - if key.mesh_key.contains(MeshPipelineKey::LIGHTMAPPED) { + if mesh_key.contains(MeshPipelineKey::LIGHTMAPPED) { shader_defs.push("LIGHTMAP".into()); } - if key - .mesh_key - .contains(MeshPipelineKey::LIGHTMAP_BICUBIC_SAMPLING) - { + if mesh_key.contains(MeshPipelineKey::LIGHTMAP_BICUBIC_SAMPLING) { shader_defs.push("LIGHTMAP_BICUBIC_SAMPLING".into()); } - if layout.0.contains(Mesh::ATTRIBUTE_COLOR) { shader_defs.push("VERTEX_COLORS".into()); vertex_attributes.push(Mesh::ATTRIBUTE_COLOR.at_shader_location(7)); } - - if key - .mesh_key - .contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) - { + if mesh_key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) { shader_defs.push("MOTION_VECTOR_PREPASS".into()); } - - if key.mesh_key.contains(MeshPipelineKey::HAS_PREVIOUS_SKIN) { + if mesh_key.contains(MeshPipelineKey::HAS_PREVIOUS_SKIN) { shader_defs.push("HAS_PREVIOUS_SKIN".into()); } - - if key.mesh_key.contains(MeshPipelineKey::HAS_PREVIOUS_MORPH) { + if mesh_key.contains(MeshPipelineKey::HAS_PREVIOUS_MORPH) { shader_defs.push("HAS_PREVIOUS_MORPH".into()); } - - // If bindless mode is on, add a `BINDLESS` define. - if self.material_pipeline.bindless { - shader_defs.push("BINDLESS".into()); - } - if self.binding_arrays_are_usable { shader_defs.push("MULTIPLE_LIGHTMAPS_IN_ARRAY".into()); } - - if key - .mesh_key - .contains(MeshPipelineKey::VISIBILITY_RANGE_DITHER) - { + if mesh_key.contains(MeshPipelineKey::VISIBILITY_RANGE_DITHER) { shader_defs.push("VISIBILITY_RANGE_DITHER".into()); } - - if key.mesh_key.intersects( + if mesh_key.intersects( MeshPipelineKey::NORMAL_PREPASS | MeshPipelineKey::MOTION_VECTOR_PREPASS | MeshPipelineKey::DEFERRED_PREPASS, ) { shader_defs.push("PREPASS_FRAGMENT".into()); } - let bind_group = setup_morph_and_skinning_defs( &self.mesh_layouts, layout, 5, - &key.mesh_key, + &mesh_key, &mut shader_defs, &mut vertex_attributes, self.skins_use_uniform_buffers, ); bind_group_layouts.insert(1, bind_group); - let vertex_buffer_layout = layout.0.get_layout(&vertex_attributes)?; - // Setup prepass fragment targets - normals in slot 0 (or None if not needed), motion vectors in slot 1 let mut targets = prepass_target_descriptors( - key.mesh_key.contains(MeshPipelineKey::NORMAL_PREPASS), - key.mesh_key - .contains(MeshPipelineKey::MOTION_VECTOR_PREPASS), - key.mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS), + mesh_key.contains(MeshPipelineKey::NORMAL_PREPASS), + mesh_key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS), + mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS), ); if targets.iter().all(Option::is_none) { @@ -619,12 +599,12 @@ where // prepass shader, or we are emulating unclipped depth in the fragment shader. let fragment_required = !targets.is_empty() || emulate_unclipped_depth - || (key.mesh_key.contains(MeshPipelineKey::MAY_DISCARD) + || (mesh_key.contains(MeshPipelineKey::MAY_DISCARD) && self.prepass_material_fragment_shader.is_some()); let fragment = fragment_required.then(|| { // Use the fragment shader from the material - let frag_shader_handle = if key.mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS) { + let frag_shader_handle = if mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS) { match self.deferred_material_fragment_shader.clone() { Some(frag_shader_handle) => frag_shader_handle, _ => PREPASS_SHADER_HANDLE, @@ -645,7 +625,7 @@ where }); // Use the vertex shader from the material if present - let vert_shader_handle = if key.mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS) { + let vert_shader_handle = if mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS) { if let Some(handle) = &self.deferred_material_vertex_shader { handle.clone() } else { @@ -656,8 +636,7 @@ where } else { PREPASS_SHADER_HANDLE }; - - let mut descriptor = RenderPipelineDescriptor { + let descriptor = RenderPipelineDescriptor { vertex: VertexState { shader: vert_shader_handle, entry_point: "vertex".into(), @@ -667,7 +646,7 @@ where fragment, layout: bind_group_layouts, primitive: PrimitiveState { - topology: key.mesh_key.primitive_topology(), + topology: mesh_key.primitive_topology(), strip_index_format: None, front_face: FrontFace::Ccw, cull_mode: None, @@ -692,7 +671,7 @@ where }, }), multisample: MultisampleState { - count: key.mesh_key.msaa_samples(), + count: mesh_key.msaa_samples(), mask: !0, alpha_to_coverage_enabled: false, }, @@ -700,12 +679,6 @@ where label: Some("prepass_pipeline".into()), zero_initialize_workgroup_memory: false, }; - - // This is a bit risky because it's possible to change something that would - // break the prepass but be fine in the main pass. - // Since this api is pretty low-level it doesn't matter that much, but it is a potential issue. - M::specialize(&self.material_pipeline, &mut descriptor, layout, key)?; - Ok(descriptor) } } @@ -790,7 +763,7 @@ pub fn prepare_prepass_view_bind_group( ) { prepass_view_bind_group.no_motion_vectors = Some(render_device.create_bind_group( "prepass_view_no_motion_vectors_bind_group", - &prepass_pipeline.view_layout_no_motion_vectors, + &prepass_pipeline.internal.view_layout_no_motion_vectors, &BindGroupEntries::with_indices(( (0, view_binding.clone()), (1, globals_binding.clone()), @@ -801,7 +774,7 @@ pub fn prepare_prepass_view_bind_group( if let Some(previous_view_uniforms_binding) = previous_view_uniforms.uniforms.binding() { prepass_view_bind_group.motion_vectors = Some(render_device.create_bind_group( "prepass_view_motion_vectors_bind_group", - &prepass_pipeline.view_layout_motion_vectors, + &prepass_pipeline.internal.view_layout_motion_vectors, &BindGroupEntries::with_indices(( (0, view_binding), (1, globals_binding),