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 Prepass,
20 DeferredPrepass,
21 CopyDeferredLightingId,
22 EndPrepasses,
23 StartMainPass,
24 MainOpaquePass,
25 MainTransmissivePass,
26 MainTransparentPass,
27 EndMainPass,
28 Taa,
29 MotionBlur,
30 Bloom,
31 AutoExposure,
32 DepthOfField,
33 PostProcessing,
34 Tonemapping,
35 Fxaa,
36 Smaa,
37 Upscaling,
38 ContrastAdaptiveSharpening,
39 EndMainPassPostProcessing,
40 }
41}
42
43pub const CORE_3D_DEPTH_FORMAT: TextureFormat = TextureFormat::Depth32Float;
45
46#[cfg(not(any(feature = "webgpu", not(target_arch = "wasm32"))))]
54pub const DEPTH_TEXTURE_SAMPLING_SUPPORTED: bool = false;
55
56#[cfg(any(feature = "webgpu", not(target_arch = "wasm32")))]
64pub const DEPTH_TEXTURE_SAMPLING_SUPPORTED: bool = true;
65
66use core::ops::Range;
67
68pub use camera_3d::*;
69pub use main_opaque_pass_3d_node::*;
70pub use main_transparent_pass_3d_node::*;
71
72use bevy_app::{App, Plugin, PostUpdate};
73use bevy_asset::{AssetId, UntypedAssetId};
74use bevy_color::LinearRgba;
75use bevy_ecs::{entity::EntityHashSet, prelude::*};
76use bevy_image::{BevyDefault, Image};
77use bevy_math::FloatOrd;
78use bevy_render::sync_world::MainEntity;
79use bevy_render::{
80 camera::{Camera, ExtractedCamera},
81 extract_component::ExtractComponentPlugin,
82 prelude::Msaa,
83 render_graph::{EmptyNode, RenderGraphApp, ViewNodeRunner},
84 render_phase::{
85 sort_phase_system, BinnedPhaseItem, CachedRenderPipelinePhaseItem, DrawFunctionId,
86 DrawFunctions, PhaseItem, PhaseItemExtraIndex, SortedPhaseItem, ViewBinnedRenderPhases,
87 ViewSortedRenderPhases,
88 },
89 render_resource::{
90 BindGroupId, CachedRenderPipelineId, Extent3d, FilterMode, Sampler, SamplerDescriptor,
91 Texture, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, TextureView,
92 },
93 renderer::RenderDevice,
94 sync_world::RenderEntity,
95 texture::{ColorAttachment, TextureCache},
96 view::{ExtractedView, ViewDepthTexture, ViewTarget},
97 Extract, ExtractSchedule, Render, RenderApp, RenderSet,
98};
99use bevy_utils::{tracing::warn, HashMap};
100
101use crate::{
102 core_3d::main_transmissive_pass_3d_node::MainTransmissivePass3dNode,
103 deferred::{
104 copy_lighting_id::CopyDeferredLightingIdNode, node::DeferredGBufferPrepassNode,
105 AlphaMask3dDeferred, Opaque3dDeferred, DEFERRED_LIGHTING_PASS_ID_FORMAT,
106 DEFERRED_PREPASS_FORMAT,
107 },
108 dof::DepthOfFieldNode,
109 prepass::{
110 node::PrepassNode, AlphaMask3dPrepass, DeferredPrepass, DepthPrepass, MotionVectorPrepass,
111 NormalPrepass, Opaque3dPrepass, OpaqueNoLightmap3dBinKey, ViewPrepassTextures,
112 MOTION_VECTOR_PREPASS_FORMAT, NORMAL_PREPASS_FORMAT,
113 },
114 skybox::SkyboxPlugin,
115 tonemapping::TonemappingNode,
116 upscaling::UpscalingNode,
117};
118
119use self::graph::{Core3d, Node3d};
120
121pub struct Core3dPlugin;
122
123impl Plugin for Core3dPlugin {
124 fn build(&self, app: &mut App) {
125 app.register_type::<Camera3d>()
126 .register_type::<ScreenSpaceTransmissionQuality>()
127 .add_plugins((SkyboxPlugin, ExtractComponentPlugin::<Camera3d>::default()))
128 .add_systems(PostUpdate, check_msaa);
129
130 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
131 return;
132 };
133 render_app
134 .init_resource::<DrawFunctions<Opaque3d>>()
135 .init_resource::<DrawFunctions<AlphaMask3d>>()
136 .init_resource::<DrawFunctions<Transmissive3d>>()
137 .init_resource::<DrawFunctions<Transparent3d>>()
138 .init_resource::<DrawFunctions<Opaque3dPrepass>>()
139 .init_resource::<DrawFunctions<AlphaMask3dPrepass>>()
140 .init_resource::<DrawFunctions<Opaque3dDeferred>>()
141 .init_resource::<DrawFunctions<AlphaMask3dDeferred>>()
142 .init_resource::<ViewBinnedRenderPhases<Opaque3d>>()
143 .init_resource::<ViewBinnedRenderPhases<AlphaMask3d>>()
144 .init_resource::<ViewBinnedRenderPhases<Opaque3dPrepass>>()
145 .init_resource::<ViewBinnedRenderPhases<AlphaMask3dPrepass>>()
146 .init_resource::<ViewBinnedRenderPhases<Opaque3dDeferred>>()
147 .init_resource::<ViewBinnedRenderPhases<AlphaMask3dDeferred>>()
148 .init_resource::<ViewSortedRenderPhases<Transmissive3d>>()
149 .init_resource::<ViewSortedRenderPhases<Transparent3d>>()
150 .add_systems(ExtractSchedule, extract_core_3d_camera_phases)
151 .add_systems(ExtractSchedule, extract_camera_prepass_phase)
152 .add_systems(
153 Render,
154 (
155 sort_phase_system::<Transmissive3d>.in_set(RenderSet::PhaseSort),
156 sort_phase_system::<Transparent3d>.in_set(RenderSet::PhaseSort),
157 prepare_core_3d_depth_textures.in_set(RenderSet::PrepareResources),
158 prepare_core_3d_transmission_textures.in_set(RenderSet::PrepareResources),
159 prepare_prepass_textures.in_set(RenderSet::PrepareResources),
160 ),
161 );
162
163 render_app
164 .add_render_sub_graph(Core3d)
165 .add_render_graph_node::<ViewNodeRunner<PrepassNode>>(Core3d, Node3d::Prepass)
166 .add_render_graph_node::<ViewNodeRunner<DeferredGBufferPrepassNode>>(
167 Core3d,
168 Node3d::DeferredPrepass,
169 )
170 .add_render_graph_node::<ViewNodeRunner<CopyDeferredLightingIdNode>>(
171 Core3d,
172 Node3d::CopyDeferredLightingId,
173 )
174 .add_render_graph_node::<EmptyNode>(Core3d, Node3d::EndPrepasses)
175 .add_render_graph_node::<EmptyNode>(Core3d, Node3d::StartMainPass)
176 .add_render_graph_node::<ViewNodeRunner<MainOpaquePass3dNode>>(
177 Core3d,
178 Node3d::MainOpaquePass,
179 )
180 .add_render_graph_node::<ViewNodeRunner<MainTransmissivePass3dNode>>(
181 Core3d,
182 Node3d::MainTransmissivePass,
183 )
184 .add_render_graph_node::<ViewNodeRunner<MainTransparentPass3dNode>>(
185 Core3d,
186 Node3d::MainTransparentPass,
187 )
188 .add_render_graph_node::<EmptyNode>(Core3d, Node3d::EndMainPass)
189 .add_render_graph_node::<ViewNodeRunner<DepthOfFieldNode>>(Core3d, Node3d::DepthOfField)
190 .add_render_graph_node::<ViewNodeRunner<TonemappingNode>>(Core3d, Node3d::Tonemapping)
191 .add_render_graph_node::<EmptyNode>(Core3d, Node3d::EndMainPassPostProcessing)
192 .add_render_graph_node::<ViewNodeRunner<UpscalingNode>>(Core3d, Node3d::Upscaling)
193 .add_render_graph_edges(
194 Core3d,
195 (
196 Node3d::Prepass,
197 Node3d::DeferredPrepass,
198 Node3d::CopyDeferredLightingId,
199 Node3d::EndPrepasses,
200 Node3d::StartMainPass,
201 Node3d::MainOpaquePass,
202 Node3d::MainTransmissivePass,
203 Node3d::MainTransparentPass,
204 Node3d::EndMainPass,
205 Node3d::Tonemapping,
206 Node3d::EndMainPassPostProcessing,
207 Node3d::Upscaling,
208 ),
209 );
210 }
211}
212
213pub struct Opaque3d {
215 pub key: Opaque3dBinKey,
217 pub representative_entity: (Entity, MainEntity),
220 pub batch_range: Range<u32>,
222 pub extra_index: PhaseItemExtraIndex,
225}
226
227#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
229pub struct Opaque3dBinKey {
230 pub pipeline: CachedRenderPipelineId,
232
233 pub draw_function: DrawFunctionId,
235
236 pub asset_id: UntypedAssetId,
241
242 pub material_bind_group_id: Option<BindGroupId>,
246
247 pub lightmap_image: Option<AssetId<Image>>,
249}
250
251impl PhaseItem for Opaque3d {
252 #[inline]
253 fn entity(&self) -> Entity {
254 self.representative_entity.0
255 }
256
257 #[inline]
258 fn main_entity(&self) -> MainEntity {
259 self.representative_entity.1
260 }
261
262 #[inline]
263 fn draw_function(&self) -> DrawFunctionId {
264 self.key.draw_function
265 }
266
267 #[inline]
268 fn batch_range(&self) -> &Range<u32> {
269 &self.batch_range
270 }
271
272 #[inline]
273 fn batch_range_mut(&mut self) -> &mut Range<u32> {
274 &mut self.batch_range
275 }
276
277 fn extra_index(&self) -> PhaseItemExtraIndex {
278 self.extra_index
279 }
280
281 fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
282 (&mut self.batch_range, &mut self.extra_index)
283 }
284}
285
286impl BinnedPhaseItem for Opaque3d {
287 type BinKey = Opaque3dBinKey;
288
289 #[inline]
290 fn new(
291 key: Self::BinKey,
292 representative_entity: (Entity, MainEntity),
293 batch_range: Range<u32>,
294 extra_index: PhaseItemExtraIndex,
295 ) -> Self {
296 Opaque3d {
297 key,
298 representative_entity,
299 batch_range,
300 extra_index,
301 }
302 }
303}
304
305impl CachedRenderPipelinePhaseItem for Opaque3d {
306 #[inline]
307 fn cached_pipeline(&self) -> CachedRenderPipelineId {
308 self.key.pipeline
309 }
310}
311
312pub struct AlphaMask3d {
313 pub key: OpaqueNoLightmap3dBinKey,
314 pub representative_entity: (Entity, MainEntity),
315 pub batch_range: Range<u32>,
316 pub extra_index: PhaseItemExtraIndex,
317}
318
319impl PhaseItem for AlphaMask3d {
320 #[inline]
321 fn entity(&self) -> Entity {
322 self.representative_entity.0
323 }
324
325 fn main_entity(&self) -> MainEntity {
326 self.representative_entity.1
327 }
328
329 #[inline]
330 fn draw_function(&self) -> DrawFunctionId {
331 self.key.draw_function
332 }
333
334 #[inline]
335 fn batch_range(&self) -> &Range<u32> {
336 &self.batch_range
337 }
338
339 #[inline]
340 fn batch_range_mut(&mut self) -> &mut Range<u32> {
341 &mut self.batch_range
342 }
343
344 #[inline]
345 fn extra_index(&self) -> PhaseItemExtraIndex {
346 self.extra_index
347 }
348
349 #[inline]
350 fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
351 (&mut self.batch_range, &mut self.extra_index)
352 }
353}
354
355impl BinnedPhaseItem for AlphaMask3d {
356 type BinKey = OpaqueNoLightmap3dBinKey;
357
358 #[inline]
359 fn new(
360 key: Self::BinKey,
361 representative_entity: (Entity, MainEntity),
362 batch_range: Range<u32>,
363 extra_index: PhaseItemExtraIndex,
364 ) -> Self {
365 Self {
366 key,
367 representative_entity,
368 batch_range,
369 extra_index,
370 }
371 }
372}
373
374impl CachedRenderPipelinePhaseItem for AlphaMask3d {
375 #[inline]
376 fn cached_pipeline(&self) -> CachedRenderPipelineId {
377 self.key.pipeline
378 }
379}
380
381pub struct Transmissive3d {
382 pub distance: f32,
383 pub pipeline: CachedRenderPipelineId,
384 pub entity: (Entity, MainEntity),
385 pub draw_function: DrawFunctionId,
386 pub batch_range: Range<u32>,
387 pub extra_index: PhaseItemExtraIndex,
388}
389
390impl PhaseItem for Transmissive3d {
391 const AUTOMATIC_BATCHING: bool = false;
401
402 #[inline]
403 fn entity(&self) -> Entity {
404 self.entity.0
405 }
406
407 #[inline]
408 fn main_entity(&self) -> MainEntity {
409 self.entity.1
410 }
411
412 #[inline]
413 fn draw_function(&self) -> DrawFunctionId {
414 self.draw_function
415 }
416
417 #[inline]
418 fn batch_range(&self) -> &Range<u32> {
419 &self.batch_range
420 }
421
422 #[inline]
423 fn batch_range_mut(&mut self) -> &mut Range<u32> {
424 &mut self.batch_range
425 }
426
427 #[inline]
428 fn extra_index(&self) -> PhaseItemExtraIndex {
429 self.extra_index
430 }
431
432 #[inline]
433 fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
434 (&mut self.batch_range, &mut self.extra_index)
435 }
436}
437
438impl SortedPhaseItem for Transmissive3d {
439 type SortKey = FloatOrd;
441
442 #[inline]
443 fn sort_key(&self) -> Self::SortKey {
444 FloatOrd(self.distance)
445 }
446
447 #[inline]
448 fn sort(items: &mut [Self]) {
449 radsort::sort_by_key(items, |item| item.distance);
450 }
451}
452
453impl CachedRenderPipelinePhaseItem for Transmissive3d {
454 #[inline]
455 fn cached_pipeline(&self) -> CachedRenderPipelineId {
456 self.pipeline
457 }
458}
459
460pub struct Transparent3d {
461 pub distance: f32,
462 pub pipeline: CachedRenderPipelineId,
463 pub entity: (Entity, MainEntity),
464 pub draw_function: DrawFunctionId,
465 pub batch_range: Range<u32>,
466 pub extra_index: PhaseItemExtraIndex,
467}
468
469impl PhaseItem for Transparent3d {
470 #[inline]
471 fn entity(&self) -> Entity {
472 self.entity.0
473 }
474
475 fn main_entity(&self) -> MainEntity {
476 self.entity.1
477 }
478
479 #[inline]
480 fn draw_function(&self) -> DrawFunctionId {
481 self.draw_function
482 }
483
484 #[inline]
485 fn batch_range(&self) -> &Range<u32> {
486 &self.batch_range
487 }
488
489 #[inline]
490 fn batch_range_mut(&mut self) -> &mut Range<u32> {
491 &mut self.batch_range
492 }
493
494 #[inline]
495 fn extra_index(&self) -> PhaseItemExtraIndex {
496 self.extra_index
497 }
498
499 #[inline]
500 fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
501 (&mut self.batch_range, &mut self.extra_index)
502 }
503}
504
505impl SortedPhaseItem for Transparent3d {
506 type SortKey = FloatOrd;
508
509 #[inline]
510 fn sort_key(&self) -> Self::SortKey {
511 FloatOrd(self.distance)
512 }
513
514 #[inline]
515 fn sort(items: &mut [Self]) {
516 radsort::sort_by_key(items, |item| item.distance);
517 }
518}
519
520impl CachedRenderPipelinePhaseItem for Transparent3d {
521 #[inline]
522 fn cached_pipeline(&self) -> CachedRenderPipelineId {
523 self.pipeline
524 }
525}
526
527pub fn extract_core_3d_camera_phases(
528 mut opaque_3d_phases: ResMut<ViewBinnedRenderPhases<Opaque3d>>,
529 mut alpha_mask_3d_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3d>>,
530 mut transmissive_3d_phases: ResMut<ViewSortedRenderPhases<Transmissive3d>>,
531 mut transparent_3d_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>,
532 cameras_3d: Extract<Query<(RenderEntity, &Camera), With<Camera3d>>>,
533 mut live_entities: Local<EntityHashSet>,
534) {
535 live_entities.clear();
536
537 for (entity, camera) in &cameras_3d {
538 if !camera.is_active {
539 continue;
540 }
541
542 opaque_3d_phases.insert_or_clear(entity);
543 alpha_mask_3d_phases.insert_or_clear(entity);
544 transmissive_3d_phases.insert_or_clear(entity);
545 transparent_3d_phases.insert_or_clear(entity);
546
547 live_entities.insert(entity);
548 }
549
550 opaque_3d_phases.retain(|entity, _| live_entities.contains(entity));
551 alpha_mask_3d_phases.retain(|entity, _| live_entities.contains(entity));
552 transmissive_3d_phases.retain(|entity, _| live_entities.contains(entity));
553 transparent_3d_phases.retain(|entity, _| live_entities.contains(entity));
554}
555
556pub fn extract_camera_prepass_phase(
558 mut commands: Commands,
559 mut opaque_3d_prepass_phases: ResMut<ViewBinnedRenderPhases<Opaque3dPrepass>>,
560 mut alpha_mask_3d_prepass_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3dPrepass>>,
561 mut opaque_3d_deferred_phases: ResMut<ViewBinnedRenderPhases<Opaque3dDeferred>>,
562 mut alpha_mask_3d_deferred_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3dDeferred>>,
563 cameras_3d: Extract<
564 Query<
565 (
566 RenderEntity,
567 &Camera,
568 Has<DepthPrepass>,
569 Has<NormalPrepass>,
570 Has<MotionVectorPrepass>,
571 Has<DeferredPrepass>,
572 ),
573 With<Camera3d>,
574 >,
575 >,
576 mut live_entities: Local<EntityHashSet>,
577) {
578 live_entities.clear();
579
580 for (entity, camera, depth_prepass, normal_prepass, motion_vector_prepass, deferred_prepass) in
581 cameras_3d.iter()
582 {
583 if !camera.is_active {
584 continue;
585 }
586
587 if depth_prepass || normal_prepass || motion_vector_prepass {
588 opaque_3d_prepass_phases.insert_or_clear(entity);
589 alpha_mask_3d_prepass_phases.insert_or_clear(entity);
590 } else {
591 opaque_3d_prepass_phases.remove(&entity);
592 alpha_mask_3d_prepass_phases.remove(&entity);
593 }
594
595 if deferred_prepass {
596 opaque_3d_deferred_phases.insert_or_clear(entity);
597 alpha_mask_3d_deferred_phases.insert_or_clear(entity);
598 } else {
599 opaque_3d_deferred_phases.remove(&entity);
600 alpha_mask_3d_deferred_phases.remove(&entity);
601 }
602 live_entities.insert(entity);
603
604 commands
605 .get_entity(entity)
606 .expect("Camera entity wasn't synced.")
607 .insert_if(DepthPrepass, || depth_prepass)
608 .insert_if(NormalPrepass, || normal_prepass)
609 .insert_if(MotionVectorPrepass, || motion_vector_prepass)
610 .insert_if(DeferredPrepass, || deferred_prepass);
611 }
612
613 opaque_3d_prepass_phases.retain(|entity, _| live_entities.contains(entity));
614 alpha_mask_3d_prepass_phases.retain(|entity, _| live_entities.contains(entity));
615 opaque_3d_deferred_phases.retain(|entity, _| live_entities.contains(entity));
616 alpha_mask_3d_deferred_phases.retain(|entity, _| live_entities.contains(entity));
617}
618
619#[allow(clippy::too_many_arguments)]
620pub fn prepare_core_3d_depth_textures(
621 mut commands: Commands,
622 mut texture_cache: ResMut<TextureCache>,
623 render_device: Res<RenderDevice>,
624 opaque_3d_phases: Res<ViewBinnedRenderPhases<Opaque3d>>,
625 alpha_mask_3d_phases: Res<ViewBinnedRenderPhases<AlphaMask3d>>,
626 transmissive_3d_phases: Res<ViewSortedRenderPhases<Transmissive3d>>,
627 transparent_3d_phases: Res<ViewSortedRenderPhases<Transparent3d>>,
628 views_3d: Query<(
629 Entity,
630 &ExtractedCamera,
631 Option<&DepthPrepass>,
632 &Camera3d,
633 &Msaa,
634 )>,
635) {
636 let mut render_target_usage = HashMap::default();
637 for (view, camera, depth_prepass, camera_3d, _msaa) in &views_3d {
638 if !opaque_3d_phases.contains_key(&view)
639 || !alpha_mask_3d_phases.contains_key(&view)
640 || !transmissive_3d_phases.contains_key(&view)
641 || !transparent_3d_phases.contains_key(&view)
642 {
643 continue;
644 };
645
646 let mut usage: TextureUsages = camera_3d.depth_texture_usages.into();
648 if depth_prepass.is_some() {
649 usage |= TextureUsages::COPY_SRC;
651 }
652 render_target_usage
653 .entry(camera.target.clone())
654 .and_modify(|u| *u |= usage)
655 .or_insert_with(|| usage);
656 }
657
658 let mut textures = HashMap::default();
659 for (entity, camera, _, camera_3d, msaa) in &views_3d {
660 let Some(physical_target_size) = camera.physical_target_size else {
661 continue;
662 };
663
664 let cached_texture = textures
665 .entry((camera.target.clone(), msaa))
666 .or_insert_with(|| {
667 let size = Extent3d {
669 depth_or_array_layers: 1,
670 width: physical_target_size.x,
671 height: physical_target_size.y,
672 };
673
674 let usage = *render_target_usage
675 .get(&camera.target.clone())
676 .expect("The depth texture usage should already exist for this target");
677
678 let descriptor = TextureDescriptor {
679 label: Some("view_depth_texture"),
680 size,
681 mip_level_count: 1,
682 sample_count: msaa.samples(),
683 dimension: TextureDimension::D2,
684 format: CORE_3D_DEPTH_FORMAT,
685 usage,
686 view_formats: &[],
687 };
688
689 texture_cache.get(&render_device, descriptor)
690 })
691 .clone();
692
693 commands.entity(entity).insert(ViewDepthTexture::new(
694 cached_texture,
695 match camera_3d.depth_load_op {
696 Camera3dDepthLoadOp::Clear(v) => Some(v),
697 Camera3dDepthLoadOp::Load => None,
698 },
699 ));
700 }
701}
702
703#[derive(Component)]
704pub struct ViewTransmissionTexture {
705 pub texture: Texture,
706 pub view: TextureView,
707 pub sampler: Sampler,
708}
709
710#[allow(clippy::too_many_arguments)]
711pub fn prepare_core_3d_transmission_textures(
712 mut commands: Commands,
713 mut texture_cache: ResMut<TextureCache>,
714 render_device: Res<RenderDevice>,
715 opaque_3d_phases: Res<ViewBinnedRenderPhases<Opaque3d>>,
716 alpha_mask_3d_phases: Res<ViewBinnedRenderPhases<AlphaMask3d>>,
717 transmissive_3d_phases: Res<ViewSortedRenderPhases<Transmissive3d>>,
718 transparent_3d_phases: Res<ViewSortedRenderPhases<Transparent3d>>,
719 views_3d: Query<(Entity, &ExtractedCamera, &Camera3d, &ExtractedView)>,
720) {
721 let mut textures = HashMap::default();
722 for (entity, camera, camera_3d, view) in &views_3d {
723 if !opaque_3d_phases.contains_key(&entity)
724 || !alpha_mask_3d_phases.contains_key(&entity)
725 || !transparent_3d_phases.contains_key(&entity)
726 {
727 continue;
728 };
729
730 let Some(transmissive_3d_phase) = transmissive_3d_phases.get(&entity) else {
731 continue;
732 };
733
734 let Some(physical_target_size) = camera.physical_target_size else {
735 continue;
736 };
737
738 if camera_3d.screen_space_specular_transmission_steps == 0 {
740 continue;
741 }
742
743 if transmissive_3d_phase.items.is_empty() {
745 continue;
746 }
747
748 let cached_texture = textures
749 .entry(camera.target.clone())
750 .or_insert_with(|| {
751 let usage = TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST;
752
753 let size = Extent3d {
755 depth_or_array_layers: 1,
756 width: physical_target_size.x,
757 height: physical_target_size.y,
758 };
759
760 let format = if view.hdr {
761 ViewTarget::TEXTURE_FORMAT_HDR
762 } else {
763 TextureFormat::bevy_default()
764 };
765
766 let descriptor = TextureDescriptor {
767 label: Some("view_transmission_texture"),
768 size,
769 mip_level_count: 1,
770 sample_count: 1, dimension: TextureDimension::D2,
772 format,
773 usage,
774 view_formats: &[],
775 };
776
777 texture_cache.get(&render_device, descriptor)
778 })
779 .clone();
780
781 let sampler = render_device.create_sampler(&SamplerDescriptor {
782 label: Some("view_transmission_sampler"),
783 mag_filter: FilterMode::Linear,
784 min_filter: FilterMode::Linear,
785 ..Default::default()
786 });
787
788 commands.entity(entity).insert(ViewTransmissionTexture {
789 texture: cached_texture.texture,
790 view: cached_texture.default_view,
791 sampler,
792 });
793 }
794}
795
796pub fn check_msaa(mut deferred_views: Query<&mut Msaa, (With<Camera>, With<DeferredPrepass>)>) {
798 for mut msaa in deferred_views.iter_mut() {
799 match *msaa {
800 Msaa::Off => (),
801 _ => {
802 warn!("MSAA is incompatible with deferred rendering and has been disabled.");
803 *msaa = Msaa::Off;
804 }
805 };
806 }
807}
808
809#[allow(clippy::too_many_arguments)]
811pub fn prepare_prepass_textures(
812 mut commands: Commands,
813 mut texture_cache: ResMut<TextureCache>,
814 render_device: Res<RenderDevice>,
815 opaque_3d_prepass_phases: Res<ViewBinnedRenderPhases<Opaque3dPrepass>>,
816 alpha_mask_3d_prepass_phases: Res<ViewBinnedRenderPhases<AlphaMask3dPrepass>>,
817 opaque_3d_deferred_phases: Res<ViewBinnedRenderPhases<Opaque3dDeferred>>,
818 alpha_mask_3d_deferred_phases: Res<ViewBinnedRenderPhases<AlphaMask3dDeferred>>,
819 views_3d: Query<(
820 Entity,
821 &ExtractedCamera,
822 &Msaa,
823 Has<DepthPrepass>,
824 Has<NormalPrepass>,
825 Has<MotionVectorPrepass>,
826 Has<DeferredPrepass>,
827 )>,
828) {
829 let mut depth_textures = HashMap::default();
830 let mut normal_textures = HashMap::default();
831 let mut deferred_textures = HashMap::default();
832 let mut deferred_lighting_id_textures = HashMap::default();
833 let mut motion_vectors_textures = HashMap::default();
834 for (
835 entity,
836 camera,
837 msaa,
838 depth_prepass,
839 normal_prepass,
840 motion_vector_prepass,
841 deferred_prepass,
842 ) in &views_3d
843 {
844 if !opaque_3d_prepass_phases.contains_key(&entity)
845 && !alpha_mask_3d_prepass_phases.contains_key(&entity)
846 && !opaque_3d_deferred_phases.contains_key(&entity)
847 && !alpha_mask_3d_deferred_phases.contains_key(&entity)
848 {
849 continue;
850 };
851
852 let Some(physical_target_size) = camera.physical_target_size else {
853 continue;
854 };
855
856 let size = Extent3d {
857 depth_or_array_layers: 1,
858 width: physical_target_size.x,
859 height: physical_target_size.y,
860 };
861
862 let cached_depth_texture = depth_prepass.then(|| {
863 depth_textures
864 .entry(camera.target.clone())
865 .or_insert_with(|| {
866 let descriptor = TextureDescriptor {
867 label: Some("prepass_depth_texture"),
868 size,
869 mip_level_count: 1,
870 sample_count: msaa.samples(),
871 dimension: TextureDimension::D2,
872 format: CORE_3D_DEPTH_FORMAT,
873 usage: TextureUsages::COPY_DST
874 | TextureUsages::RENDER_ATTACHMENT
875 | TextureUsages::TEXTURE_BINDING,
876 view_formats: &[],
877 };
878 texture_cache.get(&render_device, descriptor)
879 })
880 .clone()
881 });
882
883 let cached_normals_texture = normal_prepass.then(|| {
884 normal_textures
885 .entry(camera.target.clone())
886 .or_insert_with(|| {
887 texture_cache.get(
888 &render_device,
889 TextureDescriptor {
890 label: Some("prepass_normal_texture"),
891 size,
892 mip_level_count: 1,
893 sample_count: msaa.samples(),
894 dimension: TextureDimension::D2,
895 format: NORMAL_PREPASS_FORMAT,
896 usage: TextureUsages::RENDER_ATTACHMENT
897 | TextureUsages::TEXTURE_BINDING,
898 view_formats: &[],
899 },
900 )
901 })
902 .clone()
903 });
904
905 let cached_motion_vectors_texture = motion_vector_prepass.then(|| {
906 motion_vectors_textures
907 .entry(camera.target.clone())
908 .or_insert_with(|| {
909 texture_cache.get(
910 &render_device,
911 TextureDescriptor {
912 label: Some("prepass_motion_vectors_textures"),
913 size,
914 mip_level_count: 1,
915 sample_count: msaa.samples(),
916 dimension: TextureDimension::D2,
917 format: MOTION_VECTOR_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_texture = deferred_prepass.then(|| {
928 deferred_textures
929 .entry(camera.target.clone())
930 .or_insert_with(|| {
931 texture_cache.get(
932 &render_device,
933 TextureDescriptor {
934 label: Some("prepass_deferred_texture"),
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: cached_depth_texture
973 .map(|t| ColorAttachment::new(t, None, Some(LinearRgba::BLACK))),
974 normal: cached_normals_texture
975 .map(|t| ColorAttachment::new(t, None, Some(LinearRgba::BLACK))),
976 motion_vectors: cached_motion_vectors_texture
980 .map(|t| ColorAttachment::new(t, None, Some(LinearRgba::BLACK))),
981 deferred: cached_deferred_texture
982 .map(|t| ColorAttachment::new(t, None, Some(LinearRgba::BLACK))),
983 deferred_lighting_pass_id: cached_deferred_lighting_pass_id_texture
984 .map(|t| ColorAttachment::new(t, None, Some(LinearRgba::BLACK))),
985 size,
986 });
987 }
988}