From 1a973b40de482f418ec9fe4ab93470b2a5ecaf40 Mon Sep 17 00:00:00 2001 From: re0312 Date: Sun, 16 Jun 2024 04:05:24 +0800 Subject: [PATCH 1/4] init --- crates/bevy_pbr/src/light/mod.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/crates/bevy_pbr/src/light/mod.rs b/crates/bevy_pbr/src/light/mod.rs index 4273de71d5cfe..24bb58c492421 100644 --- a/crates/bevy_pbr/src/light/mod.rs +++ b/crates/bevy_pbr/src/light/mod.rs @@ -682,11 +682,11 @@ pub fn check_light_mesh_visibility( >, visible_entity_ranges: Option>, ) { - fn shrink_entities(visible_entities: &mut VisibleEntities) { + fn shrink_entities(visible_entities: &mut Vec) { // Check that visible entities capacity() is no more than two times greater than len() - let capacity = visible_entities.entities.capacity(); + let capacity = visible_entities.capacity(); let reserved = capacity - .checked_div(visible_entities.entities.len()) + .checked_div(visible_entities.len()) .map_or(0, |reserve| { if reserve > 2 { capacity / (reserve / 2) @@ -695,7 +695,7 @@ pub fn check_light_mesh_visibility( } }); - visible_entities.entities.shrink_to(reserved); + visible_entities.shrink_to(reserved); } let visible_entity_ranges = visible_entity_ranges.as_deref(); @@ -712,7 +712,7 @@ pub fn check_light_mesh_visibility( cascade_view_entities.resize(view_frusta.len(), Default::default()); cascade_view_entities .iter_mut() - .for_each(|x| x.entities.clear()); + .for_each(|x| x.clear::()); } None => views_to_remove.push(*view), }; @@ -798,7 +798,10 @@ pub fn check_light_mesh_visibility( } for (_, cascade_view_entities) in &mut visible_entities.entities { - cascade_view_entities.iter_mut().for_each(shrink_entities); + cascade_view_entities + .iter_mut() + .map(|x| x.get_mut::()) + .for_each(shrink_entities); } } @@ -814,7 +817,7 @@ pub fn check_light_mesh_visibility( )) = point_lights.get_mut(light_entity) { for visible_entities in cubemap_visible_entities.iter_mut() { - visible_entities.entities.clear(); + visible_entities.clear::(); } // NOTE: If shadow mapping is disabled for the light then it must have no visible entities @@ -882,7 +885,7 @@ pub fn check_light_mesh_visibility( } for visible_entities in cubemap_visible_entities.iter_mut() { - shrink_entities(visible_entities); + shrink_entities(visible_entities.get_mut::()); } } @@ -890,7 +893,7 @@ pub fn check_light_mesh_visibility( if let Ok((point_light, transform, frustum, mut visible_entities, maybe_view_mask)) = spot_lights.get_mut(light_entity) { - visible_entities.entities.clear(); + visible_entities.clear::(); // NOTE: If shadow mapping is disabled for the light then it must have no visible entities if !point_light.shadows_enabled { @@ -949,7 +952,7 @@ pub fn check_light_mesh_visibility( } } - shrink_entities(&mut visible_entities); + shrink_entities(visible_entities.get_mut::()); } } } From d9bb9faa401d9abd3d687f7e6dfa7b3963901d73 Mon Sep 17 00:00:00 2001 From: re0312 Date: Tue, 18 Jun 2024 09:19:57 +0800 Subject: [PATCH 2/4] split --- crates/bevy_pbr/src/lib.rs | 5 +- crates/bevy_pbr/src/light/mod.rs | 86 ++++++++++++++++++++------------ 2 files changed, 58 insertions(+), 33 deletions(-) diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index a6c94badf477d..27b47dccc7e4d 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -368,7 +368,10 @@ impl Plugin for PbrPlugin { .after(TransformSystem::TransformPropagate) .after(SimulationLightSystems::AssignLightsToClusters), check_visibility::.in_set(VisibilitySystems::CheckVisibility), - check_light_mesh_visibility + ( + check_dir_light_mesh_visibility, + check_point_light_mesh_visibility, + ) .in_set(SimulationLightSystems::CheckLightVisibility) .after(VisibilitySystems::CalculateBounds) .after(TransformSystem::TransformPropagate) diff --git a/crates/bevy_pbr/src/light/mod.rs b/crates/bevy_pbr/src/light/mod.rs index 05cea8305a6fe..7299547e71f46 100644 --- a/crates/bevy_pbr/src/light/mod.rs +++ b/crates/bevy_pbr/src/light/mod.rs @@ -638,22 +638,23 @@ pub fn update_spot_light_frusta( } } -pub fn check_light_mesh_visibility( - visible_point_lights: Query<&VisibleClusterableObjects>, - mut point_lights: Query<( - &PointLight, - &GlobalTransform, - &CubemapFrusta, - &mut CubemapVisibleEntities, - Option<&RenderLayers>, - )>, - mut spot_lights: Query<( - &SpotLight, - &GlobalTransform, - &Frustum, - &mut VisibleEntities, - Option<&RenderLayers>, - )>, +fn shrink_entities(visible_entities: &mut Vec) { + // Check that visible entities capacity() is no more than two times greater than len() + let capacity = visible_entities.capacity(); + let reserved = capacity + .checked_div(visible_entities.len()) + .map_or(0, |reserve| { + if reserve > 2 { + capacity / (reserve / 2) + } else { + capacity + } + }); + + visible_entities.shrink_to(reserved); +} + +pub fn check_dir_light_mesh_visibility( mut directional_lights: Query< ( &DirectionalLight, @@ -682,22 +683,6 @@ pub fn check_light_mesh_visibility( >, visible_entity_ranges: Option>, ) { - fn shrink_entities(visible_entities: &mut Vec) { - // Check that visible entities capacity() is no more than two times greater than len() - let capacity = visible_entities.capacity(); - let reserved = capacity - .checked_div(visible_entities.len()) - .map_or(0, |reserve| { - if reserve > 2 { - capacity / (reserve / 2) - } else { - capacity - } - }); - - visible_entities.shrink_to(reserved); - } - let visible_entity_ranges = visible_entity_ranges.as_deref(); // Directional lights @@ -804,6 +789,43 @@ pub fn check_light_mesh_visibility( .for_each(shrink_entities); } } +} + +pub fn check_point_light_mesh_visibility( + visible_point_lights: Query<&VisibleClusterableObjects>, + mut point_lights: Query<( + &PointLight, + &GlobalTransform, + &CubemapFrusta, + &mut CubemapVisibleEntities, + Option<&RenderLayers>, + )>, + mut spot_lights: Query<( + &SpotLight, + &GlobalTransform, + &Frustum, + &mut VisibleEntities, + Option<&RenderLayers>, + )>, + mut visible_entity_query: Query< + ( + Entity, + &InheritedVisibility, + &mut ViewVisibility, + Option<&RenderLayers>, + Option<&Aabb>, + Option<&GlobalTransform>, + Has, + ), + ( + Without, + Without, + With>, + ), + >, + visible_entity_ranges: Option>, +) { + let visible_entity_ranges = visible_entity_ranges.as_deref(); for visible_lights in &visible_point_lights { for light_entity in visible_lights.entities.iter().copied() { From 6ce8c81221b1027bb98cb152b4627c2b5e720bfe Mon Sep 17 00:00:00 2001 From: re0312 Date: Tue, 18 Jun 2024 09:31:51 +0800 Subject: [PATCH 3/4] opt --- crates/bevy_pbr/src/light/mod.rs | 397 +++++++++++++++++-------------- 1 file changed, 222 insertions(+), 175 deletions(-) diff --git a/crates/bevy_pbr/src/light/mod.rs b/crates/bevy_pbr/src/light/mod.rs index 7299547e71f46..123756e49102b 100644 --- a/crates/bevy_pbr/src/light/mod.rs +++ b/crates/bevy_pbr/src/light/mod.rs @@ -1,3 +1,5 @@ +use std::ops::DerefMut; + use bevy_ecs::entity::EntityHashMap; use bevy_ecs::prelude::*; use bevy_math::{Mat4, Vec3A, Vec4}; @@ -14,6 +16,7 @@ use bevy_render::{ }, }; use bevy_transform::components::{GlobalTransform, Transform}; +use bevy_utils::Parallel; use crate::*; @@ -655,21 +658,21 @@ fn shrink_entities(visible_entities: &mut Vec) { } pub fn check_dir_light_mesh_visibility( + mut commands: Commands, mut directional_lights: Query< ( &DirectionalLight, &CascadesFrusta, &mut CascadesVisibleEntities, Option<&RenderLayers>, - &mut ViewVisibility, + &ViewVisibility, ), Without, >, - mut visible_entity_query: Query< + visible_entity_query: Query< ( Entity, &InheritedVisibility, - &mut ViewVisibility, Option<&RenderLayers>, Option<&Aabb>, Option<&GlobalTransform>, @@ -682,113 +685,144 @@ pub fn check_dir_light_mesh_visibility( ), >, visible_entity_ranges: Option>, + mut visible_entities_queue: Local>>, + mut view_visible_entities_queue: Local>>>, ) { let visible_entity_ranges = visible_entity_ranges.as_deref(); - // Directional lights - for (directional_light, frusta, mut visible_entities, maybe_view_mask, light_view_visibility) in - &mut directional_lights - { - // Re-use already allocated entries where possible. - let mut views_to_remove = Vec::new(); - for (view, cascade_view_entities) in &mut visible_entities.entities { - match frusta.frusta.get(view) { - Some(view_frusta) => { - cascade_view_entities.resize(view_frusta.len(), Default::default()); - cascade_view_entities - .iter_mut() - .for_each(|x| x.clear::()); - } - None => views_to_remove.push(*view), - }; - } - for (view, frusta) in &frusta.frusta { - visible_entities - .entities - .entry(*view) - .or_insert_with(|| vec![VisibleEntities::default(); frusta.len()]); - } - for v in views_to_remove { - visible_entities.entities.remove(&v); - } - - // NOTE: If shadow mapping is disabled for the light then it must have no visible entities - if !directional_light.shadows_enabled || !light_view_visibility.get() { - continue; - } + directional_lights.iter_mut().for_each( + |( + directional_light, + frusta, + mut cascades_visible_entities, + maybe_view_mask, + light_view_visibility, + )| { + let mut views_to_remove = Vec::new(); + for (view, cascade_view_entities) in &mut cascades_visible_entities.entities { + match frusta.frusta.get(view) { + Some(view_frusta) => { + cascade_view_entities.resize(view_frusta.len(), Default::default()); + cascade_view_entities + .iter_mut() + .for_each(|x| x.entities.clear()); + } + None => views_to_remove.push(*view), + }; + } + for (view, frusta) in &frusta.frusta { + cascades_visible_entities + .entities + .entry(*view) + .or_insert_with(|| vec![VisibleEntities::default(); frusta.len()]); + } - let view_mask = maybe_view_mask.unwrap_or_default(); - - for ( - entity, - inherited_visibility, - mut view_visibility, - maybe_entity_mask, - maybe_aabb, - maybe_transform, - has_visibility_range, - ) in &mut visible_entity_query - { - if !inherited_visibility.get() { - continue; + for v in views_to_remove { + cascades_visible_entities.entities.remove(&v); } - let entity_mask = maybe_entity_mask.unwrap_or_default(); - if !view_mask.intersects(entity_mask) { - continue; + // NOTE: If shadow mapping is disabled for the light then it must have no visible entities + if !directional_light.shadows_enabled || !light_view_visibility.get() { + return; } - // If we have an aabb and transform, do frustum culling - if let (Some(aabb), Some(transform)) = (maybe_aabb, maybe_transform) { - for (view, view_frusta) in &frusta.frusta { - let view_visible_entities = visible_entities - .entities - .get_mut(view) - .expect("Per-view visible entities should have been inserted already"); - - // Check visibility ranges. - if has_visibility_range - && visible_entity_ranges.is_some_and(|visible_entity_ranges| { - !visible_entity_ranges.entity_is_in_range_of_view(entity, *view) - }) - { - continue; - } + let view_mask = maybe_view_mask.unwrap_or_default(); + + for (view, view_frusta) in &frusta.frusta { + visible_entity_query.par_iter().for_each_init( + || { + let mut entities = view_visible_entities_queue.borrow_local_mut(); + let cap = entities.first().map(|v| v.capacity()).unwrap_or_default(); + entities.resize(view_frusta.len(), Vec::with_capacity(cap)); + (visible_entities_queue.borrow_local_mut(), entities) + }, + |(queue0, queue1), + ( + entity, + inherited_visibility, + maybe_entity_mask, + maybe_aabb, + maybe_transform, + has_visibility_range, + )| { + if !inherited_visibility.get() { + return; + } - for (frustum, frustum_visible_entities) in - view_frusta.iter().zip(view_visible_entities) - { - // Disable near-plane culling, as a shadow caster could lie before the near plane. - if !frustum.intersects_obb(aabb, &transform.affine(), false, true) { - continue; + let entity_mask = maybe_entity_mask.unwrap_or_default(); + if !view_mask.intersects(entity_mask) { + return; } - view_visibility.set(); - frustum_visible_entities.get_mut::().push(entity); - } - } - } else { - view_visibility.set(); - for view in frusta.frusta.keys() { - let view_visible_entities = visible_entities + // Check visibility ranges. + if has_visibility_range + && visible_entity_ranges.is_some_and(|visible_entity_ranges| { + !visible_entity_ranges.entity_is_in_range_of_view(entity, *view) + }) + { + return; + } + + if let (Some(aabb), Some(transform)) = (maybe_aabb, maybe_transform) { + let mut visible = false; + for (frustum, frustum_visible_entities) in + view_frusta.iter().zip(queue1.iter_mut()) + { + // Disable near-plane culling, as a shadow caster could lie before the near plane. + if !frustum.intersects_obb(aabb, &transform.affine(), false, true) { + continue; + } + visible = true; + + frustum_visible_entities.push(entity); + } + if visible { + queue0.push(entity); + } + } else { + queue0.push(entity); + for frustum_visible_entities in queue1.iter_mut() { + frustum_visible_entities.push(entity); + } + } + }, + ); + for entities in view_visible_entities_queue.iter_mut() { + cascades_visible_entities .entities .get_mut(view) - .expect("Per-view visible entities should have been inserted already"); - - for frustum_visible_entities in view_visible_entities { - frustum_visible_entities.get_mut::().push(entity); - } + .unwrap() + .iter_mut() + .map(|v| v.get_mut::()) + .zip(entities.iter_mut()) + .for_each(|(dst, source)| { + dst.append(source); + }); } } - } + }, + ); - for (_, cascade_view_entities) in &mut visible_entities.entities { + for (_, _, mut cascades_visible_entities, _, _) in &mut directional_lights { + for (_, cascade_view_entities) in &mut cascades_visible_entities.entities { cascade_view_entities .iter_mut() .map(|x| x.get_mut::()) .for_each(shrink_entities); } } + + // TODO: use resource to avoid unnecessary memory alloc + let mut defer_queue = std::mem::take(visible_entities_queue.deref_mut()); + commands.add(move |world: &mut World| { + let mut query = world.query::<&mut ViewVisibility>(); + for entities in defer_queue.iter_mut() { + let mut iter = query.iter_many_mut(world, entities.iter()); + while let Some(mut t) = iter.fetch_next() { + t.set(); + } + } + }); } pub fn check_point_light_mesh_visibility( @@ -824,9 +858,10 @@ pub fn check_point_light_mesh_visibility( ), >, visible_entity_ranges: Option>, + mut cubmap_visible_entity_queue: Local; 6]>>, + mut spot_visible_entity_queue: Local>>, ) { let visible_entity_ranges = visible_entity_ranges.as_deref(); - for visible_lights in &visible_point_lights { for light_entity in visible_lights.entities.iter().copied() { // Point lights @@ -853,57 +888,64 @@ pub fn check_point_light_mesh_visibility( radius: point_light.range, }; - for ( - entity, - inherited_visibility, - mut view_visibility, - maybe_entity_mask, - maybe_aabb, - maybe_transform, - has_visibility_range, - ) in &mut visible_entity_query - { - if !inherited_visibility.get() { - continue; - } - - let entity_mask = maybe_entity_mask.unwrap_or_default(); - if !view_mask.intersects(entity_mask) { - continue; - } - - // Check visibility ranges. - if has_visibility_range - && visible_entity_ranges.is_some_and(|visible_entity_ranges| { - !visible_entity_ranges.entity_is_in_range_of_any_view(entity) - }) - { - continue; - } - - // If we have an aabb and transform, do frustum culling - if let (Some(aabb), Some(transform)) = (maybe_aabb, maybe_transform) { - let model_to_world = transform.affine(); - // Do a cheap sphere vs obb test to prune out most meshes outside the sphere of the light - if !light_sphere.intersects_obb(aabb, &model_to_world) { - continue; + visible_entity_query.par_iter_mut().for_each_init( + || cubmap_visible_entity_queue.borrow_local_mut(), + |queue, + ( + entity, + inherited_visibility, + mut view_visibility, + maybe_entity_mask, + maybe_aabb, + maybe_transform, + has_visibility_range, + )| { + if !inherited_visibility.get() { + return; } - - for (frustum, visible_entities) in cubemap_frusta - .iter() - .zip(cubemap_visible_entities.iter_mut()) + let entity_mask = maybe_entity_mask.unwrap_or_default(); + if !view_mask.intersects(entity_mask) { + return; + } + if has_visibility_range + && visible_entity_ranges.is_some_and(|visible_entity_ranges| { + !visible_entity_ranges.entity_is_in_range_of_any_view(entity) + }) { - if frustum.intersects_obb(aabb, &model_to_world, true, true) { - view_visibility.set(); - visible_entities.push::(entity); - } + return; } - } else { - view_visibility.set(); - for visible_entities in cubemap_visible_entities.iter_mut() { - visible_entities.push::(entity); + + // If we have an aabb and transform, do frustum culling + if let (Some(aabb), Some(transform)) = (maybe_aabb, maybe_transform) { + let model_to_world = transform.affine(); + // Do a cheap sphere vs obb test to prune out most meshes outside the sphere of the light + if !light_sphere.intersects_obb(aabb, &model_to_world) { + return; + } + + for (frustum, visible_entities) in + cubemap_frusta.iter().zip(queue.iter_mut()) + { + if frustum.intersects_obb(aabb, &model_to_world, true, true) { + view_visibility.set(); + visible_entities.push(entity); + } + } + } else { + view_visibility.set(); + for visible_entities in queue.iter_mut() { + visible_entities.push(entity); + } } - } + }, + ); + + for entities in cubmap_visible_entity_queue.iter_mut() { + cubemap_visible_entities + .iter_mut() + .map(|v| v.get_mut::()) + .zip(entities.iter_mut()) + .for_each(|(dst, source)| dst.append(source)); } for visible_entities in cubemap_visible_entities.iter_mut() { @@ -928,50 +970,55 @@ pub fn check_point_light_mesh_visibility( radius: point_light.range, }; - for ( - entity, - inherited_visibility, - mut view_visibility, - maybe_entity_mask, - maybe_aabb, - maybe_transform, - has_visibility_range, - ) in &mut visible_entity_query - { - if !inherited_visibility.get() { - continue; - } - - let entity_mask = maybe_entity_mask.unwrap_or_default(); - if !view_mask.intersects(entity_mask) { - continue; - } - - // Check visibility ranges. - if has_visibility_range - && visible_entity_ranges.is_some_and(|visible_entity_ranges| { - !visible_entity_ranges.entity_is_in_range_of_any_view(entity) - }) - { - continue; - } + visible_entity_query.par_iter_mut().for_each_init( + || spot_visible_entity_queue.borrow_local_mut(), + |queue, + ( + entity, + inherited_visibility, + mut view_visibility, + maybe_entity_mask, + maybe_aabb, + maybe_transform, + has_visibility_range, + )| { + if !inherited_visibility.get() { + return; + } - // If we have an aabb and transform, do frustum culling - if let (Some(aabb), Some(transform)) = (maybe_aabb, maybe_transform) { - let model_to_world = transform.affine(); - // Do a cheap sphere vs obb test to prune out most meshes outside the sphere of the light - if !light_sphere.intersects_obb(aabb, &model_to_world) { - continue; + let entity_mask = maybe_entity_mask.unwrap_or_default(); + if !view_mask.intersects(entity_mask) { + return; } + // Check visibility ranges. + if has_visibility_range + && visible_entity_ranges.is_some_and(|visible_entity_ranges| { + !visible_entity_ranges.entity_is_in_range_of_any_view(entity) + }) + { + return; + } + + if let (Some(aabb), Some(transform)) = (maybe_aabb, maybe_transform) { + let model_to_world = transform.affine(); + // Do a cheap sphere vs obb test to prune out most meshes outside the sphere of the light + if !light_sphere.intersects_obb(aabb, &model_to_world) { + return; + } - if frustum.intersects_obb(aabb, &model_to_world, true, true) { + if frustum.intersects_obb(aabb, &model_to_world, true, true) { + view_visibility.set(); + queue.push(entity); + } + } else { view_visibility.set(); - visible_entities.push::(entity); + queue.push(entity); } - } else { - view_visibility.set(); - visible_entities.push::(entity); - } + }, + ); + + for entities in spot_visible_entity_queue.iter_mut() { + visible_entities.get_mut::().append(entities); } shrink_entities(visible_entities.get_mut::()); From 5d6c74c8ca54eda6953a749d91dd032a0a4133fa Mon Sep 17 00:00:00 2001 From: re0312 Date: Tue, 18 Jun 2024 09:32:27 +0800 Subject: [PATCH 4/4] Revert "opt" This reverts commit 6ce8c81221b1027bb98cb152b4627c2b5e720bfe. --- crates/bevy_pbr/src/light/mod.rs | 397 ++++++++++++++----------------- 1 file changed, 175 insertions(+), 222 deletions(-) diff --git a/crates/bevy_pbr/src/light/mod.rs b/crates/bevy_pbr/src/light/mod.rs index 123756e49102b..7299547e71f46 100644 --- a/crates/bevy_pbr/src/light/mod.rs +++ b/crates/bevy_pbr/src/light/mod.rs @@ -1,5 +1,3 @@ -use std::ops::DerefMut; - use bevy_ecs::entity::EntityHashMap; use bevy_ecs::prelude::*; use bevy_math::{Mat4, Vec3A, Vec4}; @@ -16,7 +14,6 @@ use bevy_render::{ }, }; use bevy_transform::components::{GlobalTransform, Transform}; -use bevy_utils::Parallel; use crate::*; @@ -658,21 +655,21 @@ fn shrink_entities(visible_entities: &mut Vec) { } pub fn check_dir_light_mesh_visibility( - mut commands: Commands, mut directional_lights: Query< ( &DirectionalLight, &CascadesFrusta, &mut CascadesVisibleEntities, Option<&RenderLayers>, - &ViewVisibility, + &mut ViewVisibility, ), Without, >, - visible_entity_query: Query< + mut visible_entity_query: Query< ( Entity, &InheritedVisibility, + &mut ViewVisibility, Option<&RenderLayers>, Option<&Aabb>, Option<&GlobalTransform>, @@ -685,144 +682,113 @@ pub fn check_dir_light_mesh_visibility( ), >, visible_entity_ranges: Option>, - mut visible_entities_queue: Local>>, - mut view_visible_entities_queue: Local>>>, ) { let visible_entity_ranges = visible_entity_ranges.as_deref(); - directional_lights.iter_mut().for_each( - |( - directional_light, - frusta, - mut cascades_visible_entities, - maybe_view_mask, - light_view_visibility, - )| { - let mut views_to_remove = Vec::new(); - for (view, cascade_view_entities) in &mut cascades_visible_entities.entities { - match frusta.frusta.get(view) { - Some(view_frusta) => { - cascade_view_entities.resize(view_frusta.len(), Default::default()); - cascade_view_entities - .iter_mut() - .for_each(|x| x.entities.clear()); - } - None => views_to_remove.push(*view), - }; - } - for (view, frusta) in &frusta.frusta { - cascades_visible_entities - .entities - .entry(*view) - .or_insert_with(|| vec![VisibleEntities::default(); frusta.len()]); - } + // Directional lights + for (directional_light, frusta, mut visible_entities, maybe_view_mask, light_view_visibility) in + &mut directional_lights + { + // Re-use already allocated entries where possible. + let mut views_to_remove = Vec::new(); + for (view, cascade_view_entities) in &mut visible_entities.entities { + match frusta.frusta.get(view) { + Some(view_frusta) => { + cascade_view_entities.resize(view_frusta.len(), Default::default()); + cascade_view_entities + .iter_mut() + .for_each(|x| x.clear::()); + } + None => views_to_remove.push(*view), + }; + } + for (view, frusta) in &frusta.frusta { + visible_entities + .entities + .entry(*view) + .or_insert_with(|| vec![VisibleEntities::default(); frusta.len()]); + } + for v in views_to_remove { + visible_entities.entities.remove(&v); + } - for v in views_to_remove { - cascades_visible_entities.entities.remove(&v); - } + // NOTE: If shadow mapping is disabled for the light then it must have no visible entities + if !directional_light.shadows_enabled || !light_view_visibility.get() { + continue; + } - // NOTE: If shadow mapping is disabled for the light then it must have no visible entities - if !directional_light.shadows_enabled || !light_view_visibility.get() { - return; + let view_mask = maybe_view_mask.unwrap_or_default(); + + for ( + entity, + inherited_visibility, + mut view_visibility, + maybe_entity_mask, + maybe_aabb, + maybe_transform, + has_visibility_range, + ) in &mut visible_entity_query + { + if !inherited_visibility.get() { + continue; } - let view_mask = maybe_view_mask.unwrap_or_default(); - - for (view, view_frusta) in &frusta.frusta { - visible_entity_query.par_iter().for_each_init( - || { - let mut entities = view_visible_entities_queue.borrow_local_mut(); - let cap = entities.first().map(|v| v.capacity()).unwrap_or_default(); - entities.resize(view_frusta.len(), Vec::with_capacity(cap)); - (visible_entities_queue.borrow_local_mut(), entities) - }, - |(queue0, queue1), - ( - entity, - inherited_visibility, - maybe_entity_mask, - maybe_aabb, - maybe_transform, - has_visibility_range, - )| { - if !inherited_visibility.get() { - return; - } + let entity_mask = maybe_entity_mask.unwrap_or_default(); + if !view_mask.intersects(entity_mask) { + continue; + } - let entity_mask = maybe_entity_mask.unwrap_or_default(); - if !view_mask.intersects(entity_mask) { - return; - } + // If we have an aabb and transform, do frustum culling + if let (Some(aabb), Some(transform)) = (maybe_aabb, maybe_transform) { + for (view, view_frusta) in &frusta.frusta { + let view_visible_entities = visible_entities + .entities + .get_mut(view) + .expect("Per-view visible entities should have been inserted already"); + + // Check visibility ranges. + if has_visibility_range + && visible_entity_ranges.is_some_and(|visible_entity_ranges| { + !visible_entity_ranges.entity_is_in_range_of_view(entity, *view) + }) + { + continue; + } - // Check visibility ranges. - if has_visibility_range - && visible_entity_ranges.is_some_and(|visible_entity_ranges| { - !visible_entity_ranges.entity_is_in_range_of_view(entity, *view) - }) - { - return; + for (frustum, frustum_visible_entities) in + view_frusta.iter().zip(view_visible_entities) + { + // Disable near-plane culling, as a shadow caster could lie before the near plane. + if !frustum.intersects_obb(aabb, &transform.affine(), false, true) { + continue; } - if let (Some(aabb), Some(transform)) = (maybe_aabb, maybe_transform) { - let mut visible = false; - for (frustum, frustum_visible_entities) in - view_frusta.iter().zip(queue1.iter_mut()) - { - // Disable near-plane culling, as a shadow caster could lie before the near plane. - if !frustum.intersects_obb(aabb, &transform.affine(), false, true) { - continue; - } - visible = true; - - frustum_visible_entities.push(entity); - } - if visible { - queue0.push(entity); - } - } else { - queue0.push(entity); - for frustum_visible_entities in queue1.iter_mut() { - frustum_visible_entities.push(entity); - } - } - }, - ); - for entities in view_visible_entities_queue.iter_mut() { - cascades_visible_entities + view_visibility.set(); + frustum_visible_entities.get_mut::().push(entity); + } + } + } else { + view_visibility.set(); + for view in frusta.frusta.keys() { + let view_visible_entities = visible_entities .entities .get_mut(view) - .unwrap() - .iter_mut() - .map(|v| v.get_mut::()) - .zip(entities.iter_mut()) - .for_each(|(dst, source)| { - dst.append(source); - }); + .expect("Per-view visible entities should have been inserted already"); + + for frustum_visible_entities in view_visible_entities { + frustum_visible_entities.get_mut::().push(entity); + } } } - }, - ); + } - for (_, _, mut cascades_visible_entities, _, _) in &mut directional_lights { - for (_, cascade_view_entities) in &mut cascades_visible_entities.entities { + for (_, cascade_view_entities) in &mut visible_entities.entities { cascade_view_entities .iter_mut() .map(|x| x.get_mut::()) .for_each(shrink_entities); } } - - // TODO: use resource to avoid unnecessary memory alloc - let mut defer_queue = std::mem::take(visible_entities_queue.deref_mut()); - commands.add(move |world: &mut World| { - let mut query = world.query::<&mut ViewVisibility>(); - for entities in defer_queue.iter_mut() { - let mut iter = query.iter_many_mut(world, entities.iter()); - while let Some(mut t) = iter.fetch_next() { - t.set(); - } - } - }); } pub fn check_point_light_mesh_visibility( @@ -858,10 +824,9 @@ pub fn check_point_light_mesh_visibility( ), >, visible_entity_ranges: Option>, - mut cubmap_visible_entity_queue: Local; 6]>>, - mut spot_visible_entity_queue: Local>>, ) { let visible_entity_ranges = visible_entity_ranges.as_deref(); + for visible_lights in &visible_point_lights { for light_entity in visible_lights.entities.iter().copied() { // Point lights @@ -888,64 +853,57 @@ pub fn check_point_light_mesh_visibility( radius: point_light.range, }; - visible_entity_query.par_iter_mut().for_each_init( - || cubmap_visible_entity_queue.borrow_local_mut(), - |queue, - ( - entity, - inherited_visibility, - mut view_visibility, - maybe_entity_mask, - maybe_aabb, - maybe_transform, - has_visibility_range, - )| { - if !inherited_visibility.get() { - return; - } - let entity_mask = maybe_entity_mask.unwrap_or_default(); - if !view_mask.intersects(entity_mask) { - return; - } - if has_visibility_range - && visible_entity_ranges.is_some_and(|visible_entity_ranges| { - !visible_entity_ranges.entity_is_in_range_of_any_view(entity) - }) - { - return; - } + for ( + entity, + inherited_visibility, + mut view_visibility, + maybe_entity_mask, + maybe_aabb, + maybe_transform, + has_visibility_range, + ) in &mut visible_entity_query + { + if !inherited_visibility.get() { + continue; + } - // If we have an aabb and transform, do frustum culling - if let (Some(aabb), Some(transform)) = (maybe_aabb, maybe_transform) { - let model_to_world = transform.affine(); - // Do a cheap sphere vs obb test to prune out most meshes outside the sphere of the light - if !light_sphere.intersects_obb(aabb, &model_to_world) { - return; - } + let entity_mask = maybe_entity_mask.unwrap_or_default(); + if !view_mask.intersects(entity_mask) { + continue; + } - for (frustum, visible_entities) in - cubemap_frusta.iter().zip(queue.iter_mut()) - { - if frustum.intersects_obb(aabb, &model_to_world, true, true) { - view_visibility.set(); - visible_entities.push(entity); - } - } - } else { - view_visibility.set(); - for visible_entities in queue.iter_mut() { - visible_entities.push(entity); - } + // Check visibility ranges. + if has_visibility_range + && visible_entity_ranges.is_some_and(|visible_entity_ranges| { + !visible_entity_ranges.entity_is_in_range_of_any_view(entity) + }) + { + continue; + } + + // If we have an aabb and transform, do frustum culling + if let (Some(aabb), Some(transform)) = (maybe_aabb, maybe_transform) { + let model_to_world = transform.affine(); + // Do a cheap sphere vs obb test to prune out most meshes outside the sphere of the light + if !light_sphere.intersects_obb(aabb, &model_to_world) { + continue; } - }, - ); - for entities in cubmap_visible_entity_queue.iter_mut() { - cubemap_visible_entities - .iter_mut() - .map(|v| v.get_mut::()) - .zip(entities.iter_mut()) - .for_each(|(dst, source)| dst.append(source)); + for (frustum, visible_entities) in cubemap_frusta + .iter() + .zip(cubemap_visible_entities.iter_mut()) + { + if frustum.intersects_obb(aabb, &model_to_world, true, true) { + view_visibility.set(); + visible_entities.push::(entity); + } + } + } else { + view_visibility.set(); + for visible_entities in cubemap_visible_entities.iter_mut() { + visible_entities.push::(entity); + } + } } for visible_entities in cubemap_visible_entities.iter_mut() { @@ -970,55 +928,50 @@ pub fn check_point_light_mesh_visibility( radius: point_light.range, }; - visible_entity_query.par_iter_mut().for_each_init( - || spot_visible_entity_queue.borrow_local_mut(), - |queue, - ( - entity, - inherited_visibility, - mut view_visibility, - maybe_entity_mask, - maybe_aabb, - maybe_transform, - has_visibility_range, - )| { - if !inherited_visibility.get() { - return; - } + for ( + entity, + inherited_visibility, + mut view_visibility, + maybe_entity_mask, + maybe_aabb, + maybe_transform, + has_visibility_range, + ) in &mut visible_entity_query + { + if !inherited_visibility.get() { + continue; + } - let entity_mask = maybe_entity_mask.unwrap_or_default(); - if !view_mask.intersects(entity_mask) { - return; - } - // Check visibility ranges. - if has_visibility_range - && visible_entity_ranges.is_some_and(|visible_entity_ranges| { - !visible_entity_ranges.entity_is_in_range_of_any_view(entity) - }) - { - return; - } + let entity_mask = maybe_entity_mask.unwrap_or_default(); + if !view_mask.intersects(entity_mask) { + continue; + } - if let (Some(aabb), Some(transform)) = (maybe_aabb, maybe_transform) { - let model_to_world = transform.affine(); - // Do a cheap sphere vs obb test to prune out most meshes outside the sphere of the light - if !light_sphere.intersects_obb(aabb, &model_to_world) { - return; - } + // Check visibility ranges. + if has_visibility_range + && visible_entity_ranges.is_some_and(|visible_entity_ranges| { + !visible_entity_ranges.entity_is_in_range_of_any_view(entity) + }) + { + continue; + } - if frustum.intersects_obb(aabb, &model_to_world, true, true) { - view_visibility.set(); - queue.push(entity); - } - } else { - view_visibility.set(); - queue.push(entity); + // If we have an aabb and transform, do frustum culling + if let (Some(aabb), Some(transform)) = (maybe_aabb, maybe_transform) { + let model_to_world = transform.affine(); + // Do a cheap sphere vs obb test to prune out most meshes outside the sphere of the light + if !light_sphere.intersects_obb(aabb, &model_to_world) { + continue; } - }, - ); - for entities in spot_visible_entity_queue.iter_mut() { - visible_entities.get_mut::().append(entities); + if frustum.intersects_obb(aabb, &model_to_world, true, true) { + view_visibility.set(); + visible_entities.push::(entity); + } + } else { + view_visibility.set(); + visible_entities.push::(entity); + } } shrink_entities(visible_entities.get_mut::());