bevy_core_pipeline/core_3d/
mod.rs

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