Lighting Should Only hold Vec<Entity> instead of TypeId<Vec<Entity>> (#14073)

# Objective
- After #13894, I noticed the performance of `many_lights `dropped from
120+ to 60+. I reviewed the PR but couldn't identify any mistakes. After
profiling, I discovered that `Hashmap::Clone `was very slow when its not
empty, causing `extract_light` to increase from 3ms to 8ms.
- Lighting only checks visibility for 3D Meshes. We don't need to
maintain a TypeIdMap for this, as it not only impacts performance
negatively but also reduces ergonomics.

## Solution

- use VisibleMeshEntities for lighint visibility checking.


## Performance
cargo run --release --example many_lights  --features bevy/trace_tracy 
name="bevy_pbr::light::check_point_light_mesh_visibility"}

![image](https://github.com/bevyengine/bevy/assets/45868716/8bad061a-f936-45a0-9bb9-4fbdaceec08b)

system{name="bevy_pbr::render::light::extract_lights"}

![image](https://github.com/bevyengine/bevy/assets/45868716/ca75b46c-b4ad-45d3-8c8d-66442447b753)


## Migration Guide

> now `SpotLightBundle` , `CascadesVisibleEntities `and
`CubemapVisibleEntities `use VisibleMeshEntities instead of
`VisibleEntities`

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
This commit is contained in:
re0312 2024-07-15 01:00:54 +08:00 committed by GitHub
parent e13c72d8a4
commit 36c6f29832
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 36 additions and 30 deletions

View file

@ -3,13 +3,14 @@ use crate::{
StandardMaterial,
};
use bevy_asset::Handle;
use bevy_ecs::entity::EntityHashMap;
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::entity::{Entity, EntityHashMap};
use bevy_ecs::{bundle::Bundle, component::Component, reflect::ReflectComponent};
use bevy_reflect::Reflect;
use bevy_render::{
mesh::Mesh,
primitives::{CascadesFrusta, CubemapFrusta, Frustum},
view::{InheritedVisibility, ViewVisibility, Visibility, VisibleEntities},
view::{InheritedVisibility, ViewVisibility, Visibility},
};
use bevy_transform::components::{GlobalTransform, Transform};
@ -45,27 +46,37 @@ impl<M: Material> Default for MaterialMeshBundle<M> {
}
}
/// Collection of mesh entities visible for 3D lighting.
/// This component contains all mesh entities visible from the current light view.
/// The collection is updated automatically by [`crate::SimulationLightSystems`].
#[derive(Component, Clone, Debug, Default, Reflect, Deref, DerefMut)]
#[reflect(Component)]
pub struct VisibleMeshEntities {
#[reflect(ignore)]
pub entities: Vec<Entity>,
}
#[derive(Component, Clone, Debug, Default, Reflect)]
#[reflect(Component)]
pub struct CubemapVisibleEntities {
#[reflect(ignore)]
data: [VisibleEntities; 6],
data: [VisibleMeshEntities; 6],
}
impl CubemapVisibleEntities {
pub fn get(&self, i: usize) -> &VisibleEntities {
pub fn get(&self, i: usize) -> &VisibleMeshEntities {
&self.data[i]
}
pub fn get_mut(&mut self, i: usize) -> &mut VisibleEntities {
pub fn get_mut(&mut self, i: usize) -> &mut VisibleMeshEntities {
&mut self.data[i]
}
pub fn iter(&self) -> impl DoubleEndedIterator<Item = &VisibleEntities> {
pub fn iter(&self) -> impl DoubleEndedIterator<Item = &VisibleMeshEntities> {
self.data.iter()
}
pub fn iter_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut VisibleEntities> {
pub fn iter_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut VisibleMeshEntities> {
self.data.iter_mut()
}
}
@ -75,7 +86,7 @@ impl CubemapVisibleEntities {
pub struct CascadesVisibleEntities {
/// Map of view entity to the visible entities for each cascade frustum.
#[reflect(ignore)]
pub entities: EntityHashMap<Vec<VisibleEntities>>,
pub entities: EntityHashMap<Vec<VisibleMeshEntities>>,
}
/// A component bundle for [`PointLight`] entities.
@ -98,7 +109,7 @@ pub struct PointLightBundle {
#[derive(Debug, Bundle, Default, Clone)]
pub struct SpotLightBundle {
pub spot_light: SpotLight,
pub visible_entities: VisibleEntities,
pub visible_entities: VisibleMeshEntities,
pub frustum: Frustum,
pub transform: Transform,
pub global_transform: GlobalTransform,

View file

@ -11,8 +11,7 @@ use bevy_render::{
mesh::Mesh,
primitives::{Aabb, CascadesFrusta, CubemapFrusta, Frustum, Sphere},
view::{
InheritedVisibility, RenderLayers, ViewVisibility, VisibilityRange, VisibleEntities,
VisibleEntityRanges, WithMesh,
InheritedVisibility, RenderLayers, ViewVisibility, VisibilityRange, VisibleEntityRanges,
},
};
use bevy_transform::components::{GlobalTransform, Transform};
@ -100,7 +99,7 @@ impl Default for PointLightShadowMap {
}
/// A convenient alias for `Or<(With<PointLight>, With<SpotLight>,
/// With<DirectionalLight>)>`, for use with [`VisibleEntities`].
/// With<DirectionalLight>)>`, for use with [`bevy_render::view::VisibleEntities`].
pub type WithLight = Or<(With<PointLight>, With<SpotLight>, With<DirectionalLight>)>;
/// Controls the resolution of [`DirectionalLight`] shadow maps.
@ -698,9 +697,7 @@ pub fn check_dir_light_mesh_visibility(
match frusta.frusta.get(view) {
Some(view_frusta) => {
cascade_view_entities.resize(view_frusta.len(), Default::default());
cascade_view_entities
.iter_mut()
.for_each(VisibleEntities::clear::<WithMesh>);
cascade_view_entities.iter_mut().for_each(|x| x.clear());
}
None => views_to_remove.push(*view),
};
@ -709,7 +706,7 @@ pub fn check_dir_light_mesh_visibility(
visible_entities
.entities
.entry(*view)
.or_insert_with(|| vec![VisibleEntities::default(); frusta.len()]);
.or_insert_with(|| vec![VisibleMeshEntities::default(); frusta.len()]);
}
for v in views_to_remove {
@ -790,7 +787,6 @@ pub fn check_dir_light_mesh_visibility(
.get_mut(view)
.unwrap()
.iter_mut()
.map(VisibleEntities::get_mut::<WithMesh>)
.zip(entities.iter_mut())
.for_each(|(dst, source)| {
dst.append(source);
@ -801,7 +797,7 @@ pub fn check_dir_light_mesh_visibility(
for (_, cascade_view_entities) in &mut visible_entities.entities {
cascade_view_entities
.iter_mut()
.map(VisibleEntities::get_mut::<WithMesh>)
.map(DerefMut::deref_mut)
.for_each(shrink_entities);
}
}
@ -833,7 +829,7 @@ pub fn check_point_light_mesh_visibility(
&SpotLight,
&GlobalTransform,
&Frustum,
&mut VisibleEntities,
&mut VisibleMeshEntities,
Option<&RenderLayers>,
)>,
mut visible_entity_query: Query<
@ -869,7 +865,7 @@ pub fn check_point_light_mesh_visibility(
)) = point_lights.get_mut(light_entity)
{
for visible_entities in cubemap_visible_entities.iter_mut() {
visible_entities.clear::<WithMesh>();
visible_entities.entities.clear();
}
// NOTE: If shadow mapping is disabled for the light then it must have no visible entities
@ -940,13 +936,12 @@ pub fn check_point_light_mesh_visibility(
for entities in cubemap_visible_entities_queue.iter_mut() {
cubemap_visible_entities
.iter_mut()
.map(VisibleEntities::get_mut::<WithMesh>)
.zip(entities.iter_mut())
.for_each(|(dst, source)| dst.append(source));
.for_each(|(dst, source)| dst.entities.append(source));
}
for visible_entities in cubemap_visible_entities.iter_mut() {
shrink_entities(visible_entities.get_mut::<WithMesh>());
shrink_entities(visible_entities);
}
}
@ -954,7 +949,7 @@ pub fn check_point_light_mesh_visibility(
if let Ok((point_light, transform, frustum, mut visible_entities, maybe_view_mask)) =
spot_lights.get_mut(light_entity)
{
visible_entities.clear::<WithMesh>();
visible_entities.clear();
// NOTE: If shadow mapping is disabled for the light then it must have no visible entities
if !point_light.shadows_enabled {
@ -1015,10 +1010,10 @@ pub fn check_point_light_mesh_visibility(
);
for entities in spot_visible_entities_queue.iter_mut() {
visible_entities.get_mut::<WithMesh>().append(entities);
visible_entities.append(entities);
}
shrink_entities(visible_entities.get_mut::<WithMesh>());
shrink_entities(visible_entities.deref_mut());
}
}
}

View file

@ -15,7 +15,7 @@ use bevy_render::{
render_resource::*,
renderer::{RenderContext, RenderDevice, RenderQueue},
texture::*,
view::{ExtractedView, RenderLayers, ViewVisibility, VisibleEntities, WithMesh},
view::{ExtractedView, RenderLayers, ViewVisibility},
Extract,
};
use bevy_transform::{components::GlobalTransform, prelude::Transform};
@ -186,7 +186,7 @@ pub fn extract_lights(
spot_lights: Extract<
Query<(
&SpotLight,
&VisibleEntities,
&VisibleMeshEntities,
&GlobalTransform,
&ViewVisibility,
&Frustum,
@ -1174,7 +1174,7 @@ pub fn queue_shadows<M: Material>(
mut view_light_entities: Query<&LightEntity>,
point_light_entities: Query<&CubemapVisibleEntities, With<ExtractedPointLight>>,
directional_light_entities: Query<&CascadesVisibleEntities, With<ExtractedDirectionalLight>>,
spot_light_entities: Query<&VisibleEntities, With<ExtractedPointLight>>,
spot_light_entities: Query<&VisibleMeshEntities, With<ExtractedPointLight>>,
) where
M::Data: PartialEq + Eq + Hash + Clone,
{
@ -1218,7 +1218,7 @@ pub fn queue_shadows<M: Material>(
// NOTE: Lights with shadow mapping disabled will have no visible entities
// so no meshes will be queued
for entity in visible_entities.iter::<WithMesh>().copied() {
for entity in visible_entities.iter().copied() {
let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(entity)
else {
continue;