Move non-generic parts of the PrepassPipeline to internal field (#18322)

# Objective

- The prepass pipeline has a generic bound on the specialize function
but 95% of it doesn't need it

## Solution

- Move most of the fields to an internal struct and use a separate
specialize function for those fields

## Testing

- Ran the 3d_scene and it worked like before

---

## Migration Guide

If you were using a field of the `PrepassPipeline`, most of them have
now been move to `PrepassPipeline::internal`.

## Notes

Here's the cargo bloat size comparison (from this tool
https://github.com/bevyengine/bevy/discussions/14864):

```
before:
    (
        "<bevy_pbr::prepass::PrepassPipeline<M> as bevy_render::render_resource::pipeline_specializer::SpecializedMeshPipeline>::specialize",
        25416,
        0.05582993,
    ),

after:
    (
        "<bevy_pbr::prepass::PrepassPipeline<M> as bevy_render::render_resource::pipeline_specializer::SpecializedMeshPipeline>::specialize",
        2496,
        0.005490916,
    ),
    (
        "bevy_pbr::prepass::PrepassPipelineInternal::specialize",
        11444,
        0.025175499,
    ),
```

The size for the specialize function that is generic is now much
smaller, so users won't need to recompile it for every material.
This commit is contained in:
IceSentry 2025-03-25 14:47:31 -04:00 committed by GitHub
parent db923d6319
commit 65d9a7535a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 73 additions and 97 deletions

View File

@ -336,9 +336,12 @@ pub fn prepare_material_meshlet_meshes_prepass<M: Material>(
shader_defs.push("MESHLET_MESH_MATERIAL_PASS".into()); shader_defs.push("MESHLET_MESH_MATERIAL_PASS".into());
let view_layout = if view_key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) { 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 { } 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) { let fragment_shader = if view_key.contains(MeshPipelineKey::DEFERRED_PREPASS) {
@ -357,7 +360,7 @@ pub fn prepare_material_meshlet_meshes_prepass<M: Material>(
layout: vec![ layout: vec![
view_layout, view_layout,
resource_manager.material_shade_bind_group_layout.clone(), resource_manager.material_shade_bind_group_layout.clone(),
prepass_pipeline.material_layout.clone(), prepass_pipeline.internal.material_layout.clone(),
], ],
push_constant_ranges: vec![], push_constant_ranges: vec![],
vertex: VertexState { vertex: VertexState {

View File

@ -287,6 +287,13 @@ pub fn update_mesh_previous_global_transforms(
#[derive(Resource)] #[derive(Resource)]
pub struct PrepassPipeline<M: Material> { pub struct PrepassPipeline<M: Material> {
pub internal: PrepassPipelineInternal,
pub material_pipeline: MaterialPipeline<M>,
}
/// 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_motion_vectors: BindGroupLayout,
pub view_layout_no_motion_vectors: BindGroupLayout, pub view_layout_no_motion_vectors: BindGroupLayout,
pub mesh_layouts: MeshLayouts, pub mesh_layouts: MeshLayouts,
@ -295,7 +302,6 @@ pub struct PrepassPipeline<M: Material> {
pub prepass_material_fragment_shader: Option<Handle<Shader>>, pub prepass_material_fragment_shader: Option<Handle<Shader>>,
pub deferred_material_vertex_shader: Option<Handle<Shader>>, pub deferred_material_vertex_shader: Option<Handle<Shader>>,
pub deferred_material_fragment_shader: Option<Handle<Shader>>, pub deferred_material_fragment_shader: Option<Handle<Shader>>,
pub material_pipeline: MaterialPipeline<M>,
/// Whether skins will use uniform buffers on account of storage buffers /// Whether skins will use uniform buffers on account of storage buffers
/// being unavailable on this platform. /// being unavailable on this platform.
@ -306,8 +312,6 @@ pub struct PrepassPipeline<M: Material> {
/// Whether binding arrays (a.k.a. bindless textures) are usable on the /// Whether binding arrays (a.k.a. bindless textures) are usable on the
/// current render device. /// current render device.
pub binding_arrays_are_usable: bool, pub binding_arrays_are_usable: bool,
_marker: PhantomData<M>,
} }
impl<M: Material> FromWorld for PrepassPipeline<M> { impl<M: Material> FromWorld for PrepassPipeline<M> {
@ -372,8 +376,7 @@ impl<M: Material> FromWorld for PrepassPipeline<M> {
let depth_clip_control_supported = render_device let depth_clip_control_supported = render_device
.features() .features()
.contains(WgpuFeatures::DEPTH_CLIP_CONTROL); .contains(WgpuFeatures::DEPTH_CLIP_CONTROL);
let internal = PrepassPipelineInternal {
PrepassPipeline {
view_layout_motion_vectors, view_layout_motion_vectors,
view_layout_no_motion_vectors, view_layout_no_motion_vectors,
mesh_layouts: mesh_pipeline.mesh_layouts.clone(), mesh_layouts: mesh_pipeline.mesh_layouts.clone(),
@ -398,11 +401,13 @@ impl<M: Material> FromWorld for PrepassPipeline<M> {
ShaderRef::Path(path) => Some(asset_server.load(path)), ShaderRef::Path(path) => Some(asset_server.load(path)),
}, },
material_layout: M::bind_group_layout(render_device), material_layout: M::bind_group_layout(render_device),
material_pipeline: world.resource::<MaterialPipeline<M>>().clone(),
skins_use_uniform_buffers: skin::skins_use_uniform_buffers(render_device), skins_use_uniform_buffers: skin::skins_use_uniform_buffers(render_device),
depth_clip_control_supported, depth_clip_control_supported,
binding_arrays_are_usable: binding_arrays_are_usable(render_device, render_adapter), binding_arrays_are_usable: binding_arrays_are_usable(render_device, render_adapter),
_marker: PhantomData, };
PrepassPipeline {
internal,
material_pipeline: world.resource::<MaterialPipeline<M>>().clone(),
} }
} }
} }
@ -418,15 +423,38 @@ where
key: Self::Key, key: Self::Key,
layout: &MeshVertexBufferLayoutRef, layout: &MeshVertexBufferLayoutRef,
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> { ) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
let mut bind_group_layouts = vec![if key let mut shader_defs = Vec::new();
.mesh_key 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<ShaderDefVal>,
layout: &MeshVertexBufferLayoutRef,
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
let mut shader_defs = shader_defs;
let mut bind_group_layouts = vec![if mesh_key
.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) .contains(MeshPipelineKey::MOTION_VECTOR_PREPASS)
{ {
self.view_layout_motion_vectors.clone() self.view_layout_motion_vectors.clone()
} else { } else {
self.view_layout_no_motion_vectors.clone() self.view_layout_no_motion_vectors.clone()
}]; }];
let mut shader_defs = Vec::new();
let mut vertex_attributes = Vec::new(); let mut vertex_attributes = Vec::new();
// Let the shader code know that it's running in a prepass pipeline. // 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. // 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. // The main limitation right now is that bind group order is hardcoded in shaders.
bind_group_layouts.push(self.material_layout.clone()); bind_group_layouts.push(self.material_layout.clone());
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
shader_defs.push("WEBGL2".into()); shader_defs.push("WEBGL2".into());
shader_defs.push("VERTEX_OUTPUT_INSTANCE_INDEX".into()); shader_defs.push("VERTEX_OUTPUT_INSTANCE_INDEX".into());
if mesh_key.contains(MeshPipelineKey::DEPTH_PREPASS) {
if key.mesh_key.contains(MeshPipelineKey::DEPTH_PREPASS) {
shader_defs.push("DEPTH_PREPASS".into()); shader_defs.push("DEPTH_PREPASS".into());
} }
if mesh_key.contains(MeshPipelineKey::MAY_DISCARD) {
if key.mesh_key.contains(MeshPipelineKey::MAY_DISCARD) {
shader_defs.push("MAY_DISCARD".into()); shader_defs.push("MAY_DISCARD".into());
} }
let blend_key = mesh_key.intersection(MeshPipelineKey::BLEND_RESERVED_BITS);
let blend_key = key
.mesh_key
.intersection(MeshPipelineKey::BLEND_RESERVED_BITS);
if blend_key == MeshPipelineKey::BLEND_PREMULTIPLIED_ALPHA { if blend_key == MeshPipelineKey::BLEND_PREMULTIPLIED_ALPHA {
shader_defs.push("BLEND_PREMULTIPLIED_ALPHA".into()); shader_defs.push("BLEND_PREMULTIPLIED_ALPHA".into());
} }
if blend_key == MeshPipelineKey::BLEND_ALPHA { if blend_key == MeshPipelineKey::BLEND_ALPHA {
shader_defs.push("BLEND_ALPHA".into()); shader_defs.push("BLEND_ALPHA".into());
} }
if layout.0.contains(Mesh::ATTRIBUTE_POSITION) { if layout.0.contains(Mesh::ATTRIBUTE_POSITION) {
shader_defs.push("VERTEX_POSITIONS".into()); shader_defs.push("VERTEX_POSITIONS".into());
vertex_attributes.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0)); 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, // 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. // or emulated by setting depth in the fragment shader for GPUs that don't support it natively.
let emulate_unclipped_depth = key let emulate_unclipped_depth = mesh_key.contains(MeshPipelineKey::UNCLIPPED_DEPTH_ORTHO)
.mesh_key
.contains(MeshPipelineKey::UNCLIPPED_DEPTH_ORTHO)
&& !self.depth_clip_control_supported; && !self.depth_clip_control_supported;
if emulate_unclipped_depth { if emulate_unclipped_depth {
shader_defs.push("UNCLIPPED_DEPTH_ORTHO_EMULATION".into()); shader_defs.push("UNCLIPPED_DEPTH_ORTHO_EMULATION".into());
@ -482,36 +499,28 @@ where
// https://github.com/bevyengine/bevy/pull/8877 // https://github.com/bevyengine/bevy/pull/8877
shader_defs.push("PREPASS_FRAGMENT".into()); shader_defs.push("PREPASS_FRAGMENT".into());
} }
let unclipped_depth = key let unclipped_depth = mesh_key.contains(MeshPipelineKey::UNCLIPPED_DEPTH_ORTHO)
.mesh_key
.contains(MeshPipelineKey::UNCLIPPED_DEPTH_ORTHO)
&& self.depth_clip_control_supported; && self.depth_clip_control_supported;
if layout.0.contains(Mesh::ATTRIBUTE_UV_0) { if layout.0.contains(Mesh::ATTRIBUTE_UV_0) {
shader_defs.push("VERTEX_UVS".into()); shader_defs.push("VERTEX_UVS".into());
shader_defs.push("VERTEX_UVS_A".into()); shader_defs.push("VERTEX_UVS_A".into());
vertex_attributes.push(Mesh::ATTRIBUTE_UV_0.at_shader_location(1)); vertex_attributes.push(Mesh::ATTRIBUTE_UV_0.at_shader_location(1));
} }
if layout.0.contains(Mesh::ATTRIBUTE_UV_1) { if layout.0.contains(Mesh::ATTRIBUTE_UV_1) {
shader_defs.push("VERTEX_UVS".into()); shader_defs.push("VERTEX_UVS".into());
shader_defs.push("VERTEX_UVS_B".into()); shader_defs.push("VERTEX_UVS_B".into());
vertex_attributes.push(Mesh::ATTRIBUTE_UV_1.at_shader_location(2)); vertex_attributes.push(Mesh::ATTRIBUTE_UV_1.at_shader_location(2));
} }
if mesh_key.contains(MeshPipelineKey::NORMAL_PREPASS) {
if key.mesh_key.contains(MeshPipelineKey::NORMAL_PREPASS) {
shader_defs.push("NORMAL_PREPASS".into()); shader_defs.push("NORMAL_PREPASS".into());
} }
if mesh_key.intersects(MeshPipelineKey::NORMAL_PREPASS | MeshPipelineKey::DEFERRED_PREPASS)
if key
.mesh_key
.intersects(MeshPipelineKey::NORMAL_PREPASS | MeshPipelineKey::DEFERRED_PREPASS)
{ {
shader_defs.push("NORMAL_PREPASS_OR_DEFERRED_PREPASS".into()); shader_defs.push("NORMAL_PREPASS_OR_DEFERRED_PREPASS".into());
if layout.0.contains(Mesh::ATTRIBUTE_NORMAL) { if layout.0.contains(Mesh::ATTRIBUTE_NORMAL) {
shader_defs.push("VERTEX_NORMALS".into()); shader_defs.push("VERTEX_NORMALS".into());
vertex_attributes.push(Mesh::ATTRIBUTE_NORMAL.at_shader_location(3)); 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!( warn!(
"The default normal prepass expects the mesh to have vertex normal attributes." "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)); vertex_attributes.push(Mesh::ATTRIBUTE_TANGENT.at_shader_location(4));
} }
} }
if mesh_key
if key
.mesh_key
.intersects(MeshPipelineKey::MOTION_VECTOR_PREPASS | MeshPipelineKey::DEFERRED_PREPASS) .intersects(MeshPipelineKey::MOTION_VECTOR_PREPASS | MeshPipelineKey::DEFERRED_PREPASS)
{ {
shader_defs.push("MOTION_VECTOR_PREPASS_OR_DEFERRED_PREPASS".into()); shader_defs.push("MOTION_VECTOR_PREPASS_OR_DEFERRED_PREPASS".into());
} }
if mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS) {
if key.mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS) {
shader_defs.push("DEFERRED_PREPASS".into()); shader_defs.push("DEFERRED_PREPASS".into());
} }
if mesh_key.contains(MeshPipelineKey::LIGHTMAPPED) {
if key.mesh_key.contains(MeshPipelineKey::LIGHTMAPPED) {
shader_defs.push("LIGHTMAP".into()); shader_defs.push("LIGHTMAP".into());
} }
if key if mesh_key.contains(MeshPipelineKey::LIGHTMAP_BICUBIC_SAMPLING) {
.mesh_key
.contains(MeshPipelineKey::LIGHTMAP_BICUBIC_SAMPLING)
{
shader_defs.push("LIGHTMAP_BICUBIC_SAMPLING".into()); shader_defs.push("LIGHTMAP_BICUBIC_SAMPLING".into());
} }
if layout.0.contains(Mesh::ATTRIBUTE_COLOR) { if layout.0.contains(Mesh::ATTRIBUTE_COLOR) {
shader_defs.push("VERTEX_COLORS".into()); shader_defs.push("VERTEX_COLORS".into());
vertex_attributes.push(Mesh::ATTRIBUTE_COLOR.at_shader_location(7)); vertex_attributes.push(Mesh::ATTRIBUTE_COLOR.at_shader_location(7));
} }
if mesh_key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) {
if key
.mesh_key
.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS)
{
shader_defs.push("MOTION_VECTOR_PREPASS".into()); shader_defs.push("MOTION_VECTOR_PREPASS".into());
} }
if mesh_key.contains(MeshPipelineKey::HAS_PREVIOUS_SKIN) {
if key.mesh_key.contains(MeshPipelineKey::HAS_PREVIOUS_SKIN) {
shader_defs.push("HAS_PREVIOUS_SKIN".into()); shader_defs.push("HAS_PREVIOUS_SKIN".into());
} }
if mesh_key.contains(MeshPipelineKey::HAS_PREVIOUS_MORPH) {
if key.mesh_key.contains(MeshPipelineKey::HAS_PREVIOUS_MORPH) {
shader_defs.push("HAS_PREVIOUS_MORPH".into()); 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 { if self.binding_arrays_are_usable {
shader_defs.push("MULTIPLE_LIGHTMAPS_IN_ARRAY".into()); shader_defs.push("MULTIPLE_LIGHTMAPS_IN_ARRAY".into());
} }
if mesh_key.contains(MeshPipelineKey::VISIBILITY_RANGE_DITHER) {
if key
.mesh_key
.contains(MeshPipelineKey::VISIBILITY_RANGE_DITHER)
{
shader_defs.push("VISIBILITY_RANGE_DITHER".into()); shader_defs.push("VISIBILITY_RANGE_DITHER".into());
} }
if mesh_key.intersects(
if key.mesh_key.intersects(
MeshPipelineKey::NORMAL_PREPASS MeshPipelineKey::NORMAL_PREPASS
| MeshPipelineKey::MOTION_VECTOR_PREPASS | MeshPipelineKey::MOTION_VECTOR_PREPASS
| MeshPipelineKey::DEFERRED_PREPASS, | MeshPipelineKey::DEFERRED_PREPASS,
) { ) {
shader_defs.push("PREPASS_FRAGMENT".into()); shader_defs.push("PREPASS_FRAGMENT".into());
} }
let bind_group = setup_morph_and_skinning_defs( let bind_group = setup_morph_and_skinning_defs(
&self.mesh_layouts, &self.mesh_layouts,
layout, layout,
5, 5,
&key.mesh_key, &mesh_key,
&mut shader_defs, &mut shader_defs,
&mut vertex_attributes, &mut vertex_attributes,
self.skins_use_uniform_buffers, self.skins_use_uniform_buffers,
); );
bind_group_layouts.insert(1, bind_group); bind_group_layouts.insert(1, bind_group);
let vertex_buffer_layout = layout.0.get_layout(&vertex_attributes)?; 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 // Setup prepass fragment targets - normals in slot 0 (or None if not needed), motion vectors in slot 1
let mut targets = prepass_target_descriptors( let mut targets = prepass_target_descriptors(
key.mesh_key.contains(MeshPipelineKey::NORMAL_PREPASS), mesh_key.contains(MeshPipelineKey::NORMAL_PREPASS),
key.mesh_key mesh_key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS),
.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS), mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS),
key.mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS),
); );
if targets.iter().all(Option::is_none) { if targets.iter().all(Option::is_none) {
@ -619,12 +599,12 @@ where
// prepass shader, or we are emulating unclipped depth in the fragment shader. // prepass shader, or we are emulating unclipped depth in the fragment shader.
let fragment_required = !targets.is_empty() let fragment_required = !targets.is_empty()
|| emulate_unclipped_depth || emulate_unclipped_depth
|| (key.mesh_key.contains(MeshPipelineKey::MAY_DISCARD) || (mesh_key.contains(MeshPipelineKey::MAY_DISCARD)
&& self.prepass_material_fragment_shader.is_some()); && self.prepass_material_fragment_shader.is_some());
let fragment = fragment_required.then(|| { let fragment = fragment_required.then(|| {
// Use the fragment shader from the material // 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() { match self.deferred_material_fragment_shader.clone() {
Some(frag_shader_handle) => frag_shader_handle, Some(frag_shader_handle) => frag_shader_handle,
_ => PREPASS_SHADER_HANDLE, _ => PREPASS_SHADER_HANDLE,
@ -645,7 +625,7 @@ where
}); });
// Use the vertex shader from the material if present // 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 { if let Some(handle) = &self.deferred_material_vertex_shader {
handle.clone() handle.clone()
} else { } else {
@ -656,8 +636,7 @@ where
} else { } else {
PREPASS_SHADER_HANDLE PREPASS_SHADER_HANDLE
}; };
let descriptor = RenderPipelineDescriptor {
let mut descriptor = RenderPipelineDescriptor {
vertex: VertexState { vertex: VertexState {
shader: vert_shader_handle, shader: vert_shader_handle,
entry_point: "vertex".into(), entry_point: "vertex".into(),
@ -667,7 +646,7 @@ where
fragment, fragment,
layout: bind_group_layouts, layout: bind_group_layouts,
primitive: PrimitiveState { primitive: PrimitiveState {
topology: key.mesh_key.primitive_topology(), topology: mesh_key.primitive_topology(),
strip_index_format: None, strip_index_format: None,
front_face: FrontFace::Ccw, front_face: FrontFace::Ccw,
cull_mode: None, cull_mode: None,
@ -692,7 +671,7 @@ where
}, },
}), }),
multisample: MultisampleState { multisample: MultisampleState {
count: key.mesh_key.msaa_samples(), count: mesh_key.msaa_samples(),
mask: !0, mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
}, },
@ -700,12 +679,6 @@ where
label: Some("prepass_pipeline".into()), label: Some("prepass_pipeline".into()),
zero_initialize_workgroup_memory: false, 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) Ok(descriptor)
} }
} }
@ -790,7 +763,7 @@ pub fn prepare_prepass_view_bind_group<M: Material>(
) { ) {
prepass_view_bind_group.no_motion_vectors = Some(render_device.create_bind_group( prepass_view_bind_group.no_motion_vectors = Some(render_device.create_bind_group(
"prepass_view_no_motion_vectors_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(( &BindGroupEntries::with_indices((
(0, view_binding.clone()), (0, view_binding.clone()),
(1, globals_binding.clone()), (1, globals_binding.clone()),
@ -801,7 +774,7 @@ pub fn prepare_prepass_view_bind_group<M: Material>(
if let Some(previous_view_uniforms_binding) = previous_view_uniforms.uniforms.binding() { 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_bind_group.motion_vectors = Some(render_device.create_bind_group(
"prepass_view_motion_vectors_bind_group", "prepass_view_motion_vectors_bind_group",
&prepass_pipeline.view_layout_motion_vectors, &prepass_pipeline.internal.view_layout_motion_vectors,
&BindGroupEntries::with_indices(( &BindGroupEntries::with_indices((
(0, view_binding), (0, view_binding),
(1, globals_binding), (1, globals_binding),