Skip to main content

bevy_pbr/prepass/
mod.rs

1mod prepass_bindings;
2
3use crate::{
4    alpha_mode_pipeline_key, binding_arrays_are_usable, buffer_layout,
5    collect_meshes_for_gpu_building, init_material_pipeline, set_mesh_motion_vector_flags,
6    setup_morph_and_skinning_defs, skin, DeferredAlphaMaskDrawFunction, DeferredFragmentShader,
7    DeferredOpaqueDrawFunction, DeferredVertexShader, DrawMesh, MaterialPipeline, MeshLayouts,
8    MeshPipeline, MeshPipelineKey, PreparedMaterial, PrepassAlphaMaskDrawFunction,
9    PrepassFragmentShader, PrepassOpaqueDepthOnlyDrawFunction, PrepassOpaqueDrawFunction,
10    PrepassVertexShader, RenderLightmaps, RenderMaterialInstances, RenderMeshInstanceFlags,
11    RenderMeshInstances, SetMaterialBindGroup, SetMeshBindGroup, ShadowView,
12};
13use bevy_app::{App, Plugin, PreUpdate};
14use bevy_asset::{embedded_asset, load_embedded_asset, AssetServer, Handle};
15use bevy_camera::{Camera, Camera3d};
16use bevy_core_pipeline::{core_3d::CORE_3D_DEPTH_FORMAT, deferred::*, prepass::*};
17use bevy_ecs::{
18    prelude::*,
19    system::{
20        lifetimeless::{Read, SRes},
21        SystemParam, SystemParamItem, SystemState,
22    },
23};
24use bevy_material::{
25    key::{ErasedMaterialPipelineKey, ErasedMeshPipelineKey},
26    AlphaMode, MaterialProperties, OpaqueRendererMethod, RenderPhaseType,
27};
28use bevy_math::{Affine3A, Mat4, Vec4};
29use bevy_mesh::{Mesh, Mesh3d, MeshVertexBufferLayoutRef};
30use bevy_render::{
31    batching::gpu_preprocessing::GpuPreprocessingSupport,
32    camera::{DirtySpecializations, PendingQueues},
33    globals::{GlobalsBuffer, GlobalsUniform},
34    mesh::{allocator::MeshAllocator, RenderMesh},
35    render_asset::{prepare_assets, RenderAssets},
36    render_phase::*,
37    render_resource::{binding_types::uniform_buffer, *},
38    renderer::{RenderAdapter, RenderDevice, RenderQueue},
39    sync_world::RenderEntity,
40    view::{
41        ExtractedView, Msaa, RenderVisibilityRanges, RenderVisibleEntities, RetainedViewEntity,
42        ViewUniform, ViewUniformOffset, ViewUniforms, VISIBILITY_RANGES_STORAGE_BUFFER_COUNT,
43    },
44    Extract, ExtractSchedule, GpuResourceAppExt, Render, RenderApp, RenderDebugFlags,
45    RenderStartup, RenderSystems,
46};
47use bevy_shader::{load_shader_library, Shader, ShaderDefVal};
48use bevy_transform::prelude::GlobalTransform;
49use core::any::TypeId;
50pub use prepass_bindings::*;
51use tracing::{error, warn};
52
53#[cfg(feature = "meshlet")]
54use crate::meshlet::{
55    prepare_material_meshlet_meshes_prepass, queue_material_meshlet_meshes, InstanceManager,
56    MeshletMesh3d,
57};
58
59use alloc::sync::Arc;
60use bevy_derive::{Deref, DerefMut};
61use bevy_ecs::{change_detection::Tick, system::SystemChangeTick};
62use bevy_platform::collections::{HashMap, HashSet};
63use bevy_platform::hash::FixedHasher;
64use bevy_render::{
65    erased_render_asset::ErasedRenderAssets,
66    sync_world::{MainEntity, MainEntityHashMap},
67    RenderSystems::{PrepareAssets, PrepareResources},
68};
69use bevy_utils::default;
70
71/// Sets up everything required to use the prepass pipeline.
72///
73/// This does not add the actual prepasses, see [`PrepassPlugin`] for that.
74pub struct PrepassPipelinePlugin;
75
76impl Plugin for PrepassPipelinePlugin {
77    fn build(&self, app: &mut App) {
78        embedded_asset!(app, "prepass.wgsl");
79
80        load_shader_library!(app, "prepass_bindings.wgsl");
81        load_shader_library!(app, "prepass_utils.wgsl");
82        load_shader_library!(app, "prepass_io.wgsl");
83
84        let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
85            return;
86        };
87
88        render_app
89            .add_systems(
90                RenderStartup,
91                (
92                    init_prepass_pipeline.after(init_material_pipeline),
93                    init_prepass_view_bind_group,
94                )
95                    .chain(),
96            )
97            .add_systems(
98                Render,
99                prepare_prepass_view_bind_group.in_set(RenderSystems::PrepareBindGroups),
100            )
101            .init_gpu_resource::<SpecializedMeshPipelines<PrepassPipelineSpecializer>>();
102    }
103}
104
105/// Sets up the prepasses for a material.
106///
107/// This depends on the [`PrepassPipelinePlugin`].
108pub struct PrepassPlugin {
109    /// Debugging flags that can optionally be set when constructing the renderer.
110    pub debug_flags: RenderDebugFlags,
111}
112
113impl PrepassPlugin {
114    /// Creates a new [`PrepassPlugin`] with the given debug flags.
115    pub fn new(debug_flags: RenderDebugFlags) -> Self {
116        PrepassPlugin { debug_flags }
117    }
118}
119
120impl Plugin for PrepassPlugin {
121    fn build(&self, app: &mut App) {
122        let no_prepass_plugin_loaded = app
123            .world()
124            .get_resource::<AnyPrepassPluginLoaded>()
125            .is_none();
126
127        if no_prepass_plugin_loaded {
128            app.insert_resource(AnyPrepassPluginLoaded)
129                // At the start of each frame, last frame's GlobalTransforms become this frame's PreviousGlobalTransforms
130                // and last frame's view projection matrices become this frame's PreviousViewProjections
131                .add_systems(
132                    PreUpdate,
133                    (
134                        update_mesh_previous_global_transforms,
135                        update_previous_view_data,
136                    ),
137                )
138                .add_plugins((
139                    BinnedRenderPhasePlugin::<Opaque3dPrepass, MeshPipeline>::new(self.debug_flags),
140                    BinnedRenderPhasePlugin::<AlphaMask3dPrepass, MeshPipeline>::new(
141                        self.debug_flags,
142                    ),
143                ));
144        }
145
146        let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
147            return;
148        };
149
150        if no_prepass_plugin_loaded {
151            render_app
152                .add_systems(ExtractSchedule, extract_camera_previous_view_data)
153                .add_systems(
154                    Render,
155                    prepare_previous_view_uniforms.in_set(PrepareResources),
156                );
157        }
158
159        render_app
160            .init_gpu_resource::<ViewKeyPrepassCache>()
161            .init_gpu_resource::<SpecializedPrepassMaterialPipelineCache>()
162            .init_gpu_resource::<PendingPrepassMeshMaterialQueues>()
163            .add_render_command::<Opaque3dPrepass, DrawPrepass>()
164            .add_render_command::<Opaque3dPrepass, DrawDepthOnlyPrepass>()
165            .add_render_command::<AlphaMask3dPrepass, DrawPrepass>()
166            .add_render_command::<Opaque3dDeferred, DrawPrepass>()
167            .add_render_command::<AlphaMask3dDeferred, DrawPrepass>()
168            .add_systems(
169                Render,
170                (
171                    check_prepass_views_need_specialization.in_set(PrepareAssets),
172                    specialize_prepass_material_meshes
173                        .in_set(RenderSystems::PrepareMeshes)
174                        .after(prepare_assets::<RenderMesh>)
175                        .after(collect_meshes_for_gpu_building)
176                        .after(set_mesh_motion_vector_flags),
177                    queue_prepass_material_meshes.in_set(RenderSystems::QueueMeshes),
178                ),
179            );
180
181        #[cfg(feature = "meshlet")]
182        render_app.add_systems(
183            Render,
184            prepare_material_meshlet_meshes_prepass
185                .in_set(RenderSystems::QueueMeshes)
186                .before(queue_material_meshlet_meshes)
187                .run_if(resource_exists::<InstanceManager>),
188        );
189    }
190}
191
192#[derive(Resource)]
193struct AnyPrepassPluginLoaded;
194
195pub fn update_previous_view_data(
196    mut commands: Commands,
197    query: Query<(Entity, &Camera, &GlobalTransform), Or<(With<Camera3d>, With<ShadowView>)>>,
198) {
199    for (entity, camera, camera_transform) in &query {
200        let world_from_view = camera_transform.affine();
201        let view_from_world = Mat4::from(world_from_view.inverse());
202        let view_from_clip = camera.clip_from_view().inverse();
203
204        commands.entity(entity).try_insert(PreviousViewData {
205            view_from_world,
206            clip_from_world: camera.clip_from_view() * view_from_world,
207            clip_from_view: camera.clip_from_view(),
208            world_from_clip: Mat4::from(world_from_view) * view_from_clip,
209            view_from_clip,
210        });
211    }
212}
213
214#[derive(Component, PartialEq, Clone, Default)]
215pub struct PreviousGlobalTransform(pub Affine3A);
216
217#[cfg(not(feature = "meshlet"))]
218type PreviousMeshFilter = With<Mesh3d>;
219#[cfg(feature = "meshlet")]
220type PreviousMeshFilter = Or<(With<Mesh3d>, With<MeshletMesh3d>)>;
221
222pub fn update_mesh_previous_global_transforms(
223    mut commands: Commands,
224    views: Query<&Camera, Or<(With<Camera3d>, With<ShadowView>)>>,
225    new_meshes: Query<
226        (Entity, &GlobalTransform),
227        (PreviousMeshFilter, Without<PreviousGlobalTransform>),
228    >,
229    mut meshes: Query<(Ref<GlobalTransform>, &mut PreviousGlobalTransform), PreviousMeshFilter>,
230) {
231    if !views.iter().any(|camera| camera.is_active) {
232        return;
233    }
234
235    for (entity, transform) in &new_meshes {
236        let new_previous_transform = PreviousGlobalTransform(transform.affine());
237        commands.entity(entity).try_insert(new_previous_transform);
238    }
239    meshes.par_iter_mut().for_each(|(transform, mut previous)| {
240        if transform.is_changed_after(previous.last_changed()) {
241            *previous = PreviousGlobalTransform(transform.affine());
242        }
243    });
244}
245
246#[derive(Resource, Clone)]
247pub struct PrepassPipeline {
248    pub view_layout_motion_vectors: BindGroupLayoutDescriptor,
249    pub view_layout_no_motion_vectors: BindGroupLayoutDescriptor,
250    pub mesh_layouts: MeshLayouts,
251    pub empty_layout: BindGroupLayoutDescriptor,
252    pub default_prepass_shader: Handle<Shader>,
253
254    /// Whether skins will use uniform buffers on account of storage buffers
255    /// being unavailable on this platform.
256    pub skins_use_uniform_buffers: bool,
257
258    pub depth_clip_control_supported: bool,
259
260    /// Whether binding arrays (a.k.a. bindless textures) are usable on the
261    /// current render device.
262    pub binding_arrays_are_usable: bool,
263    pub material_pipeline: MaterialPipeline,
264}
265
266pub fn init_prepass_pipeline(
267    mut commands: Commands,
268    render_device: Res<RenderDevice>,
269    render_adapter: Res<RenderAdapter>,
270    mesh_pipeline: Res<MeshPipeline>,
271    material_pipeline: Res<MaterialPipeline>,
272    asset_server: Res<AssetServer>,
273) {
274    let visibility_ranges_buffer_binding_type =
275        render_device.get_supported_read_only_binding_type(VISIBILITY_RANGES_STORAGE_BUFFER_COUNT);
276
277    let view_layout_motion_vectors = BindGroupLayoutDescriptor::new(
278        "prepass_view_layout_motion_vectors",
279        &BindGroupLayoutEntries::with_indices(
280            ShaderStages::VERTEX_FRAGMENT,
281            (
282                // View
283                (0, uniform_buffer::<ViewUniform>(true)),
284                // Globals
285                (1, uniform_buffer::<GlobalsUniform>(false)),
286                // PreviousViewUniforms
287                (2, uniform_buffer::<PreviousViewData>(true)),
288                // VisibilityRanges
289                (
290                    14,
291                    buffer_layout(
292                        visibility_ranges_buffer_binding_type,
293                        false,
294                        Some(Vec4::min_size()),
295                    )
296                    .visibility(ShaderStages::VERTEX),
297                ),
298            ),
299        ),
300    );
301
302    let view_layout_no_motion_vectors = BindGroupLayoutDescriptor::new(
303        "prepass_view_layout_no_motion_vectors",
304        &BindGroupLayoutEntries::with_indices(
305            ShaderStages::VERTEX_FRAGMENT,
306            (
307                // View
308                (0, uniform_buffer::<ViewUniform>(true)),
309                // Globals
310                (1, uniform_buffer::<GlobalsUniform>(false)),
311                // VisibilityRanges
312                (
313                    14,
314                    buffer_layout(
315                        visibility_ranges_buffer_binding_type,
316                        false,
317                        Some(Vec4::min_size()),
318                    )
319                    .visibility(ShaderStages::VERTEX),
320                ),
321            ),
322        ),
323    );
324
325    let depth_clip_control_supported = render_device
326        .features()
327        .contains(WgpuFeatures::DEPTH_CLIP_CONTROL);
328    commands.insert_resource(PrepassPipeline {
329        view_layout_motion_vectors,
330        view_layout_no_motion_vectors,
331        mesh_layouts: mesh_pipeline.mesh_layouts.clone(),
332        default_prepass_shader: load_embedded_asset!(asset_server.as_ref(), "prepass.wgsl"),
333        skins_use_uniform_buffers: skin::skins_use_uniform_buffers(&render_device.limits()),
334        depth_clip_control_supported,
335        binding_arrays_are_usable: binding_arrays_are_usable(&render_device, &render_adapter),
336        empty_layout: BindGroupLayoutDescriptor::new("prepass_empty_layout", &[]),
337        material_pipeline: material_pipeline.clone(),
338    });
339}
340
341pub struct PrepassPipelineSpecializer {
342    pub pipeline: PrepassPipeline,
343    pub properties: Arc<MaterialProperties>,
344}
345
346impl SpecializedMeshPipeline for PrepassPipelineSpecializer {
347    type Key = ErasedMaterialPipelineKey;
348
349    fn specialize(
350        &self,
351        key: Self::Key,
352        layout: &MeshVertexBufferLayoutRef,
353    ) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
354        let mut shader_defs = Vec::new();
355        if self.properties.bindless {
356            shader_defs.push("BINDLESS".into());
357        }
358        let mut descriptor = self.pipeline.specialize(
359            key.mesh_key.downcast(),
360            shader_defs,
361            layout,
362            &self.properties,
363        )?;
364
365        // This is a bit risky because it's possible to change something that would
366        // break the prepass but be fine in the main pass.
367        // Since this api is pretty low-level it doesn't matter that much, but it is a potential issue.
368        if let Some(specialize) = self.properties.user_specialize {
369            specialize(
370                &self.pipeline.material_pipeline,
371                &mut descriptor,
372                layout,
373                key,
374            )?;
375        }
376
377        Ok(descriptor)
378    }
379}
380
381fn is_depth_only_opaque_prepass(mesh_key: MeshPipelineKey) -> bool {
382    mesh_key.intersection(MeshPipelineKey::ALL_PREPASS_BITS) == MeshPipelineKey::DEPTH_PREPASS
383}
384
385impl PrepassPipeline {
386    fn specialize(
387        &self,
388        mesh_key: MeshPipelineKey,
389        shader_defs: Vec<ShaderDefVal>,
390        layout: &MeshVertexBufferLayoutRef,
391        material_properties: &MaterialProperties,
392    ) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
393        let mut shader_defs = shader_defs;
394        let mut bind_group_layouts = vec![
395            if mesh_key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) {
396                self.view_layout_motion_vectors.clone()
397            } else {
398                self.view_layout_no_motion_vectors.clone()
399            },
400            self.empty_layout.clone(),
401        ];
402        let mut vertex_attributes = Vec::new();
403
404        // Let the shader code know that it's running in a prepass pipeline.
405        // (PBR code will use this to detect that it's running in deferred mode,
406        // since that's the only time it gets called from a prepass pipeline.)
407        shader_defs.push("PREPASS_PIPELINE".into());
408
409        shader_defs.push(ShaderDefVal::UInt(
410            "MATERIAL_BIND_GROUP".into(),
411            crate::MATERIAL_BIND_GROUP_INDEX as u32,
412        ));
413        // For directional light shadow map views, use unclipped depth via either the native GPU feature,
414        // or emulated by setting depth in the fragment shader for GPUs that don't support it natively.
415        let emulate_unclipped_depth = mesh_key.contains(MeshPipelineKey::UNCLIPPED_DEPTH_ORTHO)
416            && !self.depth_clip_control_supported;
417        if is_depth_only_opaque_prepass(mesh_key) && !emulate_unclipped_depth {
418            bind_group_layouts.push(self.empty_layout.clone());
419        } else {
420            bind_group_layouts.push(
421                material_properties
422                    .material_layout
423                    .as_ref()
424                    .unwrap()
425                    .clone(),
426            );
427        }
428        #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
429        shader_defs.push("WEBGL2".into());
430        shader_defs.push("VERTEX_OUTPUT_INSTANCE_INDEX".into());
431        let view_projection = mesh_key.intersection(MeshPipelineKey::VIEW_PROJECTION_RESERVED_BITS);
432        if view_projection == MeshPipelineKey::VIEW_PROJECTION_NONSTANDARD {
433            shader_defs.push("VIEW_PROJECTION_NONSTANDARD".into());
434        } else if view_projection == MeshPipelineKey::VIEW_PROJECTION_PERSPECTIVE {
435            shader_defs.push("VIEW_PROJECTION_PERSPECTIVE".into());
436        } else if view_projection == MeshPipelineKey::VIEW_PROJECTION_ORTHOGRAPHIC {
437            shader_defs.push("VIEW_PROJECTION_ORTHOGRAPHIC".into());
438        }
439        if mesh_key.contains(MeshPipelineKey::DEPTH_PREPASS) {
440            shader_defs.push("DEPTH_PREPASS".into());
441        }
442        if mesh_key.contains(MeshPipelineKey::MAY_DISCARD) {
443            shader_defs.push("MAY_DISCARD".into());
444        }
445        let blend_key = mesh_key.intersection(MeshPipelineKey::BLEND_RESERVED_BITS);
446        if blend_key == MeshPipelineKey::BLEND_PREMULTIPLIED_ALPHA {
447            shader_defs.push("BLEND_PREMULTIPLIED_ALPHA".into());
448        }
449        if blend_key == MeshPipelineKey::BLEND_ALPHA {
450            shader_defs.push("BLEND_ALPHA".into());
451        }
452        if layout.0.contains(Mesh::ATTRIBUTE_POSITION) {
453            shader_defs.push("VERTEX_POSITIONS".into());
454            vertex_attributes.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0));
455        }
456        if emulate_unclipped_depth {
457            shader_defs.push("UNCLIPPED_DEPTH_ORTHO_EMULATION".into());
458            // PERF: This line forces the "prepass fragment shader" to always run in
459            // common scenarios like "directional light calculation". Doing so resolves
460            // a pretty nasty depth clamping bug, but it also feels a bit excessive.
461            // We should try to find a way to resolve this without forcing the fragment
462            // shader to run.
463            // https://github.com/bevyengine/bevy/pull/8877
464            shader_defs.push("PREPASS_FRAGMENT".into());
465        }
466        let unclipped_depth = mesh_key.contains(MeshPipelineKey::UNCLIPPED_DEPTH_ORTHO)
467            && self.depth_clip_control_supported;
468        if layout.0.contains(Mesh::ATTRIBUTE_UV_0) {
469            shader_defs.push("VERTEX_UVS".into());
470            shader_defs.push("VERTEX_UVS_A".into());
471            vertex_attributes.push(Mesh::ATTRIBUTE_UV_0.at_shader_location(1));
472        }
473        if layout.0.contains(Mesh::ATTRIBUTE_UV_1) {
474            shader_defs.push("VERTEX_UVS".into());
475            shader_defs.push("VERTEX_UVS_B".into());
476            vertex_attributes.push(Mesh::ATTRIBUTE_UV_1.at_shader_location(2));
477        }
478        if mesh_key.contains(MeshPipelineKey::NORMAL_PREPASS) {
479            shader_defs.push("NORMAL_PREPASS".into());
480        }
481        if mesh_key.intersects(MeshPipelineKey::NORMAL_PREPASS | MeshPipelineKey::DEFERRED_PREPASS)
482        {
483            shader_defs.push("NORMAL_PREPASS_OR_DEFERRED_PREPASS".into());
484            if layout.0.contains(Mesh::ATTRIBUTE_NORMAL) {
485                shader_defs.push("VERTEX_NORMALS".into());
486                vertex_attributes.push(Mesh::ATTRIBUTE_NORMAL.at_shader_location(3));
487            } else if mesh_key.contains(MeshPipelineKey::NORMAL_PREPASS) {
488                warn!(
489                    "The default normal prepass expects the mesh to have vertex normal attributes."
490                );
491            }
492            if layout.0.contains(Mesh::ATTRIBUTE_TANGENT) {
493                shader_defs.push("VERTEX_TANGENTS".into());
494                vertex_attributes.push(Mesh::ATTRIBUTE_TANGENT.at_shader_location(4));
495            }
496        }
497        if mesh_key
498            .intersects(MeshPipelineKey::MOTION_VECTOR_PREPASS | MeshPipelineKey::DEFERRED_PREPASS)
499        {
500            shader_defs.push("MOTION_VECTOR_PREPASS_OR_DEFERRED_PREPASS".into());
501        }
502        if mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS) {
503            shader_defs.push("DEFERRED_PREPASS".into());
504        }
505        if mesh_key.contains(MeshPipelineKey::LIGHTMAPPED) {
506            shader_defs.push("LIGHTMAP".into());
507        }
508        if mesh_key.contains(MeshPipelineKey::LIGHTMAP_BICUBIC_SAMPLING) {
509            shader_defs.push("LIGHTMAP_BICUBIC_SAMPLING".into());
510        }
511        if layout.0.contains(Mesh::ATTRIBUTE_COLOR) {
512            shader_defs.push("VERTEX_COLORS".into());
513            vertex_attributes.push(Mesh::ATTRIBUTE_COLOR.at_shader_location(7));
514        }
515        if mesh_key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS) {
516            shader_defs.push("MOTION_VECTOR_PREPASS".into());
517        }
518        if mesh_key.contains(MeshPipelineKey::HAS_PREVIOUS_SKIN) {
519            shader_defs.push("HAS_PREVIOUS_SKIN".into());
520        }
521        if mesh_key.contains(MeshPipelineKey::HAS_PREVIOUS_MORPH) {
522            shader_defs.push("HAS_PREVIOUS_MORPH".into());
523        }
524        if self.binding_arrays_are_usable {
525            shader_defs.push("MULTIPLE_LIGHTMAPS_IN_ARRAY".into());
526        }
527        if mesh_key.contains(MeshPipelineKey::VISIBILITY_RANGE_DITHER) {
528            shader_defs.push("VISIBILITY_RANGE_DITHER".into());
529        }
530        if mesh_key.intersects(
531            MeshPipelineKey::NORMAL_PREPASS
532                | MeshPipelineKey::MOTION_VECTOR_PREPASS
533                | MeshPipelineKey::DEFERRED_PREPASS,
534        ) {
535            shader_defs.push("PREPASS_FRAGMENT".into());
536        }
537        let bind_group = setup_morph_and_skinning_defs(
538            &self.mesh_layouts,
539            layout,
540            5,
541            &mesh_key,
542            &mut shader_defs,
543            &mut vertex_attributes,
544            self.skins_use_uniform_buffers,
545        );
546        bind_group_layouts.insert(2, bind_group);
547        let vertex_buffer_layout = layout.0.get_layout(&vertex_attributes)?;
548        // Setup prepass fragment targets - normals in slot 0 (or None if not needed), motion vectors in slot 1
549        let mut targets = prepass_target_descriptors(
550            mesh_key.contains(MeshPipelineKey::NORMAL_PREPASS),
551            mesh_key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS),
552            mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS),
553        );
554
555        if targets.iter().all(Option::is_none) {
556            // if no targets are required then clear the list, so that no fragment shader is required
557            // (though one may still be used for discarding depth buffer writes)
558            targets.clear();
559        }
560
561        // The fragment shader is only used when the normal prepass or motion vectors prepass
562        // is enabled, the material uses alpha cutoff values and doesn't rely on the standard
563        // prepass shader, or we are emulating unclipped depth in the fragment shader.
564        let fragment_required = !targets.is_empty()
565            || emulate_unclipped_depth
566            || (mesh_key.contains(MeshPipelineKey::MAY_DISCARD)
567                && material_properties
568                    .get_shader(PrepassFragmentShader)
569                    .is_some());
570
571        let fragment = fragment_required.then(|| {
572            // Use the fragment shader from the material
573            let frag_shader_handle = if mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS) {
574                match material_properties.get_shader(DeferredFragmentShader) {
575                    Some(frag_shader_handle) => frag_shader_handle,
576                    None => self.default_prepass_shader.clone(),
577                }
578            } else {
579                match material_properties.get_shader(PrepassFragmentShader) {
580                    Some(frag_shader_handle) => frag_shader_handle,
581                    None => self.default_prepass_shader.clone(),
582                }
583            };
584
585            FragmentState {
586                shader: frag_shader_handle,
587                shader_defs: shader_defs.clone(),
588                targets,
589                ..default()
590            }
591        });
592
593        // Use the vertex shader from the material if present
594        let vert_shader_handle = if mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS) {
595            if let Some(handle) = material_properties.get_shader(DeferredVertexShader) {
596                handle
597            } else {
598                self.default_prepass_shader.clone()
599            }
600        } else if let Some(handle) = material_properties.get_shader(PrepassVertexShader) {
601            handle
602        } else {
603            self.default_prepass_shader.clone()
604        };
605        let descriptor = RenderPipelineDescriptor {
606            vertex: VertexState {
607                shader: vert_shader_handle,
608                shader_defs,
609                buffers: vec![vertex_buffer_layout],
610                ..default()
611            },
612            fragment,
613            layout: bind_group_layouts,
614            primitive: PrimitiveState {
615                topology: mesh_key.primitive_topology(),
616                strip_index_format: mesh_key.strip_index_format(),
617                unclipped_depth,
618                ..default()
619            },
620            depth_stencil: Some(DepthStencilState {
621                format: CORE_3D_DEPTH_FORMAT,
622                depth_write_enabled: Some(true),
623                depth_compare: Some(CompareFunction::GreaterEqual),
624                stencil: StencilState {
625                    front: StencilFaceState::IGNORE,
626                    back: StencilFaceState::IGNORE,
627                    read_mask: 0,
628                    write_mask: 0,
629                },
630                bias: DepthBiasState {
631                    constant: 0,
632                    slope_scale: 0.0,
633                    clamp: 0.0,
634                },
635            }),
636            multisample: MultisampleState {
637                count: mesh_key.msaa_samples(),
638                mask: !0,
639                alpha_to_coverage_enabled: false,
640            },
641            label: Some("prepass_pipeline".into()),
642            ..default()
643        };
644        Ok(descriptor)
645    }
646}
647
648// Extract the render phases for the prepass
649pub fn extract_camera_previous_view_data(
650    mut commands: Commands,
651    cameras_3d: Extract<Query<(RenderEntity, &Camera, Option<&PreviousViewData>), With<Camera3d>>>,
652) {
653    for (entity, camera, maybe_previous_view_data) in cameras_3d.iter() {
654        let mut entity = commands
655            .get_entity(entity)
656            .expect("Camera entity wasn't synced.");
657        if camera.is_active {
658            if let Some(previous_view_data) = maybe_previous_view_data {
659                entity.insert(previous_view_data.clone());
660            }
661        } else {
662            entity.remove::<PreviousViewData>();
663        }
664    }
665}
666
667pub fn prepare_previous_view_uniforms(
668    mut commands: Commands,
669    render_device: Res<RenderDevice>,
670    render_queue: Res<RenderQueue>,
671    mut previous_view_uniforms: ResMut<PreviousViewUniforms>,
672    views: Query<
673        (Entity, &ExtractedView, Option<&PreviousViewData>),
674        Or<(With<Camera3d>, With<ShadowView>)>,
675    >,
676) {
677    let views_iter = views.iter();
678    let view_count = views_iter.len();
679    let Some(mut writer) =
680        previous_view_uniforms
681            .uniforms
682            .get_writer(view_count, &render_device, &render_queue)
683    else {
684        return;
685    };
686
687    for (entity, camera, maybe_previous_view_uniforms) in views_iter {
688        let prev_view_data = match maybe_previous_view_uniforms {
689            Some(previous_view) => previous_view.clone(),
690            None => {
691                let world_from_view = camera.world_from_view.affine();
692                let view_from_world = Mat4::from(world_from_view.inverse());
693                let view_from_clip = camera.clip_from_view.inverse();
694
695                PreviousViewData {
696                    view_from_world,
697                    clip_from_world: camera.clip_from_view * view_from_world,
698                    clip_from_view: camera.clip_from_view,
699                    world_from_clip: Mat4::from(world_from_view) * view_from_clip,
700                    view_from_clip,
701                }
702            }
703        };
704
705        commands.entity(entity).insert(PreviousViewUniformOffset {
706            offset: writer.write(&prev_view_data),
707        });
708    }
709}
710
711#[derive(Resource)]
712pub struct PrepassViewBindGroup {
713    pub motion_vectors: Option<BindGroup>,
714    pub no_motion_vectors: Option<BindGroup>,
715    pub empty_bind_group: BindGroup,
716}
717
718pub fn init_prepass_view_bind_group(
719    mut commands: Commands,
720    render_device: Res<RenderDevice>,
721    pipeline_cache: Res<PipelineCache>,
722    pipeline: Res<PrepassPipeline>,
723) {
724    let empty_bind_group = render_device.create_bind_group(
725        "prepass_view_empty_bind_group",
726        &pipeline_cache.get_bind_group_layout(&pipeline.empty_layout),
727        &[],
728    );
729    commands.insert_resource(PrepassViewBindGroup {
730        motion_vectors: None,
731        no_motion_vectors: None,
732        empty_bind_group,
733    });
734}
735
736pub fn prepare_prepass_view_bind_group(
737    render_device: Res<RenderDevice>,
738    pipeline_cache: Res<PipelineCache>,
739    prepass_pipeline: Res<PrepassPipeline>,
740    view_uniforms: Res<ViewUniforms>,
741    globals_buffer: Res<GlobalsBuffer>,
742    previous_view_uniforms: Res<PreviousViewUniforms>,
743    visibility_ranges: Res<RenderVisibilityRanges>,
744    mut prepass_view_bind_group: ResMut<PrepassViewBindGroup>,
745) {
746    if let (Some(view_binding), Some(globals_binding), Some(visibility_ranges_buffer)) = (
747        view_uniforms.uniforms.binding(),
748        globals_buffer.buffer.binding(),
749        visibility_ranges.buffer().buffer(),
750    ) {
751        prepass_view_bind_group.no_motion_vectors = Some(render_device.create_bind_group(
752            "prepass_view_no_motion_vectors_bind_group",
753            &pipeline_cache.get_bind_group_layout(&prepass_pipeline.view_layout_no_motion_vectors),
754            &BindGroupEntries::with_indices((
755                (0, view_binding.clone()),
756                (1, globals_binding.clone()),
757                (14, visibility_ranges_buffer.as_entire_binding()),
758            )),
759        ));
760
761        if let Some(previous_view_uniforms_binding) = previous_view_uniforms.uniforms.binding() {
762            prepass_view_bind_group.motion_vectors = Some(render_device.create_bind_group(
763                "prepass_view_motion_vectors_bind_group",
764                &pipeline_cache.get_bind_group_layout(&prepass_pipeline.view_layout_motion_vectors),
765                &BindGroupEntries::with_indices((
766                    (0, view_binding),
767                    (1, globals_binding),
768                    (2, previous_view_uniforms_binding),
769                    (14, visibility_ranges_buffer.as_entire_binding()),
770                )),
771            ));
772        }
773    }
774}
775
776/// Stores the [`SpecializedPrepassMaterialViewPipelineCache`] for each view.
777#[derive(Resource, Deref, DerefMut, Default)]
778pub struct SpecializedPrepassMaterialPipelineCache {
779    // view_entity -> view pipeline cache
780    #[deref]
781    map: HashMap<RetainedViewEntity, SpecializedPrepassMaterialViewPipelineCache>,
782}
783
784/// Stores the cached render pipeline ID for each entity in a single view, as
785/// well as the last time it was changed.
786#[derive(Deref, DerefMut, Default)]
787pub struct SpecializedPrepassMaterialViewPipelineCache {
788    // material entity -> (tick, pipeline_id, draw_function)
789    #[deref]
790    map: MainEntityHashMap<(Tick, CachedRenderPipelineId, DrawFunctionId)>,
791}
792
793#[derive(Resource, Deref, DerefMut, Default, Clone)]
794pub struct ViewKeyPrepassCache(HashMap<RetainedViewEntity, MeshPipelineKey>);
795
796pub fn check_prepass_views_need_specialization(
797    mut view_key_cache: ResMut<ViewKeyPrepassCache>,
798    mut dirty_specializations: ResMut<DirtySpecializations>,
799    mut views: Query<(
800        &ExtractedView,
801        &Msaa,
802        Option<&DepthPrepass>,
803        Option<&NormalPrepass>,
804        Option<&MotionVectorPrepass>,
805    )>,
806) {
807    for (view, msaa, depth_prepass, normal_prepass, motion_vector_prepass) in views.iter_mut() {
808        let mut view_key = MeshPipelineKey::from_msaa_samples(msaa.samples());
809        if depth_prepass.is_some() {
810            view_key |= MeshPipelineKey::DEPTH_PREPASS;
811        }
812        if normal_prepass.is_some() {
813            view_key |= MeshPipelineKey::NORMAL_PREPASS;
814        }
815        if motion_vector_prepass.is_some() {
816            view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS;
817        }
818
819        if let Some(current_key) = view_key_cache.get_mut(&view.retained_view_entity) {
820            if *current_key != view_key {
821                view_key_cache.insert(view.retained_view_entity, view_key);
822                dirty_specializations
823                    .views
824                    .insert(view.retained_view_entity);
825            }
826        } else {
827            view_key_cache.insert(view.retained_view_entity, view_key);
828            dirty_specializations
829                .views
830                .insert(view.retained_view_entity);
831        }
832    }
833}
834
835pub(crate) struct PrepassSpecializationWorkItem {
836    visible_entity: MainEntity,
837    retained_view_entity: RetainedViewEntity,
838    mesh_key: MeshPipelineKey,
839    layout: MeshVertexBufferLayoutRef,
840    properties: Arc<MaterialProperties>,
841    material_type_id: TypeId,
842}
843
844/// Holds all entities with mesh materials for which the prepass couldn't be
845/// specialized and/or queued because their materials hadn't loaded yet.
846///
847/// See the [`PendingQueues`] documentation for more information.
848#[derive(Default, Deref, DerefMut, Resource)]
849pub struct PendingPrepassMeshMaterialQueues(pub PendingQueues);
850
851#[derive(SystemParam)]
852pub(crate) struct SpecializePrepassSystemParam<'w, 's> {
853    render_meshes: Res<'w, RenderAssets<RenderMesh>>,
854    render_materials: Res<'w, ErasedRenderAssets<PreparedMaterial>>,
855    render_mesh_instances: Res<'w, RenderMeshInstances>,
856    render_material_instances: Res<'w, RenderMaterialInstances>,
857    render_lightmaps: Res<'w, RenderLightmaps>,
858    render_visibility_ranges: Res<'w, RenderVisibilityRanges>,
859    view_key_cache: Res<'w, ViewKeyPrepassCache>,
860    views: Query<
861        'w,
862        's,
863        (
864            &'static ExtractedView,
865            &'static RenderVisibleEntities,
866            &'static Msaa,
867            Option<&'static MotionVectorPrepass>,
868            Option<&'static DeferredPrepass>,
869        ),
870    >,
871    opaque_prepass_render_phases: Res<'w, ViewBinnedRenderPhases<Opaque3dPrepass>>,
872    alpha_mask_prepass_render_phases: Res<'w, ViewBinnedRenderPhases<AlphaMask3dPrepass>>,
873    opaque_deferred_render_phases: Res<'w, ViewBinnedRenderPhases<Opaque3dDeferred>>,
874    alpha_mask_deferred_render_phases: Res<'w, ViewBinnedRenderPhases<AlphaMask3dDeferred>>,
875    specialized_prepass_material_pipeline_cache:
876        ResMut<'w, SpecializedPrepassMaterialPipelineCache>,
877    pending_prepass_mesh_material_queues: ResMut<'w, PendingPrepassMeshMaterialQueues>,
878    dirty_specializations: Res<'w, DirtySpecializations>,
879    this_run: SystemChangeTick,
880}
881
882pub(crate) fn specialize_prepass_material_meshes(
883    world: &mut World,
884    state: &mut SystemState<SpecializePrepassSystemParam>,
885    mut work_items: Local<Vec<PrepassSpecializationWorkItem>>,
886    mut removals: Local<Vec<(RetainedViewEntity, MainEntity)>>,
887    mut all_views: Local<HashSet<RetainedViewEntity, FixedHasher>>,
888) {
889    work_items.clear();
890    removals.clear();
891    all_views.clear();
892
893    let this_run;
894
895    {
896        let SpecializePrepassSystemParam {
897            render_meshes,
898            render_materials,
899            render_mesh_instances,
900            render_material_instances,
901            render_lightmaps,
902            render_visibility_ranges,
903            view_key_cache,
904            views,
905            opaque_prepass_render_phases,
906            alpha_mask_prepass_render_phases,
907            opaque_deferred_render_phases,
908            alpha_mask_deferred_render_phases,
909            mut specialized_prepass_material_pipeline_cache,
910            mut pending_prepass_mesh_material_queues,
911            dirty_specializations,
912            this_run: system_change_tick,
913        } = state.get_mut(world).unwrap();
914
915        this_run = system_change_tick.this_run();
916
917        for (extracted_view, visible_entities, msaa, motion_vector_prepass, deferred_prepass) in
918            &views
919        {
920            if !opaque_deferred_render_phases.contains_key(&extracted_view.retained_view_entity)
921                && !alpha_mask_deferred_render_phases
922                    .contains_key(&extracted_view.retained_view_entity)
923                && !opaque_prepass_render_phases.contains_key(&extracted_view.retained_view_entity)
924                && !alpha_mask_prepass_render_phases
925                    .contains_key(&extracted_view.retained_view_entity)
926            {
927                continue;
928            }
929
930            let Some(view_key) = view_key_cache.get(&extracted_view.retained_view_entity) else {
931                continue;
932            };
933
934            all_views.insert(extracted_view.retained_view_entity);
935
936            let Some(render_visible_mesh_entities) = visible_entities.get::<Mesh3d>() else {
937                continue;
938            };
939
940            // Fetch the pending mesh material queues for this view.
941            let view_pending_prepass_mesh_material_queues = pending_prepass_mesh_material_queues
942                .prepare_for_new_frame(extracted_view.retained_view_entity);
943
944            // Initialize the pending queues.
945            let mut maybe_specialized_prepass_material_pipeline_cache =
946                specialized_prepass_material_pipeline_cache
947                    .get_mut(&extracted_view.retained_view_entity);
948
949            // Remove cached pipeline IDs corresponding to entities that
950            // either have been removed or need to be respecialized.
951            if let Some(ref mut specialized_prepass_material_pipeline_cache) =
952                maybe_specialized_prepass_material_pipeline_cache
953            {
954                if dirty_specializations
955                    .must_wipe_specializations_for_view(extracted_view.retained_view_entity)
956                {
957                    specialized_prepass_material_pipeline_cache.clear();
958                } else {
959                    for &renderable_entity in dirty_specializations.iter_to_despecialize() {
960                        specialized_prepass_material_pipeline_cache.remove(&renderable_entity);
961                    }
962                }
963            }
964
965            // Now process all meshes that need to be specialized.
966            for (render_entity, visible_entity) in dirty_specializations.iter_to_specialize(
967                extracted_view.retained_view_entity,
968                render_visible_mesh_entities,
969                &view_pending_prepass_mesh_material_queues.prev_frame,
970            ) {
971                if maybe_specialized_prepass_material_pipeline_cache
972                    .as_ref()
973                    .is_some_and(|specialized_prepass_material_pipeline_cache| {
974                        specialized_prepass_material_pipeline_cache.contains_key(visible_entity)
975                    })
976                {
977                    continue;
978                }
979
980                // Check for material instance, mesh, and material. If any of
981                // these fail, it's probably because the relevant asset hasn't
982                // loaded yet. In that case, add the entity to the list of
983                // pending mesh materials and bail.
984                let Some(material_instance) =
985                    render_material_instances.instances.get(visible_entity)
986                else {
987                    view_pending_prepass_mesh_material_queues
988                        .current_frame
989                        .insert((*render_entity, *visible_entity));
990                    continue;
991                };
992                let Some(mesh_instance) =
993                    render_mesh_instances.render_mesh_queue_data(*visible_entity)
994                else {
995                    view_pending_prepass_mesh_material_queues
996                        .current_frame
997                        .insert((*render_entity, *visible_entity));
998                    continue;
999                };
1000                let Some(material) = render_materials.get(material_instance.asset_id) else {
1001                    view_pending_prepass_mesh_material_queues
1002                        .current_frame
1003                        .insert((*render_entity, *visible_entity));
1004                    continue;
1005                };
1006                if !material.properties.prepass_enabled {
1007                    // If the material was previously specialized for prepass, remove it
1008                    removals.push((extracted_view.retained_view_entity, *visible_entity));
1009                    continue;
1010                }
1011                let Some(mesh) = render_meshes.get(mesh_instance.mesh_asset_id()) else {
1012                    continue;
1013                };
1014
1015                let mut mesh_key =
1016                    *view_key | MeshPipelineKey::from_bits_retain(mesh.key_bits.bits());
1017
1018                let alpha_mode = material.properties.alpha_mode;
1019                match alpha_mode {
1020                    AlphaMode::Opaque | AlphaMode::AlphaToCoverage | AlphaMode::Mask(_) => {
1021                        mesh_key |= alpha_mode_pipeline_key(alpha_mode, msaa);
1022                    }
1023                    AlphaMode::Blend
1024                    | AlphaMode::Premultiplied
1025                    | AlphaMode::Add
1026                    | AlphaMode::Multiply => {
1027                        // In case this material was previously in a valid alpha_mode, remove it to
1028                        // stop the queue system from assuming its retained cache to be valid.
1029                        removals.push((extracted_view.retained_view_entity, *visible_entity));
1030                        continue;
1031                    }
1032                }
1033
1034                if material.properties.reads_view_transmission_texture {
1035                    // No-op: Materials reading from `ViewTransmissionTexture` are not rendered in the `Opaque3d`
1036                    // phase, and are therefore also excluded from the prepass much like alpha-blended materials.
1037                    removals.push((extracted_view.retained_view_entity, *visible_entity));
1038                    continue;
1039                }
1040
1041                let forward = match material.properties.render_method {
1042                    OpaqueRendererMethod::Forward => true,
1043                    OpaqueRendererMethod::Deferred => false,
1044                    OpaqueRendererMethod::Auto => unreachable!(),
1045                };
1046
1047                let deferred = deferred_prepass.is_some() && !forward;
1048
1049                if deferred {
1050                    mesh_key |= MeshPipelineKey::DEFERRED_PREPASS;
1051                }
1052
1053                if let Some(lightmap) = render_lightmaps.render_lightmaps.get(visible_entity) {
1054                    // Even though we don't use the lightmap in the forward prepass, the
1055                    // `SetMeshBindGroup` render command will bind the data for it. So
1056                    // we need to include the appropriate flag in the mesh pipeline key
1057                    // to ensure that the necessary bind group layout entries are
1058                    // present.
1059                    mesh_key |= MeshPipelineKey::LIGHTMAPPED;
1060
1061                    if lightmap.bicubic_sampling && deferred {
1062                        mesh_key |= MeshPipelineKey::LIGHTMAP_BICUBIC_SAMPLING;
1063                    }
1064                }
1065
1066                if render_visibility_ranges
1067                    .entity_has_crossfading_visibility_ranges(*visible_entity)
1068                {
1069                    mesh_key |= MeshPipelineKey::VISIBILITY_RANGE_DITHER;
1070                }
1071
1072                // If the previous frame has skins or morph targets, note that.
1073                if motion_vector_prepass.is_some() {
1074                    if mesh_instance
1075                        .flags()
1076                        .contains(RenderMeshInstanceFlags::HAS_PREVIOUS_SKIN)
1077                    {
1078                        mesh_key |= MeshPipelineKey::HAS_PREVIOUS_SKIN;
1079                    }
1080                    if mesh_instance
1081                        .flags()
1082                        .contains(RenderMeshInstanceFlags::HAS_PREVIOUS_MORPH)
1083                    {
1084                        mesh_key |= MeshPipelineKey::HAS_PREVIOUS_MORPH;
1085                    }
1086                }
1087
1088                work_items.push(PrepassSpecializationWorkItem {
1089                    visible_entity: *visible_entity,
1090                    retained_view_entity: extracted_view.retained_view_entity,
1091                    mesh_key,
1092                    layout: mesh.layout.clone(),
1093                    properties: material.properties.clone(),
1094                    material_type_id: material_instance.asset_id.type_id(),
1095                });
1096            }
1097        }
1098
1099        pending_prepass_mesh_material_queues.expire_stale_views(&all_views);
1100    }
1101
1102    let depth_clip_control_supported = world
1103        .resource::<PrepassPipeline>()
1104        .depth_clip_control_supported;
1105
1106    for item in work_items.drain(..) {
1107        let Some(prepass_specialize) = item.properties.prepass_specialize else {
1108            continue;
1109        };
1110
1111        let key = ErasedMaterialPipelineKey {
1112            type_id: item.material_type_id,
1113            mesh_key: ErasedMeshPipelineKey::new(item.mesh_key),
1114            material_key: item.properties.material_key.clone(),
1115        };
1116
1117        let emulate_unclipped_depth = item
1118            .mesh_key
1119            .contains(MeshPipelineKey::UNCLIPPED_DEPTH_ORTHO)
1120            && !depth_clip_control_supported;
1121        let deferred = item.mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS);
1122        let draw_function = match item.properties.render_phase_type {
1123            RenderPhaseType::Opaque => {
1124                if deferred {
1125                    item.properties
1126                        .get_draw_function(DeferredOpaqueDrawFunction)
1127                } else if is_depth_only_opaque_prepass(item.mesh_key) && !emulate_unclipped_depth {
1128                    item.properties
1129                        .get_draw_function(PrepassOpaqueDepthOnlyDrawFunction)
1130                } else {
1131                    item.properties.get_draw_function(PrepassOpaqueDrawFunction)
1132                }
1133            }
1134            RenderPhaseType::AlphaMask => {
1135                if deferred {
1136                    item.properties
1137                        .get_draw_function(DeferredAlphaMaskDrawFunction)
1138                } else {
1139                    item.properties
1140                        .get_draw_function(PrepassAlphaMaskDrawFunction)
1141                }
1142            }
1143            RenderPhaseType::Transmissive | RenderPhaseType::Transparent => continue,
1144        };
1145
1146        let Some(draw_function) = draw_function else {
1147            continue;
1148        };
1149
1150        match prepass_specialize(world, key, &item.layout, &item.properties) {
1151            Ok(pipeline_id) => {
1152                world
1153                    .resource_mut::<SpecializedPrepassMaterialPipelineCache>()
1154                    .entry(item.retained_view_entity)
1155                    .or_default()
1156                    .insert(item.visible_entity, (this_run, pipeline_id, draw_function));
1157            }
1158            Err(err) => error!("{}", err),
1159        }
1160    }
1161
1162    if !removals.is_empty() {
1163        let mut cache = world.resource_mut::<SpecializedPrepassMaterialPipelineCache>();
1164        for (view, entity) in removals.drain(..) {
1165            if let Some(view_cache) = cache.get_mut(&view) {
1166                view_cache.remove(&entity);
1167            }
1168        }
1169    }
1170
1171    world
1172        .resource_mut::<SpecializedPrepassMaterialPipelineCache>()
1173        .retain(|view, _| all_views.contains(view));
1174}
1175
1176pub fn queue_prepass_material_meshes(
1177    render_mesh_instances: Res<RenderMeshInstances>,
1178    render_materials: Res<ErasedRenderAssets<PreparedMaterial>>,
1179    render_material_instances: Res<RenderMaterialInstances>,
1180    mesh_allocator: Res<MeshAllocator>,
1181    gpu_preprocessing_support: Res<GpuPreprocessingSupport>,
1182    mut opaque_prepass_render_phases: ResMut<ViewBinnedRenderPhases<Opaque3dPrepass>>,
1183    mut alpha_mask_prepass_render_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3dPrepass>>,
1184    mut opaque_deferred_render_phases: ResMut<ViewBinnedRenderPhases<Opaque3dDeferred>>,
1185    mut alpha_mask_deferred_render_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3dDeferred>>,
1186    views: Query<(&ExtractedView, &RenderVisibleEntities)>,
1187    specialized_material_pipeline_cache: Res<SpecializedPrepassMaterialPipelineCache>,
1188    mut pending_prepass_mesh_material_queues: ResMut<PendingPrepassMeshMaterialQueues>,
1189    dirty_specializations: Res<DirtySpecializations>,
1190) {
1191    for (extracted_view, visible_entities) in &views {
1192        let (
1193            mut opaque_phase,
1194            mut alpha_mask_phase,
1195            mut opaque_deferred_phase,
1196            mut alpha_mask_deferred_phase,
1197        ) = (
1198            opaque_prepass_render_phases.get_mut(&extracted_view.retained_view_entity),
1199            alpha_mask_prepass_render_phases.get_mut(&extracted_view.retained_view_entity),
1200            opaque_deferred_render_phases.get_mut(&extracted_view.retained_view_entity),
1201            alpha_mask_deferred_render_phases.get_mut(&extracted_view.retained_view_entity),
1202        );
1203
1204        let Some(view_specialized_material_pipeline_cache) =
1205            specialized_material_pipeline_cache.get(&extracted_view.retained_view_entity)
1206        else {
1207            continue;
1208        };
1209
1210        // Skip if there's no place to put the mesh.
1211        if opaque_phase.is_none()
1212            && alpha_mask_phase.is_none()
1213            && opaque_deferred_phase.is_none()
1214            && alpha_mask_deferred_phase.is_none()
1215        {
1216            continue;
1217        }
1218
1219        let Some(render_visible_mesh_entities) = visible_entities.get::<Mesh3d>() else {
1220            continue;
1221        };
1222
1223        // Fetch the pending mesh material queues for this view.
1224        let view_pending_prepass_mesh_material_queues = pending_prepass_mesh_material_queues
1225            .get_mut(&extracted_view.retained_view_entity)
1226            .expect(
1227                "View pending prepass mesh material queues should have been created in \
1228                 `specialize_prepass_material_meshes`",
1229            );
1230
1231        // First, remove meshes that need to be respecialized, and those that were removed, from the bins.
1232        for &main_entity in dirty_specializations.iter_to_dequeue(
1233            extracted_view.retained_view_entity,
1234            render_visible_mesh_entities,
1235        ) {
1236            if let Some(ref mut opaque_phase) = opaque_phase {
1237                opaque_phase.remove(main_entity);
1238            }
1239            if let Some(ref mut alpha_mask_phase) = alpha_mask_phase {
1240                alpha_mask_phase.remove(main_entity);
1241            }
1242            if let Some(ref mut opaque_deferred_phase) = opaque_deferred_phase {
1243                opaque_deferred_phase.remove(main_entity);
1244            }
1245            if let Some(ref mut alpha_mask_deferred_phase) = alpha_mask_deferred_phase {
1246                alpha_mask_deferred_phase.remove(main_entity);
1247            }
1248        }
1249
1250        // Now iterate through all newly-visible entities and those needing respecialization.
1251        for (render_entity, visible_entity) in dirty_specializations.iter_to_queue(
1252            extracted_view.retained_view_entity,
1253            render_visible_mesh_entities,
1254            &view_pending_prepass_mesh_material_queues.prev_frame,
1255        ) {
1256            let Some(&(_, pipeline_id, draw_function)) =
1257                view_specialized_material_pipeline_cache.get(visible_entity)
1258            else {
1259                continue;
1260            };
1261
1262            // Check for material instance, mesh, and material. If any of these
1263            // fail, it's probably because the relevant asset hasn't loaded yet.
1264            // In that case, add the entity to the list of pending mesh
1265            // materials and bail.
1266            let Some(material_instance) = render_material_instances.instances.get(visible_entity)
1267            else {
1268                view_pending_prepass_mesh_material_queues
1269                    .current_frame
1270                    .insert((*render_entity, *visible_entity));
1271                continue;
1272            };
1273            let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(*visible_entity)
1274            else {
1275                view_pending_prepass_mesh_material_queues
1276                    .current_frame
1277                    .insert((*render_entity, *visible_entity));
1278                continue;
1279            };
1280            let Some(material) = render_materials.get(material_instance.asset_id) else {
1281                view_pending_prepass_mesh_material_queues
1282                    .current_frame
1283                    .insert((*render_entity, *visible_entity));
1284                continue;
1285            };
1286            let Some(mesh_slabs) = mesh_allocator.mesh_slabs(&mesh_instance.mesh_asset_id()) else {
1287                continue;
1288            };
1289
1290            let deferred = match material.properties.render_method {
1291                OpaqueRendererMethod::Forward => false,
1292                OpaqueRendererMethod::Deferred => true,
1293                OpaqueRendererMethod::Auto => unreachable!(),
1294            };
1295
1296            match material.properties.render_phase_type {
1297                RenderPhaseType::Opaque => {
1298                    if deferred {
1299                        opaque_deferred_phase.as_mut().unwrap().add(
1300                            OpaqueNoLightmap3dBatchSetKey {
1301                                draw_function,
1302                                pipeline: pipeline_id,
1303                                material_bind_group_index: Some(material.binding.group.0),
1304                                slabs: mesh_slabs,
1305                            },
1306                            OpaqueNoLightmap3dBinKey {
1307                                asset_id: mesh_instance.mesh_asset_id().into(),
1308                            },
1309                            (*render_entity, *visible_entity),
1310                            mesh_instance.current_uniform_index,
1311                            BinnedRenderPhaseType::mesh(
1312                                mesh_instance.should_batch(),
1313                                &gpu_preprocessing_support,
1314                            ),
1315                        );
1316                    } else if let Some(opaque_phase) = opaque_phase.as_mut() {
1317                        let depth_only_draw_function = material
1318                            .properties
1319                            .get_draw_function(PrepassOpaqueDepthOnlyDrawFunction);
1320                        let material_bind_group_index =
1321                            if Some(draw_function) == depth_only_draw_function {
1322                                None
1323                            } else {
1324                                Some(material.binding.group.0)
1325                            };
1326                        opaque_phase.add(
1327                            OpaqueNoLightmap3dBatchSetKey {
1328                                draw_function,
1329                                pipeline: pipeline_id,
1330                                material_bind_group_index,
1331                                slabs: mesh_slabs,
1332                            },
1333                            OpaqueNoLightmap3dBinKey {
1334                                asset_id: mesh_instance.mesh_asset_id().into(),
1335                            },
1336                            (*render_entity, *visible_entity),
1337                            mesh_instance.current_uniform_index,
1338                            BinnedRenderPhaseType::mesh(
1339                                mesh_instance.should_batch(),
1340                                &gpu_preprocessing_support,
1341                            ),
1342                        );
1343                    }
1344                }
1345                RenderPhaseType::AlphaMask => {
1346                    if deferred {
1347                        alpha_mask_deferred_phase.as_mut().unwrap().add(
1348                            OpaqueNoLightmap3dBatchSetKey {
1349                                draw_function,
1350                                pipeline: pipeline_id,
1351                                material_bind_group_index: Some(material.binding.group.0),
1352                                slabs: mesh_slabs,
1353                            },
1354                            OpaqueNoLightmap3dBinKey {
1355                                asset_id: mesh_instance.mesh_asset_id().into(),
1356                            },
1357                            (*render_entity, *visible_entity),
1358                            mesh_instance.current_uniform_index,
1359                            BinnedRenderPhaseType::mesh(
1360                                mesh_instance.should_batch(),
1361                                &gpu_preprocessing_support,
1362                            ),
1363                        );
1364                    } else if let Some(alpha_mask_phase) = alpha_mask_phase.as_mut() {
1365                        alpha_mask_phase.add(
1366                            OpaqueNoLightmap3dBatchSetKey {
1367                                draw_function,
1368                                pipeline: pipeline_id,
1369                                material_bind_group_index: Some(material.binding.group.0),
1370                                slabs: mesh_slabs,
1371                            },
1372                            OpaqueNoLightmap3dBinKey {
1373                                asset_id: mesh_instance.mesh_asset_id().into(),
1374                            },
1375                            (*render_entity, *visible_entity),
1376                            mesh_instance.current_uniform_index,
1377                            BinnedRenderPhaseType::mesh(
1378                                mesh_instance.should_batch(),
1379                                &gpu_preprocessing_support,
1380                            ),
1381                        );
1382                    }
1383                }
1384                _ => {}
1385            }
1386        }
1387    }
1388}
1389
1390pub struct SetPrepassViewBindGroup<const I: usize>;
1391impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetPrepassViewBindGroup<I> {
1392    type Param = SRes<PrepassViewBindGroup>;
1393    type ViewQuery = (
1394        Read<ViewUniformOffset>,
1395        Has<MotionVectorPrepass>,
1396        Option<Read<PreviousViewUniformOffset>>,
1397    );
1398    type ItemQuery = ();
1399
1400    #[inline]
1401    fn render<'w>(
1402        _item: &P,
1403        (view_uniform_offset, has_motion_vector_prepass, previous_view_uniform_offset): (
1404            &'_ ViewUniformOffset,
1405            bool,
1406            Option<&'_ PreviousViewUniformOffset>,
1407        ),
1408        _entity: Option<()>,
1409        prepass_view_bind_group: SystemParamItem<'w, '_, Self::Param>,
1410        pass: &mut TrackedRenderPass<'w>,
1411    ) -> RenderCommandResult {
1412        let prepass_view_bind_group = prepass_view_bind_group.into_inner();
1413
1414        match previous_view_uniform_offset {
1415            Some(previous_view_uniform_offset) if has_motion_vector_prepass => {
1416                pass.set_bind_group(
1417                    I,
1418                    prepass_view_bind_group.motion_vectors.as_ref().unwrap(),
1419                    &[
1420                        view_uniform_offset.offset,
1421                        previous_view_uniform_offset.offset,
1422                    ],
1423                );
1424            }
1425            _ => {
1426                pass.set_bind_group(
1427                    I,
1428                    prepass_view_bind_group.no_motion_vectors.as_ref().unwrap(),
1429                    &[view_uniform_offset.offset],
1430                );
1431            }
1432        }
1433        RenderCommandResult::Success
1434    }
1435}
1436
1437pub struct SetPrepassViewEmptyBindGroup<const I: usize>;
1438impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetPrepassViewEmptyBindGroup<I> {
1439    type Param = SRes<PrepassViewBindGroup>;
1440    type ViewQuery = ();
1441    type ItemQuery = ();
1442
1443    #[inline]
1444    fn render<'w>(
1445        _item: &P,
1446        _view: (),
1447        _entity: Option<()>,
1448        prepass_view_bind_group: SystemParamItem<'w, '_, Self::Param>,
1449        pass: &mut TrackedRenderPass<'w>,
1450    ) -> RenderCommandResult {
1451        let prepass_view_bind_group = prepass_view_bind_group.into_inner();
1452        pass.set_bind_group(I, &prepass_view_bind_group.empty_bind_group, &[]);
1453        RenderCommandResult::Success
1454    }
1455}
1456
1457pub struct SetPrepassEmptyMaterialBindGroup<const I: usize>;
1458impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetPrepassEmptyMaterialBindGroup<I> {
1459    type Param = SRes<PrepassViewBindGroup>;
1460    type ViewQuery = ();
1461    type ItemQuery = ();
1462
1463    #[inline]
1464    fn render<'w>(
1465        _item: &P,
1466        _view: (),
1467        _entity: Option<()>,
1468        prepass_view_bind_group: SystemParamItem<'w, '_, Self::Param>,
1469        pass: &mut TrackedRenderPass<'w>,
1470    ) -> RenderCommandResult {
1471        let prepass_view_bind_group = prepass_view_bind_group.into_inner();
1472        pass.set_bind_group(I, &prepass_view_bind_group.empty_bind_group, &[]);
1473        RenderCommandResult::Success
1474    }
1475}
1476
1477pub type DrawPrepass = (
1478    SetItemPipeline,
1479    SetPrepassViewBindGroup<0>,
1480    SetPrepassViewEmptyBindGroup<1>,
1481    SetMeshBindGroup<2>,
1482    SetMaterialBindGroup<3>,
1483    DrawMesh,
1484);
1485
1486pub type DrawDepthOnlyPrepass = (
1487    SetItemPipeline,
1488    SetPrepassViewBindGroup<0>,
1489    SetPrepassViewEmptyBindGroup<1>,
1490    SetMeshBindGroup<2>,
1491    SetPrepassEmptyMaterialBindGroup<3>,
1492    DrawMesh,
1493);