From e4b5efa5e47c6cf9209393f7d78f0218ae25f3f6 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Sun, 22 Dec 2024 18:02:07 -0500 Subject: [PATCH 01/12] Key render phases off the main world view entity, not the render world view entity. We won't be able to retain render phases from frame to frame if the keys are unstable. It's not as simple as simply keying off the main world entity, however, because some main world entities extract to multiple render world entities. For example, directional lights extract to multiple shadow cascades, and point lights extract to one view per cubemap face. Therefore, we key off a new type, `RetainedViewEntity`, which contains the main entity plus a *subview ID*. This is part of the preparation for retained bins. --- .../src/core_2d/main_opaque_pass_2d_node.rs | 9 +- .../core_2d/main_transparent_pass_2d_node.rs | 7 +- crates/bevy_core_pipeline/src/core_2d/mod.rs | 36 +++--- .../src/core_3d/main_opaque_pass_3d_node.rs | 15 +-- .../core_3d/main_transmissive_pass_3d_node.rs | 7 +- .../core_3d/main_transparent_pass_3d_node.rs | 7 +- crates/bevy_core_pipeline/src/core_3d/mod.rs | 100 +++++++++------- .../bevy_core_pipeline/src/deferred/node.rs | 12 +- crates/bevy_core_pipeline/src/prepass/node.rs | 10 +- crates/bevy_gizmos/src/pipeline_2d.rs | 14 ++- crates/bevy_gizmos/src/pipeline_3d.rs | 10 +- crates/bevy_pbr/src/material.rs | 10 +- crates/bevy_pbr/src/prepass/mod.rs | 31 +++-- crates/bevy_pbr/src/render/light.rs | 112 +++++++++++------- .../src/batching/gpu_preprocessing.rs | 12 +- crates/bevy_render/src/camera/camera.rs | 5 +- crates/bevy_render/src/render_phase/mod.rs | 18 +-- crates/bevy_render/src/view/mod.rs | 38 ++++++ crates/bevy_sprite/src/mesh2d/material.rs | 11 +- crates/bevy_sprite/src/render/mod.rs | 6 +- crates/bevy_ui/src/render/box_shadow.rs | 8 +- crates/bevy_ui/src/render/mod.rs | 26 ++-- crates/bevy_ui/src/render/render_pass.rs | 13 +- .../src/render/ui_material_pipeline.rs | 9 +- .../src/render/ui_texture_slice_pipeline.rs | 7 +- examples/2d/mesh2d_manual.rs | 7 +- examples/shader/custom_phase_item.rs | 6 +- examples/shader/custom_shader_instancing.rs | 7 +- examples/shader/specialized_mesh_pipeline.rs | 6 +- 29 files changed, 340 insertions(+), 219 deletions(-) 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 91093d0da5c94..ac0e8257477ca 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 bevy_utils::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 e365be954775b..efbc7e542aa58 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 bevy_utils::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 d57134aa3ec07..8f197bdff1602 100644 --- a/crates/bevy_core_pipeline/src/core_2d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_2d/mod.rs @@ -34,16 +34,18 @@ use core::ops::Range; use bevy_asset::UntypedAssetId; use bevy_render::{ - batching::gpu_preprocessing::GpuPreprocessingMode, render_phase::PhaseItemBinKey, + batching::gpu_preprocessing::GpuPreprocessingMode, + render_phase::PhaseItemBinKey, + view::{ExtractedView, RetainedViewEntity}, }; -use bevy_utils::HashMap; +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}, @@ -59,7 +61,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, @@ -393,20 +395,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. @@ -421,11 +427,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 b51f36354340a..c4190db077ad0 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 bevy_utils::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 225ce81da6c3a..42eaed0bcb4c2 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 bevy_utils::tracing::error; #[cfg(feature = "trace")] @@ -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 4f0d3d0722f0e..b4ed896864804 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 bevy_utils::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 f70ad1391473f..59fcd64f7b19f 100644 --- a/crates/bevy_core_pipeline/src/core_3d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_3d/mod.rs @@ -69,7 +69,7 @@ use bevy_render::{ batching::gpu_preprocessing::{GpuPreprocessingMode, GpuPreprocessingSupport}, mesh::allocator::SlabId, render_phase::PhaseItemBinKey, - view::NoIndirectDrawing, + view::{NoIndirectDrawing, RetainedViewEntity}, }; pub use camera_3d::*; pub use main_opaque_pass_3d_node::*; @@ -78,7 +78,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::{ @@ -101,7 +101,7 @@ use bevy_render::{ view::{ExtractedView, ViewDepthTexture, ViewTarget}, Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; -use bevy_utils::{tracing::warn, HashMap}; +use bevy_utils::{tracing::warn, HashMap, HashSet}; use nonmax::NonMaxU32; use crate::{ @@ -571,13 +571,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; } @@ -590,18 +590,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 @@ -616,6 +619,7 @@ pub fn extract_camera_prepass_phase( cameras_3d: Extract< Query< ( + Entity, RenderEntity, &Camera, Has, @@ -627,12 +631,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, @@ -654,22 +659,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) @@ -680,10 +690,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)); } #[allow(clippy::too_many_arguments)] @@ -698,17 +708,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; }; @@ -726,7 +737,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; }; @@ -889,6 +901,7 @@ pub fn prepare_prepass_textures( views_3d: Query<( Entity, &ExtractedCamera, + &ExtractedView, &Msaa, Has, Has, @@ -904,6 +917,7 @@ pub fn prepare_prepass_textures( for ( entity, camera, + view, msaa, depth_prepass, normal_prepass, @@ -911,10 +925,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 5aa89a8e94a0d..00981b9f48321 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 17f5dfb2cfe84..1de99ac3dd6c8 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 bevy_utils::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 89d6cec6260b6..40648a28824b7 100644 --- a/crates/bevy_gizmos/src/pipeline_2d.rs +++ b/crates/bevy_gizmos/src/pipeline_2d.rs @@ -297,7 +297,7 @@ fn queue_line_gizmos_2d( line_gizmos: Query<(Entity, &MainEntity, &GizmoMeshConfig)>, line_gizmo_assets: Res>, mut transparent_render_phases: ResMut>, - mut 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 @@ -305,8 +305,9 @@ fn queue_line_gizmos_2d( .get_id::() .unwrap(); - for (view_entity, view, msaa, render_layers) in &mut 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; }; @@ -375,15 +376,16 @@ fn queue_line_joint_gizmos_2d( line_gizmos: Query<(Entity, &MainEntity, &GizmoMeshConfig)>, line_gizmo_assets: Res>, mut transparent_render_phases: ResMut>, - mut 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 &mut 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 025cc4c7c033b..7b219be7a54b4 100644 --- a/crates/bevy_gizmos/src/pipeline_3d.rs +++ b/crates/bevy_gizmos/src/pipeline_3d.rs @@ -293,7 +293,6 @@ fn queue_line_gizmos_3d( line_gizmo_assets: Res>, mut transparent_render_phases: ResMut>, mut views: Query<( - Entity, &ExtractedView, &Msaa, Option<&RenderLayers>, @@ -312,14 +311,14 @@ fn queue_line_gizmos_3d( .unwrap(); for ( - view_entity, view, msaa, render_layers, (normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass), ) in &mut 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; }; @@ -408,7 +407,6 @@ fn queue_line_joint_gizmos_3d( line_gizmo_assets: Res>, mut transparent_render_phases: ResMut>, mut views: Query<( - Entity, &ExtractedView, &Msaa, Option<&RenderLayers>, @@ -426,14 +424,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 &mut 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 f894774af140d..d9c9ea5c5ac39 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -639,7 +639,6 @@ pub fn queue_material_meshes( mut transmissive_render_phases: ResMut>, mut transparent_render_phases: ResMut>, views: Query<( - Entity, &ExtractedView, &RenderVisibleEntities, &Msaa, @@ -666,7 +665,6 @@ pub fn queue_material_meshes( M::Data: PartialEq + Eq + Hash + Clone, { for ( - view_entity, view, visible_entities, msaa, @@ -688,10 +686,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 ac5cbeca4216b..7268e2d321a61 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -769,18 +769,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, { @@ -801,7 +798,7 @@ pub fn queue_prepass_material_meshes( .get_id::>() .unwrap(); for ( - view, + extracted_view, visible_entities, msaa, depth_prepass, @@ -816,10 +813,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 85fa1fca6dee3..08c905acbec80 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, @@ -40,7 +40,7 @@ use bevy_utils::tracing::info_span; use bevy_utils::{ default, tracing::{error, warn}, - HashMap, + HashMap, HashSet, }; use core::{hash::Hash, ops::Range}; @@ -217,6 +217,7 @@ pub fn extract_lights( global_point_lights: Extract>, point_lights: Extract< Query<( + Entity, RenderEntity, &PointLight, &CubemapVisibleEntities, @@ -228,6 +229,7 @@ pub fn extract_lights( >, spot_lights: Extract< Query<( + Entity, RenderEntity, &SpotLight, &VisibleMeshEntities, @@ -240,6 +242,7 @@ pub fn extract_lights( directional_lights: Extract< Query< ( + Entity, RenderEntity, &DirectionalLight, &CascadesVisibleEntities, @@ -278,6 +281,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, @@ -331,6 +335,7 @@ pub fn extract_lights( extracted_point_light, render_cubemap_visible_entities, (*frusta).clone(), + MainEntity::from(main_entity), ), )); } @@ -340,6 +345,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, @@ -391,6 +397,7 @@ pub fn extract_lights( }, render_visible_entities, *frustum, + MainEntity::from(main_entity), ), )); } @@ -399,6 +406,7 @@ pub fn extract_lights( commands.insert_or_spawn_batch(spot_lights_values); for ( + main_entity, entity, directional_light, visible_entities, @@ -478,6 +486,7 @@ pub fn extract_lights( RenderCascadesVisibleEntities { entities: cascade_visible_entities, }, + MainEntity::from(main_entity), )); } } @@ -712,13 +721,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, @@ -774,7 +784,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 {}.", @@ -785,50 +795,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); @@ -837,7 +847,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(|(entity_1, light_1, _), (entity_2, light_2, _)| { + point_lights.sort_by(|(entity_1, _, light_1, _), (entity_2, _, light_2, _)| { clusterable_object_order( ClusterableObjectOrderData { entity: entity_1, @@ -858,7 +868,7 @@ pub fn prepare_lights( // shadows // - then by entity as a stable key to ensure that a consistent set of // lights are chosen if the light count limit is exceeded. - directional_lights.sort_by(|(entity_1, light_1), (entity_2, light_2)| { + directional_lights.sort_by(|(entity_1, _, light_1), (entity_2, _, light_2)| { directional_light_order( (entity_1, &light_1.volumetric, &light_1.shadows_enabled), (entity_2, &light_2.volumetric, &light_2.shadows_enabled), @@ -872,7 +882,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 @@ -961,7 +971,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) @@ -1163,7 +1173,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)) @@ -1231,6 +1241,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, @@ -1241,6 +1254,7 @@ pub fn prepare_lights( ), }, ExtractedView { + retained_view_entity, viewport: UVec4::new( 0, 0, @@ -1268,18 +1282,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; @@ -1330,12 +1346,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, @@ -1360,15 +1379,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) @@ -1460,6 +1479,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, @@ -1468,6 +1490,7 @@ pub fn prepare_lights( ), }, ExtractedView { + retained_view_entity, viewport: UVec4::new( 0, 0, @@ -1496,8 +1519,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); } } @@ -1561,8 +1584,8 @@ pub fn queue_shadows( pipeline_cache: Res, render_lightmaps: Res, mesh_allocator: Res, - view_lights: Query<(Entity, &ViewLightEntities)>, - view_light_entities: Query<&LightEntity>, + view_lights: Query<(Entity, &ViewLightEntities, &ExtractedView)>, + view_light_entities: Query<(&LightEntity, &ExtractedView)>, point_light_entities: Query<&RenderCubemapVisibleEntities, With>, directional_light_entities: Query< &RenderCascadesVisibleEntities, @@ -1572,13 +1595,17 @@ pub fn queue_shadows( ) where M::Data: PartialEq + Eq + Hash + Clone, { - for (entity, view_lights) in &view_lights { + for (entity, view_lights, view) 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; }; @@ -1816,7 +1843,7 @@ impl CachedRenderPipelinePhaseItem for Shadow { pub struct ShadowPassNode { main_view_query: QueryState>, - view_light_query: QueryState>, + view_light_query: QueryState<(Read, Read)>, } impl ShadowPassNode { @@ -1853,14 +1880,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 cd1aed53e221b..501204b63b562 100644 --- a/crates/bevy_render/src/batching/gpu_preprocessing.rs +++ b/crates/bevy_render/src/batching/gpu_preprocessing.rs @@ -514,7 +514,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, @@ -527,8 +527,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; }; @@ -632,7 +632,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, @@ -646,8 +646,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 d552000d0b5fd..dcc5f2f008502 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -10,7 +10,7 @@ use crate::{ texture::GpuImage, view::{ ColorGrading, ExtractedView, ExtractedWindows, Msaa, NoIndirectDrawing, RenderLayers, - RenderVisibleEntities, ViewUniformOffset, Visibility, VisibleEntities, + RenderVisibleEntities, RetainedViewEntity, ViewUniformOffset, Visibility, VisibleEntities, }, Extract, }; @@ -1047,6 +1047,7 @@ pub fn extract_cameras( mut commands: Commands, query: Extract< Query<( + Entity, RenderEntity, &Camera, &CameraRenderGraph, @@ -1067,6 +1068,7 @@ pub fn extract_cameras( ) { let primary_window = primary_window.iter().next(); for ( + main_entity, render_entity, camera, camera_render_graph, @@ -1153,6 +1155,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 5899bc9c01ea2..cc1c5e1affa94 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; 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; @@ -282,8 +282,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)); @@ -685,7 +689,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; @@ -702,8 +706,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 06e2db79f28df..635f1c1252abc 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,45 @@ 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, + } + } +} + #[derive(Component)] pub struct ExtractedView { + 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 fcc830301c554..e6a7d2d1e4378 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 51e5f41b97fd1..82b00443b8971 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -504,7 +504,6 @@ pub fn queue_sprites( extracted_sprites: Res, mut transparent_render_phases: ResMut>, mut views: Query<( - Entity, &RenderVisibleEntities, &ExtractedView, &Msaa, @@ -514,8 +513,9 @@ pub fn queue_sprites( ) { let draw_sprite_function = draw_functions.read().id::(); - for (view_entity, visible_entities, view, msaa, tonemapping, dither) in &mut 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 40363bf386c48..5e206655008c6 100644 --- a/crates/bevy_ui/src/render/box_shadow.rs +++ b/crates/bevy_ui/src/render/box_shadow.rs @@ -341,18 +341,18 @@ pub fn queue_shadows( box_shadow_pipeline: Res, mut pipelines: ResMut>, mut transparent_render_phases: ResMut>, - mut views: Query<(Entity, &ExtractedView, Option<&BoxShadowSamples>)>, + mut views: Query<(&ExtractedView, Option<&BoxShadowSamples>)>, 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_mut(extracted_shadow.camera_entity) - else { + let Ok((view, shadow_samples)) = views.get_mut(extracted_shadow.camera_entity) else { continue; }; - 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_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index f4fd9af02374e..82abbd3a65606 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -18,13 +18,14 @@ 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::Image; use bevy_math::{FloatOrd, Mat4, Rect, UVec4, Vec2, Vec3, Vec3Swizzles, Vec4Swizzles}; use bevy_render::render_phase::ViewSortedRenderPhases; 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, @@ -50,7 +51,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; @@ -543,6 +544,7 @@ pub fn extract_default_ui_camera_view( query: Extract< Query< ( + Entity, RenderEntity, &Camera, Option<&UiAntiAlias>, @@ -551,11 +553,11 @@ pub fn extract_default_ui_camera_view( Or<(With, With)>, >, >, - mut live_entities: Local, + mut live_entities: Local>, ) { live_entities.clear(); - for (entity, camera, ui_anti_alias, shadow_samples) in &query { + for (main_entity, entity, camera, ui_anti_alias, shadow_samples) in &query { // ignore inactive cameras if !camera.is_active { commands @@ -575,9 +577,13 @@ pub fn extract_default_ui_camera_view( 0.0, UI_CAMERA_FAR, ); + // We use subview index 1 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(), 1); let default_camera_view = commands .spawn(( ExtractedView { + retained_view_entity, clip_from_view: projection_matrix, world_from_view: GlobalTransform::from_xyz( 0.0, @@ -605,9 +611,9 @@ pub fn extract_default_ui_camera_view( if let Some(shadow_samples) = shadow_samples { entity_commands.insert(*shadow_samples); } - transparent_render_phases.insert_or_clear(entity); + transparent_render_phases.insert_or_clear(retained_view_entity); - live_entities.insert(entity); + live_entities.insert(retained_view_entity); } } @@ -805,18 +811,18 @@ pub fn queue_uinodes( ui_pipeline: Res, mut pipelines: ResMut>, mut transparent_render_phases: ResMut>, - mut views: Query<(Entity, &ExtractedView, Option<&UiAntiAlias>)>, + mut views: Query<(&ExtractedView, Option<&UiAntiAlias>)>, 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_mut(extracted_uinode.camera_entity) - else { + let Ok((view, ui_anti_alias)) = views.get_mut(extracted_uinode.camera_entity) else { continue; }; - 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_ui/src/render/render_pass.rs b/crates/bevy_ui/src/render/render_pass.rs index 1e3a9d81ba43c..3480c9e9a27d1 100644 --- a/crates/bevy_ui/src/render/render_pass.rs +++ b/crates/bevy_ui/src/render/render_pass.rs @@ -19,7 +19,11 @@ use bevy_render::{ use bevy_utils::tracing::error; pub struct UiPassNode { - ui_view_query: QueryState<(&'static ViewTarget, &'static ExtractedCamera), With>, + ui_view_query: QueryState<( + &'static ViewTarget, + &'static ExtractedCamera, + &'static ExtractedView, + )>, default_camera_view_query: QueryState<&'static DefaultCameraView>, } @@ -52,13 +56,16 @@ impl Node for UiPassNode { return Ok(()); }; - let Some(transparent_phase) = transparent_render_phases.get(&input_view_entity) else { + let Ok((target, camera, view)) = self.ui_view_query.get_manual(world, input_view_entity) + else { return Ok(()); }; - let Ok((target, camera)) = self.ui_view_query.get_manual(world, input_view_entity) else { + 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 481dce07d37f1..c81ba7ced164a 100644 --- a/crates/bevy_ui/src/render/ui_material_pipeline.rs +++ b/crates/bevy_ui/src/render/ui_material_pipeline.rs @@ -342,6 +342,7 @@ pub struct ExtractedUiMaterialNode { // it is defaulted to a single camera if only one exists. // Nodes with ambiguous camera will be ignored. pub camera_entity: Entity, + pub camera_retained_view_entity: RetainedViewEntity, pub main_entity: MainEntity, } @@ -374,7 +375,7 @@ pub fn extract_ui_material_nodes( Option<&TargetCamera>, )>, >, - render_entity_lookup: Extract>, + render_entity_lookup: Extract>, ) { // If there is only one camera, we use it as default let default_single_camera = default_ui_camera.get(); @@ -384,7 +385,8 @@ pub fn extract_ui_material_nodes( continue; }; - let Ok(camera_entity) = render_entity_lookup.get(camera_entity) else { + let Ok((camera_main_entity, camera_entity)) = render_entity_lookup.get(camera_entity) + else { continue; }; @@ -418,6 +420,7 @@ pub fn extract_ui_material_nodes( border, clip: clip.map(|clip| clip.clip), camera_entity, + camera_retained_view_entity: RetainedViewEntity::new(camera_main_entity.into(), 0), main_entity: entity.into(), }, ); @@ -628,7 +631,7 @@ pub fn queue_ui_material_nodes( continue; }; let Some(transparent_phase) = - transparent_render_phases.get_mut(&extracted_uinode.camera_entity) + transparent_render_phases.get_mut(&extracted_uinode.camera_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 657e4a95aef9c..a3275017072e3 100644 --- a/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs +++ b/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs @@ -344,17 +344,18 @@ pub fn queue_ui_slices( ui_slicer_pipeline: Res, mut pipelines: ResMut>, mut transparent_render_phases: ResMut>, - mut views: Query<(Entity, &ExtractedView)>, + mut 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_mut(extracted_slicer.camera_entity) else { + let Ok(view) = views.get_mut(extracted_slicer.camera_entity) else { continue; }; - 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/examples/2d/mesh2d_manual.rs b/examples/2d/mesh2d_manual.rs index 9870d48e6589b..90f6414e2891d 100644 --- a/examples/2d/mesh2d_manual.rs +++ b/examples/2d/mesh2d_manual.rs @@ -369,14 +369,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 3eb8b6a754279..97fcd361e4acf 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 2809c7d9944e5..148a7da072eae 100644 --- a/examples/shader/custom_shader_instancing.rs +++ b/examples/shader/custom_shader_instancing.rs @@ -133,12 +133,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 8e9913eb2b221..9309a1ba56e2a 100644 --- a/examples/shader/specialized_mesh_pipeline.rs +++ b/examples/shader/specialized_mesh_pipeline.rs @@ -268,7 +268,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, ) { @@ -280,8 +280,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; }; From c5abc9377c500b6eb79d6ca8044a32dce7018d65 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 24 Dec 2024 22:08:25 -0500 Subject: [PATCH 02/12] Try to fix CI --- crates/bevy_pbr/src/render/light.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 08c905acbec80..3507cf5bd6ca2 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -1584,7 +1584,7 @@ pub fn queue_shadows( pipeline_cache: Res, render_lightmaps: Res, mesh_allocator: Res, - view_lights: Query<(Entity, &ViewLightEntities, &ExtractedView)>, + view_lights: Query<(Entity, &ViewLightEntities), With>, view_light_entities: Query<(&LightEntity, &ExtractedView)>, point_light_entities: Query<&RenderCubemapVisibleEntities, With>, directional_light_entities: Query< @@ -1595,7 +1595,7 @@ pub fn queue_shadows( ) where M::Data: PartialEq + Eq + Hash + Clone, { - for (entity, view_lights, view) in &view_lights { + 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, extracted_view_light)) = From a97b28091e78bb473cd7366cb8fa52a6b1144f77 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Sun, 29 Dec 2024 18:06:58 -0500 Subject: [PATCH 03/12] Unbreak UI --- crates/bevy_render/src/view/mod.rs | 8 +++ crates/bevy_ui/src/render/mod.rs | 84 +++++++++++++++++++++--- crates/bevy_ui/src/render/render_pass.rs | 24 +++++-- 3 files changed, 100 insertions(+), 16 deletions(-) diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index 635f1c1252abc..1ec4575375131 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -221,8 +221,16 @@ impl RetainedViewEntity { } } +/// 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: /// diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 82abbd3a65606..e2c1ab993c0cf 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -22,14 +22,16 @@ use bevy_ecs::entity::EntityHashMap; use bevy_ecs::prelude::*; use bevy_image::Image; 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, RunGraphOnViewNode}, render_phase::{sort_phase_system, AddRenderCommand, DrawFunctions}, render_resource::*, renderer::{RenderDevice, RenderQueue}, @@ -45,6 +47,7 @@ use bevy_render::{ }; use bevy_sprite::TextureAtlasLayout; use bevy_sprite::{BorderRect, SpriteAssetEvents}; +use bevy_utils::tracing::warn; #[cfg(feature = "bevy_ui_debug")] pub use debug_overlay::UiDebugOptions; @@ -58,6 +61,7 @@ use core::ops::Range; use graph::{NodeUi, SubGraphUi}; pub use pipeline::*; pub use render_pass::*; +use std::result::Result; pub use ui_material_pipeline::*; use ui_texture_slice_pipeline::UiTextureSlicerPlugin; @@ -98,6 +102,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, @@ -126,6 +131,7 @@ pub fn build_ui_render(app: &mut App) { .configure_sets( ExtractSchedule, ( + RenderUiSystem::ExtractCameraViews, RenderUiSystem::ExtractBoxShadows, RenderUiSystem::ExtractBackgrounds, RenderUiSystem::ExtractImages, @@ -139,7 +145,7 @@ pub fn build_ui_render(app: &mut App) { .add_systems( ExtractSchedule, ( - extract_default_ui_camera_view, + extract_default_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), @@ -164,7 +170,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); @@ -172,7 +178,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); @@ -251,6 +257,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::<&DefaultCameraView>() else { + return Ok(()); + }; + let Some(default_camera_view) = render_views.iter(world).next() else { + return Ok(()); + }; + + // Run the subgraph on the UI view. + graph.run_sub_graph(SubGraphUi, vec![], Some(default_camera_view.0))?; + Ok(()) + } +} + #[allow(clippy::too_many_arguments)] pub fn extract_uinode_background_colors( mut commands: Commands, @@ -534,9 +565,31 @@ 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, 3D cameras extract to two views: a 3D one and a UI one. +/// The main 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)] pub struct DefaultCameraView(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 [`DefaultCameraView`]. +#[derive(Component)] +pub struct UiViewTarget(pub Entity); + /// Extracts all UI elements associated with a camera into the render world. pub fn extract_default_ui_camera_view( mut commands: Commands, @@ -577,9 +630,11 @@ pub fn extract_default_ui_camera_view( 0.0, UI_CAMERA_FAR, ); - // We use subview index 1 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(), 1); + // 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 default_camera_view = commands .spawn(( ExtractedView { @@ -598,12 +653,16 @@ pub fn extract_default_ui_camera_view( )), color_grading: Default::default(), }, + // Link to the main camera view. + UiViewTarget(entity), TemporaryRenderEntity, )) .id(); + let mut entity_commands = commands .get_entity(entity) .expect("Camera entity wasn't synced."); + // Link from the main 2D/3D camera view to the UI view. entity_commands.insert(DefaultCameraView(default_camera_view)); if let Some(ui_anti_alias) = ui_anti_alias { entity_commands.insert(*ui_anti_alias); @@ -811,13 +870,20 @@ pub fn queue_uinodes( ui_pipeline: Res, mut pipelines: ResMut>, mut transparent_render_phases: ResMut>, - mut views: Query<(&ExtractedView, Option<&UiAntiAlias>)>, + mut render_views: Query<(&DefaultCameraView, 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, ui_anti_alias)) = views.get_mut(extracted_uinode.camera_entity) else { + let Ok((default_camera_view, ui_anti_alias)) = + render_views.get_mut(extracted_uinode.camera_entity) + else { + continue; + }; + + let Ok(view) = camera_views.get(default_camera_view.0) else { continue; }; diff --git a/crates/bevy_ui/src/render/render_pass.rs b/crates/bevy_ui/src/render/render_pass.rs index 3480c9e9a27d1..2fb28aeeb526f 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::DefaultCameraView; use bevy_ecs::{ prelude::*, @@ -19,11 +19,8 @@ use bevy_render::{ use bevy_utils::tracing::error; pub struct UiPassNode { - ui_view_query: QueryState<( - &'static ViewTarget, - &'static ExtractedCamera, - &'static ExtractedView, - )>, + ui_view_query: QueryState<(&'static ExtractedView, &'static UiViewTarget)>, + ui_view_target_query: QueryState<(&'static ViewTarget, &'static ExtractedCamera)>, default_camera_view_query: QueryState<&'static DefaultCameraView>, } @@ -31,6 +28,7 @@ impl UiPassNode { pub fn new(world: &mut World) -> Self { Self { ui_view_query: world.query_filtered(), + ui_view_target_query: world.query(), default_camera_view_query: world.query(), } } @@ -39,6 +37,7 @@ impl UiPassNode { impl Node for UiPassNode { fn update(&mut self, world: &mut World) { self.ui_view_query.update_archetypes(world); + self.ui_view_target_query.update_archetypes(world); self.default_camera_view_query.update_archetypes(world); } @@ -48,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) = @@ -56,7 +56,17 @@ impl Node for UiPassNode { return Ok(()); }; - let Ok((target, camera, view)) = self.ui_view_query.get_manual(world, input_view_entity) + // Query the UI view components. + let Ok((view, ui_view_target)) = self.ui_view_query.get_manual(world, input_view_entity) + else { + return Ok(()); + }; + + // Fetch the render target and the camera from the main render view. + // These components won't be present on the UI view. + let Ok((target, camera)) = self + .ui_view_target_query + .get_manual(world, ui_view_target.0) else { return Ok(()); }; From 4329998ceb42094052ae386f1a8af15d8090b1f0 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Mon, 30 Dec 2024 14:40:25 -0600 Subject: [PATCH 04/12] Warning police --- crates/bevy_ui/src/render/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index e2c1ab993c0cf..1313d0f911df3 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -31,7 +31,7 @@ use bevy_render::view::RetainedViewEntity; use bevy_render::{ camera::Camera, render_asset::RenderAssets, - render_graph::{Node as RenderGraphNode, RenderGraph, RunGraphOnViewNode}, + render_graph::{Node as RenderGraphNode, RenderGraph}, render_phase::{sort_phase_system, AddRenderCommand, DrawFunctions}, render_resource::*, renderer::{RenderDevice, RenderQueue}, @@ -47,7 +47,6 @@ use bevy_render::{ }; use bevy_sprite::TextureAtlasLayout; use bevy_sprite::{BorderRect, SpriteAssetEvents}; -use bevy_utils::tracing::warn; #[cfg(feature = "bevy_ui_debug")] pub use debug_overlay::UiDebugOptions; From adf791aaa7f1ad2856342413f4ef9d9d13ded5b7 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Mon, 30 Dec 2024 14:52:33 -0600 Subject: [PATCH 05/12] Clippy police --- crates/bevy_ui/src/render/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 1313d0f911df3..5d4f2e2b0961e 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -60,7 +60,6 @@ use core::ops::Range; use graph::{NodeUi, SubGraphUi}; pub use pipeline::*; pub use render_pass::*; -use std::result::Result; pub use ui_material_pipeline::*; use ui_texture_slice_pipeline::UiTextureSlicerPlugin; From 1d92069c57542479d864751103c852f539ca1fd2 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Sat, 4 Jan 2025 10:35:40 +0000 Subject: [PATCH 06/12] Fixed extraction and queue functions of the shadow, material and slicing plugins to use the correct view entities. --- crates/bevy_ui/src/render/box_shadow.rs | 19 ++++++++++----- .../src/render/ui_material_pipeline.rs | 23 +++++++++++-------- .../src/render/ui_texture_slice_pipeline.rs | 9 ++++++-- 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/crates/bevy_ui/src/render/box_shadow.rs b/crates/bevy_ui/src/render/box_shadow.rs index 5e206655008c6..ccb47b18dc355 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, DefaultCameraView, QUAD_INDICES, QUAD_VERTEX_POSITIONS}; pub const BOX_SHADOW_SHADER_HANDLE: Handle = Handle::weak_from_u128(17717747047134343426); @@ -258,7 +258,7 @@ pub fn extract_shadows( continue; }; - let Ok(camera_entity) = mapping.get(camera_entity) else { + let Ok(render_camera_entity) = mapping.get(camera_entity) else { continue; }; @@ -268,7 +268,7 @@ pub fn extract_shadows( } let ui_physical_viewport_size = camera_query - .get(camera_entity) + .get(render_camera_entity) .ok() .and_then(|(_, c)| { c.physical_viewport_size() @@ -325,7 +325,7 @@ pub fn extract_shadows( color: drop_shadow.color.into(), bounds: shadow_size + 6. * blur_radius, clip: clip.map(|clip| clip.clip), - camera_entity, + camera_entity: render_camera_entity, radius, blur_radius, size: shadow_size, @@ -341,13 +341,20 @@ pub fn queue_shadows( box_shadow_pipeline: Res, mut pipelines: ResMut>, mut transparent_render_phases: ResMut>, - mut views: Query<(&ExtractedView, Option<&BoxShadowSamples>)>, + mut render_views: Query<(&DefaultCameraView, 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, shadow_samples)) = views.get_mut(extracted_shadow.camera_entity) else { + let Ok((default_camera_view, shadow_samples)) = + render_views.get_mut(extracted_shadow.camera_entity) + else { + continue; + }; + + let Ok(view) = camera_views.get(default_camera_view.0) else { continue; }; diff --git a/crates/bevy_ui/src/render/ui_material_pipeline.rs b/crates/bevy_ui/src/render/ui_material_pipeline.rs index c81ba7ced164a..53f780417a240 100644 --- a/crates/bevy_ui/src/render/ui_material_pipeline.rs +++ b/crates/bevy_ui/src/render/ui_material_pipeline.rs @@ -342,7 +342,6 @@ pub struct ExtractedUiMaterialNode { // it is defaulted to a single camera if only one exists. // Nodes with ambiguous camera will be ignored. pub camera_entity: Entity, - pub camera_retained_view_entity: RetainedViewEntity, pub main_entity: MainEntity, } @@ -375,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(); @@ -385,8 +384,7 @@ pub fn extract_ui_material_nodes( continue; }; - let Ok((camera_main_entity, camera_entity)) = render_entity_lookup.get(camera_entity) - else { + let Ok(render_camera_entity) = mapping.get(camera_entity) else { continue; }; @@ -419,8 +417,7 @@ pub fn extract_ui_material_nodes( }, border, clip: clip.map(|clip| clip.clip), - camera_entity, - camera_retained_view_entity: RetainedViewEntity::new(camera_main_entity.into(), 0), + camera_entity: render_camera_entity, main_entity: entity.into(), }, ); @@ -617,7 +614,8 @@ pub fn queue_ui_material_nodes( pipeline_cache: Res, render_materials: Res>>, mut transparent_render_phases: ResMut>, - mut views: Query<&ExtractedView>, + mut render_views: Query<&DefaultCameraView, With>, + camera_views: Query<&ExtractedView>, ) where M::Data: PartialEq + Eq + Hash + Clone, { @@ -627,11 +625,16 @@ pub fn queue_ui_material_nodes( let Some(material) = render_materials.get(extracted_uinode.material) else { continue; }; - let Ok(view) = views.get_mut(extracted_uinode.camera_entity) else { + + let Ok(default_camera_view) = render_views.get_mut(extracted_uinode.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.camera_retained_view_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 a3275017072e3..e7a2ddff313a5 100644 --- a/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs +++ b/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs @@ -344,13 +344,18 @@ pub fn queue_ui_slices( ui_slicer_pipeline: Res, mut pipelines: ResMut>, mut transparent_render_phases: ResMut>, - mut views: Query<&ExtractedView>, + mut render_views: Query<&DefaultCameraView, 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) = views.get_mut(extracted_slicer.camera_entity) else { + let Ok(default_camera_view) = render_views.get_mut(extracted_slicer.camera_entity) else { + continue; + }; + + let Ok(view) = camera_views.get(default_camera_view.0) else { continue; }; From 8a123ffaaece830e08e3f3223ebe36280d6e1b81 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Sat, 4 Jan 2025 21:27:49 -0800 Subject: [PATCH 07/12] Fix up multiple windows --- crates/bevy_ui/src/render/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index cf3655ee0bfd1..59564bf59ab2e 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -269,7 +269,7 @@ impl RenderGraphNode for RunUiSubgraphOnUiViewNode { let Some(mut render_views) = world.try_query::<&DefaultCameraView>() else { return Ok(()); }; - let Some(default_camera_view) = render_views.iter(world).next() else { + let Ok(default_camera_view) = render_views.get(world, graph.view_entity()) else { return Ok(()); }; From fe384ad568882a4d058ca0d36ac4825116ea6e6b Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Sun, 5 Jan 2025 10:53:58 -0800 Subject: [PATCH 08/12] Clippy police --- crates/bevy_ui/src/render/box_shadow.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/bevy_ui/src/render/box_shadow.rs b/crates/bevy_ui/src/render/box_shadow.rs index ccb47b18dc355..2ff5c39d38a0c 100644 --- a/crates/bevy_ui/src/render/box_shadow.rs +++ b/crates/bevy_ui/src/render/box_shadow.rs @@ -336,6 +336,7 @@ pub fn extract_shadows( } } +#[allow(clippy::too_many_arguments)] pub fn queue_shadows( extracted_box_shadows: ResMut, box_shadow_pipeline: Res, From b33205775a637b3f459203890695d5b1d0e562d2 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Sun, 5 Jan 2025 11:03:42 -0800 Subject: [PATCH 09/12] Clippy police 2 --- crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs | 1 + 1 file changed, 1 insertion(+) 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 e7a2ddff313a5..1b4ef6d9aff35 100644 --- a/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs +++ b/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs @@ -339,6 +339,7 @@ pub fn extract_ui_texture_slices( } } +#[allow(clippy::too_many_arguments)] pub fn queue_ui_slices( extracted_ui_slicers: ResMut, ui_slicer_pipeline: Res, From 7db57072069422bda1154c7208f384ac91536e32 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 7 Jan 2025 01:10:34 -0800 Subject: [PATCH 10/12] Remove bad import --- crates/bevy_pbr/src/render/light.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index f85771a7ecb39..6a73749708d8f 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -35,8 +35,6 @@ use bevy_render::{ sync_world::{MainEntity, RenderEntity}, }; use bevy_transform::{components::GlobalTransform, prelude::Transform}; -#[cfg(feature = "trace")] -use bevy_utils::tracing::info_span; use bevy_utils::{default, HashMap, HashSet}; use core::{hash::Hash, ops::Range}; #[cfg(feature = "trace")] From fb5da364e79fbacb2e03c261e9aed05eb0baf95c Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 7 Jan 2025 10:51:15 -0800 Subject: [PATCH 11/12] Update comment --- crates/bevy_ui/src/render/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 204913393c201..17f217362b8eb 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -564,9 +564,9 @@ 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, 3D cameras extract to two views: a 3D one and a UI one. -/// The main 3D camera gets subview 0, and the corresponding UI camera gets this -/// subview, 1. +/// 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 From aecad7f5592cf2a417bc59d48d61542e6d369d9a Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Sat, 11 Jan 2025 21:56:25 -0800 Subject: [PATCH 12/12] Lint police --- crates/bevy_ui/src/render/box_shadow.rs | 2 +- crates/bevy_ui/src/render/mod.rs | 1 - crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs | 5 ++++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/bevy_ui/src/render/box_shadow.rs b/crates/bevy_ui/src/render/box_shadow.rs index 5425dc9aaf992..2b332c04f2a9d 100644 --- a/crates/bevy_ui/src/render/box_shadow.rs +++ b/crates/bevy_ui/src/render/box_shadow.rs @@ -337,7 +337,7 @@ pub fn extract_shadows( } } -#[allow(clippy::too_many_arguments)] +#[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, diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 3b26f7404cf85..95106f1c0cd6d 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -278,7 +278,6 @@ impl RenderGraphNode for RunUiSubgraphOnUiViewNode { } } -#[allow(clippy::too_many_arguments)] pub fn extract_uinode_background_colors( mut commands: Commands, mut extracted_uinodes: ResMut, 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 87437dfd43195..8d142aea853e3 100644 --- a/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs +++ b/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs @@ -338,7 +338,10 @@ pub fn extract_ui_texture_slices( } } -#[allow(clippy::too_many_arguments)] +#[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,