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