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