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::());