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