diff --git a/crates/bevy_core_pipeline/src/core_2d/main_opaque_pass_2d_node.rs b/crates/bevy_core_pipeline/src/core_2d/main_opaque_pass_2d_node.rs index d30ae71c64a88..60f355c1153db 100644 --- a/crates/bevy_core_pipeline/src/core_2d/main_opaque_pass_2d_node.rs +++ b/crates/bevy_core_pipeline/src/core_2d/main_opaque_pass_2d_node.rs @@ -7,7 +7,7 @@ use bevy_render::{ render_phase::{TrackedRenderPass, ViewBinnedRenderPhases}, render_resource::{CommandEncoderDescriptor, RenderPassDescriptor, StoreOp}, renderer::RenderContext, - view::{ViewDepthTexture, ViewTarget}, + view::{ExtractedView, ViewDepthTexture, ViewTarget}, }; use tracing::error; #[cfg(feature = "trace")] @@ -22,6 +22,7 @@ pub struct MainOpaquePass2dNode; impl ViewNode for MainOpaquePass2dNode { type ViewQuery = ( &'static ExtractedCamera, + &'static ExtractedView, &'static ViewTarget, &'static ViewDepthTexture, ); @@ -30,7 +31,7 @@ impl ViewNode for MainOpaquePass2dNode { &self, graph: &mut RenderGraphContext, render_context: &mut RenderContext<'w>, - (camera, target, depth): QueryItem<'w, Self::ViewQuery>, + (camera, view, target, depth): QueryItem<'w, Self::ViewQuery>, world: &'w World, ) -> Result<(), NodeRunError> { let (Some(opaque_phases), Some(alpha_mask_phases)) = ( @@ -47,8 +48,8 @@ impl ViewNode for MainOpaquePass2dNode { let view_entity = graph.view_entity(); let (Some(opaque_phase), Some(alpha_mask_phase)) = ( - opaque_phases.get(&view_entity), - alpha_mask_phases.get(&view_entity), + opaque_phases.get(&view.retained_view_entity), + alpha_mask_phases.get(&view.retained_view_entity), ) else { return Ok(()); }; diff --git a/crates/bevy_core_pipeline/src/core_2d/main_transparent_pass_2d_node.rs b/crates/bevy_core_pipeline/src/core_2d/main_transparent_pass_2d_node.rs index abd26dfce290a..bce42cead700e 100644 --- a/crates/bevy_core_pipeline/src/core_2d/main_transparent_pass_2d_node.rs +++ b/crates/bevy_core_pipeline/src/core_2d/main_transparent_pass_2d_node.rs @@ -7,7 +7,7 @@ use bevy_render::{ render_phase::ViewSortedRenderPhases, render_resource::{RenderPassDescriptor, StoreOp}, renderer::RenderContext, - view::{ViewDepthTexture, ViewTarget}, + view::{ExtractedView, ViewDepthTexture, ViewTarget}, }; use tracing::error; #[cfg(feature = "trace")] @@ -19,6 +19,7 @@ pub struct MainTransparentPass2dNode {} impl ViewNode for MainTransparentPass2dNode { type ViewQuery = ( &'static ExtractedCamera, + &'static ExtractedView, &'static ViewTarget, &'static ViewDepthTexture, ); @@ -27,7 +28,7 @@ impl ViewNode for MainTransparentPass2dNode { &self, graph: &mut RenderGraphContext, render_context: &mut RenderContext<'w>, - (camera, target, depth): bevy_ecs::query::QueryItem<'w, Self::ViewQuery>, + (camera, view, target, depth): bevy_ecs::query::QueryItem<'w, Self::ViewQuery>, world: &'w World, ) -> Result<(), NodeRunError> { let Some(transparent_phases) = @@ -37,7 +38,7 @@ impl ViewNode for MainTransparentPass2dNode { }; let view_entity = graph.view_entity(); - let Some(transparent_phase) = transparent_phases.get(&view_entity) else { + let Some(transparent_phase) = transparent_phases.get(&view.retained_view_entity) else { return Ok(()); }; diff --git a/crates/bevy_core_pipeline/src/core_2d/mod.rs b/crates/bevy_core_pipeline/src/core_2d/mod.rs index 288b08b6ae4c0..2d018edaa0ec3 100644 --- a/crates/bevy_core_pipeline/src/core_2d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_2d/mod.rs @@ -33,15 +33,18 @@ pub mod graph { use core::ops::Range; use bevy_asset::UntypedAssetId; -use bevy_render::batching::gpu_preprocessing::GpuPreprocessingMode; -use bevy_utils::HashMap; +use bevy_render::{ + batching::gpu_preprocessing::GpuPreprocessingMode, + view::{ExtractedView, RetainedViewEntity}, +}; +use bevy_utils::{HashMap, HashSet}; pub use camera_2d::*; pub use main_opaque_pass_2d_node::*; pub use main_transparent_pass_2d_node::*; use crate::{tonemapping::TonemappingNode, upscaling::UpscalingNode}; use bevy_app::{App, Plugin}; -use bevy_ecs::{entity::EntityHashSet, prelude::*}; +use bevy_ecs::prelude::*; use bevy_math::FloatOrd; use bevy_render::{ camera::{Camera, ExtractedCamera}, @@ -57,7 +60,7 @@ use bevy_render::{ TextureFormat, TextureUsages, }, renderer::RenderDevice, - sync_world::{MainEntity, RenderEntity}, + sync_world::MainEntity, texture::TextureCache, view::{Msaa, ViewDepthTexture}, Extract, ExtractSchedule, Render, RenderApp, RenderSet, @@ -397,20 +400,24 @@ pub fn extract_core_2d_camera_phases( mut transparent_2d_phases: ResMut>, mut opaque_2d_phases: ResMut>, mut alpha_mask_2d_phases: ResMut>, - cameras_2d: Extract>>, - mut live_entities: Local, + cameras_2d: Extract>>, + mut live_entities: Local>, ) { live_entities.clear(); - for (entity, camera) in &cameras_2d { + for (main_entity, camera) in &cameras_2d { if !camera.is_active { continue; } - transparent_2d_phases.insert_or_clear(entity); - opaque_2d_phases.insert_or_clear(entity, GpuPreprocessingMode::None); - alpha_mask_2d_phases.insert_or_clear(entity, GpuPreprocessingMode::None); - live_entities.insert(entity); + // This is the main 2D camera, so we use the first subview index (0). + let retained_view_entity = RetainedViewEntity::new(main_entity.into(), 0); + + transparent_2d_phases.insert_or_clear(retained_view_entity); + opaque_2d_phases.insert_or_clear(retained_view_entity, GpuPreprocessingMode::None); + alpha_mask_2d_phases.insert_or_clear(retained_view_entity, GpuPreprocessingMode::None); + + live_entities.insert(retained_view_entity); } // Clear out all dead views. @@ -425,11 +432,13 @@ pub fn prepare_core_2d_depth_textures( render_device: Res, transparent_2d_phases: Res>, opaque_2d_phases: Res>, - views_2d: Query<(Entity, &ExtractedCamera, &Msaa), (With,)>, + views_2d: Query<(Entity, &ExtractedCamera, &ExtractedView, &Msaa), (With,)>, ) { let mut textures = >::default(); - for (view, camera, msaa) in &views_2d { - if !opaque_2d_phases.contains_key(&view) || !transparent_2d_phases.contains_key(&view) { + for (view, camera, extracted_view, msaa) in &views_2d { + if !opaque_2d_phases.contains_key(&extracted_view.retained_view_entity) + || !transparent_2d_phases.contains_key(&extracted_view.retained_view_entity) + { continue; }; diff --git a/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs b/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs index 7fe338f42166b..3b1bc96c9014d 100644 --- a/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs +++ b/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs @@ -2,7 +2,7 @@ use crate::{ core_3d::Opaque3d, skybox::{SkyboxBindGroup, SkyboxPipelineId}, }; -use bevy_ecs::{entity::Entity, prelude::World, query::QueryItem}; +use bevy_ecs::{prelude::World, query::QueryItem}; use bevy_render::{ camera::ExtractedCamera, diagnostic::RecordDiagnostics, @@ -10,7 +10,7 @@ use bevy_render::{ render_phase::{TrackedRenderPass, ViewBinnedRenderPhases}, render_resource::{CommandEncoderDescriptor, PipelineCache, RenderPassDescriptor, StoreOp}, renderer::RenderContext, - view::{ViewDepthTexture, ViewTarget, ViewUniformOffset}, + view::{ExtractedView, ViewDepthTexture, ViewTarget, ViewUniformOffset}, }; use tracing::error; #[cfg(feature = "trace")] @@ -24,8 +24,8 @@ use super::AlphaMask3d; pub struct MainOpaquePass3dNode; impl ViewNode for MainOpaquePass3dNode { type ViewQuery = ( - Entity, &'static ExtractedCamera, + &'static ExtractedView, &'static ViewTarget, &'static ViewDepthTexture, Option<&'static SkyboxPipelineId>, @@ -38,8 +38,8 @@ impl ViewNode for MainOpaquePass3dNode { graph: &mut RenderGraphContext, render_context: &mut RenderContext<'w>, ( - view, camera, + extracted_view, target, depth, skybox_pipeline, @@ -55,9 +55,10 @@ impl ViewNode for MainOpaquePass3dNode { return Ok(()); }; - let (Some(opaque_phase), Some(alpha_mask_phase)) = - (opaque_phases.get(&view), alpha_mask_phases.get(&view)) - else { + let (Some(opaque_phase), Some(alpha_mask_phase)) = ( + opaque_phases.get(&extracted_view.retained_view_entity), + alpha_mask_phases.get(&extracted_view.retained_view_entity), + ) else { return Ok(()); }; diff --git a/crates/bevy_core_pipeline/src/core_3d/main_transmissive_pass_3d_node.rs b/crates/bevy_core_pipeline/src/core_3d/main_transmissive_pass_3d_node.rs index 72551803236e9..0a2e98f0bf9ac 100644 --- a/crates/bevy_core_pipeline/src/core_3d/main_transmissive_pass_3d_node.rs +++ b/crates/bevy_core_pipeline/src/core_3d/main_transmissive_pass_3d_node.rs @@ -7,7 +7,7 @@ use bevy_render::{ render_phase::ViewSortedRenderPhases, render_resource::{Extent3d, RenderPassDescriptor, StoreOp}, renderer::RenderContext, - view::{ViewDepthTexture, ViewTarget}, + view::{ExtractedView, ViewDepthTexture, ViewTarget}, }; use core::ops::Range; use tracing::error; @@ -22,6 +22,7 @@ pub struct MainTransmissivePass3dNode; impl ViewNode for MainTransmissivePass3dNode { type ViewQuery = ( &'static ExtractedCamera, + &'static ExtractedView, &'static Camera3d, &'static ViewTarget, Option<&'static ViewTransmissionTexture>, @@ -32,7 +33,7 @@ impl ViewNode for MainTransmissivePass3dNode { &self, graph: &mut RenderGraphContext, render_context: &mut RenderContext, - (camera, camera_3d, target, transmission, depth): QueryItem, + (camera, view, camera_3d, target, transmission, depth): QueryItem, world: &World, ) -> Result<(), NodeRunError> { let view_entity = graph.view_entity(); @@ -43,7 +44,7 @@ impl ViewNode for MainTransmissivePass3dNode { return Ok(()); }; - let Some(transmissive_phase) = transmissive_phases.get(&view_entity) else { + let Some(transmissive_phase) = transmissive_phases.get(&view.retained_view_entity) else { return Ok(()); }; diff --git a/crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs b/crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs index 719a7ae5362a2..36fe8417c4de2 100644 --- a/crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs +++ b/crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs @@ -7,7 +7,7 @@ use bevy_render::{ render_phase::ViewSortedRenderPhases, render_resource::{RenderPassDescriptor, StoreOp}, renderer::RenderContext, - view::{ViewDepthTexture, ViewTarget}, + view::{ExtractedView, ViewDepthTexture, ViewTarget}, }; use tracing::error; #[cfg(feature = "trace")] @@ -21,6 +21,7 @@ pub struct MainTransparentPass3dNode; impl ViewNode for MainTransparentPass3dNode { type ViewQuery = ( &'static ExtractedCamera, + &'static ExtractedView, &'static ViewTarget, &'static ViewDepthTexture, ); @@ -28,7 +29,7 @@ impl ViewNode for MainTransparentPass3dNode { &self, graph: &mut RenderGraphContext, render_context: &mut RenderContext, - (camera, target, depth): QueryItem, + (camera, view, target, depth): QueryItem, world: &World, ) -> Result<(), NodeRunError> { let view_entity = graph.view_entity(); @@ -39,7 +40,7 @@ impl ViewNode for MainTransparentPass3dNode { return Ok(()); }; - let Some(transparent_phase) = transparent_phases.get(&view_entity) else { + let Some(transparent_phase) = transparent_phases.get(&view.retained_view_entity) else { return Ok(()); }; diff --git a/crates/bevy_core_pipeline/src/core_3d/mod.rs b/crates/bevy_core_pipeline/src/core_3d/mod.rs index 4bd42104cbce6..02ac01a9922be 100644 --- a/crates/bevy_core_pipeline/src/core_3d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_3d/mod.rs @@ -68,7 +68,7 @@ use core::ops::Range; use bevy_render::{ batching::gpu_preprocessing::{GpuPreprocessingMode, GpuPreprocessingSupport}, mesh::allocator::SlabId, - view::NoIndirectDrawing, + view::{NoIndirectDrawing, RetainedViewEntity}, }; pub use camera_3d::*; pub use main_opaque_pass_3d_node::*; @@ -77,7 +77,7 @@ pub use main_transparent_pass_3d_node::*; use bevy_app::{App, Plugin, PostUpdate}; use bevy_asset::UntypedAssetId; use bevy_color::LinearRgba; -use bevy_ecs::{entity::EntityHashSet, prelude::*}; +use bevy_ecs::prelude::*; use bevy_image::BevyDefault; use bevy_math::FloatOrd; use bevy_render::{ @@ -100,7 +100,7 @@ use bevy_render::{ view::{ExtractedView, ViewDepthTexture, ViewTarget}, Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; -use bevy_utils::HashMap; +use bevy_utils::{HashMap, HashSet}; use nonmax::NonMaxU32; use tracing::warn; @@ -574,13 +574,13 @@ pub fn extract_core_3d_camera_phases( mut alpha_mask_3d_phases: ResMut>, mut transmissive_3d_phases: ResMut>, mut transparent_3d_phases: ResMut>, - cameras_3d: Extract), With>>, - mut live_entities: Local, + cameras_3d: Extract), With>>, + mut live_entities: Local>, gpu_preprocessing_support: Res, ) { live_entities.clear(); - for (entity, camera, no_indirect_drawing) in &cameras_3d { + for (main_entity, camera, no_indirect_drawing) in &cameras_3d { if !camera.is_active { continue; } @@ -593,18 +593,21 @@ pub fn extract_core_3d_camera_phases( GpuPreprocessingMode::PreprocessingOnly }); - opaque_3d_phases.insert_or_clear(entity, gpu_preprocessing_mode); - alpha_mask_3d_phases.insert_or_clear(entity, gpu_preprocessing_mode); - transmissive_3d_phases.insert_or_clear(entity); - transparent_3d_phases.insert_or_clear(entity); + // This is the main 3D camera, so use the first subview index (0). + let retained_view_entity = RetainedViewEntity::new(main_entity.into(), 0); - live_entities.insert(entity); + opaque_3d_phases.insert_or_clear(retained_view_entity, gpu_preprocessing_mode); + alpha_mask_3d_phases.insert_or_clear(retained_view_entity, gpu_preprocessing_mode); + transmissive_3d_phases.insert_or_clear(retained_view_entity); + transparent_3d_phases.insert_or_clear(retained_view_entity); + + live_entities.insert(retained_view_entity); } - opaque_3d_phases.retain(|entity, _| live_entities.contains(entity)); - alpha_mask_3d_phases.retain(|entity, _| live_entities.contains(entity)); - transmissive_3d_phases.retain(|entity, _| live_entities.contains(entity)); - transparent_3d_phases.retain(|entity, _| live_entities.contains(entity)); + opaque_3d_phases.retain(|view_entity, _| live_entities.contains(view_entity)); + alpha_mask_3d_phases.retain(|view_entity, _| live_entities.contains(view_entity)); + transmissive_3d_phases.retain(|view_entity, _| live_entities.contains(view_entity)); + transparent_3d_phases.retain(|view_entity, _| live_entities.contains(view_entity)); } // Extract the render phases for the prepass @@ -618,6 +621,7 @@ pub fn extract_camera_prepass_phase( cameras_3d: Extract< Query< ( + Entity, RenderEntity, &Camera, Has, @@ -629,12 +633,13 @@ pub fn extract_camera_prepass_phase( With, >, >, - mut live_entities: Local, + mut live_entities: Local>, gpu_preprocessing_support: Res, ) { live_entities.clear(); for ( + main_entity, entity, camera, no_indirect_drawing, @@ -656,22 +661,27 @@ pub fn extract_camera_prepass_phase( GpuPreprocessingMode::PreprocessingOnly }); + // This is the main 3D camera, so we use the first subview index (0). + let retained_view_entity = RetainedViewEntity::new(main_entity.into(), 0); + if depth_prepass || normal_prepass || motion_vector_prepass { - opaque_3d_prepass_phases.insert_or_clear(entity, gpu_preprocessing_mode); - alpha_mask_3d_prepass_phases.insert_or_clear(entity, gpu_preprocessing_mode); + opaque_3d_prepass_phases.insert_or_clear(retained_view_entity, gpu_preprocessing_mode); + alpha_mask_3d_prepass_phases + .insert_or_clear(retained_view_entity, gpu_preprocessing_mode); } else { - opaque_3d_prepass_phases.remove(&entity); - alpha_mask_3d_prepass_phases.remove(&entity); + opaque_3d_prepass_phases.remove(&retained_view_entity); + alpha_mask_3d_prepass_phases.remove(&retained_view_entity); } if deferred_prepass { - opaque_3d_deferred_phases.insert_or_clear(entity, gpu_preprocessing_mode); - alpha_mask_3d_deferred_phases.insert_or_clear(entity, gpu_preprocessing_mode); + opaque_3d_deferred_phases.insert_or_clear(retained_view_entity, gpu_preprocessing_mode); + alpha_mask_3d_deferred_phases + .insert_or_clear(retained_view_entity, gpu_preprocessing_mode); } else { - opaque_3d_deferred_phases.remove(&entity); - alpha_mask_3d_deferred_phases.remove(&entity); + opaque_3d_deferred_phases.remove(&retained_view_entity); + alpha_mask_3d_deferred_phases.remove(&retained_view_entity); } - live_entities.insert(entity); + live_entities.insert(retained_view_entity); commands .get_entity(entity) @@ -682,10 +692,10 @@ pub fn extract_camera_prepass_phase( .insert_if(DeferredPrepass, || deferred_prepass); } - opaque_3d_prepass_phases.retain(|entity, _| live_entities.contains(entity)); - alpha_mask_3d_prepass_phases.retain(|entity, _| live_entities.contains(entity)); - opaque_3d_deferred_phases.retain(|entity, _| live_entities.contains(entity)); - alpha_mask_3d_deferred_phases.retain(|entity, _| live_entities.contains(entity)); + opaque_3d_prepass_phases.retain(|view_entity, _| live_entities.contains(view_entity)); + alpha_mask_3d_prepass_phases.retain(|view_entity, _| live_entities.contains(view_entity)); + opaque_3d_deferred_phases.retain(|view_entity, _| live_entities.contains(view_entity)); + alpha_mask_3d_deferred_phases.retain(|view_entity, _| live_entities.contains(view_entity)); } pub fn prepare_core_3d_depth_textures( @@ -699,17 +709,18 @@ pub fn prepare_core_3d_depth_textures( views_3d: Query<( Entity, &ExtractedCamera, + &ExtractedView, Option<&DepthPrepass>, &Camera3d, &Msaa, )>, ) { let mut render_target_usage = >::default(); - for (view, camera, depth_prepass, camera_3d, _msaa) in &views_3d { - if !opaque_3d_phases.contains_key(&view) - || !alpha_mask_3d_phases.contains_key(&view) - || !transmissive_3d_phases.contains_key(&view) - || !transparent_3d_phases.contains_key(&view) + for (_, camera, extracted_view, depth_prepass, camera_3d, _msaa) in &views_3d { + if !opaque_3d_phases.contains_key(&extracted_view.retained_view_entity) + || !alpha_mask_3d_phases.contains_key(&extracted_view.retained_view_entity) + || !transmissive_3d_phases.contains_key(&extracted_view.retained_view_entity) + || !transparent_3d_phases.contains_key(&extracted_view.retained_view_entity) { continue; }; @@ -727,7 +738,7 @@ pub fn prepare_core_3d_depth_textures( } let mut textures = >::default(); - for (entity, camera, _, camera_3d, msaa) in &views_3d { + for (entity, camera, _, _, camera_3d, msaa) in &views_3d { let Some(physical_target_size) = camera.physical_target_size else { continue; }; @@ -790,14 +801,15 @@ pub fn prepare_core_3d_transmission_textures( ) { let mut textures = >::default(); for (entity, camera, camera_3d, view) in &views_3d { - if !opaque_3d_phases.contains_key(&entity) - || !alpha_mask_3d_phases.contains_key(&entity) - || !transparent_3d_phases.contains_key(&entity) + if !opaque_3d_phases.contains_key(&view.retained_view_entity) + || !alpha_mask_3d_phases.contains_key(&view.retained_view_entity) + || !transparent_3d_phases.contains_key(&view.retained_view_entity) { continue; }; - let Some(transmissive_3d_phase) = transmissive_3d_phases.get(&entity) else { + let Some(transmissive_3d_phase) = transmissive_3d_phases.get(&view.retained_view_entity) + else { continue; }; @@ -888,6 +900,7 @@ pub fn prepare_prepass_textures( views_3d: Query<( Entity, &ExtractedCamera, + &ExtractedView, &Msaa, Has, Has, @@ -903,6 +916,7 @@ pub fn prepare_prepass_textures( for ( entity, camera, + view, msaa, depth_prepass, normal_prepass, @@ -910,10 +924,10 @@ pub fn prepare_prepass_textures( deferred_prepass, ) in &views_3d { - if !opaque_3d_prepass_phases.contains_key(&entity) - && !alpha_mask_3d_prepass_phases.contains_key(&entity) - && !opaque_3d_deferred_phases.contains_key(&entity) - && !alpha_mask_3d_deferred_phases.contains_key(&entity) + if !opaque_3d_prepass_phases.contains_key(&view.retained_view_entity) + && !alpha_mask_3d_prepass_phases.contains_key(&view.retained_view_entity) + && !opaque_3d_deferred_phases.contains_key(&view.retained_view_entity) + && !alpha_mask_3d_deferred_phases.contains_key(&view.retained_view_entity) { continue; }; diff --git a/crates/bevy_core_pipeline/src/deferred/node.rs b/crates/bevy_core_pipeline/src/deferred/node.rs index 99287ae4f405d..5485e8fc0034d 100644 --- a/crates/bevy_core_pipeline/src/deferred/node.rs +++ b/crates/bevy_core_pipeline/src/deferred/node.rs @@ -1,6 +1,7 @@ use bevy_ecs::{prelude::*, query::QueryItem}; use bevy_render::render_graph::ViewNode; +use bevy_render::view::ExtractedView; use bevy_render::{ camera::ExtractedCamera, render_graph::{NodeRunError, RenderGraphContext}, @@ -25,8 +26,8 @@ pub struct DeferredGBufferPrepassNode; impl ViewNode for DeferredGBufferPrepassNode { type ViewQuery = ( - Entity, &'static ExtractedCamera, + &'static ExtractedView, &'static ViewDepthTexture, &'static ViewPrepassTextures, ); @@ -35,7 +36,10 @@ impl ViewNode for DeferredGBufferPrepassNode { &self, graph: &mut RenderGraphContext, render_context: &mut RenderContext<'w>, - (view, camera, view_depth_texture, view_prepass_textures): QueryItem<'w, Self::ViewQuery>, + (camera, extracted_view, view_depth_texture, view_prepass_textures): QueryItem< + 'w, + Self::ViewQuery, + >, world: &'w World, ) -> Result<(), NodeRunError> { let (Some(opaque_deferred_phases), Some(alpha_mask_deferred_phases)) = ( @@ -46,8 +50,8 @@ impl ViewNode for DeferredGBufferPrepassNode { }; let (Some(opaque_deferred_phase), Some(alpha_mask_deferred_phase)) = ( - opaque_deferred_phases.get(&view), - alpha_mask_deferred_phases.get(&view), + opaque_deferred_phases.get(&extracted_view.retained_view_entity), + alpha_mask_deferred_phases.get(&extracted_view.retained_view_entity), ) else { return Ok(()); }; diff --git a/crates/bevy_core_pipeline/src/prepass/node.rs b/crates/bevy_core_pipeline/src/prepass/node.rs index 4a32ebfc016d9..9019890d7ed86 100644 --- a/crates/bevy_core_pipeline/src/prepass/node.rs +++ b/crates/bevy_core_pipeline/src/prepass/node.rs @@ -6,7 +6,7 @@ use bevy_render::{ render_phase::{TrackedRenderPass, ViewBinnedRenderPhases}, render_resource::{CommandEncoderDescriptor, PipelineCache, RenderPassDescriptor, StoreOp}, renderer::RenderContext, - view::{ViewDepthTexture, ViewUniformOffset}, + view::{ExtractedView, ViewDepthTexture, ViewUniformOffset}, }; use tracing::error; #[cfg(feature = "trace")] @@ -27,8 +27,8 @@ pub struct PrepassNode; impl ViewNode for PrepassNode { type ViewQuery = ( - Entity, &'static ExtractedCamera, + &'static ExtractedView, &'static ViewDepthTexture, &'static ViewPrepassTextures, &'static ViewUniformOffset, @@ -43,8 +43,8 @@ impl ViewNode for PrepassNode { graph: &mut RenderGraphContext, render_context: &mut RenderContext<'w>, ( - view, camera, + extracted_view, view_depth_texture, view_prepass_textures, view_uniform_offset, @@ -63,8 +63,8 @@ impl ViewNode for PrepassNode { }; let (Some(opaque_prepass_phase), Some(alpha_mask_prepass_phase)) = ( - opaque_prepass_phases.get(&view), - alpha_mask_prepass_phases.get(&view), + opaque_prepass_phases.get(&extracted_view.retained_view_entity), + alpha_mask_prepass_phases.get(&extracted_view.retained_view_entity), ) else { return Ok(()); }; diff --git a/crates/bevy_gizmos/src/pipeline_2d.rs b/crates/bevy_gizmos/src/pipeline_2d.rs index 55ce5322ff40b..96df48e164ca6 100644 --- a/crates/bevy_gizmos/src/pipeline_2d.rs +++ b/crates/bevy_gizmos/src/pipeline_2d.rs @@ -296,7 +296,7 @@ fn queue_line_gizmos_2d( line_gizmos: Query<(Entity, &MainEntity, &GizmoMeshConfig)>, line_gizmo_assets: Res>, mut transparent_render_phases: ResMut>, - views: Query<(Entity, &ExtractedView, &Msaa, Option<&RenderLayers>)>, + mut views: Query<(&ExtractedView, &Msaa, Option<&RenderLayers>)>, ) { let draw_function = draw_functions.read().get_id::().unwrap(); let draw_function_strip = draw_functions @@ -304,8 +304,9 @@ fn queue_line_gizmos_2d( .get_id::() .unwrap(); - for (view_entity, view, msaa, render_layers) in &views { - let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else { + for (view, msaa, render_layers) in &mut views { + let Some(transparent_phase) = transparent_render_phases.get_mut(&view.retained_view_entity) + else { continue; }; @@ -372,15 +373,16 @@ fn queue_line_joint_gizmos_2d( line_gizmos: Query<(Entity, &MainEntity, &GizmoMeshConfig)>, line_gizmo_assets: Res>, mut transparent_render_phases: ResMut>, - views: Query<(Entity, &ExtractedView, &Msaa, Option<&RenderLayers>)>, + mut views: Query<(&ExtractedView, &Msaa, Option<&RenderLayers>)>, ) { let draw_function = draw_functions .read() .get_id::() .unwrap(); - for (view_entity, view, msaa, render_layers) in &views { - let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else { + for (view, msaa, render_layers) in &mut views { + let Some(transparent_phase) = transparent_render_phases.get_mut(&view.retained_view_entity) + else { continue; }; diff --git a/crates/bevy_gizmos/src/pipeline_3d.rs b/crates/bevy_gizmos/src/pipeline_3d.rs index 89814bcbffd83..458cb28e70ef7 100644 --- a/crates/bevy_gizmos/src/pipeline_3d.rs +++ b/crates/bevy_gizmos/src/pipeline_3d.rs @@ -292,7 +292,6 @@ fn queue_line_gizmos_3d( line_gizmo_assets: Res>, mut transparent_render_phases: ResMut>, views: Query<( - Entity, &ExtractedView, &Msaa, Option<&RenderLayers>, @@ -311,14 +310,14 @@ fn queue_line_gizmos_3d( .unwrap(); for ( - view_entity, view, msaa, render_layers, (normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass), ) in &views { - let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else { + let Some(transparent_phase) = transparent_render_phases.get_mut(&view.retained_view_entity) + else { continue; }; @@ -406,7 +405,6 @@ fn queue_line_joint_gizmos_3d( line_gizmo_assets: Res>, mut transparent_render_phases: ResMut>, views: Query<( - Entity, &ExtractedView, &Msaa, Option<&RenderLayers>, @@ -424,14 +422,14 @@ fn queue_line_joint_gizmos_3d( .unwrap(); for ( - view_entity, view, msaa, render_layers, (normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass), ) in &views { - let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else { + let Some(transparent_phase) = transparent_render_phases.get_mut(&view.retained_view_entity) + else { continue; }; diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index c39c9c89b64c2..c22952226dc96 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -638,7 +638,6 @@ pub fn queue_material_meshes( mut transmissive_render_phases: ResMut>, mut transparent_render_phases: ResMut>, views: Query<( - Entity, &ExtractedView, &RenderVisibleEntities, &Msaa, @@ -665,7 +664,6 @@ pub fn queue_material_meshes( M::Data: PartialEq + Eq + Hash + Clone, { for ( - view_entity, view, visible_entities, msaa, @@ -687,10 +685,10 @@ pub fn queue_material_meshes( Some(transmissive_phase), Some(transparent_phase), ) = ( - opaque_render_phases.get_mut(&view_entity), - alpha_mask_render_phases.get_mut(&view_entity), - transmissive_render_phases.get_mut(&view_entity), - transparent_render_phases.get_mut(&view_entity), + opaque_render_phases.get_mut(&view.retained_view_entity), + alpha_mask_render_phases.get_mut(&view.retained_view_entity), + transmissive_render_phases.get_mut(&view.retained_view_entity), + transparent_render_phases.get_mut(&view.retained_view_entity), ) else { continue; diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index 6b0c319a2d7e3..9b34c04dc1bb6 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -793,18 +793,15 @@ pub fn queue_prepass_material_meshes( mut alpha_mask_prepass_render_phases: ResMut>, mut opaque_deferred_render_phases: ResMut>, mut alpha_mask_deferred_render_phases: ResMut>, - views: Query< - ( - Entity, - &RenderVisibleEntities, - &Msaa, - Option<&DepthPrepass>, - Option<&NormalPrepass>, - Option<&MotionVectorPrepass>, - Option<&DeferredPrepass>, - ), - With, - >, + views: Query<( + &ExtractedView, + &RenderVisibleEntities, + &Msaa, + Option<&DepthPrepass>, + Option<&NormalPrepass>, + Option<&MotionVectorPrepass>, + Option<&DeferredPrepass>, + )>, ) where M::Data: PartialEq + Eq + Hash + Clone, { @@ -825,7 +822,7 @@ pub fn queue_prepass_material_meshes( .get_id::>() .unwrap(); for ( - view, + extracted_view, visible_entities, msaa, depth_prepass, @@ -840,10 +837,10 @@ pub fn queue_prepass_material_meshes( mut opaque_deferred_phase, mut alpha_mask_deferred_phase, ) = ( - opaque_prepass_render_phases.get_mut(&view), - alpha_mask_prepass_render_phases.get_mut(&view), - opaque_deferred_render_phases.get_mut(&view), - alpha_mask_deferred_render_phases.get_mut(&view), + opaque_prepass_render_phases.get_mut(&extracted_view.retained_view_entity), + alpha_mask_prepass_render_phases.get_mut(&extracted_view.retained_view_entity), + opaque_deferred_render_phases.get_mut(&extracted_view.retained_view_entity), + alpha_mask_deferred_render_phases.get_mut(&extracted_view.retained_view_entity), ); // Skip if there's no place to put the mesh. diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 2f52f94aa7620..873d2eede44cc 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -15,7 +15,7 @@ use bevy_render::{ batching::gpu_preprocessing::{GpuPreprocessingMode, GpuPreprocessingSupport}, camera::SortedCameras, mesh::allocator::MeshAllocator, - view::NoIndirectDrawing, + view::{NoIndirectDrawing, RetainedViewEntity}, }; use bevy_render::{ diagnostic::RecordDiagnostics, @@ -35,7 +35,7 @@ use bevy_render::{ sync_world::{MainEntity, RenderEntity}, }; use bevy_transform::{components::GlobalTransform, prelude::Transform}; -use bevy_utils::{default, HashMap}; +use bevy_utils::{default, HashMap, HashSet}; use core::{hash::Hash, ops::Range}; #[cfg(feature = "trace")] use tracing::info_span; @@ -213,6 +213,7 @@ pub fn extract_lights( global_point_lights: Extract>, point_lights: Extract< Query<( + Entity, RenderEntity, &PointLight, &CubemapVisibleEntities, @@ -224,6 +225,7 @@ pub fn extract_lights( >, spot_lights: Extract< Query<( + Entity, RenderEntity, &SpotLight, &VisibleMeshEntities, @@ -236,6 +238,7 @@ pub fn extract_lights( directional_lights: Extract< Query< ( + Entity, RenderEntity, &DirectionalLight, &CascadesVisibleEntities, @@ -274,6 +277,7 @@ pub fn extract_lights( let mut point_lights_values = Vec::with_capacity(*previous_point_lights_len); for entity in global_point_lights.iter().copied() { let Ok(( + main_entity, render_entity, point_light, cubemap_visible_entities, @@ -327,6 +331,7 @@ pub fn extract_lights( extracted_point_light, render_cubemap_visible_entities, (*frusta).clone(), + MainEntity::from(main_entity), ), )); } @@ -336,6 +341,7 @@ pub fn extract_lights( let mut spot_lights_values = Vec::with_capacity(*previous_spot_lights_len); for entity in global_point_lights.iter().copied() { if let Ok(( + main_entity, render_entity, spot_light, visible_entities, @@ -387,6 +393,7 @@ pub fn extract_lights( }, render_visible_entities, *frustum, + MainEntity::from(main_entity), ), )); } @@ -395,6 +402,7 @@ pub fn extract_lights( commands.insert_or_spawn_batch(spot_lights_values); for ( + main_entity, entity, directional_light, visible_entities, @@ -474,6 +482,7 @@ pub fn extract_lights( RenderCascadesVisibleEntities { entities: cascade_visible_entities, }, + MainEntity::from(main_entity), )); } } @@ -707,13 +716,14 @@ pub fn prepare_lights( mut max_directional_lights_warning_emitted, mut max_cascades_per_light_warning_emitted, mut live_shadow_mapping_lights, - ): (Local, Local, Local), + ): (Local, Local, Local>), point_lights: Query<( Entity, + &MainEntity, &ExtractedPointLight, AnyOf<(&CubemapFrusta, &Frustum)>, )>, - directional_lights: Query<(Entity, &ExtractedDirectionalLight)>, + directional_lights: Query<(Entity, &MainEntity, &ExtractedDirectionalLight)>, mut light_view_entities: Query<&mut LightViewEntities>, sorted_cameras: Res, gpu_preprocessing_support: Res, @@ -769,7 +779,7 @@ pub fn prepare_lights( if !*max_cascades_per_light_warning_emitted && directional_lights .iter() - .any(|(_, light)| light.cascade_shadow_config.bounds.len() > MAX_CASCADES_PER_LIGHT) + .any(|(_, _, light)| light.cascade_shadow_config.bounds.len() > MAX_CASCADES_PER_LIGHT) { warn!( "The number of cascades configured for a directional light exceeds the supported limit of {}.", @@ -780,50 +790,50 @@ pub fn prepare_lights( let point_light_count = point_lights .iter() - .filter(|light| light.1.spot_light_angles.is_none()) + .filter(|light| light.2.spot_light_angles.is_none()) .count(); let point_light_volumetric_enabled_count = point_lights .iter() - .filter(|(_, light, _)| light.volumetric && light.spot_light_angles.is_none()) + .filter(|(_, _, light, _)| light.volumetric && light.spot_light_angles.is_none()) .count() .min(max_texture_cubes); let point_light_shadow_maps_count = point_lights .iter() - .filter(|light| light.1.shadows_enabled && light.1.spot_light_angles.is_none()) + .filter(|light| light.2.shadows_enabled && light.2.spot_light_angles.is_none()) .count() .min(max_texture_cubes); let directional_volumetric_enabled_count = directional_lights .iter() .take(MAX_DIRECTIONAL_LIGHTS) - .filter(|(_, light)| light.volumetric) + .filter(|(_, _, light)| light.volumetric) .count() .min(max_texture_array_layers / MAX_CASCADES_PER_LIGHT); let directional_shadow_enabled_count = directional_lights .iter() .take(MAX_DIRECTIONAL_LIGHTS) - .filter(|(_, light)| light.shadows_enabled) + .filter(|(_, _, light)| light.shadows_enabled) .count() .min(max_texture_array_layers / MAX_CASCADES_PER_LIGHT); let spot_light_count = point_lights .iter() - .filter(|(_, light, _)| light.spot_light_angles.is_some()) + .filter(|(_, _, light, _)| light.spot_light_angles.is_some()) .count() .min(max_texture_array_layers - directional_shadow_enabled_count * MAX_CASCADES_PER_LIGHT); let spot_light_volumetric_enabled_count = point_lights .iter() - .filter(|(_, light, _)| light.volumetric && light.spot_light_angles.is_some()) + .filter(|(_, _, light, _)| light.volumetric && light.spot_light_angles.is_some()) .count() .min(max_texture_array_layers - directional_shadow_enabled_count * MAX_CASCADES_PER_LIGHT); let spot_light_shadow_maps_count = point_lights .iter() - .filter(|(_, light, _)| light.shadows_enabled && light.spot_light_angles.is_some()) + .filter(|(_, _, light, _)| light.shadows_enabled && light.spot_light_angles.is_some()) .count() .min(max_texture_array_layers - directional_shadow_enabled_count * MAX_CASCADES_PER_LIGHT); @@ -832,7 +842,7 @@ pub fn prepare_lights( // - then those with shadows enabled first, so that the index can be used to render at most `point_light_shadow_maps_count` // point light shadows and `spot_light_shadow_maps_count` spot light shadow maps, // - then by entity as a stable key to ensure that a consistent set of lights are chosen if the light count limit is exceeded. - point_lights.sort_by_cached_key(|(entity, light, _)| { + point_lights.sort_by_cached_key(|(entity, _, light, _)| { ( ClusterableObjectType::from_point_or_spot_light(light).ordering(), *entity, @@ -849,8 +859,9 @@ pub fn prepare_lights( // lights are chosen if the light count limit is exceeded. // - because entities are unique, we can use `sort_unstable_by_key` // and still end up with a stable order. - directional_lights - .sort_unstable_by_key(|(entity, light)| (light.volumetric, light.shadows_enabled, *entity)); + directional_lights.sort_unstable_by_key(|(entity, _, light)| { + (light.volumetric, light.shadows_enabled, *entity) + }); if global_light_meta.entity_to_index.capacity() < point_lights.len() { global_light_meta @@ -859,7 +870,7 @@ pub fn prepare_lights( } let mut gpu_point_lights = Vec::new(); - for (index, &(entity, light, _)) in point_lights.iter().enumerate() { + for (index, &(entity, _, light, _)) in point_lights.iter().enumerate() { let mut flags = PointLightFlags::NONE; // Lights are sorted, shadow enabled lights are first @@ -948,7 +959,7 @@ pub fn prepare_lights( let mut gpu_directional_lights = [GpuDirectionalLight::default(); MAX_DIRECTIONAL_LIGHTS]; let mut num_directional_cascades_enabled = 0usize; - for (index, (_light_entity, light)) in directional_lights + for (index, (_light_entity, _, light)) in directional_lights .iter() .enumerate() .take(MAX_DIRECTIONAL_LIGHTS) @@ -1150,7 +1161,7 @@ pub fn prepare_lights( }; // TODO: this should select lights based on relevance to the view instead of the first ones that show up in a query - for &(light_entity, light, (point_light_frusta, _)) in point_lights + for &(light_entity, light_main_entity, light, (point_light_frusta, _)) in point_lights .iter() // Lights are sorted, shadow enabled lights are first .take(point_light_count.min(max_texture_cubes)) @@ -1218,6 +1229,9 @@ pub fn prepare_lights( }) .clone(); + let retained_view_entity = + RetainedViewEntity::new(*light_main_entity, face_index as u32); + commands.entity(view_light_entity).insert(( ShadowView { depth_attachment, @@ -1228,6 +1242,7 @@ pub fn prepare_lights( ), }, ExtractedView { + retained_view_entity, viewport: UVec4::new( 0, 0, @@ -1255,18 +1270,20 @@ pub fn prepare_lights( if first { // Subsequent views with the same light entity will reuse the same shadow map - shadow_render_phases.insert_or_clear(view_light_entity, gpu_preprocessing_mode); - live_shadow_mapping_lights.insert(view_light_entity); + shadow_render_phases + .insert_or_clear(retained_view_entity, gpu_preprocessing_mode); + live_shadow_mapping_lights.insert(retained_view_entity); } } } // spot lights - for (light_index, &(light_entity, light, (_, spot_light_frustum))) in point_lights - .iter() - .skip(point_light_count) - .take(spot_light_count) - .enumerate() + for (light_index, &(light_entity, light_main_entity, light, (_, spot_light_frustum))) in + point_lights + .iter() + .skip(point_light_count) + .take(spot_light_count) + .enumerate() { let Ok(mut light_view_entities) = light_view_entities.get_mut(light_entity) else { continue; @@ -1317,12 +1334,15 @@ pub fn prepare_lights( let view_light_entity = light_view_entities[0]; + let retained_view_entity = RetainedViewEntity::new(*light_main_entity, 0); + commands.entity(view_light_entity).insert(( ShadowView { depth_attachment, pass_name: format!("shadow pass spot light {light_index}"), }, ExtractedView { + retained_view_entity, viewport: UVec4::new( 0, 0, @@ -1347,15 +1367,15 @@ pub fn prepare_lights( if first { // Subsequent views with the same light entity will reuse the same shadow map - shadow_render_phases.insert_or_clear(view_light_entity, gpu_preprocessing_mode); - live_shadow_mapping_lights.insert(view_light_entity); + shadow_render_phases.insert_or_clear(retained_view_entity, gpu_preprocessing_mode); + live_shadow_mapping_lights.insert(retained_view_entity); } } // directional lights let mut directional_depth_texture_array_index = 0u32; let view_layers = maybe_layers.unwrap_or_default(); - for (light_index, &(light_entity, light)) in directional_lights + for (light_index, &(light_entity, light_main_entity, light)) in directional_lights .iter() .enumerate() .take(MAX_DIRECTIONAL_LIGHTS) @@ -1447,6 +1467,9 @@ pub fn prepare_lights( frustum.half_spaces[4] = HalfSpace::new(frustum.half_spaces[4].normal().extend(f32::INFINITY)); + let retained_view_entity = + RetainedViewEntity::new(*light_main_entity, cascade_index as u32); + commands.entity(view_light_entity).insert(( ShadowView { depth_attachment, @@ -1455,6 +1478,7 @@ pub fn prepare_lights( ), }, ExtractedView { + retained_view_entity, viewport: UVec4::new( 0, 0, @@ -1483,8 +1507,8 @@ pub fn prepare_lights( // Subsequent views with the same light entity will **NOT** reuse the same shadow map // (Because the cascades are unique to each view) // TODO: Implement GPU culling for shadow passes. - shadow_render_phases.insert_or_clear(view_light_entity, gpu_preprocessing_mode); - live_shadow_mapping_lights.insert(view_light_entity); + shadow_render_phases.insert_or_clear(retained_view_entity, gpu_preprocessing_mode); + live_shadow_mapping_lights.insert(retained_view_entity); } } @@ -1546,8 +1570,8 @@ pub fn queue_shadows( render_lightmaps: Res, gpu_preprocessing_support: Res, mesh_allocator: Res, - view_lights: Query<(Entity, &ViewLightEntities)>, - view_light_entities: Query<&LightEntity>, + view_lights: Query<(Entity, &ViewLightEntities), With>, + view_light_entities: Query<(&LightEntity, &ExtractedView)>, point_light_entities: Query<&RenderCubemapVisibleEntities, With>, directional_light_entities: Query< &RenderCascadesVisibleEntities, @@ -1560,10 +1584,14 @@ pub fn queue_shadows( for (entity, view_lights) in &view_lights { let draw_shadow_mesh = shadow_draw_functions.read().id::>(); for view_light_entity in view_lights.lights.iter().copied() { - let Ok(light_entity) = view_light_entities.get(view_light_entity) else { + let Ok((light_entity, extracted_view_light)) = + view_light_entities.get(view_light_entity) + else { continue; }; - let Some(shadow_phase) = shadow_render_phases.get_mut(&view_light_entity) else { + let Some(shadow_phase) = + shadow_render_phases.get_mut(&extracted_view_light.retained_view_entity) + else { continue; }; @@ -1801,7 +1829,7 @@ impl CachedRenderPipelinePhaseItem for Shadow { pub struct ShadowPassNode { main_view_query: QueryState>, - view_light_query: QueryState>, + view_light_query: QueryState<(Read, Read)>, } impl ShadowPassNode { @@ -1838,14 +1866,17 @@ impl Node for ShadowPassNode { if let Ok(view_lights) = self.main_view_query.get_manual(world, view_entity) { for view_light_entity in view_lights.lights.iter().copied() { - let Some(shadow_phase) = shadow_render_phases.get(&view_light_entity) else { + let Ok((view_light, extracted_light_view)) = + self.view_light_query.get_manual(world, view_light_entity) + else { continue; }; - let view_light = self - .view_light_query - .get_manual(world, view_light_entity) - .unwrap(); + let Some(shadow_phase) = + shadow_render_phases.get(&extracted_light_view.retained_view_entity) + else { + continue; + }; let depth_stencil_attachment = Some(view_light.depth_attachment.get_attachment(StoreOp::Store)); diff --git a/crates/bevy_render/src/batching/gpu_preprocessing.rs b/crates/bevy_render/src/batching/gpu_preprocessing.rs index 9f42d622fdf1a..a98afc02de48a 100644 --- a/crates/bevy_render/src/batching/gpu_preprocessing.rs +++ b/crates/bevy_render/src/batching/gpu_preprocessing.rs @@ -561,7 +561,7 @@ pub fn batch_and_prepare_sorted_render_phase( gpu_array_buffer: ResMut>, mut indirect_parameters_buffer: ResMut, mut sorted_render_phases: ResMut>, - mut views: Query<(Entity, Has), With>, + mut views: Query<(Entity, &ExtractedView, Has)>, system_param_item: StaticSystemParam, ) where I: CachedRenderPipelinePhaseItem + SortedPhaseItem, @@ -574,8 +574,8 @@ pub fn batch_and_prepare_sorted_render_phase( .. } = gpu_array_buffer.into_inner(); - for (view, no_indirect_drawing) in &mut views { - let Some(phase) = sorted_render_phases.get_mut(&view) else { + for (view, extracted_view, no_indirect_drawing) in &mut views { + let Some(phase) = sorted_render_phases.get_mut(&extracted_view.retained_view_entity) else { continue; }; @@ -699,7 +699,7 @@ pub fn batch_and_prepare_binned_render_phase( gpu_array_buffer: ResMut>, mut indirect_parameters_buffer: ResMut, mut binned_render_phases: ResMut>, - mut views: Query<(Entity, Has), With>, + mut views: Query<(Entity, &ExtractedView, Has)>, param: StaticSystemParam, ) where BPI: BinnedPhaseItem, @@ -713,8 +713,8 @@ pub fn batch_and_prepare_binned_render_phase( .. } = gpu_array_buffer.into_inner(); - for (view, no_indirect_drawing) in &mut views { - let Some(phase) = binned_render_phases.get_mut(&view) else { + for (view, extracted_view, no_indirect_drawing) in &mut views { + let Some(phase) = binned_render_phases.get_mut(&extracted_view.retained_view_entity) else { continue; }; diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 65608d5d0672d..b3b6b6f7c70aa 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -14,7 +14,7 @@ use crate::{ texture::GpuImage, view::{ ColorGrading, ExtractedView, ExtractedWindows, Msaa, NoIndirectDrawing, RenderLayers, - RenderVisibleEntities, ViewUniformOffset, Visibility, VisibleEntities, + RenderVisibleEntities, RetainedViewEntity, ViewUniformOffset, Visibility, VisibleEntities, }, Extract, }; @@ -1045,6 +1045,7 @@ pub fn extract_cameras( mut commands: Commands, query: Extract< Query<( + Entity, RenderEntity, &Camera, &CameraRenderGraph, @@ -1065,6 +1066,7 @@ pub fn extract_cameras( ) { let primary_window = primary_window.iter().next(); for ( + main_entity, render_entity, camera, camera_render_graph, @@ -1151,6 +1153,7 @@ pub fn extract_cameras( hdr: camera.hdr, }, ExtractedView { + retained_view_entity: RetainedViewEntity::new(main_entity.into(), 0), clip_from_view: camera.clip_from_view(), world_from_view: *transform, clip_from_world: None, diff --git a/crates/bevy_render/src/render_phase/mod.rs b/crates/bevy_render/src/render_phase/mod.rs index 6f22d4ee078b6..d65b46da7cdf6 100644 --- a/crates/bevy_render/src/render_phase/mod.rs +++ b/crates/bevy_render/src/render_phase/mod.rs @@ -39,6 +39,7 @@ pub use rangefinder::*; use crate::batching::gpu_preprocessing::{GpuPreprocessingMode, GpuPreprocessingSupport}; use crate::sync_world::MainEntity; +use crate::view::RetainedViewEntity; use crate::{ batching::{ self, @@ -50,7 +51,6 @@ use crate::{ Render, RenderApp, RenderSet, }; use bevy_ecs::{ - entity::EntityHashMap, prelude::*, system::{lifetimeless::SRes, SystemParamItem}, }; @@ -63,7 +63,7 @@ use smallvec::SmallVec; /// They're cleared out every frame, but storing them in a resource like this /// allows us to reuse allocations. #[derive(Resource, Deref, DerefMut)] -pub struct ViewBinnedRenderPhases(pub EntityHashMap>) +pub struct ViewBinnedRenderPhases(pub HashMap>) where BPI: BinnedPhaseItem; @@ -326,8 +326,12 @@ impl ViewBinnedRenderPhases where BPI: BinnedPhaseItem, { - pub fn insert_or_clear(&mut self, entity: Entity, gpu_preprocessing: GpuPreprocessingMode) { - match self.entry(entity) { + pub fn insert_or_clear( + &mut self, + retained_view_entity: RetainedViewEntity, + gpu_preprocessing: GpuPreprocessingMode, + ) { + match self.entry(retained_view_entity) { Entry::Occupied(mut entry) => entry.get_mut().clear(), Entry::Vacant(entry) => { entry.insert(BinnedRenderPhase::::new(gpu_preprocessing)); @@ -792,7 +796,7 @@ where /// They're cleared out every frame, but storing them in a resource like this /// allows us to reuse allocations. #[derive(Resource, Deref, DerefMut)] -pub struct ViewSortedRenderPhases(pub EntityHashMap>) +pub struct ViewSortedRenderPhases(pub HashMap>) where SPI: SortedPhaseItem; @@ -809,8 +813,8 @@ impl ViewSortedRenderPhases where SPI: SortedPhaseItem, { - pub fn insert_or_clear(&mut self, entity: Entity) { - match self.entry(entity) { + pub fn insert_or_clear(&mut self, retained_view_entity: RetainedViewEntity) { + match self.entry(retained_view_entity) { Entry::Occupied(mut entry) => entry.get_mut().clear(), Entry::Vacant(entry) => { entry.insert(default()); diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index ecadba107fbe9..3eb0df75e2f2c 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -17,6 +17,7 @@ use crate::{ render_phase::ViewRangefinder3d, render_resource::{DynamicUniformBuffer, ShaderType, Texture, TextureView}, renderer::{RenderDevice, RenderQueue}, + sync_world::MainEntity, texture::{ CachedTexture, ColorAttachment, DepthAttachment, GpuImage, OutputColorAttachment, TextureCache, @@ -184,8 +185,53 @@ impl Msaa { } } +/// An identifier for a view that is stable across frames. +/// +/// We can't use [`Entity`] for this because render world entities aren't +/// stable, and we can't use just [`MainEntity`] because some main world views +/// extract to multiple render world views. For example, a directional light +/// extracts to one render world view per cascade, and a point light extracts to +/// one render world view per cubemap face. So we pair the main entity with a +/// *subview index*, which *together* uniquely identify a view in the render +/// world in a way that's stable from frame to frame. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct RetainedViewEntity { + /// The main entity that this view corresponds to. + pub main_entity: MainEntity, + + /// The index of the view corresponding to the entity. + /// + /// For example, for point lights that cast shadows, this is the index of + /// the cubemap face (0 through 5 inclusive). For directional lights, this + /// is the index of the cascade. + pub subview_index: u32, +} + +impl RetainedViewEntity { + /// Creates a new [`RetainedViewEntity`] from the given main world entity + /// and subview index. + /// + /// See [`RetainedViewEntity::subview_index`] for an explanation of what + /// `subview_index` is. + pub fn new(main_entity: MainEntity, subview_index: u32) -> Self { + Self { + main_entity, + subview_index, + } + } +} + +/// Describes a camera in the render world. +/// +/// Each entity in the main world can potentially extract to multiple subviews, +/// each of which has a [`RetainedViewEntity::subview_index`]. For instance, 3D +/// cameras extract to both a 3D camera subview with index 0 and a special UI +/// subview with index 1. Likewise, point lights with shadows extract to 6 +/// subviews, one for each side of the shadow cubemap. #[derive(Component)] pub struct ExtractedView { + /// The entity in the main world corresponding to this render world view. + pub retained_view_entity: RetainedViewEntity, /// Typically a right-handed projection matrix, one of either: /// /// Perspective (infinite reverse z) diff --git a/crates/bevy_sprite/src/mesh2d/material.rs b/crates/bevy_sprite/src/mesh2d/material.rs index 479cdec5ec024..39b6b15f1a6ea 100644 --- a/crates/bevy_sprite/src/mesh2d/material.rs +++ b/crates/bevy_sprite/src/mesh2d/material.rs @@ -484,7 +484,6 @@ pub fn queue_material2d_meshes( mut opaque_render_phases: ResMut>, mut alpha_mask_render_phases: ResMut>, views: Query<( - Entity, &ExtractedView, &RenderVisibleEntities, &Msaa, @@ -498,14 +497,16 @@ pub fn queue_material2d_meshes( return; } - for (view_entity, view, visible_entities, msaa, tonemapping, dither) in &views { - let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else { + for (view, visible_entities, msaa, tonemapping, dither) in &views { + let Some(transparent_phase) = transparent_render_phases.get_mut(&view.retained_view_entity) + else { continue; }; - let Some(opaque_phase) = opaque_render_phases.get_mut(&view_entity) else { + let Some(opaque_phase) = opaque_render_phases.get_mut(&view.retained_view_entity) else { continue; }; - let Some(alpha_mask_phase) = alpha_mask_render_phases.get_mut(&view_entity) else { + let Some(alpha_mask_phase) = alpha_mask_render_phases.get_mut(&view.retained_view_entity) + else { continue; }; diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index 6d697dd64ab43..3dae793d323dd 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -500,8 +500,7 @@ pub fn queue_sprites( pipeline_cache: Res, extracted_sprites: Res, mut transparent_render_phases: ResMut>, - views: Query<( - Entity, + mut views: Query<( &RenderVisibleEntities, &ExtractedView, &Msaa, @@ -511,8 +510,9 @@ pub fn queue_sprites( ) { let draw_sprite_function = draw_functions.read().id::(); - for (view_entity, visible_entities, view, msaa, tonemapping, dither) in &views { - let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else { + for (visible_entities, view, msaa, tonemapping, dither) in &mut views { + let Some(transparent_phase) = transparent_render_phases.get_mut(&view.retained_view_entity) + else { continue; }; diff --git a/crates/bevy_ui/src/render/box_shadow.rs b/crates/bevy_ui/src/render/box_shadow.rs index b2c968d275e86..7f18e935ab8eb 100644 --- a/crates/bevy_ui/src/render/box_shadow.rs +++ b/crates/bevy_ui/src/render/box_shadow.rs @@ -34,7 +34,7 @@ use bevy_render::{ use bevy_transform::prelude::GlobalTransform; use bytemuck::{Pod, Zeroable}; -use super::{stack_z_offsets, QUAD_INDICES, QUAD_VERTEX_POSITIONS}; +use super::{stack_z_offsets, UiCameraView, QUAD_INDICES, QUAD_VERTEX_POSITIONS}; pub const BOX_SHADOW_SHADER_HANDLE: Handle = Handle::weak_from_u128(17717747047134343426); @@ -269,7 +269,7 @@ pub fn extract_shadows( } let ui_physical_viewport_size = camera_query - .get(camera_entity) + .get(extracted_camera_entity) .ok() .and_then(|(_, c)| { c.physical_viewport_size() @@ -337,24 +337,34 @@ pub fn extract_shadows( } } +#[expect( + clippy::too_many_arguments, + reason = "it's a system that needs a lot of them" +)] pub fn queue_shadows( extracted_box_shadows: ResMut, box_shadow_pipeline: Res, mut pipelines: ResMut>, mut transparent_render_phases: ResMut>, - views: Query<(Entity, &ExtractedView, Option<&BoxShadowSamples>)>, + mut render_views: Query<(&UiCameraView, Option<&BoxShadowSamples>), With>, + camera_views: Query<&ExtractedView>, pipeline_cache: Res, draw_functions: Res>, ) { let draw_function = draw_functions.read().id::(); for (entity, extracted_shadow) in extracted_box_shadows.box_shadows.iter() { - let Ok((view_entity, view, shadow_samples)) = - views.get(extracted_shadow.extracted_camera_entity) + let Ok((default_camera_view, shadow_samples)) = + render_views.get_mut(extracted_shadow.extracted_camera_entity) else { continue; }; - let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else { + let Ok(view) = camera_views.get(default_camera_view.0) else { + continue; + }; + + let Some(transparent_phase) = transparent_render_phases.get_mut(&view.retained_view_entity) + else { continue; }; diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 745213899eb0d..bbadf8aef7601 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -18,17 +18,20 @@ use bevy_color::{Alpha, ColorToComponents, LinearRgba}; use bevy_core_pipeline::core_2d::graph::{Core2d, Node2d}; use bevy_core_pipeline::core_3d::graph::{Core3d, Node3d}; use bevy_core_pipeline::{core_2d::Camera2d, core_3d::Camera3d}; -use bevy_ecs::entity::{EntityHashMap, EntityHashSet}; +use bevy_ecs::entity::EntityHashMap; use bevy_ecs::prelude::*; use bevy_image::prelude::*; use bevy_math::{FloatOrd, Mat4, Rect, UVec4, Vec2, Vec3, Vec3Swizzles, Vec4Swizzles}; +use bevy_render::render_graph::{NodeRunError, RenderGraphContext}; use bevy_render::render_phase::ViewSortedRenderPhases; +use bevy_render::renderer::RenderContext; use bevy_render::sync_world::MainEntity; use bevy_render::texture::TRANSPARENT_IMAGE_HANDLE; +use bevy_render::view::RetainedViewEntity; use bevy_render::{ camera::Camera, render_asset::RenderAssets, - render_graph::{RenderGraph, RunGraphOnViewNode}, + render_graph::{Node as RenderGraphNode, RenderGraph}, render_phase::{sort_phase_system, AddRenderCommand, DrawFunctions}, render_resource::*, renderer::{RenderDevice, RenderQueue}, @@ -49,7 +52,7 @@ pub use debug_overlay::UiDebugOptions; use crate::{Display, Node}; use bevy_text::{ComputedTextBlock, PositionedGlyph, TextColor, TextLayoutInfo}; use bevy_transform::components::GlobalTransform; -use bevy_utils::HashMap; +use bevy_utils::{HashMap, HashSet}; use box_shadow::BoxShadowPlugin; use bytemuck::{Pod, Zeroable}; use core::ops::Range; @@ -96,6 +99,7 @@ pub const UI_SHADER_HANDLE: Handle = Handle::weak_from_u128(130128470471 #[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)] pub enum RenderUiSystem { + ExtractCameraViews, ExtractBoxShadows, ExtractBackgrounds, ExtractImages, @@ -124,6 +128,7 @@ pub fn build_ui_render(app: &mut App) { .configure_sets( ExtractSchedule, ( + RenderUiSystem::ExtractCameraViews, RenderUiSystem::ExtractBoxShadows, RenderUiSystem::ExtractBackgrounds, RenderUiSystem::ExtractImages, @@ -137,7 +142,7 @@ pub fn build_ui_render(app: &mut App) { .add_systems( ExtractSchedule, ( - extract_ui_camera_view, + extract_ui_camera_view.in_set(RenderUiSystem::ExtractCameraViews), extract_uinode_background_colors.in_set(RenderUiSystem::ExtractBackgrounds), extract_uinode_images.in_set(RenderUiSystem::ExtractImages), extract_uinode_borders.in_set(RenderUiSystem::ExtractBorders), @@ -162,7 +167,7 @@ pub fn build_ui_render(app: &mut App) { if let Some(graph_2d) = graph.get_sub_graph_mut(Core2d) { graph_2d.add_sub_graph(SubGraphUi, ui_graph_2d); - graph_2d.add_node(NodeUi::UiPass, RunGraphOnViewNode::new(SubGraphUi)); + graph_2d.add_node(NodeUi::UiPass, RunUiSubgraphOnUiViewNode); graph_2d.add_node_edge(Node2d::EndMainPass, NodeUi::UiPass); graph_2d.add_node_edge(Node2d::EndMainPassPostProcessing, NodeUi::UiPass); graph_2d.add_node_edge(NodeUi::UiPass, Node2d::Upscaling); @@ -170,7 +175,7 @@ pub fn build_ui_render(app: &mut App) { if let Some(graph_3d) = graph.get_sub_graph_mut(Core3d) { graph_3d.add_sub_graph(SubGraphUi, ui_graph_3d); - graph_3d.add_node(NodeUi::UiPass, RunGraphOnViewNode::new(SubGraphUi)); + graph_3d.add_node(NodeUi::UiPass, RunUiSubgraphOnUiViewNode); graph_3d.add_node_edge(Node3d::EndMainPass, NodeUi::UiPass); graph_3d.add_node_edge(Node3d::EndMainPassPostProcessing, NodeUi::UiPass); graph_3d.add_node_edge(NodeUi::UiPass, Node3d::Upscaling); @@ -246,6 +251,31 @@ impl ExtractedUiNodes { } } +/// A [`RenderGraphNode`] that executes the UI rendering subgraph on the UI +/// view. +struct RunUiSubgraphOnUiViewNode; + +impl RenderGraphNode for RunUiSubgraphOnUiViewNode { + fn run<'w>( + &self, + graph: &mut RenderGraphContext, + _: &mut RenderContext<'w>, + world: &'w World, + ) -> Result<(), NodeRunError> { + // Fetch the UI view. + let Some(mut render_views) = world.try_query::<&UiCameraView>() else { + return Ok(()); + }; + let Ok(default_camera_view) = render_views.get(world, graph.view_entity()) else { + return Ok(()); + }; + + // Run the subgraph on the UI view. + graph.run_sub_graph(SubGraphUi, vec![], Some(default_camera_view.0))?; + Ok(()) + } +} + pub fn extract_uinode_background_colors( mut commands: Commands, mut extracted_uinodes: ResMut, @@ -527,10 +557,32 @@ const UI_CAMERA_FAR: f32 = 1000.0; // TODO: Evaluate if we still need this. const UI_CAMERA_TRANSFORM_OFFSET: f32 = -0.1; +/// The ID of the subview associated with a camera on which UI is to be drawn. +/// +/// When UI is present, cameras extract to two views: the main 2D/3D one and a +/// UI one. The main 2D or 3D camera gets subview 0, and the corresponding UI +/// camera gets this subview, 1. +const UI_CAMERA_SUBVIEW: u32 = 1; + +/// A render-world component that lives on the main render target view and +/// specifies the corresponding UI view. +/// +/// For example, if UI is being rendered to a 3D camera, this component lives on +/// the 3D camera and contains the entity corresponding to the UI view. #[derive(Component)] /// Entity id of the temporary render entity with the corresponding extracted UI view. pub struct UiCameraView(pub Entity); +/// A render-world component that lives on the UI view and specifies the +/// corresponding main render target view. +/// +/// For example, if the UI is being rendered to a 3D camera, this component +/// lives on the UI view and contains the entity corresponding to the 3D camera. +/// +/// This is the inverse of [`UiCameraView`]. +#[derive(Component)] +pub struct UiViewTarget(pub Entity); + /// Extracts all UI elements associated with a camera into the render world. pub fn extract_ui_camera_view( mut commands: Commands, @@ -538,6 +590,7 @@ pub fn extract_ui_camera_view( query: Extract< Query< ( + Entity, RenderEntity, &Camera, Option<&UiAntiAlias>, @@ -546,11 +599,11 @@ pub fn extract_ui_camera_view( Or<(With, With)>, >, >, - mut live_entities: Local, + mut live_entities: Local>, ) { live_entities.clear(); - for (render_entity, camera, ui_anti_alias, shadow_samples) in &query { + for (main_entity, render_entity, camera, ui_anti_alias, shadow_samples) in &query { // ignore inactive cameras if !camera.is_active { commands @@ -570,9 +623,15 @@ pub fn extract_ui_camera_view( 0.0, UI_CAMERA_FAR, ); + // We use `UI_CAMERA_SUBVIEW` here so as not to conflict with the + // main 3D or 2D camera, which will have subview index 0. + let retained_view_entity = + RetainedViewEntity::new(main_entity.into(), UI_CAMERA_SUBVIEW); + // Creates the UI view. let ui_camera_view = commands .spawn(( ExtractedView { + retained_view_entity, clip_from_view: projection_matrix, world_from_view: GlobalTransform::from_xyz( 0.0, @@ -587,12 +646,16 @@ pub fn extract_ui_camera_view( )), color_grading: Default::default(), }, + // Link to the main camera view. + UiViewTarget(render_entity), TemporaryRenderEntity, )) .id(); + let mut entity_commands = commands .get_entity(render_entity) .expect("Camera entity wasn't synced."); + // Link from the main 2D/3D camera view to the UI view. entity_commands.insert(UiCameraView(ui_camera_view)); if let Some(ui_anti_alias) = ui_anti_alias { entity_commands.insert(*ui_anti_alias); @@ -600,9 +663,9 @@ pub fn extract_ui_camera_view( if let Some(shadow_samples) = shadow_samples { entity_commands.insert(*shadow_samples); } - transparent_render_phases.insert_or_clear(render_entity); + transparent_render_phases.insert_or_clear(retained_view_entity); - live_entities.insert(render_entity); + live_entities.insert(retained_view_entity); } } @@ -790,19 +853,25 @@ pub fn queue_uinodes( ui_pipeline: Res, mut pipelines: ResMut>, mut transparent_render_phases: ResMut>, - views: Query<(Entity, &ExtractedView, Option<&UiAntiAlias>)>, + mut render_views: Query<(&UiCameraView, Option<&UiAntiAlias>), With>, + camera_views: Query<&ExtractedView>, pipeline_cache: Res, draw_functions: Res>, ) { let draw_function = draw_functions.read().id::(); for (entity, extracted_uinode) in extracted_uinodes.uinodes.iter() { - let Ok((view_entity, view, ui_anti_alias)) = - views.get(extracted_uinode.extracted_camera_entity) + let Ok((default_camera_view, ui_anti_alias)) = + render_views.get_mut(extracted_uinode.extracted_camera_entity) else { continue; }; - let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else { + let Ok(view) = camera_views.get(default_camera_view.0) else { + continue; + }; + + let Some(transparent_phase) = transparent_render_phases.get_mut(&view.retained_view_entity) + else { continue; }; diff --git a/crates/bevy_ui/src/render/render_pass.rs b/crates/bevy_ui/src/render/render_pass.rs index e764853374cb6..d26844fcb6161 100644 --- a/crates/bevy_ui/src/render/render_pass.rs +++ b/crates/bevy_ui/src/render/render_pass.rs @@ -1,6 +1,6 @@ use core::ops::Range; -use super::{ImageNodeBindGroups, UiBatch, UiMeta}; +use super::{ImageNodeBindGroups, UiBatch, UiMeta, UiViewTarget}; use crate::UiCameraView; use bevy_ecs::{ prelude::*, @@ -19,15 +19,16 @@ use bevy_render::{ use tracing::error; pub struct UiPassNode { - camera_view_query: - QueryState<(&'static ViewTarget, &'static ExtractedCamera), With>, + ui_view_query: QueryState<(&'static ExtractedView, &'static UiViewTarget)>, + ui_view_target_query: QueryState<(&'static ViewTarget, &'static ExtractedCamera)>, ui_camera_view_query: QueryState<&'static UiCameraView>, } impl UiPassNode { pub fn new(world: &mut World) -> Self { Self { - camera_view_query: world.query_filtered(), + ui_view_query: world.query_filtered(), + ui_view_target_query: world.query(), ui_camera_view_query: world.query(), } } @@ -35,7 +36,8 @@ impl UiPassNode { impl Node for UiPassNode { fn update(&mut self, world: &mut World) { - self.camera_view_query.update_archetypes(world); + self.ui_view_query.update_archetypes(world); + self.ui_view_target_query.update_archetypes(world); self.ui_camera_view_query.update_archetypes(world); } @@ -45,6 +47,7 @@ impl Node for UiPassNode { render_context: &mut RenderContext, world: &World, ) -> Result<(), NodeRunError> { + // Extract the UI view. let input_view_entity = graph.view_entity(); let Some(transparent_render_phases) = @@ -53,14 +56,24 @@ impl Node for UiPassNode { return Ok(()); }; - let Some(transparent_phase) = transparent_render_phases.get(&input_view_entity) else { + // Query the UI view components. + let Ok((view, ui_view_target)) = self.ui_view_query.get_manual(world, input_view_entity) + else { + return Ok(()); + }; + + let Ok((target, camera)) = self + .ui_view_target_query + .get_manual(world, ui_view_target.0) + else { return Ok(()); }; - let Ok((target, camera)) = self.camera_view_query.get_manual(world, input_view_entity) + let Some(transparent_phase) = transparent_render_phases.get(&view.retained_view_entity) else { return Ok(()); }; + if transparent_phase.items.is_empty() { return Ok(()); } diff --git a/crates/bevy_ui/src/render/ui_material_pipeline.rs b/crates/bevy_ui/src/render/ui_material_pipeline.rs index b5d93c1cc630d..c47729105e79a 100644 --- a/crates/bevy_ui/src/render/ui_material_pipeline.rs +++ b/crates/bevy_ui/src/render/ui_material_pipeline.rs @@ -374,7 +374,7 @@ pub fn extract_ui_material_nodes( Option<&TargetCamera>, )>, >, - render_entity_lookup: Extract>, + mapping: Extract>, ) { // If there is only one camera, we use it as default let default_single_camera = default_ui_camera.get(); @@ -384,7 +384,7 @@ pub fn extract_ui_material_nodes( continue; }; - let Ok(extracted_camera_entity) = render_entity_lookup.get(camera_entity) else { + let Ok(extracted_camera_entity) = mapping.get(camera_entity) else { continue; }; @@ -612,7 +612,8 @@ pub fn queue_ui_material_nodes( pipeline_cache: Res, render_materials: Res>>, mut transparent_render_phases: ResMut>, - views: Query<&ExtractedView>, + mut render_views: Query<&UiCameraView, With>, + camera_views: Query<&ExtractedView>, ) where M::Data: PartialEq + Eq + Hash + Clone, { @@ -622,11 +623,18 @@ pub fn queue_ui_material_nodes( let Some(material) = render_materials.get(extracted_uinode.material) else { continue; }; - let Ok(view) = views.get(extracted_uinode.extracted_camera_entity) else { + + let Ok(default_camera_view) = + render_views.get_mut(extracted_uinode.extracted_camera_entity) + else { + continue; + }; + + let Ok(view) = camera_views.get(default_camera_view.0) else { continue; }; - let Some(transparent_phase) = - transparent_render_phases.get_mut(&extracted_uinode.extracted_camera_entity) + + let Some(transparent_phase) = transparent_render_phases.get_mut(&view.retained_view_entity) else { continue; }; diff --git a/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs b/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs index 3d605a576e0b1..989207b3a2728 100644 --- a/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs +++ b/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs @@ -338,22 +338,34 @@ pub fn extract_ui_texture_slices( } } +#[expect( + clippy::too_many_arguments, + reason = "it's a system that needs a lot of them" +)] pub fn queue_ui_slices( extracted_ui_slicers: ResMut, ui_slicer_pipeline: Res, mut pipelines: ResMut>, mut transparent_render_phases: ResMut>, - views: Query<(Entity, &ExtractedView)>, + mut render_views: Query<&UiCameraView, With>, + camera_views: Query<&ExtractedView>, pipeline_cache: Res, draw_functions: Res>, ) { let draw_function = draw_functions.read().id::(); for (entity, extracted_slicer) in extracted_ui_slicers.slices.iter() { - let Ok((view_entity, view)) = views.get(extracted_slicer.extracted_camera_entity) else { + let Ok(default_camera_view) = + render_views.get_mut(extracted_slicer.extracted_camera_entity) + else { continue; }; - let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else { + let Ok(view) = camera_views.get(default_camera_view.0) else { + continue; + }; + + let Some(transparent_phase) = transparent_render_phases.get_mut(&view.retained_view_entity) + else { continue; }; diff --git a/examples/2d/mesh2d_manual.rs b/examples/2d/mesh2d_manual.rs index da35c2a53dbb3..09acb6dbb99bc 100644 --- a/examples/2d/mesh2d_manual.rs +++ b/examples/2d/mesh2d_manual.rs @@ -368,14 +368,15 @@ pub fn queue_colored_mesh2d( render_meshes: Res>, render_mesh_instances: Res, mut transparent_render_phases: ResMut>, - views: Query<(Entity, &RenderVisibleEntities, &ExtractedView, &Msaa)>, + views: Query<(&RenderVisibleEntities, &ExtractedView, &Msaa)>, ) { if render_mesh_instances.is_empty() { return; } // Iterate each view (a camera is a view) - for (view_entity, visible_entities, view, msaa) in &views { - let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else { + for (visible_entities, view, msaa) in &views { + let Some(transparent_phase) = transparent_render_phases.get_mut(&view.retained_view_entity) + else { continue; }; diff --git a/examples/shader/custom_phase_item.rs b/examples/shader/custom_phase_item.rs index 98d858bc3ab61..be4fb105f834e 100644 --- a/examples/shader/custom_phase_item.rs +++ b/examples/shader/custom_phase_item.rs @@ -223,7 +223,7 @@ fn queue_custom_phase_item( mut opaque_render_phases: ResMut>, opaque_draw_functions: Res>, mut specialized_render_pipelines: ResMut>, - views: Query<(Entity, &RenderVisibleEntities, &Msaa), With>, + views: Query<(&ExtractedView, &RenderVisibleEntities, &Msaa)>, ) { let draw_custom_phase_item = opaque_draw_functions .read() @@ -232,8 +232,8 @@ fn queue_custom_phase_item( // Render phases are per-view, so we need to iterate over all views so that // the entity appears in them. (In this example, we have only one view, but // it's good practice to loop over all views anyway.) - for (view_entity, view_visible_entities, msaa) in views.iter() { - let Some(opaque_phase) = opaque_render_phases.get_mut(&view_entity) else { + for (view, view_visible_entities, msaa) in views.iter() { + let Some(opaque_phase) = opaque_render_phases.get_mut(&view.retained_view_entity) else { continue; }; diff --git a/examples/shader/custom_shader_instancing.rs b/examples/shader/custom_shader_instancing.rs index a6d1e30ef6092..3cdfad774dbae 100644 --- a/examples/shader/custom_shader_instancing.rs +++ b/examples/shader/custom_shader_instancing.rs @@ -132,12 +132,13 @@ fn queue_custom( render_mesh_instances: Res, material_meshes: Query<(Entity, &MainEntity), With>, mut transparent_render_phases: ResMut>, - views: Query<(Entity, &ExtractedView, &Msaa)>, + views: Query<(&ExtractedView, &Msaa)>, ) { let draw_custom = transparent_3d_draw_functions.read().id::(); - for (view_entity, view, msaa) in &views { - let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else { + for (view, msaa) in &views { + let Some(transparent_phase) = transparent_render_phases.get_mut(&view.retained_view_entity) + else { continue; }; diff --git a/examples/shader/specialized_mesh_pipeline.rs b/examples/shader/specialized_mesh_pipeline.rs index 23f531ab97a1b..ee5c68bc11a16 100644 --- a/examples/shader/specialized_mesh_pipeline.rs +++ b/examples/shader/specialized_mesh_pipeline.rs @@ -267,7 +267,7 @@ fn queue_custom_mesh_pipeline( mut opaque_render_phases: ResMut>, opaque_draw_functions: Res>, mut specialized_mesh_pipelines: ResMut>, - views: Query<(Entity, &RenderVisibleEntities, &ExtractedView, &Msaa), With>, + views: Query<(&RenderVisibleEntities, &ExtractedView, &Msaa), With>, render_meshes: Res>, render_mesh_instances: Res, ) { @@ -279,8 +279,8 @@ fn queue_custom_mesh_pipeline( // Render phases are per-view, so we need to iterate over all views so that // the entity appears in them. (In this example, we have only one view, but // it's good practice to loop over all views anyway.) - for (view_entity, view_visible_entities, view, msaa) in views.iter() { - let Some(opaque_phase) = opaque_render_phases.get_mut(&view_entity) else { + for (view_visible_entities, view, msaa) in views.iter() { + let Some(opaque_phase) = opaque_render_phases.get_mut(&view.retained_view_entity) else { continue; };