Remove entities from specialization caches when despawned. (#18627)

# Objective

Fixes #17872 

## Solution

This should have basically no impact on static scenes. We can optimize
more later if anything comes up. Needing to iterate the two level bin is
a bit unfortunate but shouldn't matter for apps that use a single
camera.
This commit is contained in:
charlotte 2025-03-31 11:15:11 -07:00 committed by GitHub
parent bb87bd4d02
commit 592822b702
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 66 additions and 14 deletions

View File

@ -34,6 +34,7 @@ use bevy_platform_support::collections::{HashMap, HashSet};
use bevy_platform_support::hash::FixedHasher;
use bevy_reflect::std_traits::ReflectDefault;
use bevy_reflect::Reflect;
use bevy_render::camera::extract_cameras;
use bevy_render::mesh::mark_3d_meshes_as_changed_if_their_assets_changed;
use bevy_render::render_asset::prepare_assets;
use bevy_render::renderer::RenderQueue;
@ -315,7 +316,7 @@ where
ExtractSchedule,
(
extract_mesh_materials::<M>.before(ExtractMeshesSet),
extract_entities_needs_specialization::<M>,
extract_entities_needs_specialization::<M>.after(extract_cameras),
),
)
.add_systems(
@ -707,6 +708,15 @@ fn extract_mesh_materials<M: Material>(
pub fn extract_entities_needs_specialization<M>(
entities_needing_specialization: Extract<Res<EntitiesNeedingSpecialization<M>>>,
mut entity_specialization_ticks: ResMut<EntitySpecializationTicks<M>>,
mut removed_mesh_material_components: Extract<RemovedComponents<MeshMaterial3d<M>>>,
mut specialized_material_pipeline_cache: ResMut<SpecializedMaterialPipelineCache<M>>,
mut specialized_prepass_material_pipeline_cache: Option<
ResMut<SpecializedPrepassMaterialPipelineCache<M>>,
>,
mut specialized_shadow_material_pipeline_cache: Option<
ResMut<SpecializedShadowMaterialPipelineCache<M>>,
>,
views: Query<&ExtractedView>,
ticks: SystemChangeTick,
) where
M: Material,
@ -715,6 +725,29 @@ pub fn extract_entities_needs_specialization<M>(
// Update the entity's specialization tick with this run's tick
entity_specialization_ticks.insert((*entity).into(), ticks.this_run());
}
// Clean up any despawned entities
for entity in removed_mesh_material_components.read() {
entity_specialization_ticks.remove(&MainEntity::from(entity));
for view in views {
if let Some(cache) =
specialized_material_pipeline_cache.get_mut(&view.retained_view_entity)
{
cache.remove(&MainEntity::from(entity));
}
if let Some(cache) = specialized_prepass_material_pipeline_cache
.as_mut()
.and_then(|c| c.get_mut(&view.retained_view_entity))
{
cache.remove(&MainEntity::from(entity));
}
if let Some(cache) = specialized_shadow_material_pipeline_cache
.as_mut()
.and_then(|c| c.get_mut(&view.retained_view_entity))
{
cache.remove(&MainEntity::from(entity));
}
}
}
}
#[derive(Resource, Deref, DerefMut, Clone, Debug)]
@ -789,12 +822,15 @@ impl<M> Default for SpecializedMaterialViewPipelineCache<M> {
pub fn check_entities_needing_specialization<M>(
needs_specialization: Query<
Entity,
Or<(
Changed<Mesh3d>,
AssetChanged<Mesh3d>,
Changed<MeshMaterial3d<M>>,
AssetChanged<MeshMaterial3d<M>>,
)>,
(
Or<(
Changed<Mesh3d>,
AssetChanged<Mesh3d>,
Changed<MeshMaterial3d<M>>,
AssetChanged<MeshMaterial3d<M>>,
)>,
With<MeshMaterial3d<M>>,
),
>,
mut entities_needing_specialization: ResMut<EntitiesNeedingSpecialization<M>>,
) where

View File

@ -21,6 +21,7 @@ use bevy_ecs::{
use bevy_math::FloatOrd;
use bevy_platform_support::collections::HashMap;
use bevy_reflect::{prelude::ReflectDefault, Reflect};
use bevy_render::camera::extract_cameras;
use bevy_render::render_phase::{DrawFunctionId, InputUniformIndex};
use bevy_render::render_resource::CachedRenderPipelineId;
use bevy_render::view::RenderVisibleEntities;
@ -286,7 +287,7 @@ where
.add_systems(
ExtractSchedule,
(
extract_entities_needs_specialization::<M>,
extract_entities_needs_specialization::<M>.after(extract_cameras),
extract_mesh_materials_2d::<M>,
),
)
@ -555,6 +556,9 @@ pub const fn tonemapping_pipeline_key(tonemapping: Tonemapping) -> Mesh2dPipelin
pub fn extract_entities_needs_specialization<M>(
entities_needing_specialization: Extract<Res<EntitiesNeedingSpecialization<M>>>,
mut entity_specialization_ticks: ResMut<EntitySpecializationTicks<M>>,
mut removed_mesh_material_components: Extract<RemovedComponents<MeshMaterial2d<M>>>,
mut specialized_material2d_pipeline_cache: ResMut<SpecializedMaterial2dPipelineCache<M>>,
views: Query<&MainEntity, With<ExtractedView>>,
ticks: SystemChangeTick,
) where
M: Material2d,
@ -563,6 +567,15 @@ pub fn extract_entities_needs_specialization<M>(
// Update the entity's specialization tick with this run's tick
entity_specialization_ticks.insert((*entity).into(), ticks.this_run());
}
// Clean up any despawned entities
for entity in removed_mesh_material_components.read() {
entity_specialization_ticks.remove(&MainEntity::from(entity));
for view in views {
if let Some(cache) = specialized_material2d_pipeline_cache.get_mut(view) {
cache.remove(&MainEntity::from(entity));
}
}
}
}
#[derive(Clone, Resource, Deref, DerefMut, Debug)]
@ -637,12 +650,15 @@ impl<M> Default for SpecializedMaterial2dViewPipelineCache<M> {
pub fn check_entities_needing_specialization<M>(
needs_specialization: Query<
Entity,
Or<(
Changed<Mesh2d>,
AssetChanged<Mesh2d>,
Changed<MeshMaterial2d<M>>,
AssetChanged<MeshMaterial2d<M>>,
)>,
(
Or<(
Changed<Mesh2d>,
AssetChanged<Mesh2d>,
Changed<MeshMaterial2d<M>>,
AssetChanged<MeshMaterial2d<M>>,
)>,
With<MeshMaterial2d<M>>,
),
>,
mut entities_needing_specialization: ResMut<EntitiesNeedingSpecialization<M>>,
) where