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