bevy_core_pipeline/core_3d/
mod.rs

1mod main_opaque_pass_3d_node;
2mod main_transmissive_pass_3d_node;
3mod main_transparent_pass_3d_node;
4
5pub mod graph {
6    use bevy_render::render_graph::{RenderLabel, RenderSubGraph};
7
8    #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderSubGraph)]
9    pub struct Core3d;
10
11    pub mod input {
12        pub const VIEW_ENTITY: &str = "view_entity";
13    }
14
15    #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)]
16    pub enum Node3d {
17        MsaaWriteback,
18        EarlyPrepass,
19        EarlyDownsampleDepth,
20        LatePrepass,
21        EarlyDeferredPrepass,
22        LateDeferredPrepass,
23        CopyDeferredLightingId,
24        EndPrepasses,
25        StartMainPass,
26        MainOpaquePass,
27        MainTransmissivePass,
28        MainTransparentPass,
29        EndMainPass,
30        Wireframe,
31        StartMainPassPostProcessing,
32        LateDownsampleDepth,
33        MotionBlur,
34        Taa,
35        DlssSuperResolution,
36        DlssRayReconstruction,
37        Bloom,
38        AutoExposure,
39        DepthOfField,
40        PostProcessing,
41        Tonemapping,
42        Fxaa,
43        Smaa,
44        Upscaling,
45        ContrastAdaptiveSharpening,
46        EndMainPassPostProcessing,
47    }
48}
49
50// PERF: vulkan docs recommend using 24 bit depth for better performance
51pub const CORE_3D_DEPTH_FORMAT: TextureFormat = TextureFormat::Depth32Float;
52
53/// True if multisampled depth textures are supported on this platform.
54///
55/// In theory, Naga supports depth textures on WebGL 2. In practice, it doesn't,
56/// because of a silly bug whereby Naga assumes that all depth textures are
57/// `sampler2DShadow` and will cheerfully generate invalid GLSL that tries to
58/// perform non-percentage-closer-filtering with such a sampler. Therefore we
59/// disable depth of field and screen space reflections entirely on WebGL 2.
60#[cfg(not(any(feature = "webgpu", not(target_arch = "wasm32"))))]
61pub const DEPTH_TEXTURE_SAMPLING_SUPPORTED: bool = false;
62
63/// True if multisampled depth textures are supported on this platform.
64///
65/// In theory, Naga supports depth textures on WebGL 2. In practice, it doesn't,
66/// because of a silly bug whereby Naga assumes that all depth textures are
67/// `sampler2DShadow` and will cheerfully generate invalid GLSL that tries to
68/// perform non-percentage-closer-filtering with such a sampler. Therefore we
69/// disable depth of field and screen space reflections entirely on WebGL 2.
70#[cfg(any(feature = "webgpu", not(target_arch = "wasm32")))]
71pub const DEPTH_TEXTURE_SAMPLING_SUPPORTED: bool = true;
72
73use core::ops::Range;
74
75use bevy_camera::{Camera, Camera3d, Camera3dDepthLoadOp};
76use bevy_diagnostic::FrameCount;
77use bevy_render::{
78    batching::gpu_preprocessing::{GpuPreprocessingMode, GpuPreprocessingSupport},
79    camera::CameraRenderGraph,
80    experimental::occlusion_culling::OcclusionCulling,
81    mesh::allocator::SlabId,
82    render_phase::PhaseItemBatchSetKey,
83    texture::CachedTexture,
84    view::{prepare_view_targets, NoIndirectDrawing, RetainedViewEntity},
85};
86pub use main_opaque_pass_3d_node::*;
87pub use main_transparent_pass_3d_node::*;
88
89use bevy_app::{App, Plugin, PostUpdate};
90use bevy_asset::UntypedAssetId;
91use bevy_color::LinearRgba;
92use bevy_ecs::prelude::*;
93use bevy_image::{BevyDefault, ToExtents};
94use bevy_math::FloatOrd;
95use bevy_platform::collections::{HashMap, HashSet};
96use bevy_render::{
97    camera::ExtractedCamera,
98    extract_component::ExtractComponentPlugin,
99    prelude::Msaa,
100    render_graph::{EmptyNode, RenderGraphExt, ViewNodeRunner},
101    render_phase::{
102        sort_phase_system, BinnedPhaseItem, CachedRenderPipelinePhaseItem, DrawFunctionId,
103        DrawFunctions, PhaseItem, PhaseItemExtraIndex, SortedPhaseItem, ViewBinnedRenderPhases,
104        ViewSortedRenderPhases,
105    },
106    render_resource::{
107        CachedRenderPipelineId, FilterMode, Sampler, SamplerDescriptor, Texture, TextureDescriptor,
108        TextureDimension, TextureFormat, TextureUsages, TextureView,
109    },
110    renderer::RenderDevice,
111    sync_world::{MainEntity, RenderEntity},
112    texture::{ColorAttachment, TextureCache},
113    view::{ExtractedView, ViewDepthTexture, ViewTarget},
114    Extract, ExtractSchedule, Render, RenderApp, RenderSystems,
115};
116use nonmax::NonMaxU32;
117use tracing::warn;
118
119use crate::{
120    core_3d::main_transmissive_pass_3d_node::MainTransmissivePass3dNode,
121    deferred::{
122        copy_lighting_id::CopyDeferredLightingIdNode,
123        node::{EarlyDeferredGBufferPrepassNode, LateDeferredGBufferPrepassNode},
124        AlphaMask3dDeferred, Opaque3dDeferred, DEFERRED_LIGHTING_PASS_ID_FORMAT,
125        DEFERRED_PREPASS_FORMAT,
126    },
127    prepass::{
128        node::{EarlyPrepassNode, LatePrepassNode},
129        AlphaMask3dPrepass, DeferredPrepass, DeferredPrepassDoubleBuffer, DepthPrepass,
130        DepthPrepassDoubleBuffer, MotionVectorPrepass, NormalPrepass, Opaque3dPrepass,
131        OpaqueNoLightmap3dBatchSetKey, OpaqueNoLightmap3dBinKey, ViewPrepassTextures,
132        MOTION_VECTOR_PREPASS_FORMAT, NORMAL_PREPASS_FORMAT,
133    },
134    skybox::SkyboxPlugin,
135    tonemapping::{DebandDither, Tonemapping, TonemappingNode},
136    upscaling::UpscalingNode,
137};
138
139use self::graph::{Core3d, Node3d};
140
141pub struct Core3dPlugin;
142
143impl Plugin for Core3dPlugin {
144    fn build(&self, app: &mut App) {
145        app.register_required_components_with::<Camera3d, DebandDither>(|| DebandDither::Enabled)
146            .register_required_components_with::<Camera3d, CameraRenderGraph>(|| {
147                CameraRenderGraph::new(Core3d)
148            })
149            .register_required_components::<Camera3d, Tonemapping>()
150            .add_plugins((SkyboxPlugin, ExtractComponentPlugin::<Camera3d>::default()))
151            .add_systems(PostUpdate, check_msaa);
152
153        let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
154            return;
155        };
156        render_app
157            .init_resource::<DrawFunctions<Opaque3d>>()
158            .init_resource::<DrawFunctions<AlphaMask3d>>()
159            .init_resource::<DrawFunctions<Transmissive3d>>()
160            .init_resource::<DrawFunctions<Transparent3d>>()
161            .init_resource::<DrawFunctions<Opaque3dPrepass>>()
162            .init_resource::<DrawFunctions<AlphaMask3dPrepass>>()
163            .init_resource::<DrawFunctions<Opaque3dDeferred>>()
164            .init_resource::<DrawFunctions<AlphaMask3dDeferred>>()
165            .init_resource::<ViewBinnedRenderPhases<Opaque3d>>()
166            .init_resource::<ViewBinnedRenderPhases<AlphaMask3d>>()
167            .init_resource::<ViewBinnedRenderPhases<Opaque3dPrepass>>()
168            .init_resource::<ViewBinnedRenderPhases<AlphaMask3dPrepass>>()
169            .init_resource::<ViewBinnedRenderPhases<Opaque3dDeferred>>()
170            .init_resource::<ViewBinnedRenderPhases<AlphaMask3dDeferred>>()
171            .init_resource::<ViewSortedRenderPhases<Transmissive3d>>()
172            .init_resource::<ViewSortedRenderPhases<Transparent3d>>()
173            .add_systems(ExtractSchedule, extract_core_3d_camera_phases)
174            .add_systems(ExtractSchedule, extract_camera_prepass_phase)
175            .add_systems(
176                Render,
177                (
178                    sort_phase_system::<Transmissive3d>.in_set(RenderSystems::PhaseSort),
179                    sort_phase_system::<Transparent3d>.in_set(RenderSystems::PhaseSort),
180                    configure_occlusion_culling_view_targets
181                        .after(prepare_view_targets)
182                        .in_set(RenderSystems::ManageViews),
183                    prepare_core_3d_depth_textures.in_set(RenderSystems::PrepareResources),
184                    prepare_core_3d_transmission_textures.in_set(RenderSystems::PrepareResources),
185                    prepare_prepass_textures.in_set(RenderSystems::PrepareResources),
186                ),
187            );
188
189        render_app
190            .add_render_sub_graph(Core3d)
191            .add_render_graph_node::<ViewNodeRunner<EarlyPrepassNode>>(Core3d, Node3d::EarlyPrepass)
192            .add_render_graph_node::<ViewNodeRunner<LatePrepassNode>>(Core3d, Node3d::LatePrepass)
193            .add_render_graph_node::<ViewNodeRunner<EarlyDeferredGBufferPrepassNode>>(
194                Core3d,
195                Node3d::EarlyDeferredPrepass,
196            )
197            .add_render_graph_node::<ViewNodeRunner<LateDeferredGBufferPrepassNode>>(
198                Core3d,
199                Node3d::LateDeferredPrepass,
200            )
201            .add_render_graph_node::<ViewNodeRunner<CopyDeferredLightingIdNode>>(
202                Core3d,
203                Node3d::CopyDeferredLightingId,
204            )
205            .add_render_graph_node::<EmptyNode>(Core3d, Node3d::EndPrepasses)
206            .add_render_graph_node::<EmptyNode>(Core3d, Node3d::StartMainPass)
207            .add_render_graph_node::<ViewNodeRunner<MainOpaquePass3dNode>>(
208                Core3d,
209                Node3d::MainOpaquePass,
210            )
211            .add_render_graph_node::<ViewNodeRunner<MainTransmissivePass3dNode>>(
212                Core3d,
213                Node3d::MainTransmissivePass,
214            )
215            .add_render_graph_node::<ViewNodeRunner<MainTransparentPass3dNode>>(
216                Core3d,
217                Node3d::MainTransparentPass,
218            )
219            .add_render_graph_node::<EmptyNode>(Core3d, Node3d::EndMainPass)
220            .add_render_graph_node::<EmptyNode>(Core3d, Node3d::StartMainPassPostProcessing)
221            .add_render_graph_node::<ViewNodeRunner<TonemappingNode>>(Core3d, Node3d::Tonemapping)
222            .add_render_graph_node::<EmptyNode>(Core3d, Node3d::EndMainPassPostProcessing)
223            .add_render_graph_node::<ViewNodeRunner<UpscalingNode>>(Core3d, Node3d::Upscaling)
224            .add_render_graph_edges(
225                Core3d,
226                (
227                    Node3d::EarlyPrepass,
228                    Node3d::EarlyDeferredPrepass,
229                    Node3d::LatePrepass,
230                    Node3d::LateDeferredPrepass,
231                    Node3d::CopyDeferredLightingId,
232                    Node3d::EndPrepasses,
233                    Node3d::StartMainPass,
234                    Node3d::MainOpaquePass,
235                    Node3d::MainTransmissivePass,
236                    Node3d::MainTransparentPass,
237                    Node3d::EndMainPass,
238                    Node3d::StartMainPassPostProcessing,
239                    Node3d::Tonemapping,
240                    Node3d::EndMainPassPostProcessing,
241                    Node3d::Upscaling,
242                ),
243            );
244    }
245}
246
247/// Opaque 3D [`BinnedPhaseItem`]s.
248pub struct Opaque3d {
249    /// Determines which objects can be placed into a *batch set*.
250    ///
251    /// Objects in a single batch set can potentially be multi-drawn together,
252    /// if it's enabled and the current platform supports it.
253    pub batch_set_key: Opaque3dBatchSetKey,
254    /// The key, which determines which can be batched.
255    pub bin_key: Opaque3dBinKey,
256    /// An entity from which data will be fetched, including the mesh if
257    /// applicable.
258    pub representative_entity: (Entity, MainEntity),
259    /// The ranges of instances.
260    pub batch_range: Range<u32>,
261    /// An extra index, which is either a dynamic offset or an index in the
262    /// indirect parameters list.
263    pub extra_index: PhaseItemExtraIndex,
264}
265
266/// Information that must be identical in order to place opaque meshes in the
267/// same *batch set*.
268///
269/// A batch set is a set of batches that can be multi-drawn together, if
270/// multi-draw is in use.
271#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
272pub struct Opaque3dBatchSetKey {
273    /// The identifier of the render pipeline.
274    pub pipeline: CachedRenderPipelineId,
275
276    /// The function used to draw.
277    pub draw_function: DrawFunctionId,
278
279    /// The ID of a bind group specific to the material instance.
280    ///
281    /// In the case of PBR, this is the `MaterialBindGroupIndex`.
282    pub material_bind_group_index: Option<u32>,
283
284    /// The ID of the slab of GPU memory that contains vertex data.
285    ///
286    /// For non-mesh items, you can fill this with 0 if your items can be
287    /// multi-drawn, or with a unique value if they can't.
288    pub vertex_slab: SlabId,
289
290    /// The ID of the slab of GPU memory that contains index data, if present.
291    ///
292    /// For non-mesh items, you can safely fill this with `None`.
293    pub index_slab: Option<SlabId>,
294
295    /// Index of the slab that the lightmap resides in, if a lightmap is
296    /// present.
297    pub lightmap_slab: Option<NonMaxU32>,
298}
299
300impl PhaseItemBatchSetKey for Opaque3dBatchSetKey {
301    fn indexed(&self) -> bool {
302        self.index_slab.is_some()
303    }
304}
305
306/// Data that must be identical in order to *batch* phase items together.
307///
308/// Note that a *batch set* (if multi-draw is in use) contains multiple batches.
309#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
310pub struct Opaque3dBinKey {
311    /// The asset that this phase item is associated with.
312    ///
313    /// Normally, this is the ID of the mesh, but for non-mesh items it might be
314    /// the ID of another type of asset.
315    pub asset_id: UntypedAssetId,
316}
317
318impl PhaseItem for Opaque3d {
319    #[inline]
320    fn entity(&self) -> Entity {
321        self.representative_entity.0
322    }
323
324    #[inline]
325    fn main_entity(&self) -> MainEntity {
326        self.representative_entity.1
327    }
328
329    #[inline]
330    fn draw_function(&self) -> DrawFunctionId {
331        self.batch_set_key.draw_function
332    }
333
334    #[inline]
335    fn batch_range(&self) -> &Range<u32> {
336        &self.batch_range
337    }
338
339    #[inline]
340    fn batch_range_mut(&mut self) -> &mut Range<u32> {
341        &mut self.batch_range
342    }
343
344    fn extra_index(&self) -> PhaseItemExtraIndex {
345        self.extra_index.clone()
346    }
347
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 Opaque3d {
354    type BatchSetKey = Opaque3dBatchSetKey;
355    type BinKey = Opaque3dBinKey;
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        Opaque3d {
366            batch_set_key,
367            bin_key,
368            representative_entity,
369            batch_range,
370            extra_index,
371        }
372    }
373}
374
375impl CachedRenderPipelinePhaseItem for Opaque3d {
376    #[inline]
377    fn cached_pipeline(&self) -> CachedRenderPipelineId {
378        self.batch_set_key.pipeline
379    }
380}
381
382pub struct AlphaMask3d {
383    /// Determines which objects can be placed into a *batch set*.
384    ///
385    /// Objects in a single batch set can potentially be multi-drawn together,
386    /// if it's enabled and the current platform supports it.
387    pub batch_set_key: OpaqueNoLightmap3dBatchSetKey,
388    /// The key, which determines which can be batched.
389    pub bin_key: OpaqueNoLightmap3dBinKey,
390    pub representative_entity: (Entity, MainEntity),
391    pub batch_range: Range<u32>,
392    pub extra_index: PhaseItemExtraIndex,
393}
394
395impl PhaseItem for AlphaMask3d {
396    #[inline]
397    fn entity(&self) -> Entity {
398        self.representative_entity.0
399    }
400
401    fn main_entity(&self) -> MainEntity {
402        self.representative_entity.1
403    }
404
405    #[inline]
406    fn draw_function(&self) -> DrawFunctionId {
407        self.batch_set_key.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 BinnedPhaseItem for AlphaMask3d {
432    type BinKey = OpaqueNoLightmap3dBinKey;
433    type BatchSetKey = OpaqueNoLightmap3dBatchSetKey;
434
435    #[inline]
436    fn new(
437        batch_set_key: Self::BatchSetKey,
438        bin_key: Self::BinKey,
439        representative_entity: (Entity, MainEntity),
440        batch_range: Range<u32>,
441        extra_index: PhaseItemExtraIndex,
442    ) -> Self {
443        Self {
444            batch_set_key,
445            bin_key,
446            representative_entity,
447            batch_range,
448            extra_index,
449        }
450    }
451}
452
453impl CachedRenderPipelinePhaseItem for AlphaMask3d {
454    #[inline]
455    fn cached_pipeline(&self) -> CachedRenderPipelineId {
456        self.batch_set_key.pipeline
457    }
458}
459
460pub struct Transmissive3d {
461    pub distance: f32,
462    pub pipeline: CachedRenderPipelineId,
463    pub entity: (Entity, MainEntity),
464    pub draw_function: DrawFunctionId,
465    pub batch_range: Range<u32>,
466    pub extra_index: PhaseItemExtraIndex,
467    /// Whether the mesh in question is indexed (uses an index buffer in
468    /// addition to its vertex buffer).
469    pub indexed: bool,
470}
471
472impl PhaseItem for Transmissive3d {
473    /// For now, automatic batching is disabled for transmissive items because their rendering is
474    /// split into multiple steps depending on [`Camera3d::screen_space_specular_transmission_steps`],
475    /// which the batching system doesn't currently know about.
476    ///
477    /// Having batching enabled would cause the same item to be drawn multiple times across different
478    /// steps, whenever the batching range crossed a step boundary.
479    ///
480    /// Eventually, we could add support for this by having the batching system break up the batch ranges
481    /// using the same logic as the transmissive pass, but for now it's simpler to just disable batching.
482    const AUTOMATIC_BATCHING: bool = false;
483
484    #[inline]
485    fn entity(&self) -> Entity {
486        self.entity.0
487    }
488
489    #[inline]
490    fn main_entity(&self) -> MainEntity {
491        self.entity.1
492    }
493
494    #[inline]
495    fn draw_function(&self) -> DrawFunctionId {
496        self.draw_function
497    }
498
499    #[inline]
500    fn batch_range(&self) -> &Range<u32> {
501        &self.batch_range
502    }
503
504    #[inline]
505    fn batch_range_mut(&mut self) -> &mut Range<u32> {
506        &mut self.batch_range
507    }
508
509    #[inline]
510    fn extra_index(&self) -> PhaseItemExtraIndex {
511        self.extra_index.clone()
512    }
513
514    #[inline]
515    fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
516        (&mut self.batch_range, &mut self.extra_index)
517    }
518}
519
520impl SortedPhaseItem for Transmissive3d {
521    // NOTE: Values increase towards the camera. Back-to-front ordering for transmissive means we need an ascending sort.
522    type SortKey = FloatOrd;
523
524    #[inline]
525    fn sort_key(&self) -> Self::SortKey {
526        FloatOrd(self.distance)
527    }
528
529    #[inline]
530    fn sort(items: &mut [Self]) {
531        radsort::sort_by_key(items, |item| item.distance);
532    }
533
534    #[inline]
535    fn indexed(&self) -> bool {
536        self.indexed
537    }
538}
539
540impl CachedRenderPipelinePhaseItem for Transmissive3d {
541    #[inline]
542    fn cached_pipeline(&self) -> CachedRenderPipelineId {
543        self.pipeline
544    }
545}
546
547pub struct Transparent3d {
548    pub distance: f32,
549    pub pipeline: CachedRenderPipelineId,
550    pub entity: (Entity, MainEntity),
551    pub draw_function: DrawFunctionId,
552    pub batch_range: Range<u32>,
553    pub extra_index: PhaseItemExtraIndex,
554    /// Whether the mesh in question is indexed (uses an index buffer in
555    /// addition to its vertex buffer).
556    pub indexed: bool,
557}
558
559impl PhaseItem for Transparent3d {
560    #[inline]
561    fn entity(&self) -> Entity {
562        self.entity.0
563    }
564
565    fn main_entity(&self) -> MainEntity {
566        self.entity.1
567    }
568
569    #[inline]
570    fn draw_function(&self) -> DrawFunctionId {
571        self.draw_function
572    }
573
574    #[inline]
575    fn batch_range(&self) -> &Range<u32> {
576        &self.batch_range
577    }
578
579    #[inline]
580    fn batch_range_mut(&mut self) -> &mut Range<u32> {
581        &mut self.batch_range
582    }
583
584    #[inline]
585    fn extra_index(&self) -> PhaseItemExtraIndex {
586        self.extra_index.clone()
587    }
588
589    #[inline]
590    fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
591        (&mut self.batch_range, &mut self.extra_index)
592    }
593}
594
595impl SortedPhaseItem for Transparent3d {
596    // NOTE: Values increase towards the camera. Back-to-front ordering for transparent means we need an ascending sort.
597    type SortKey = FloatOrd;
598
599    #[inline]
600    fn sort_key(&self) -> Self::SortKey {
601        FloatOrd(self.distance)
602    }
603
604    #[inline]
605    fn sort(items: &mut [Self]) {
606        radsort::sort_by_key(items, |item| item.distance);
607    }
608
609    #[inline]
610    fn indexed(&self) -> bool {
611        self.indexed
612    }
613}
614
615impl CachedRenderPipelinePhaseItem for Transparent3d {
616    #[inline]
617    fn cached_pipeline(&self) -> CachedRenderPipelineId {
618        self.pipeline
619    }
620}
621
622pub fn extract_core_3d_camera_phases(
623    mut opaque_3d_phases: ResMut<ViewBinnedRenderPhases<Opaque3d>>,
624    mut alpha_mask_3d_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3d>>,
625    mut transmissive_3d_phases: ResMut<ViewSortedRenderPhases<Transmissive3d>>,
626    mut transparent_3d_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>,
627    cameras_3d: Extract<Query<(Entity, &Camera, Has<NoIndirectDrawing>), With<Camera3d>>>,
628    mut live_entities: Local<HashSet<RetainedViewEntity>>,
629    gpu_preprocessing_support: Res<GpuPreprocessingSupport>,
630) {
631    live_entities.clear();
632
633    for (main_entity, camera, no_indirect_drawing) in &cameras_3d {
634        if !camera.is_active {
635            continue;
636        }
637
638        // If GPU culling is in use, use it (and indirect mode); otherwise, just
639        // preprocess the meshes.
640        let gpu_preprocessing_mode = gpu_preprocessing_support.min(if !no_indirect_drawing {
641            GpuPreprocessingMode::Culling
642        } else {
643            GpuPreprocessingMode::PreprocessingOnly
644        });
645
646        // This is the main 3D camera, so use the first subview index (0).
647        let retained_view_entity = RetainedViewEntity::new(main_entity.into(), None, 0);
648
649        opaque_3d_phases.prepare_for_new_frame(retained_view_entity, gpu_preprocessing_mode);
650        alpha_mask_3d_phases.prepare_for_new_frame(retained_view_entity, gpu_preprocessing_mode);
651        transmissive_3d_phases.insert_or_clear(retained_view_entity);
652        transparent_3d_phases.insert_or_clear(retained_view_entity);
653
654        live_entities.insert(retained_view_entity);
655    }
656
657    opaque_3d_phases.retain(|view_entity, _| live_entities.contains(view_entity));
658    alpha_mask_3d_phases.retain(|view_entity, _| live_entities.contains(view_entity));
659    transmissive_3d_phases.retain(|view_entity, _| live_entities.contains(view_entity));
660    transparent_3d_phases.retain(|view_entity, _| live_entities.contains(view_entity));
661}
662
663// Extract the render phases for the prepass
664
665pub fn extract_camera_prepass_phase(
666    mut commands: Commands,
667    mut opaque_3d_prepass_phases: ResMut<ViewBinnedRenderPhases<Opaque3dPrepass>>,
668    mut alpha_mask_3d_prepass_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3dPrepass>>,
669    mut opaque_3d_deferred_phases: ResMut<ViewBinnedRenderPhases<Opaque3dDeferred>>,
670    mut alpha_mask_3d_deferred_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3dDeferred>>,
671    cameras_3d: Extract<
672        Query<
673            (
674                Entity,
675                RenderEntity,
676                &Camera,
677                Has<NoIndirectDrawing>,
678                Has<DepthPrepass>,
679                Has<NormalPrepass>,
680                Has<MotionVectorPrepass>,
681                Has<DeferredPrepass>,
682                Has<DepthPrepassDoubleBuffer>,
683                Has<DeferredPrepassDoubleBuffer>,
684            ),
685            With<Camera3d>,
686        >,
687    >,
688    mut live_entities: Local<HashSet<RetainedViewEntity>>,
689    gpu_preprocessing_support: Res<GpuPreprocessingSupport>,
690) {
691    live_entities.clear();
692
693    for (
694        main_entity,
695        entity,
696        camera,
697        no_indirect_drawing,
698        depth_prepass,
699        normal_prepass,
700        motion_vector_prepass,
701        deferred_prepass,
702        depth_prepass_double_buffer,
703        deferred_prepass_double_buffer,
704    ) in cameras_3d.iter()
705    {
706        if !camera.is_active {
707            continue;
708        }
709
710        // If GPU culling is in use, use it (and indirect mode); otherwise, just
711        // preprocess the meshes.
712        let gpu_preprocessing_mode = gpu_preprocessing_support.min(if !no_indirect_drawing {
713            GpuPreprocessingMode::Culling
714        } else {
715            GpuPreprocessingMode::PreprocessingOnly
716        });
717
718        // This is the main 3D camera, so we use the first subview index (0).
719        let retained_view_entity = RetainedViewEntity::new(main_entity.into(), None, 0);
720
721        if depth_prepass || normal_prepass || motion_vector_prepass {
722            opaque_3d_prepass_phases
723                .prepare_for_new_frame(retained_view_entity, gpu_preprocessing_mode);
724            alpha_mask_3d_prepass_phases
725                .prepare_for_new_frame(retained_view_entity, gpu_preprocessing_mode);
726        } else {
727            opaque_3d_prepass_phases.remove(&retained_view_entity);
728            alpha_mask_3d_prepass_phases.remove(&retained_view_entity);
729        }
730
731        if deferred_prepass {
732            opaque_3d_deferred_phases
733                .prepare_for_new_frame(retained_view_entity, gpu_preprocessing_mode);
734            alpha_mask_3d_deferred_phases
735                .prepare_for_new_frame(retained_view_entity, gpu_preprocessing_mode);
736        } else {
737            opaque_3d_deferred_phases.remove(&retained_view_entity);
738            alpha_mask_3d_deferred_phases.remove(&retained_view_entity);
739        }
740        live_entities.insert(retained_view_entity);
741
742        // Add or remove prepasses as appropriate.
743
744        let mut camera_commands = commands
745            .get_entity(entity)
746            .expect("Camera entity wasn't synced.");
747
748        if depth_prepass {
749            camera_commands.insert(DepthPrepass);
750        } else {
751            camera_commands.remove::<DepthPrepass>();
752        }
753
754        if normal_prepass {
755            camera_commands.insert(NormalPrepass);
756        } else {
757            camera_commands.remove::<NormalPrepass>();
758        }
759
760        if motion_vector_prepass {
761            camera_commands.insert(MotionVectorPrepass);
762        } else {
763            camera_commands.remove::<MotionVectorPrepass>();
764        }
765
766        if deferred_prepass {
767            camera_commands.insert(DeferredPrepass);
768        } else {
769            camera_commands.remove::<DeferredPrepass>();
770        }
771
772        if depth_prepass_double_buffer {
773            camera_commands.insert(DepthPrepassDoubleBuffer);
774        } else {
775            camera_commands.remove::<DepthPrepassDoubleBuffer>();
776        }
777
778        if deferred_prepass_double_buffer {
779            camera_commands.insert(DeferredPrepassDoubleBuffer);
780        } else {
781            camera_commands.remove::<DeferredPrepassDoubleBuffer>();
782        }
783    }
784
785    opaque_3d_prepass_phases.retain(|view_entity, _| live_entities.contains(view_entity));
786    alpha_mask_3d_prepass_phases.retain(|view_entity, _| live_entities.contains(view_entity));
787    opaque_3d_deferred_phases.retain(|view_entity, _| live_entities.contains(view_entity));
788    alpha_mask_3d_deferred_phases.retain(|view_entity, _| live_entities.contains(view_entity));
789}
790
791pub fn prepare_core_3d_depth_textures(
792    mut commands: Commands,
793    mut texture_cache: ResMut<TextureCache>,
794    render_device: Res<RenderDevice>,
795    opaque_3d_phases: Res<ViewBinnedRenderPhases<Opaque3d>>,
796    alpha_mask_3d_phases: Res<ViewBinnedRenderPhases<AlphaMask3d>>,
797    transmissive_3d_phases: Res<ViewSortedRenderPhases<Transmissive3d>>,
798    transparent_3d_phases: Res<ViewSortedRenderPhases<Transparent3d>>,
799    views_3d: Query<(
800        Entity,
801        &ExtractedCamera,
802        &ExtractedView,
803        Option<&DepthPrepass>,
804        &Camera3d,
805        &Msaa,
806    )>,
807) {
808    let mut render_target_usage = <HashMap<_, _>>::default();
809    for (_, camera, extracted_view, depth_prepass, camera_3d, _msaa) in &views_3d {
810        if !opaque_3d_phases.contains_key(&extracted_view.retained_view_entity)
811            || !alpha_mask_3d_phases.contains_key(&extracted_view.retained_view_entity)
812            || !transmissive_3d_phases.contains_key(&extracted_view.retained_view_entity)
813            || !transparent_3d_phases.contains_key(&extracted_view.retained_view_entity)
814        {
815            continue;
816        };
817
818        // Default usage required to write to the depth texture
819        let mut usage: TextureUsages = camera_3d.depth_texture_usages.into();
820        if depth_prepass.is_some() {
821            // Required to read the output of the prepass
822            usage |= TextureUsages::COPY_SRC;
823        }
824        render_target_usage
825            .entry(camera.target.clone())
826            .and_modify(|u| *u |= usage)
827            .or_insert_with(|| usage);
828    }
829
830    let mut textures = <HashMap<_, _>>::default();
831    for (entity, camera, _, _, camera_3d, msaa) in &views_3d {
832        let Some(physical_target_size) = camera.physical_target_size else {
833            continue;
834        };
835
836        let cached_texture = textures
837            .entry((camera.target.clone(), msaa))
838            .or_insert_with(|| {
839                let usage = *render_target_usage
840                    .get(&camera.target.clone())
841                    .expect("The depth texture usage should already exist for this target");
842
843                let descriptor = TextureDescriptor {
844                    label: Some("view_depth_texture"),
845                    // The size of the depth texture
846                    size: physical_target_size.to_extents(),
847                    mip_level_count: 1,
848                    sample_count: msaa.samples(),
849                    dimension: TextureDimension::D2,
850                    format: CORE_3D_DEPTH_FORMAT,
851                    usage,
852                    view_formats: &[],
853                };
854
855                texture_cache.get(&render_device, descriptor)
856            })
857            .clone();
858
859        commands.entity(entity).insert(ViewDepthTexture::new(
860            cached_texture,
861            match camera_3d.depth_load_op {
862                Camera3dDepthLoadOp::Clear(v) => Some(v),
863                Camera3dDepthLoadOp::Load => None,
864            },
865        ));
866    }
867}
868
869#[derive(Component)]
870pub struct ViewTransmissionTexture {
871    pub texture: Texture,
872    pub view: TextureView,
873    pub sampler: Sampler,
874}
875
876pub fn prepare_core_3d_transmission_textures(
877    mut commands: Commands,
878    mut texture_cache: ResMut<TextureCache>,
879    render_device: Res<RenderDevice>,
880    opaque_3d_phases: Res<ViewBinnedRenderPhases<Opaque3d>>,
881    alpha_mask_3d_phases: Res<ViewBinnedRenderPhases<AlphaMask3d>>,
882    transmissive_3d_phases: Res<ViewSortedRenderPhases<Transmissive3d>>,
883    transparent_3d_phases: Res<ViewSortedRenderPhases<Transparent3d>>,
884    views_3d: Query<(Entity, &ExtractedCamera, &Camera3d, &ExtractedView)>,
885) {
886    let mut textures = <HashMap<_, _>>::default();
887    for (entity, camera, camera_3d, view) in &views_3d {
888        if !opaque_3d_phases.contains_key(&view.retained_view_entity)
889            || !alpha_mask_3d_phases.contains_key(&view.retained_view_entity)
890            || !transparent_3d_phases.contains_key(&view.retained_view_entity)
891        {
892            continue;
893        };
894
895        let Some(transmissive_3d_phase) = transmissive_3d_phases.get(&view.retained_view_entity)
896        else {
897            continue;
898        };
899
900        let Some(physical_target_size) = camera.physical_target_size else {
901            continue;
902        };
903
904        // Don't prepare a transmission texture if the number of steps is set to 0
905        if camera_3d.screen_space_specular_transmission_steps == 0 {
906            continue;
907        }
908
909        // Don't prepare a transmission texture if there are no transmissive items to render
910        if transmissive_3d_phase.items.is_empty() {
911            continue;
912        }
913
914        let cached_texture = textures
915            .entry(camera.target.clone())
916            .or_insert_with(|| {
917                let usage = TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST;
918
919                let format = if view.hdr {
920                    ViewTarget::TEXTURE_FORMAT_HDR
921                } else {
922                    TextureFormat::bevy_default()
923                };
924
925                let descriptor = TextureDescriptor {
926                    label: Some("view_transmission_texture"),
927                    // The size of the transmission texture
928                    size: physical_target_size.to_extents(),
929                    mip_level_count: 1,
930                    sample_count: 1, // No need for MSAA, as we'll only copy the main texture here
931                    dimension: TextureDimension::D2,
932                    format,
933                    usage,
934                    view_formats: &[],
935                };
936
937                texture_cache.get(&render_device, descriptor)
938            })
939            .clone();
940
941        let sampler = render_device.create_sampler(&SamplerDescriptor {
942            label: Some("view_transmission_sampler"),
943            mag_filter: FilterMode::Linear,
944            min_filter: FilterMode::Linear,
945            ..Default::default()
946        });
947
948        commands.entity(entity).insert(ViewTransmissionTexture {
949            texture: cached_texture.texture,
950            view: cached_texture.default_view,
951            sampler,
952        });
953    }
954}
955
956/// Sets the `TEXTURE_BINDING` flag on the depth texture if necessary for
957/// occlusion culling.
958///
959/// We need that flag to be set in order to read from the texture.
960fn configure_occlusion_culling_view_targets(
961    mut view_targets: Query<
962        &mut Camera3d,
963        (
964            With<OcclusionCulling>,
965            Without<NoIndirectDrawing>,
966            With<DepthPrepass>,
967        ),
968    >,
969) {
970    for mut camera_3d in &mut view_targets {
971        let mut depth_texture_usages = TextureUsages::from(camera_3d.depth_texture_usages);
972        depth_texture_usages |= TextureUsages::TEXTURE_BINDING;
973        camera_3d.depth_texture_usages = depth_texture_usages.into();
974    }
975}
976
977// Disable MSAA and warn if using deferred rendering
978pub fn check_msaa(mut deferred_views: Query<&mut Msaa, (With<Camera>, With<DeferredPrepass>)>) {
979    for mut msaa in deferred_views.iter_mut() {
980        match *msaa {
981            Msaa::Off => (),
982            _ => {
983                warn!("MSAA is incompatible with deferred rendering and has been disabled.");
984                *msaa = Msaa::Off;
985            }
986        };
987    }
988}
989
990// Prepares the textures used by the prepass
991pub fn prepare_prepass_textures(
992    mut commands: Commands,
993    mut texture_cache: ResMut<TextureCache>,
994    render_device: Res<RenderDevice>,
995    frame_count: Res<FrameCount>,
996    opaque_3d_prepass_phases: Res<ViewBinnedRenderPhases<Opaque3dPrepass>>,
997    alpha_mask_3d_prepass_phases: Res<ViewBinnedRenderPhases<AlphaMask3dPrepass>>,
998    opaque_3d_deferred_phases: Res<ViewBinnedRenderPhases<Opaque3dDeferred>>,
999    alpha_mask_3d_deferred_phases: Res<ViewBinnedRenderPhases<AlphaMask3dDeferred>>,
1000    views_3d: Query<(
1001        Entity,
1002        &ExtractedCamera,
1003        &ExtractedView,
1004        &Msaa,
1005        Has<DepthPrepass>,
1006        Has<NormalPrepass>,
1007        Has<MotionVectorPrepass>,
1008        Has<DeferredPrepass>,
1009        Has<DepthPrepassDoubleBuffer>,
1010        Has<DeferredPrepassDoubleBuffer>,
1011    )>,
1012) {
1013    let mut depth_textures1 = <HashMap<_, _>>::default();
1014    let mut depth_textures2 = <HashMap<_, _>>::default();
1015    let mut normal_textures = <HashMap<_, _>>::default();
1016    let mut deferred_textures1: HashMap<_, _> = <HashMap<_, _>>::default();
1017    let mut deferred_textures2: HashMap<_, _> = <HashMap<_, _>>::default();
1018    let mut deferred_lighting_id_textures = <HashMap<_, _>>::default();
1019    let mut motion_vectors_textures = <HashMap<_, _>>::default();
1020    for (
1021        entity,
1022        camera,
1023        view,
1024        msaa,
1025        depth_prepass,
1026        normal_prepass,
1027        motion_vector_prepass,
1028        deferred_prepass,
1029        depth_prepass_double_buffer,
1030        deferred_prepass_double_buffer,
1031    ) in &views_3d
1032    {
1033        if !opaque_3d_prepass_phases.contains_key(&view.retained_view_entity)
1034            && !alpha_mask_3d_prepass_phases.contains_key(&view.retained_view_entity)
1035            && !opaque_3d_deferred_phases.contains_key(&view.retained_view_entity)
1036            && !alpha_mask_3d_deferred_phases.contains_key(&view.retained_view_entity)
1037        {
1038            commands.entity(entity).remove::<ViewPrepassTextures>();
1039            continue;
1040        };
1041
1042        let Some(physical_target_size) = camera.physical_target_size else {
1043            continue;
1044        };
1045
1046        let size = physical_target_size.to_extents();
1047
1048        let cached_depth_texture1 = depth_prepass.then(|| {
1049            depth_textures1
1050                .entry(camera.target.clone())
1051                .or_insert_with(|| {
1052                    let descriptor = TextureDescriptor {
1053                        label: Some("prepass_depth_texture_1"),
1054                        size,
1055                        mip_level_count: 1,
1056                        sample_count: msaa.samples(),
1057                        dimension: TextureDimension::D2,
1058                        format: CORE_3D_DEPTH_FORMAT,
1059                        usage: TextureUsages::COPY_DST
1060                            | TextureUsages::RENDER_ATTACHMENT
1061                            | TextureUsages::TEXTURE_BINDING,
1062                        view_formats: &[],
1063                    };
1064                    texture_cache.get(&render_device, descriptor)
1065                })
1066                .clone()
1067        });
1068
1069        let cached_depth_texture2 = depth_prepass_double_buffer.then(|| {
1070            depth_textures2
1071                .entry(camera.target.clone())
1072                .or_insert_with(|| {
1073                    let descriptor = TextureDescriptor {
1074                        label: Some("prepass_depth_texture_2"),
1075                        size,
1076                        mip_level_count: 1,
1077                        sample_count: msaa.samples(),
1078                        dimension: TextureDimension::D2,
1079                        format: CORE_3D_DEPTH_FORMAT,
1080                        usage: TextureUsages::COPY_DST
1081                            | TextureUsages::RENDER_ATTACHMENT
1082                            | TextureUsages::TEXTURE_BINDING,
1083                        view_formats: &[],
1084                    };
1085                    texture_cache.get(&render_device, descriptor)
1086                })
1087                .clone()
1088        });
1089
1090        let cached_normals_texture = normal_prepass.then(|| {
1091            normal_textures
1092                .entry(camera.target.clone())
1093                .or_insert_with(|| {
1094                    texture_cache.get(
1095                        &render_device,
1096                        TextureDescriptor {
1097                            label: Some("prepass_normal_texture"),
1098                            size,
1099                            mip_level_count: 1,
1100                            sample_count: msaa.samples(),
1101                            dimension: TextureDimension::D2,
1102                            format: NORMAL_PREPASS_FORMAT,
1103                            usage: TextureUsages::RENDER_ATTACHMENT
1104                                | TextureUsages::TEXTURE_BINDING,
1105                            view_formats: &[],
1106                        },
1107                    )
1108                })
1109                .clone()
1110        });
1111
1112        let cached_motion_vectors_texture = motion_vector_prepass.then(|| {
1113            motion_vectors_textures
1114                .entry(camera.target.clone())
1115                .or_insert_with(|| {
1116                    texture_cache.get(
1117                        &render_device,
1118                        TextureDescriptor {
1119                            label: Some("prepass_motion_vectors_textures"),
1120                            size,
1121                            mip_level_count: 1,
1122                            sample_count: msaa.samples(),
1123                            dimension: TextureDimension::D2,
1124                            format: MOTION_VECTOR_PREPASS_FORMAT,
1125                            usage: TextureUsages::RENDER_ATTACHMENT
1126                                | TextureUsages::TEXTURE_BINDING,
1127                            view_formats: &[],
1128                        },
1129                    )
1130                })
1131                .clone()
1132        });
1133
1134        let cached_deferred_texture1 = deferred_prepass.then(|| {
1135            deferred_textures1
1136                .entry(camera.target.clone())
1137                .or_insert_with(|| {
1138                    texture_cache.get(
1139                        &render_device,
1140                        TextureDescriptor {
1141                            label: Some("prepass_deferred_texture_1"),
1142                            size,
1143                            mip_level_count: 1,
1144                            sample_count: 1,
1145                            dimension: TextureDimension::D2,
1146                            format: DEFERRED_PREPASS_FORMAT,
1147                            usage: TextureUsages::RENDER_ATTACHMENT
1148                                | TextureUsages::TEXTURE_BINDING,
1149                            view_formats: &[],
1150                        },
1151                    )
1152                })
1153                .clone()
1154        });
1155
1156        let cached_deferred_texture2 = deferred_prepass_double_buffer.then(|| {
1157            deferred_textures2
1158                .entry(camera.target.clone())
1159                .or_insert_with(|| {
1160                    texture_cache.get(
1161                        &render_device,
1162                        TextureDescriptor {
1163                            label: Some("prepass_deferred_texture_2"),
1164                            size,
1165                            mip_level_count: 1,
1166                            sample_count: 1,
1167                            dimension: TextureDimension::D2,
1168                            format: DEFERRED_PREPASS_FORMAT,
1169                            usage: TextureUsages::RENDER_ATTACHMENT
1170                                | TextureUsages::TEXTURE_BINDING,
1171                            view_formats: &[],
1172                        },
1173                    )
1174                })
1175                .clone()
1176        });
1177
1178        let cached_deferred_lighting_pass_id_texture = deferred_prepass.then(|| {
1179            deferred_lighting_id_textures
1180                .entry(camera.target.clone())
1181                .or_insert_with(|| {
1182                    texture_cache.get(
1183                        &render_device,
1184                        TextureDescriptor {
1185                            label: Some("deferred_lighting_pass_id_texture"),
1186                            size,
1187                            mip_level_count: 1,
1188                            sample_count: 1,
1189                            dimension: TextureDimension::D2,
1190                            format: DEFERRED_LIGHTING_PASS_ID_FORMAT,
1191                            usage: TextureUsages::RENDER_ATTACHMENT
1192                                | TextureUsages::TEXTURE_BINDING,
1193                            view_formats: &[],
1194                        },
1195                    )
1196                })
1197                .clone()
1198        });
1199
1200        commands.entity(entity).insert(ViewPrepassTextures {
1201            depth: package_double_buffered_texture(
1202                cached_depth_texture1,
1203                cached_depth_texture2,
1204                frame_count.0,
1205            ),
1206            normal: cached_normals_texture
1207                .map(|t| ColorAttachment::new(t, None, None, Some(LinearRgba::BLACK))),
1208            // Red and Green channels are X and Y components of the motion vectors
1209            // Blue channel doesn't matter, but set to 0.0 for possible faster clear
1210            // https://gpuopen.com/performance/#clears
1211            motion_vectors: cached_motion_vectors_texture
1212                .map(|t| ColorAttachment::new(t, None, None, Some(LinearRgba::BLACK))),
1213            deferred: package_double_buffered_texture(
1214                cached_deferred_texture1,
1215                cached_deferred_texture2,
1216                frame_count.0,
1217            ),
1218            deferred_lighting_pass_id: cached_deferred_lighting_pass_id_texture
1219                .map(|t| ColorAttachment::new(t, None, None, Some(LinearRgba::BLACK))),
1220            size,
1221        });
1222    }
1223}
1224
1225fn package_double_buffered_texture(
1226    texture1: Option<CachedTexture>,
1227    texture2: Option<CachedTexture>,
1228    frame_count: u32,
1229) -> Option<ColorAttachment> {
1230    match (texture1, texture2) {
1231        (Some(t1), None) => Some(ColorAttachment::new(
1232            t1,
1233            None,
1234            None,
1235            Some(LinearRgba::BLACK),
1236        )),
1237        (Some(t1), Some(t2)) if frame_count.is_multiple_of(2) => Some(ColorAttachment::new(
1238            t1,
1239            None,
1240            Some(t2),
1241            Some(LinearRgba::BLACK),
1242        )),
1243        (Some(t1), Some(t2)) => Some(ColorAttachment::new(
1244            t2,
1245            None,
1246            Some(t1),
1247            Some(LinearRgba::BLACK),
1248        )),
1249        _ => None,
1250    }
1251}