Skip to main content

bevy_core_pipeline/core_3d/
mod.rs

1mod main_opaque_pass_3d_node;
2mod main_transparent_pass_3d_node;
3
4// PERF: vulkan docs recommend using 24 bit depth for better performance
5pub const CORE_3D_DEPTH_FORMAT: TextureFormat = TextureFormat::Depth32Float;
6
7/// True if multisampled depth textures are supported on this platform.
8///
9/// WebGL 2:
10/// - doesn't support `copy_texture_to_texture` for depth textures yet, thus it doesn't support `DepthPrepass`.
11/// - doesn't support creating multisampled textures if they are not pure `RENDER_ATTACHMENT`,
12///   so it doesn't support Msaa when reading `ViewDepthTexture`.
13/// - shadow sampler `texture_depth_2d` doesn't support sampling, only supports comparison.
14///
15/// To read depth texture on WebGL 2, we can only use `ViewDepthTexture` with `Msaa::Off` and bind depth texture as unfilterable `texture_2d<f32>`.
16/// Therefore we disable depth of field and screen space reflections entirely on WebGL 2.
17#[cfg(not(any(feature = "webgpu", not(target_arch = "wasm32"))))]
18pub const DEPTH_PREPASS_TEXTURE_SUPPORTED: bool = false;
19
20/// True if multisampled depth textures are supported on this platform.
21///
22/// WebGL 2:
23/// - doesn't support `copy_texture_to_texture` for depth textures yet, thus it doesn't support `DepthPrepass`.
24/// - doesn't support creating multisampled textures if they are not pure `RENDER_ATTACHMENT`,
25///   so it doesn't support Msaa when reading `ViewDepthTexture`.
26/// - shadow sampler `texture_depth_2d` doesn't support sampling, only supports comparison.
27///
28/// To read depth texture on WebGL 2, we can only use `ViewDepthTexture` with `Msaa::Off` and bind depth texture as unfilterable `texture_2d<f32>`.
29/// Therefore we disable depth of field and screen space reflections entirely on WebGL 2.
30#[cfg(any(feature = "webgpu", not(target_arch = "wasm32")))]
31pub const DEPTH_PREPASS_TEXTURE_SUPPORTED: bool = true;
32
33use core::{f32, ops::Range};
34
35use bevy_camera::{Camera, Camera3d, Camera3dDepthLoadOp};
36use bevy_diagnostic::FrameCount;
37use bevy_render::{
38    batching::gpu_preprocessing::{GpuPreprocessingMode, GpuPreprocessingSupport},
39    camera::CameraRenderGraph,
40    mesh::allocator::MeshSlabs,
41    occlusion_culling::OcclusionCulling,
42    render_phase::{PhaseItemBatchSetKey, ViewRangefinder3d},
43    texture::CachedTexture,
44    view::{prepare_view_targets, NoIndirectDrawing, RetainedViewEntity},
45};
46use indexmap::IndexMap;
47pub use main_opaque_pass_3d_node::*;
48pub use main_transparent_pass_3d_node::*;
49
50use bevy_app::{App, Plugin, PostUpdate};
51use bevy_asset::UntypedAssetId;
52use bevy_color::LinearRgba;
53use bevy_ecs::{entity::EntityHash, prelude::*};
54use bevy_image::ToExtents;
55use bevy_log::warn;
56use bevy_math::{FloatOrd, Vec3};
57use bevy_platform::collections::{HashMap, HashSet};
58use bevy_render::{
59    camera::ExtractedCamera,
60    extract_component::ExtractComponentPlugin,
61    prelude::Msaa,
62    render_phase::{
63        sort_phase_system, BinnedPhaseItem, CachedRenderPipelinePhaseItem, DrawFunctionId,
64        DrawFunctions, PhaseItem, PhaseItemExtraIndex, SortedPhaseItem, ViewBinnedRenderPhases,
65        ViewSortedRenderPhases,
66    },
67    render_resource::{
68        CachedRenderPipelineId, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,
69    },
70    renderer::RenderDevice,
71    sync_world::{MainEntity, RenderEntity},
72    texture::{ColorAttachment, TextureCache},
73    view::{ExtractedView, ViewDepthTexture},
74    Extract, ExtractSchedule, Render, RenderApp, RenderSystems,
75};
76use nonmax::NonMaxU32;
77
78use crate::deferred::copy_lighting_id::copy_deferred_lighting_id;
79use crate::deferred::node::{early_deferred_prepass, late_deferred_prepass};
80use crate::prepass::node::{early_prepass, late_prepass};
81use crate::tonemapping::tonemapping;
82use crate::upscaling::upscaling;
83use crate::{
84    deferred::{
85        AlphaMask3dDeferred, Opaque3dDeferred, DEFERRED_LIGHTING_PASS_ID_FORMAT,
86        DEFERRED_PREPASS_FORMAT,
87    },
88    prepass::{
89        AlphaMask3dPrepass, DeferredPrepass, DeferredPrepassDoubleBuffer, DepthPrepass,
90        DepthPrepassDoubleBuffer, MotionVectorPrepass, NormalPrepass, Opaque3dPrepass,
91        OpaqueNoLightmap3dBatchSetKey, OpaqueNoLightmap3dBinKey, ViewPrepassTextures,
92        MOTION_VECTOR_PREPASS_FORMAT, NORMAL_PREPASS_FORMAT,
93    },
94    schedule::Core3d,
95    skybox::SkyboxPlugin,
96    tonemapping::{DebandDither, Tonemapping},
97    Core3dSystems,
98};
99
100pub struct Core3dPlugin;
101
102impl Plugin for Core3dPlugin {
103    fn build(&self, app: &mut App) {
104        app.register_required_components_with::<Camera3d, DebandDither>(|| DebandDither::Enabled)
105            .register_required_components_with::<Camera3d, CameraRenderGraph>(|| {
106                CameraRenderGraph::new(Core3d)
107            })
108            .register_required_components::<Camera3d, Tonemapping>()
109            .add_plugins((SkyboxPlugin, ExtractComponentPlugin::<Camera3d>::default()))
110            .add_systems(PostUpdate, check_msaa);
111
112        let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
113            return;
114        };
115        render_app
116            .init_resource::<DrawFunctions<Opaque3d>>()
117            .init_resource::<DrawFunctions<AlphaMask3d>>()
118            .init_resource::<DrawFunctions<Transparent3d>>()
119            .init_resource::<DrawFunctions<Opaque3dPrepass>>()
120            .init_resource::<DrawFunctions<AlphaMask3dPrepass>>()
121            .init_resource::<DrawFunctions<Opaque3dDeferred>>()
122            .init_resource::<DrawFunctions<AlphaMask3dDeferred>>()
123            .init_resource::<ViewBinnedRenderPhases<Opaque3d>>()
124            .init_resource::<ViewBinnedRenderPhases<AlphaMask3d>>()
125            .init_resource::<ViewBinnedRenderPhases<Opaque3dPrepass>>()
126            .init_resource::<ViewBinnedRenderPhases<AlphaMask3dPrepass>>()
127            .init_resource::<ViewBinnedRenderPhases<Opaque3dDeferred>>()
128            .init_resource::<ViewBinnedRenderPhases<AlphaMask3dDeferred>>()
129            .init_resource::<ViewSortedRenderPhases<Transparent3d>>()
130            .add_systems(ExtractSchedule, extract_core_3d_camera_phases)
131            .add_systems(ExtractSchedule, extract_camera_prepass_phase)
132            .add_systems(
133                Render,
134                (
135                    sort_phase_system::<Transparent3d>.in_set(RenderSystems::PhaseSort),
136                    configure_occlusion_culling_view_targets
137                        .after(prepare_view_targets)
138                        .in_set(RenderSystems::PrepareViews)
139                        .ambiguous_with(RenderSystems::PrepareViews),
140                    prepare_core_3d_depth_textures.in_set(RenderSystems::PrepareResources),
141                    prepare_prepass_textures.in_set(RenderSystems::PrepareResources),
142                ),
143            )
144            .add_schedule(Core3d::base_schedule())
145            .add_systems(
146                Core3d,
147                (
148                    (
149                        early_prepass,
150                        early_deferred_prepass,
151                        late_prepass,
152                        late_deferred_prepass,
153                        copy_deferred_lighting_id,
154                    )
155                        .chain()
156                        .in_set(Core3dSystems::Prepass),
157                    (main_opaque_pass_3d, main_transparent_pass_3d)
158                        .chain()
159                        .in_set(Core3dSystems::MainPass),
160                    tonemapping.in_set(Core3dSystems::PostProcess),
161                    upscaling.after(Core3dSystems::PostProcess),
162                ),
163            );
164    }
165}
166
167/// Opaque 3D [`BinnedPhaseItem`]s.
168pub struct Opaque3d {
169    /// Determines which objects can be placed into a *batch set*.
170    ///
171    /// Objects in a single batch set can potentially be multi-drawn together,
172    /// if it's enabled and the current platform supports it.
173    pub batch_set_key: Opaque3dBatchSetKey,
174    /// The key, which determines which can be batched.
175    pub bin_key: Opaque3dBinKey,
176    /// An entity from which data will be fetched, including the mesh if
177    /// applicable.
178    pub representative_entity: (Entity, MainEntity),
179    /// The ranges of instances.
180    pub batch_range: Range<u32>,
181    /// An extra index, which is either a dynamic offset or an index in the
182    /// indirect parameters list.
183    pub extra_index: PhaseItemExtraIndex,
184}
185
186/// Information that must be identical in order to place opaque meshes in the
187/// same *batch set*.
188///
189/// A batch set is a set of batches that can be multi-drawn together, if
190/// multi-draw is in use.
191#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
192pub struct Opaque3dBatchSetKey {
193    /// The identifier of the render pipeline.
194    pub pipeline: CachedRenderPipelineId,
195
196    /// The function used to draw.
197    pub draw_function: DrawFunctionId,
198
199    /// The ID of a bind group specific to the material instance.
200    ///
201    /// In the case of PBR, this is the `MaterialBindGroupIndex`.
202    pub material_bind_group_index: Option<u32>,
203
204    /// The IDs of the slabs of GPU memory in the mesh allocator that contain
205    /// the mesh data.
206    ///
207    /// For non-mesh items, you can fill the [`MeshSlabs::vertex_slab_id`] with
208    /// 0 if your items can be multi-drawn, or with a unique value if they
209    /// can't.
210    pub slabs: MeshSlabs,
211
212    /// Index of the slab that the lightmap resides in, if a lightmap is
213    /// present.
214    pub lightmap_slab: Option<NonMaxU32>,
215}
216
217impl PhaseItemBatchSetKey for Opaque3dBatchSetKey {
218    fn indexed(&self) -> bool {
219        self.slabs.index_slab_id.is_some()
220    }
221}
222
223/// Data that must be identical in order to *batch* phase items together.
224///
225/// Note that a *batch set* (if multi-draw is in use) contains multiple batches.
226#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
227pub struct Opaque3dBinKey {
228    /// The asset that this phase item is associated with.
229    ///
230    /// Normally, this is the ID of the mesh, but for non-mesh items it might be
231    /// the ID of another type of asset.
232    pub asset_id: UntypedAssetId,
233}
234
235impl PhaseItem for Opaque3d {
236    #[inline]
237    fn entity(&self) -> Entity {
238        self.representative_entity.0
239    }
240
241    #[inline]
242    fn main_entity(&self) -> MainEntity {
243        self.representative_entity.1
244    }
245
246    #[inline]
247    fn draw_function(&self) -> DrawFunctionId {
248        self.batch_set_key.draw_function
249    }
250
251    #[inline]
252    fn batch_range(&self) -> &Range<u32> {
253        &self.batch_range
254    }
255
256    #[inline]
257    fn batch_range_mut(&mut self) -> &mut Range<u32> {
258        &mut self.batch_range
259    }
260
261    fn extra_index(&self) -> PhaseItemExtraIndex {
262        self.extra_index.clone()
263    }
264
265    fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
266        (&mut self.batch_range, &mut self.extra_index)
267    }
268}
269
270impl BinnedPhaseItem for Opaque3d {
271    type BatchSetKey = Opaque3dBatchSetKey;
272    type BinKey = Opaque3dBinKey;
273
274    #[inline]
275    fn new(
276        batch_set_key: Self::BatchSetKey,
277        bin_key: Self::BinKey,
278        representative_entity: (Entity, MainEntity),
279        batch_range: Range<u32>,
280        extra_index: PhaseItemExtraIndex,
281    ) -> Self {
282        Opaque3d {
283            batch_set_key,
284            bin_key,
285            representative_entity,
286            batch_range,
287            extra_index,
288        }
289    }
290}
291
292impl CachedRenderPipelinePhaseItem for Opaque3d {
293    #[inline]
294    fn cached_pipeline(&self) -> CachedRenderPipelineId {
295        self.batch_set_key.pipeline
296    }
297}
298
299pub struct AlphaMask3d {
300    /// Determines which objects can be placed into a *batch set*.
301    ///
302    /// Objects in a single batch set can potentially be multi-drawn together,
303    /// if it's enabled and the current platform supports it.
304    pub batch_set_key: OpaqueNoLightmap3dBatchSetKey,
305    /// The key, which determines which can be batched.
306    pub bin_key: OpaqueNoLightmap3dBinKey,
307    pub representative_entity: (Entity, MainEntity),
308    pub batch_range: Range<u32>,
309    pub extra_index: PhaseItemExtraIndex,
310}
311
312impl PhaseItem for AlphaMask3d {
313    #[inline]
314    fn entity(&self) -> Entity {
315        self.representative_entity.0
316    }
317
318    fn main_entity(&self) -> MainEntity {
319        self.representative_entity.1
320    }
321
322    #[inline]
323    fn draw_function(&self) -> DrawFunctionId {
324        self.batch_set_key.draw_function
325    }
326
327    #[inline]
328    fn batch_range(&self) -> &Range<u32> {
329        &self.batch_range
330    }
331
332    #[inline]
333    fn batch_range_mut(&mut self) -> &mut Range<u32> {
334        &mut self.batch_range
335    }
336
337    #[inline]
338    fn extra_index(&self) -> PhaseItemExtraIndex {
339        self.extra_index.clone()
340    }
341
342    #[inline]
343    fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
344        (&mut self.batch_range, &mut self.extra_index)
345    }
346}
347
348impl BinnedPhaseItem for AlphaMask3d {
349    type BinKey = OpaqueNoLightmap3dBinKey;
350    type BatchSetKey = OpaqueNoLightmap3dBatchSetKey;
351
352    #[inline]
353    fn new(
354        batch_set_key: Self::BatchSetKey,
355        bin_key: Self::BinKey,
356        representative_entity: (Entity, MainEntity),
357        batch_range: Range<u32>,
358        extra_index: PhaseItemExtraIndex,
359    ) -> Self {
360        Self {
361            batch_set_key,
362            bin_key,
363            representative_entity,
364            batch_range,
365            extra_index,
366        }
367    }
368}
369
370impl CachedRenderPipelinePhaseItem for AlphaMask3d {
371    #[inline]
372    fn cached_pipeline(&self) -> CachedRenderPipelineId {
373        self.batch_set_key.pipeline
374    }
375}
376
377pub struct Transparent3d {
378    pub sorting_info: TransparentSortingInfo3d,
379    pub distance: f32,
380    pub pipeline: CachedRenderPipelineId,
381    pub entity: (Entity, MainEntity),
382    pub draw_function: DrawFunctionId,
383    pub batch_range: Range<u32>,
384    pub extra_index: PhaseItemExtraIndex,
385    /// Whether the mesh in question is indexed (uses an index buffer in
386    /// addition to its vertex buffer).
387    pub indexed: bool,
388}
389
390impl PhaseItem for Transparent3d {
391    #[inline]
392    fn entity(&self) -> Entity {
393        self.entity.0
394    }
395
396    fn main_entity(&self) -> MainEntity {
397        self.entity.1
398    }
399
400    #[inline]
401    fn draw_function(&self) -> DrawFunctionId {
402        self.draw_function
403    }
404
405    #[inline]
406    fn batch_range(&self) -> &Range<u32> {
407        &self.batch_range
408    }
409
410    #[inline]
411    fn batch_range_mut(&mut self) -> &mut Range<u32> {
412        &mut self.batch_range
413    }
414
415    #[inline]
416    fn extra_index(&self) -> PhaseItemExtraIndex {
417        self.extra_index.clone()
418    }
419
420    #[inline]
421    fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
422        (&mut self.batch_range, &mut self.extra_index)
423    }
424}
425
426impl SortedPhaseItem for Transparent3d {
427    // NOTE: Values increase towards the camera. Back-to-front ordering for transparent means we need an ascending sort.
428    type SortKey = FloatOrd;
429
430    #[inline]
431    fn sort_key(&self) -> Self::SortKey {
432        FloatOrd(self.distance)
433    }
434
435    #[inline]
436    fn sort(items: &mut IndexMap<(Entity, MainEntity), Transparent3d, EntityHash>) {
437        items.sort_by_key(|_, item| item.sort_key());
438    }
439
440    fn recalculate_sort_keys(
441        items: &mut IndexMap<(Entity, MainEntity), Self, EntityHash>,
442        view: &ExtractedView,
443    ) {
444        // Determine the distance to the view for each phase item.
445        let rangefinder = view.rangefinder3d();
446        for item in items.values_mut() {
447            item.distance = item.sorting_info.sort_distance(&rangefinder);
448        }
449    }
450
451    #[inline]
452    fn indexed(&self) -> bool {
453        self.indexed
454    }
455}
456
457impl CachedRenderPipelinePhaseItem for Transparent3d {
458    #[inline]
459    fn cached_pipeline(&self) -> CachedRenderPipelineId {
460        self.pipeline
461    }
462}
463
464/// Information needed to perform a depth sort.
465#[derive(Clone, Copy)]
466pub enum TransparentSortingInfo3d {
467    /// No information is needed because this object should always appear on top
468    /// of other objects.
469    AlwaysOnTop,
470    /// Information needed to sort the object based on distance to the view.
471    Sorted {
472        /// The center of the mesh.
473        ///
474        /// This is the point that is used to sort.
475        mesh_center: Vec3,
476        /// An additional value that's artificially added to the distance before
477        /// sorting.
478        depth_bias: f32,
479    },
480}
481
482impl TransparentSortingInfo3d {
483    /// Calculates the value used for distance sorting for an item.
484    /// For [`Self::AlwaysOnTop`], this is [`f32::NEG_INFINITY`].
485    pub fn sort_distance(&self, rangefinder: &ViewRangefinder3d) -> f32 {
486        match *self {
487            TransparentSortingInfo3d::AlwaysOnTop => f32::NEG_INFINITY,
488            TransparentSortingInfo3d::Sorted {
489                mesh_center,
490                depth_bias,
491            } => rangefinder.distance(&mesh_center) + depth_bias,
492        }
493    }
494}
495
496pub fn extract_core_3d_camera_phases(
497    mut opaque_3d_phases: ResMut<ViewBinnedRenderPhases<Opaque3d>>,
498    mut alpha_mask_3d_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3d>>,
499    mut transparent_3d_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>,
500    cameras_3d: Extract<Query<(Entity, &Camera, Has<NoIndirectDrawing>), With<Camera3d>>>,
501    mut live_entities: Local<HashSet<RetainedViewEntity>>,
502    gpu_preprocessing_support: Res<GpuPreprocessingSupport>,
503) {
504    live_entities.clear();
505
506    for (main_entity, camera, no_indirect_drawing) in &cameras_3d {
507        if !camera.is_active {
508            continue;
509        }
510
511        // If GPU culling is in use, use it (and indirect mode); otherwise, just
512        // preprocess the meshes.
513        let gpu_preprocessing_mode = gpu_preprocessing_support.min(if !no_indirect_drawing {
514            GpuPreprocessingMode::Culling
515        } else {
516            GpuPreprocessingMode::PreprocessingOnly
517        });
518
519        // This is the main 3D camera, so use the first subview index (0).
520        let retained_view_entity = RetainedViewEntity::new(main_entity.into(), None, 0);
521
522        opaque_3d_phases.prepare_for_new_frame(retained_view_entity, gpu_preprocessing_mode);
523        alpha_mask_3d_phases.prepare_for_new_frame(retained_view_entity, gpu_preprocessing_mode);
524        transparent_3d_phases.prepare_for_new_frame(retained_view_entity);
525
526        live_entities.insert(retained_view_entity);
527    }
528
529    opaque_3d_phases.retain(|view_entity, _| live_entities.contains(view_entity));
530    alpha_mask_3d_phases.retain(|view_entity, _| live_entities.contains(view_entity));
531    transparent_3d_phases.retain(|view_entity, _| live_entities.contains(view_entity));
532}
533
534// Extract the render phases for the prepass
535
536pub fn extract_camera_prepass_phase(
537    mut commands: Commands,
538    mut opaque_3d_prepass_phases: ResMut<ViewBinnedRenderPhases<Opaque3dPrepass>>,
539    mut alpha_mask_3d_prepass_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3dPrepass>>,
540    mut opaque_3d_deferred_phases: ResMut<ViewBinnedRenderPhases<Opaque3dDeferred>>,
541    mut alpha_mask_3d_deferred_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3dDeferred>>,
542    cameras_3d: Extract<
543        Query<
544            (
545                Entity,
546                RenderEntity,
547                &Camera,
548                Has<NoIndirectDrawing>,
549                Has<DepthPrepass>,
550                Has<NormalPrepass>,
551                Has<MotionVectorPrepass>,
552                Has<DeferredPrepass>,
553                Has<DepthPrepassDoubleBuffer>,
554                Has<DeferredPrepassDoubleBuffer>,
555            ),
556            With<Camera3d>,
557        >,
558    >,
559    mut live_entities: Local<HashSet<RetainedViewEntity>>,
560    gpu_preprocessing_support: Res<GpuPreprocessingSupport>,
561) {
562    live_entities.clear();
563
564    for (
565        main_entity,
566        entity,
567        camera,
568        no_indirect_drawing,
569        depth_prepass,
570        normal_prepass,
571        motion_vector_prepass,
572        deferred_prepass,
573        depth_prepass_double_buffer,
574        deferred_prepass_double_buffer,
575    ) in cameras_3d.iter()
576    {
577        if !camera.is_active {
578            continue;
579        }
580
581        // If GPU culling is in use, use it (and indirect mode); otherwise, just
582        // preprocess the meshes.
583        let gpu_preprocessing_mode = gpu_preprocessing_support.min(if !no_indirect_drawing {
584            GpuPreprocessingMode::Culling
585        } else {
586            GpuPreprocessingMode::PreprocessingOnly
587        });
588
589        // This is the main 3D camera, so we use the first subview index (0).
590        let retained_view_entity = RetainedViewEntity::new(main_entity.into(), None, 0);
591
592        if depth_prepass || normal_prepass || motion_vector_prepass {
593            opaque_3d_prepass_phases
594                .prepare_for_new_frame(retained_view_entity, gpu_preprocessing_mode);
595            alpha_mask_3d_prepass_phases
596                .prepare_for_new_frame(retained_view_entity, gpu_preprocessing_mode);
597        } else {
598            opaque_3d_prepass_phases.remove(&retained_view_entity);
599            alpha_mask_3d_prepass_phases.remove(&retained_view_entity);
600        }
601
602        if deferred_prepass {
603            opaque_3d_deferred_phases
604                .prepare_for_new_frame(retained_view_entity, gpu_preprocessing_mode);
605            alpha_mask_3d_deferred_phases
606                .prepare_for_new_frame(retained_view_entity, gpu_preprocessing_mode);
607        } else {
608            opaque_3d_deferred_phases.remove(&retained_view_entity);
609            alpha_mask_3d_deferred_phases.remove(&retained_view_entity);
610        }
611        live_entities.insert(retained_view_entity);
612
613        // Add or remove prepasses as appropriate.
614
615        let mut camera_commands = commands
616            .get_entity(entity)
617            .expect("Camera entity wasn't synced.");
618
619        if depth_prepass {
620            camera_commands.insert(DepthPrepass);
621        } else {
622            camera_commands.remove::<DepthPrepass>();
623        }
624
625        if normal_prepass {
626            camera_commands.insert(NormalPrepass);
627        } else {
628            camera_commands.remove::<NormalPrepass>();
629        }
630
631        if motion_vector_prepass {
632            camera_commands.insert(MotionVectorPrepass);
633        } else {
634            camera_commands.remove::<MotionVectorPrepass>();
635        }
636
637        if deferred_prepass {
638            camera_commands.insert(DeferredPrepass);
639        } else {
640            camera_commands.remove::<DeferredPrepass>();
641        }
642
643        if depth_prepass_double_buffer {
644            camera_commands.insert(DepthPrepassDoubleBuffer);
645        } else {
646            camera_commands.remove::<DepthPrepassDoubleBuffer>();
647        }
648
649        if deferred_prepass_double_buffer {
650            camera_commands.insert(DeferredPrepassDoubleBuffer);
651        } else {
652            camera_commands.remove::<DeferredPrepassDoubleBuffer>();
653        }
654    }
655
656    opaque_3d_prepass_phases.retain(|view_entity, _| live_entities.contains(view_entity));
657    alpha_mask_3d_prepass_phases.retain(|view_entity, _| live_entities.contains(view_entity));
658    opaque_3d_deferred_phases.retain(|view_entity, _| live_entities.contains(view_entity));
659    alpha_mask_3d_deferred_phases.retain(|view_entity, _| live_entities.contains(view_entity));
660}
661
662pub fn prepare_core_3d_depth_textures(
663    mut commands: Commands,
664    mut texture_cache: ResMut<TextureCache>,
665    render_device: Res<RenderDevice>,
666    views_3d: Query<(
667        Entity,
668        &ExtractedCamera,
669        Option<&DepthPrepass>,
670        &Camera3d,
671        &Msaa,
672    )>,
673) {
674    let mut render_target_usage = <HashMap<_, _>>::default();
675    for (_, camera, depth_prepass, camera_3d, _msaa) in &views_3d {
676        // Default usage required to write to the depth texture
677        let mut usage: TextureUsages = camera_3d.depth_texture_usages.into();
678        if depth_prepass.is_some() {
679            // Required to read the output of the prepass
680            usage |= TextureUsages::COPY_SRC;
681        }
682        render_target_usage
683            .entry(camera.target.clone())
684            .and_modify(|u| *u |= usage)
685            .or_insert_with(|| usage);
686    }
687
688    let mut textures = <HashMap<_, _>>::default();
689    for (entity, camera, _, camera_3d, msaa) in &views_3d {
690        let Some(physical_target_size) = camera.physical_target_size else {
691            continue;
692        };
693
694        let cached_texture = textures
695            .entry((camera.target.clone(), msaa))
696            .or_insert_with(|| {
697                let usage = *render_target_usage
698                    .get(&camera.target.clone())
699                    .expect("The depth texture usage should already exist for this target");
700
701                let descriptor = TextureDescriptor {
702                    label: Some("view_depth_texture"),
703                    // The size of the depth texture
704                    size: physical_target_size.to_extents(),
705                    mip_level_count: 1,
706                    sample_count: msaa.samples(),
707                    dimension: TextureDimension::D2,
708                    format: CORE_3D_DEPTH_FORMAT,
709                    usage,
710                    view_formats: &[],
711                };
712
713                texture_cache.get(&render_device, descriptor)
714            })
715            .clone();
716
717        commands.entity(entity).insert(ViewDepthTexture::new(
718            cached_texture,
719            match camera_3d.depth_load_op {
720                Camera3dDepthLoadOp::Clear(v) => Some(v),
721                Camera3dDepthLoadOp::Load => None,
722            },
723        ));
724    }
725}
726
727/// Sets the `TEXTURE_BINDING` flag on the depth texture if necessary for
728/// occlusion culling.
729///
730/// We need that flag to be set in order to read from the texture.
731fn configure_occlusion_culling_view_targets(
732    mut view_targets: Query<
733        &mut Camera3d,
734        (
735            With<OcclusionCulling>,
736            Without<NoIndirectDrawing>,
737            With<DepthPrepass>,
738        ),
739    >,
740) {
741    for mut camera_3d in &mut view_targets {
742        let mut depth_texture_usages = TextureUsages::from(camera_3d.depth_texture_usages);
743        depth_texture_usages |= TextureUsages::TEXTURE_BINDING;
744        camera_3d.depth_texture_usages = depth_texture_usages.into();
745    }
746}
747
748// Disable MSAA and warn if using deferred rendering
749pub fn check_msaa(mut deferred_views: Query<&mut Msaa, (With<Camera>, With<DeferredPrepass>)>) {
750    for mut msaa in deferred_views.iter_mut() {
751        match *msaa {
752            Msaa::Off => (),
753            _ => {
754                warn!("MSAA is incompatible with deferred rendering and has been disabled.");
755                *msaa = Msaa::Off;
756            }
757        };
758    }
759}
760
761// Prepares the textures used by the prepass
762pub fn prepare_prepass_textures(
763    mut commands: Commands,
764    mut texture_cache: ResMut<TextureCache>,
765    render_device: Res<RenderDevice>,
766    frame_count: Res<FrameCount>,
767    opaque_3d_prepass_phases: Res<ViewBinnedRenderPhases<Opaque3dPrepass>>,
768    alpha_mask_3d_prepass_phases: Res<ViewBinnedRenderPhases<AlphaMask3dPrepass>>,
769    opaque_3d_deferred_phases: Res<ViewBinnedRenderPhases<Opaque3dDeferred>>,
770    alpha_mask_3d_deferred_phases: Res<ViewBinnedRenderPhases<AlphaMask3dDeferred>>,
771    views_3d: Query<(
772        Entity,
773        &ExtractedCamera,
774        &ExtractedView,
775        &Msaa,
776        Has<DepthPrepass>,
777        Has<NormalPrepass>,
778        Has<MotionVectorPrepass>,
779        Has<DeferredPrepass>,
780        Has<DepthPrepassDoubleBuffer>,
781        Has<DeferredPrepassDoubleBuffer>,
782    )>,
783) {
784    let mut depth_textures1 = <HashMap<_, _>>::default();
785    let mut depth_textures2 = <HashMap<_, _>>::default();
786    let mut normal_textures = <HashMap<_, _>>::default();
787    let mut deferred_textures1: HashMap<_, _> = <HashMap<_, _>>::default();
788    let mut deferred_textures2: HashMap<_, _> = <HashMap<_, _>>::default();
789    let mut deferred_lighting_id_textures = <HashMap<_, _>>::default();
790    let mut motion_vectors_textures = <HashMap<_, _>>::default();
791    for (
792        entity,
793        camera,
794        view,
795        msaa,
796        depth_prepass,
797        normal_prepass,
798        motion_vector_prepass,
799        deferred_prepass,
800        depth_prepass_double_buffer,
801        deferred_prepass_double_buffer,
802    ) in &views_3d
803    {
804        if !opaque_3d_prepass_phases.contains_key(&view.retained_view_entity)
805            && !alpha_mask_3d_prepass_phases.contains_key(&view.retained_view_entity)
806            && !opaque_3d_deferred_phases.contains_key(&view.retained_view_entity)
807            && !alpha_mask_3d_deferred_phases.contains_key(&view.retained_view_entity)
808        {
809            commands.entity(entity).remove::<ViewPrepassTextures>();
810            continue;
811        };
812
813        let Some(physical_target_size) = camera.physical_target_size else {
814            continue;
815        };
816
817        let size = physical_target_size.to_extents();
818
819        let cached_depth_texture1 = depth_prepass.then(|| {
820            depth_textures1
821                .entry(camera.target.clone())
822                .or_insert_with(|| {
823                    let descriptor = TextureDescriptor {
824                        label: Some("prepass_depth_texture_1"),
825                        size,
826                        mip_level_count: 1,
827                        sample_count: msaa.samples(),
828                        dimension: TextureDimension::D2,
829                        format: CORE_3D_DEPTH_FORMAT,
830                        usage: TextureUsages::COPY_DST
831                            | TextureUsages::RENDER_ATTACHMENT
832                            | TextureUsages::TEXTURE_BINDING,
833                        view_formats: &[],
834                    };
835                    texture_cache.get(&render_device, descriptor)
836                })
837                .clone()
838        });
839
840        let cached_depth_texture2 = depth_prepass_double_buffer.then(|| {
841            depth_textures2
842                .entry(camera.target.clone())
843                .or_insert_with(|| {
844                    let descriptor = TextureDescriptor {
845                        label: Some("prepass_depth_texture_2"),
846                        size,
847                        mip_level_count: 1,
848                        sample_count: msaa.samples(),
849                        dimension: TextureDimension::D2,
850                        format: CORE_3D_DEPTH_FORMAT,
851                        usage: TextureUsages::COPY_DST
852                            | TextureUsages::RENDER_ATTACHMENT
853                            | TextureUsages::TEXTURE_BINDING,
854                        view_formats: &[],
855                    };
856                    texture_cache.get(&render_device, descriptor)
857                })
858                .clone()
859        });
860
861        let cached_normals_texture = normal_prepass.then(|| {
862            normal_textures
863                .entry(camera.target.clone())
864                .or_insert_with(|| {
865                    texture_cache.get(
866                        &render_device,
867                        TextureDescriptor {
868                            label: Some("prepass_normal_texture"),
869                            size,
870                            mip_level_count: 1,
871                            sample_count: msaa.samples(),
872                            dimension: TextureDimension::D2,
873                            format: NORMAL_PREPASS_FORMAT,
874                            usage: TextureUsages::RENDER_ATTACHMENT
875                                | TextureUsages::TEXTURE_BINDING,
876                            view_formats: &[],
877                        },
878                    )
879                })
880                .clone()
881        });
882
883        let cached_motion_vectors_texture = motion_vector_prepass.then(|| {
884            motion_vectors_textures
885                .entry(camera.target.clone())
886                .or_insert_with(|| {
887                    texture_cache.get(
888                        &render_device,
889                        TextureDescriptor {
890                            label: Some("prepass_motion_vectors_textures"),
891                            size,
892                            mip_level_count: 1,
893                            sample_count: msaa.samples(),
894                            dimension: TextureDimension::D2,
895                            format: MOTION_VECTOR_PREPASS_FORMAT,
896                            usage: TextureUsages::RENDER_ATTACHMENT
897                                | TextureUsages::TEXTURE_BINDING,
898                            view_formats: &[],
899                        },
900                    )
901                })
902                .clone()
903        });
904
905        let cached_deferred_texture1 = deferred_prepass.then(|| {
906            deferred_textures1
907                .entry(camera.target.clone())
908                .or_insert_with(|| {
909                    texture_cache.get(
910                        &render_device,
911                        TextureDescriptor {
912                            label: Some("prepass_deferred_texture_1"),
913                            size,
914                            mip_level_count: 1,
915                            sample_count: 1,
916                            dimension: TextureDimension::D2,
917                            format: DEFERRED_PREPASS_FORMAT,
918                            usage: TextureUsages::RENDER_ATTACHMENT
919                                | TextureUsages::TEXTURE_BINDING,
920                            view_formats: &[],
921                        },
922                    )
923                })
924                .clone()
925        });
926
927        let cached_deferred_texture2 = deferred_prepass_double_buffer.then(|| {
928            deferred_textures2
929                .entry(camera.target.clone())
930                .or_insert_with(|| {
931                    texture_cache.get(
932                        &render_device,
933                        TextureDescriptor {
934                            label: Some("prepass_deferred_texture_2"),
935                            size,
936                            mip_level_count: 1,
937                            sample_count: 1,
938                            dimension: TextureDimension::D2,
939                            format: DEFERRED_PREPASS_FORMAT,
940                            usage: TextureUsages::RENDER_ATTACHMENT
941                                | TextureUsages::TEXTURE_BINDING,
942                            view_formats: &[],
943                        },
944                    )
945                })
946                .clone()
947        });
948
949        let cached_deferred_lighting_pass_id_texture = deferred_prepass.then(|| {
950            deferred_lighting_id_textures
951                .entry(camera.target.clone())
952                .or_insert_with(|| {
953                    texture_cache.get(
954                        &render_device,
955                        TextureDescriptor {
956                            label: Some("deferred_lighting_pass_id_texture"),
957                            size,
958                            mip_level_count: 1,
959                            sample_count: 1,
960                            dimension: TextureDimension::D2,
961                            format: DEFERRED_LIGHTING_PASS_ID_FORMAT,
962                            usage: TextureUsages::RENDER_ATTACHMENT
963                                | TextureUsages::TEXTURE_BINDING,
964                            view_formats: &[],
965                        },
966                    )
967                })
968                .clone()
969        });
970
971        commands.entity(entity).insert(ViewPrepassTextures {
972            depth: package_double_buffered_texture(
973                cached_depth_texture1,
974                cached_depth_texture2,
975                frame_count.0,
976            ),
977            normal: cached_normals_texture
978                .map(|t| ColorAttachment::new(t, None, None, Some(LinearRgba::BLACK.into()))),
979            // Red and Green channels are X and Y components of the motion vectors
980            // Blue channel doesn't matter, but set to 0.0 for possible faster clear
981            // https://gpuopen.com/performance/#clears
982            motion_vectors: cached_motion_vectors_texture
983                .map(|t| ColorAttachment::new(t, None, None, Some(LinearRgba::BLACK.into()))),
984            deferred: package_double_buffered_texture(
985                cached_deferred_texture1,
986                cached_deferred_texture2,
987                frame_count.0,
988            ),
989            deferred_lighting_pass_id: cached_deferred_lighting_pass_id_texture
990                .map(|t| ColorAttachment::new(t, None, None, Some(LinearRgba::BLACK.into()))),
991            size,
992        });
993    }
994}
995
996fn package_double_buffered_texture(
997    texture1: Option<CachedTexture>,
998    texture2: Option<CachedTexture>,
999    frame_count: u32,
1000) -> Option<ColorAttachment> {
1001    match (texture1, texture2) {
1002        (Some(t1), None) => Some(ColorAttachment::new(
1003            t1,
1004            None,
1005            None,
1006            Some(LinearRgba::BLACK.into()),
1007        )),
1008        (Some(t1), Some(t2)) if frame_count.is_multiple_of(2) => Some(ColorAttachment::new(
1009            t1,
1010            None,
1011            Some(t2),
1012            Some(LinearRgba::BLACK.into()),
1013        )),
1014        (Some(t1), Some(t2)) => Some(ColorAttachment::new(
1015            t2,
1016            None,
1017            Some(t1),
1018            Some(LinearRgba::BLACK.into()),
1019        )),
1020        _ => None,
1021    }
1022}