diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index 433fd5d14a683..8d7656f3012c0 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -376,6 +376,7 @@ impl Plugin for PbrPlugin { ExtractSchedule, ( extract_lights, + extract_lights_render_layers_meta, extract_ambient_light_resource, extract_ambient_light, extract_shadow_filtering_method, diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index d7187be5be754..71ae086236271 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -48,8 +48,8 @@ use bevy_render::occlusion_culling::{ }; use bevy_render::sync_world::{MainEntity, MainEntityHashMap, MainEntityHashSet, RenderEntity}; use bevy_render::view::{ - RenderExtractedShadowMapVisibleEntities, RenderShadowLodOrigin, RenderShadowMapVisibleEntities, - RenderVisibleEntities, VisibilityExtractionSystemParam, + ExtractedRenderLayersMeta, RenderExtractedShadowMapVisibleEntities, RenderShadowLodOrigin, + RenderShadowMapVisibleEntities, RenderVisibleEntities, VisibilityExtractionSystemParam, }; use bevy_render::{ batching::gpu_preprocessing::{GpuPreprocessingMode, GpuPreprocessingSupport}, @@ -951,6 +951,37 @@ pub fn extract_lights( } } +pub fn extract_lights_render_layers_meta( + mut commands: Commands, + all_light_query: Extract< + Query< + (RenderEntity, Ref), + Or<( + With, + With, + With, + With, + )>, + >, + >, + mut extracted_render_layer_meta_query: Query<&mut ExtractedRenderLayersMeta>, +) { + for (render_entity, render_layers) in all_light_query.iter() { + match extracted_render_layer_meta_query.get_mut(render_entity) { + Ok(mut meta) => { + meta.changed = render_layers.is_changed(); + } + Err(_) => { + commands + .entity(render_entity) + .insert(ExtractedRenderLayersMeta { + changed: render_layers.is_changed(), + }); + } + } + } +} + #[derive(Component, Default, Deref, DerefMut)] /// Component automatically attached to a light entity to track light-view entities /// for each view. diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 05c268f4fbc6a..8ca7c4f402e84 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -103,7 +103,7 @@ use bevy_render::camera::{DirtySpecializations, ExtractedCamera, TemporalJitter} use bevy_render::prelude::Msaa; use bevy_render::sync_world::{MainEntity, MainEntityHashMap}; use bevy_render::view::{ - texture_format_from_code, texture_format_to_code, ExtractedView, + texture_format_from_code, texture_format_to_code, ExtractedRenderLayersMeta, ExtractedView, RenderShadowMapVisibleEntities, RenderVisibleEntities, }; use bevy_render::RenderSystems::PrepareAssets; @@ -2285,34 +2285,57 @@ impl<'a> Iterator for AtomicU64ZeroBitIter<'a> { /// Each view must maintain a separate list of GPU-culled entities because the /// views and entities might belong to different render layers. pub fn collect_gpu_culled_meshes( - mut cameras: Query<(Option<&RenderLayers>, &mut RenderVisibleEntities), With>, - mut lights: Query<(Option<&RenderLayers>, &mut RenderShadowMapVisibleEntities)>, + mut cameras: Query< + ( + Option<&RenderLayers>, + Option<&ExtractedRenderLayersMeta>, + &mut RenderVisibleEntities, + ), + With, + >, + mut lights: Query<( + Option<&RenderLayers>, + Option<&ExtractedRenderLayersMeta>, + &mut RenderShadowMapVisibleEntities, + )>, render_gpu_culled_entities: Res, ) { let default_render_layers = RenderLayers::default(); // Collect cameras. - for (maybe_render_layers, mut render_visible_entities) in &mut cameras { + for (maybe_render_layers, maybe_extracted_render_layers_meta, mut render_visible_entities) in + &mut cameras + { let just_added_render_visible_entities = render_visible_entities.is_added(); collect_gpu_culled_meshes_for_subview( maybe_render_layers.unwrap_or(&default_render_layers), &mut render_visible_entities, + maybe_extracted_render_layers_meta + .is_some_and(|extracted_render_layers_meta| extracted_render_layers_meta.changed), just_added_render_visible_entities, &render_gpu_culled_entities, ); } // Collect shadow maps. - for (maybe_render_layers, mut render_shadow_map_visible_entities) in &mut lights { + for ( + maybe_render_layers, + maybe_extracted_render_layers_meta, + mut render_shadow_map_visible_entities, + ) in &mut lights + { let last_run = render_shadow_map_visible_entities.last_run(); let this_run = render_shadow_map_visible_entities.this_run(); + let view_render_layers_changed = maybe_extracted_render_layers_meta + .is_some_and(|extracted_render_layers_meta| extracted_render_layers_meta.changed); for (render_visible_entities_added_tick, render_visible_entities) in render_shadow_map_visible_entities.subviews.values_mut() { collect_gpu_culled_meshes_for_subview( maybe_render_layers.unwrap_or(&default_render_layers), render_visible_entities, + view_render_layers_changed, render_visible_entities_added_tick.is_newer_than(last_run, this_run), &render_gpu_culled_entities, ); @@ -2329,6 +2352,7 @@ pub fn collect_gpu_culled_meshes( fn collect_gpu_culled_meshes_for_subview( view_render_layers: &RenderLayers, render_visible_entities: &mut RenderVisibleEntities, + view_render_layers_changed: bool, just_added_render_visible_entities: bool, render_mesh_instance_gpu_queues: &RenderGpuCulledEntities, ) { @@ -2392,17 +2416,12 @@ fn collect_gpu_culled_meshes_for_subview( } // Process entities that changed layers. - for main_entity in &render_mesh_instance_gpu_queues.changed_layers { - let Some(render_layers) = render_mesh_instance_gpu_queues.entities.get(main_entity) - else { - continue; - }; - - // This is either treated as no change, as an addition, or as a removal. + let mut process_changed_layer = |main_entity: &MainEntity, render_layers: &RenderLayers| { + let entity_is_relevant = is_entity_relevant(render_layers); let entity_was_relevant = render_view_visible_mesh_entities .entities_gpu_culling .contains_key(main_entity); - let entity_is_relevant = is_entity_relevant(render_layers); + match (entity_was_relevant, entity_is_relevant) { (false, false) | (true, true) => { // No change; do nothing. @@ -2427,6 +2446,20 @@ fn collect_gpu_culled_meshes_for_subview( .push((Entity::PLACEHOLDER, *main_entity)); } } + }; + + if view_render_layers_changed { + for (main_entity, render_layers) in render_mesh_instance_gpu_queues.entities.iter() { + process_changed_layer(main_entity, render_layers); + } + } else { + for main_entity in &render_mesh_instance_gpu_queues.changed_layers { + let Some(render_layers) = render_mesh_instance_gpu_queues.entities.get(main_entity) + else { + continue; + }; + process_changed_layer(main_entity, render_layers); + } } // Make sure the `added_entities` list is sorted, as the diff --git a/crates/bevy_render/src/camera.rs b/crates/bevy_render/src/camera.rs index 848005cc0d46f..52a471b6d74cc 100644 --- a/crates/bevy_render/src/camera.rs +++ b/crates/bevy_render/src/camera.rs @@ -10,10 +10,10 @@ use crate::{ sync_world::{MainEntity, MainEntityHashSet, RenderEntity, SyncToRenderWorld}, texture::{GpuImage, ManualTextureViews}, view::{ - ColorGrading, ExtractedView, ExtractedWindow, Msaa, NoIndirectDrawing, - RenderExtractedVisibleEntities, RenderVisibleEntities, RenderVisibleEntitiesClass, - ResolvedCompositingSpace, RetainedViewEntity, ViewUniformOffset, - VisibilityExtractionSystemParam, + ColorGrading, ExtractedRenderLayersMeta, ExtractedView, ExtractedWindow, Msaa, + NoIndirectDrawing, RenderExtractedVisibleEntities, RenderVisibleEntities, + RenderVisibleEntitiesClass, ResolvedCompositingSpace, RetainedViewEntity, + ViewUniformOffset, VisibilityExtractionSystemParam, }, Extract, ExtractSchedule, Render, RenderApp, RenderSystems, }; @@ -41,7 +41,7 @@ use bevy_ecs::{ resource::Resource, schedule::{InternedScheduleLabel, IntoScheduleConfigs, ScheduleLabel, SystemSet}, system::{Commands, Query, Res, ResMut}, - world::DeferredWorld, + world::{DeferredWorld, Ref}, }; use bevy_image::Image; use bevy_log::warn; @@ -490,7 +490,7 @@ pub fn extract_cameras( Option<&Exposure>, Option<&TemporalJitter>, Option<&MipBias>, - Option<&RenderLayers>, + Option>, Option<&Projection>, Has, ), @@ -677,9 +677,14 @@ pub fn extract_cameras( } if let Some(render_layers) = render_layers { - commands.insert(render_layers.clone()); + commands.insert(( + (*render_layers).clone(), + ExtractedRenderLayersMeta { + changed: render_layers.is_changed(), + }, + )); } else { - commands.remove::(); + commands.remove::<(RenderLayers, ExtractedRenderLayersMeta)>(); } if let Some(projection) = projection { diff --git a/crates/bevy_render/src/view/visibility/mod.rs b/crates/bevy_render/src/view/visibility/mod.rs index aa4e8d78c3c60..c441a6818cd21 100644 --- a/crates/bevy_render/src/view/visibility/mod.rs +++ b/crates/bevy_render/src/view/visibility/mod.rs @@ -27,6 +27,18 @@ mod range; use bevy_camera::visibility::*; pub use range::*; +/// Extracted metadata about [`RenderLayers`]. +/// +/// Presented on every [`ExtractedView`](crate::view::ExtractedView) with [`RenderLayers`]. E.g. extracted camera, extracted light shadow map views. +/// +/// Currently this is used for detecting render layers changes on the view for gpu culling entities. +/// Since cpu culling entities is not retained, thus does not care about render layers metadata +#[derive(Debug, Clone, Copy, PartialEq, Eq, Component, Default)] +pub struct ExtractedRenderLayersMeta { + /// Did render layers change in this extracted frame + pub changed: bool, +} + /// Stores a list of all entities that are visible from a single view or /// subview, as well as the change lists. ///