diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index 7c6d93ec32..944bd764e4 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -19,7 +19,6 @@ use bevy_core_pipeline::{ }; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::component::Tick; -use bevy_ecs::entity::EntityHash; use bevy_ecs::system::SystemChangeTick; use bevy_ecs::{ prelude::*, @@ -28,7 +27,8 @@ use bevy_ecs::{ SystemParamItem, }, }; -use bevy_platform_support::collections::HashMap; +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::mesh::mark_3d_meshes_as_changed_if_their_assets_changed; @@ -41,7 +41,7 @@ use bevy_render::{ render_resource::*, renderer::RenderDevice, sync_world::MainEntity, - view::{ExtractedView, Msaa, RenderVisibilityRanges, ViewVisibility}, + view::{ExtractedView, Msaa, RenderVisibilityRanges, RetainedViewEntity, ViewVisibility}, Extract, }; use bevy_render::{mesh::allocator::MeshAllocator, sync_world::MainEntityHashMap}; @@ -735,11 +735,22 @@ impl Default for EntitySpecializationTicks { } } +/// Stores the [`SpecializedMaterialViewPipelineCache`] for each view. #[derive(Resource, Deref, DerefMut)] pub struct SpecializedMaterialPipelineCache { - // (view_entity, material_entity) -> (tick, pipeline_id) + // view entity -> view pipeline cache #[deref] - map: HashMap<(MainEntity, MainEntity), (Tick, CachedRenderPipelineId), EntityHash>, + map: HashMap>, + marker: PhantomData, +} + +/// Stores the cached render pipeline ID for each entity in a single view, as +/// well as the last time it was changed. +#[derive(Deref, DerefMut)] +pub struct SpecializedMaterialViewPipelineCache { + // material entity -> (tick, pipeline_id) + #[deref] + map: MainEntityHashMap<(Tick, CachedRenderPipelineId)>, marker: PhantomData, } @@ -752,6 +763,15 @@ impl Default for SpecializedMaterialPipelineCache { } } +impl Default for SpecializedMaterialViewPipelineCache { + fn default() -> Self { + Self { + map: MainEntityHashMap::default(), + marker: PhantomData, + } + } +} + pub fn check_entities_needing_specialization( needs_specialization: Query< Entity, @@ -792,7 +812,7 @@ pub fn specialize_material_meshes( Res>, Res>, ), - views: Query<(&MainEntity, &ExtractedView, &RenderVisibleEntities)>, + views: Query<(&ExtractedView, &RenderVisibleEntities)>, view_key_cache: Res, entity_specialization_ticks: Res>, view_specialization_ticks: Res, @@ -804,7 +824,13 @@ pub fn specialize_material_meshes( ) where M::Data: PartialEq + Eq + Hash + Clone, { - for (view_entity, view, visible_entities) in &views { + // Record the retained IDs of all shadow views so that we can expire old + // pipeline IDs. + let mut all_views: HashSet = HashSet::default(); + + for (view, visible_entities) in &views { + all_views.insert(view.retained_view_entity); + if !transparent_render_phases.contains_key(&view.retained_view_entity) && !opaque_render_phases.contains_key(&view.retained_view_entity) && !alpha_mask_render_phases.contains_key(&view.retained_view_entity) @@ -813,15 +839,21 @@ pub fn specialize_material_meshes( continue; } - let Some(view_key) = view_key_cache.get(view_entity) else { + let Some(view_key) = view_key_cache.get(&view.retained_view_entity) else { continue; }; + let view_tick = view_specialization_ticks + .get(&view.retained_view_entity) + .unwrap(); + let view_specialized_material_pipeline_cache = specialized_material_pipeline_cache + .entry(view.retained_view_entity) + .or_default(); + for (_, visible_entity) in visible_entities.iter::() { - let view_tick = view_specialization_ticks.get(view_entity).unwrap(); let entity_tick = entity_specialization_ticks.get(visible_entity).unwrap(); - let last_specialized_tick = specialized_material_pipeline_cache - .get(&(*view_entity, *visible_entity)) + let last_specialized_tick = view_specialized_material_pipeline_cache + .get(visible_entity) .map(|(tick, _)| *tick); let needs_specialization = last_specialized_tick.is_none_or(|tick| { view_tick.is_newer_than(tick, ticks.this_run()) @@ -901,12 +933,14 @@ pub fn specialize_material_meshes( } }; - specialized_material_pipeline_cache.insert( - (*view_entity, *visible_entity), - (ticks.this_run(), pipeline_id), - ); + view_specialized_material_pipeline_cache + .insert(*visible_entity, (ticks.this_run(), pipeline_id)); } } + + // Delete specialized pipelines belonging to views that have expired. + specialized_material_pipeline_cache + .retain(|retained_view_entity, _| all_views.contains(retained_view_entity)); } /// For each view, iterates over all the meshes visible from that view and adds @@ -921,12 +955,12 @@ pub fn queue_material_meshes( mut alpha_mask_render_phases: ResMut>, mut transmissive_render_phases: ResMut>, mut transparent_render_phases: ResMut>, - views: Query<(&MainEntity, &ExtractedView, &RenderVisibleEntities)>, + views: Query<(&ExtractedView, &RenderVisibleEntities)>, specialized_material_pipeline_cache: ResMut>, ) where M::Data: PartialEq + Eq + Hash + Clone, { - for (view_entity, view, visible_entities) in &views { + for (view, visible_entities) in &views { let ( Some(opaque_phase), Some(alpha_mask_phase), @@ -942,10 +976,16 @@ pub fn queue_material_meshes( continue; }; + let Some(view_specialized_material_pipeline_cache) = + specialized_material_pipeline_cache.get(&view.retained_view_entity) + else { + continue; + }; + let rangefinder = view.rangefinder3d(); for (render_entity, visible_entity) in visible_entities.iter::() { - let Some((current_change_tick, pipeline_id)) = specialized_material_pipeline_cache - .get(&(*view_entity, *visible_entity)) + let Some((current_change_tick, pipeline_id)) = view_specialized_material_pipeline_cache + .get(visible_entity) .map(|(current_change_tick, pipeline_id)| (*current_change_tick, *pipeline_id)) else { continue; diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index 4885238e10..c8902145c8 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -18,7 +18,7 @@ use bevy_render::{ render_resource::binding_types::uniform_buffer, renderer::RenderAdapter, sync_world::RenderEntity, - view::{RenderVisibilityRanges, VISIBILITY_RANGES_STORAGE_BUFFER_COUNT}, + view::{RenderVisibilityRanges, RetainedViewEntity, VISIBILITY_RANGES_STORAGE_BUFFER_COUNT}, ExtractSchedule, Render, RenderApp, RenderDebugFlags, RenderSet, }; pub use prepass_bindings::*; @@ -56,10 +56,9 @@ use crate::meshlet::{ use bevy_derive::{Deref, DerefMut}; use bevy_ecs::component::Tick; -use bevy_ecs::entity::EntityHash; use bevy_ecs::system::SystemChangeTick; use bevy_platform_support::collections::HashMap; -use bevy_render::sync_world::{MainEntity, MainEntityHashMap}; +use bevy_render::sync_world::MainEntityHashMap; use bevy_render::view::RenderVisibleEntities; use bevy_render::RenderSet::{PrepareAssets, PrepareResources}; use core::{hash::Hash, marker::PhantomData}; @@ -807,11 +806,22 @@ pub fn prepare_prepass_view_bind_group( } } +/// Stores the [`SpecializedPrepassMaterialViewPipelineCache`] for each view. #[derive(Resource, Deref, DerefMut)] pub struct SpecializedPrepassMaterialPipelineCache { - // (view_entity, material_entity) -> (tick, pipeline_id) + // view_entity -> view pipeline cache #[deref] - map: HashMap<(MainEntity, MainEntity), (Tick, CachedRenderPipelineId), EntityHash>, + map: HashMap>, + marker: PhantomData, +} + +/// Stores the cached render pipeline ID for each entity in a single view, as +/// well as the last time it was changed. +#[derive(Deref, DerefMut)] +pub struct SpecializedPrepassMaterialViewPipelineCache { + // material entity -> (tick, pipeline_id) + #[deref] + map: MainEntityHashMap<(Tick, CachedRenderPipelineId)>, marker: PhantomData, } @@ -824,17 +834,26 @@ impl Default for SpecializedPrepassMaterialPipelineCache { } } -#[derive(Resource, Deref, DerefMut, Default, Clone)] -pub struct ViewKeyPrepassCache(MainEntityHashMap); +impl Default for SpecializedPrepassMaterialViewPipelineCache { + fn default() -> Self { + Self { + map: HashMap::default(), + marker: PhantomData, + } + } +} #[derive(Resource, Deref, DerefMut, Default, Clone)] -pub struct ViewPrepassSpecializationTicks(MainEntityHashMap); +pub struct ViewKeyPrepassCache(HashMap); + +#[derive(Resource, Deref, DerefMut, Default, Clone)] +pub struct ViewPrepassSpecializationTicks(HashMap); pub fn check_prepass_views_need_specialization( mut view_key_cache: ResMut, mut view_specialization_ticks: ResMut, mut views: Query<( - &MainEntity, + &ExtractedView, &Msaa, Option<&DepthPrepass>, Option<&NormalPrepass>, @@ -842,9 +861,7 @@ pub fn check_prepass_views_need_specialization( )>, ticks: SystemChangeTick, ) { - for (view_entity, msaa, depth_prepass, normal_prepass, motion_vector_prepass) in - views.iter_mut() - { + for (view, msaa, depth_prepass, normal_prepass, motion_vector_prepass) in views.iter_mut() { let mut view_key = MeshPipelineKey::from_msaa_samples(msaa.samples()); if depth_prepass.is_some() { view_key |= MeshPipelineKey::DEPTH_PREPASS; @@ -856,14 +873,14 @@ pub fn check_prepass_views_need_specialization( view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS; } - if let Some(current_key) = view_key_cache.get_mut(view_entity) { + if let Some(current_key) = view_key_cache.get_mut(&view.retained_view_entity) { if *current_key != view_key { - view_key_cache.insert(*view_entity, view_key); - view_specialization_ticks.insert(*view_entity, ticks.this_run()); + view_key_cache.insert(view.retained_view_entity, view_key); + view_specialization_ticks.insert(view.retained_view_entity, ticks.this_run()); } } else { - view_key_cache.insert(*view_entity, view_key); - view_specialization_ticks.insert(*view_entity, ticks.this_run()); + view_key_cache.insert(view.retained_view_entity, view_key); + view_specialization_ticks.insert(view.retained_view_entity, ticks.this_run()); } } } @@ -878,7 +895,6 @@ pub fn specialize_prepass_material_meshes( material_bind_group_allocator: Res>, view_key_cache: Res, views: Query<( - &MainEntity, &ExtractedView, &RenderVisibleEntities, &Msaa, @@ -917,14 +933,7 @@ pub fn specialize_prepass_material_meshes( M: Material, M::Data: PartialEq + Eq + Hash + Clone, { - for ( - view_entity, - extracted_view, - visible_entities, - msaa, - motion_vector_prepass, - deferred_prepass, - ) in &views + for (extracted_view, visible_entities, msaa, motion_vector_prepass, deferred_prepass) in &views { if !opaque_deferred_render_phases.contains_key(&extracted_view.retained_view_entity) && !alpha_mask_deferred_render_phases.contains_key(&extracted_view.retained_view_entity) @@ -934,15 +943,21 @@ pub fn specialize_prepass_material_meshes( continue; } - let Some(view_key) = view_key_cache.get(view_entity) else { + let Some(view_key) = view_key_cache.get(&extracted_view.retained_view_entity) else { continue; }; + let view_tick = view_specialization_ticks + .get(&extracted_view.retained_view_entity) + .unwrap(); + let view_specialized_material_pipeline_cache = specialized_material_pipeline_cache + .entry(extracted_view.retained_view_entity) + .or_default(); + for (_, visible_entity) in visible_entities.iter::() { - let view_tick = view_specialization_ticks.get(view_entity).unwrap(); let entity_tick = entity_specialization_ticks.get(visible_entity).unwrap(); - let last_specialized_tick = specialized_material_pipeline_cache - .get(&(*view_entity, *visible_entity)) + let last_specialized_tick = view_specialized_material_pipeline_cache + .get(visible_entity) .map(|(tick, _)| *tick); let needs_specialization = last_specialized_tick.is_none_or(|tick| { view_tick.is_newer_than(tick, ticks.this_run()) @@ -1054,10 +1069,8 @@ pub fn specialize_prepass_material_meshes( } }; - specialized_material_pipeline_cache.insert( - (*view_entity, *visible_entity), - (ticks.this_run(), pipeline_id), - ); + view_specialized_material_pipeline_cache + .insert(*visible_entity, (ticks.this_run(), pipeline_id)); } } } @@ -1072,12 +1085,12 @@ pub fn queue_prepass_material_meshes( mut alpha_mask_prepass_render_phases: ResMut>, mut opaque_deferred_render_phases: ResMut>, mut alpha_mask_deferred_render_phases: ResMut>, - views: Query<(&MainEntity, &ExtractedView, &RenderVisibleEntities)>, + views: Query<(&ExtractedView, &RenderVisibleEntities)>, specialized_material_pipeline_cache: Res>, ) where M::Data: PartialEq + Eq + Hash + Clone, { - for (view_entity, extracted_view, visible_entities) in &views { + for (extracted_view, visible_entities) in &views { let ( mut opaque_phase, mut alpha_mask_phase, @@ -1090,6 +1103,12 @@ pub fn queue_prepass_material_meshes( alpha_mask_deferred_render_phases.get_mut(&extracted_view.retained_view_entity), ); + let Some(view_specialized_material_pipeline_cache) = + specialized_material_pipeline_cache.get(&extracted_view.retained_view_entity) + else { + continue; + }; + // Skip if there's no place to put the mesh. if opaque_phase.is_none() && alpha_mask_phase.is_none() @@ -1101,7 +1120,7 @@ pub fn queue_prepass_material_meshes( for (render_entity, visible_entity) in visible_entities.iter::() { let Some((current_change_tick, pipeline_id)) = - specialized_material_pipeline_cache.get(&(*view_entity, *visible_entity)) + view_specialized_material_pipeline_cache.get(visible_entity) else { continue; }; diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 80347f0d1b..6b00636523 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -14,6 +14,8 @@ use bevy_ecs::{ }; use bevy_math::{ops, Mat4, UVec4, Vec2, Vec3, Vec3Swizzles, Vec4, Vec4Swizzles}; use bevy_platform_support::collections::{HashMap, HashSet}; +use bevy_platform_support::hash::FixedHasher; +use bevy_render::sync_world::MainEntityHashMap; use bevy_render::{ batching::gpu_preprocessing::{GpuPreprocessingMode, GpuPreprocessingSupport}, camera::SortedCameras, @@ -1613,9 +1615,16 @@ pub struct LightSpecializationTicks(HashMap); #[derive(Resource, Deref, DerefMut)] pub struct SpecializedShadowMaterialPipelineCache { - // (view_light_entity, visible_entity) -> (tick, pipeline_id) + // view light entity -> view pipeline cache #[deref] - map: HashMap<(RetainedViewEntity, MainEntity), (Tick, CachedRenderPipelineId)>, + map: HashMap>, + marker: PhantomData, +} + +#[derive(Deref, DerefMut)] +pub struct SpecializedShadowMaterialViewPipelineCache { + #[deref] + map: MainEntityHashMap<(Tick, CachedRenderPipelineId)>, marker: PhantomData, } @@ -1628,6 +1637,15 @@ impl Default for SpecializedShadowMaterialPipelineCache { } } +impl Default for SpecializedShadowMaterialViewPipelineCache { + fn default() -> Self { + Self { + map: MainEntityHashMap::default(), + marker: PhantomData, + } + } +} + pub fn check_views_lights_need_specialization( view_lights: Query<&ViewLightEntities, With>, view_light_entities: Query<(&LightEntity, &ExtractedView)>, @@ -1702,6 +1720,10 @@ pub fn specialize_shadows( ) where M::Data: PartialEq + Eq + Hash + Clone, { + // Record the retained IDs of all shadow views so that we can expire old + // pipeline IDs. + let mut all_shadow_views: HashSet = HashSet::default(); + for (entity, view_lights) in &view_lights { for view_light_entity in view_lights.lights.iter().copied() { let Ok((light_entity, extracted_view_light)) = @@ -1709,6 +1731,9 @@ pub fn specialize_shadows( else { continue; }; + + all_shadow_views.insert(extracted_view_light.retained_view_entity); + if !shadow_render_phases.contains_key(&extracted_view_light.retained_view_entity) { continue; } @@ -1744,13 +1769,17 @@ pub fn specialize_shadows( // NOTE: Lights with shadow mapping disabled will have no visible entities // so no meshes will be queued + let view_tick = light_specialization_ticks + .get(&extracted_view_light.retained_view_entity) + .unwrap(); + let view_specialized_material_pipeline_cache = specialized_material_pipeline_cache + .entry(extracted_view_light.retained_view_entity) + .or_default(); + for (_, visible_entity) in visible_entities.iter().copied() { - let view_tick = light_specialization_ticks - .get(&extracted_view_light.retained_view_entity) - .unwrap(); let entity_tick = entity_specialization_ticks.get(&visible_entity).unwrap(); - let last_specialized_tick = specialized_material_pipeline_cache - .get(&(extracted_view_light.retained_view_entity, visible_entity)) + let last_specialized_tick = view_specialized_material_pipeline_cache + .get(&visible_entity) .map(|(tick, _)| *tick); let needs_specialization = last_specialized_tick.is_none_or(|tick| { view_tick.is_newer_than(tick, ticks.this_run()) @@ -1829,13 +1858,14 @@ pub fn specialize_shadows( } }; - specialized_material_pipeline_cache.insert( - (extracted_view_light.retained_view_entity, visible_entity), - (ticks.this_run(), pipeline_id), - ); + view_specialized_material_pipeline_cache + .insert(visible_entity, (ticks.this_run(), pipeline_id)); } } } + + // Delete specialized pipelines belonging to views that have expired. + specialized_material_pipeline_cache.retain(|view, _| all_shadow_views.contains(view)); } /// For each shadow cascade, iterates over all the meshes "visible" from it and @@ -1875,6 +1905,12 @@ pub fn queue_shadows( continue; }; + let Some(view_specialized_material_pipeline_cache) = + specialized_material_pipeline_cache.get(&extracted_view_light.retained_view_entity) + else { + continue; + }; + let visible_entities = match light_entity { LightEntity::Directional { light_entity, @@ -1900,8 +1936,8 @@ pub fn queue_shadows( }; for (entity, main_entity) in visible_entities.iter().copied() { - let Some((current_change_tick, pipeline_id)) = specialized_material_pipeline_cache - .get(&(extracted_view_light.retained_view_entity, main_entity)) + let Some((current_change_tick, pipeline_id)) = + view_specialized_material_pipeline_cache.get(&main_entity) else { continue; }; diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 781fb3f908..5acdbbc3d8 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -37,8 +37,8 @@ use bevy_render::{ renderer::{RenderAdapter, RenderDevice, RenderQueue}, texture::DefaultImageSampler, view::{ - self, NoFrustumCulling, NoIndirectDrawing, RenderVisibilityRanges, ViewTarget, - ViewUniformOffset, ViewVisibility, VisibilityRange, + self, NoFrustumCulling, NoIndirectDrawing, RenderVisibilityRanges, RetainedViewEntity, + ViewTarget, ViewUniformOffset, ViewVisibility, VisibilityRange, }, Extract, }; @@ -317,16 +317,15 @@ impl Plugin for MeshRenderPlugin { } #[derive(Resource, Deref, DerefMut, Default, Debug, Clone)] -pub struct ViewKeyCache(MainEntityHashMap); +pub struct ViewKeyCache(HashMap); #[derive(Resource, Deref, DerefMut, Default, Debug, Clone)] -pub struct ViewSpecializationTicks(MainEntityHashMap); +pub struct ViewSpecializationTicks(HashMap); pub fn check_views_need_specialization( mut view_key_cache: ResMut, mut view_specialization_ticks: ResMut, mut views: Query<( - &MainEntity, &ExtractedView, &Msaa, Option<&Tonemapping>, @@ -352,7 +351,6 @@ pub fn check_views_need_specialization( ticks: SystemChangeTick, ) { for ( - view_entity, view, msaa, tonemapping, @@ -444,11 +442,11 @@ pub fn check_views_need_specialization( ); } if !view_key_cache - .get_mut(view_entity) + .get_mut(&view.retained_view_entity) .is_some_and(|current_key| *current_key == view_key) { - view_key_cache.insert(*view_entity, view_key); - view_specialization_ticks.insert(*view_entity, ticks.this_run()); + view_key_cache.insert(view.retained_view_entity, view_key); + view_specialization_ticks.insert(view.retained_view_entity, ticks.this_run()); } } } diff --git a/crates/bevy_sprite/src/mesh2d/material.rs b/crates/bevy_sprite/src/mesh2d/material.rs index d0c4a1fea6..fbe2c2132c 100644 --- a/crates/bevy_sprite/src/mesh2d/material.rs +++ b/crates/bevy_sprite/src/mesh2d/material.rs @@ -13,7 +13,6 @@ use bevy_core_pipeline::{ }; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::component::Tick; -use bevy_ecs::entity::EntityHash; use bevy_ecs::system::SystemChangeTick; use bevy_ecs::{ prelude::*, @@ -590,11 +589,22 @@ impl Default for EntitySpecializationTicks { } } +/// Stores the [`SpecializedMaterial2dViewPipelineCache`] for each view. #[derive(Resource, Deref, DerefMut)] pub struct SpecializedMaterial2dPipelineCache { - // (view_entity, material_entity) -> (tick, pipeline_id) + // view_entity -> view pipeline cache #[deref] - map: HashMap<(MainEntity, MainEntity), (Tick, CachedRenderPipelineId), EntityHash>, + map: MainEntityHashMap>, + marker: PhantomData, +} + +/// Stores the cached render pipeline ID for each entity in a single view, as +/// well as the last time it was changed. +#[derive(Deref, DerefMut)] +pub struct SpecializedMaterial2dViewPipelineCache { + // material entity -> (tick, pipeline_id) + #[deref] + map: MainEntityHashMap<(Tick, CachedRenderPipelineId)>, marker: PhantomData, } @@ -607,6 +617,15 @@ impl Default for SpecializedMaterial2dPipelineCache { } } +impl Default for SpecializedMaterial2dViewPipelineCache { + fn default() -> Self { + Self { + map: HashMap::default(), + marker: PhantomData, + } + } +} + pub fn check_entities_needing_specialization( needs_specialization: Query< Entity, @@ -665,11 +684,15 @@ pub fn specialize_material2d_meshes( continue; }; + let view_tick = view_specialization_ticks.get(view_entity).unwrap(); + let view_specialized_material_pipeline_cache = specialized_material_pipeline_cache + .entry(*view_entity) + .or_default(); + for (_, visible_entity) in visible_entities.iter::() { - let view_tick = view_specialization_ticks.get(view_entity).unwrap(); let entity_tick = entity_specialization_ticks.get(visible_entity).unwrap(); - let last_specialized_tick = specialized_material_pipeline_cache - .get(&(*view_entity, *visible_entity)) + let last_specialized_tick = view_specialized_material_pipeline_cache + .get(visible_entity) .map(|(tick, _)| *tick); let needs_specialization = last_specialized_tick.is_none_or(|tick| { view_tick.is_newer_than(tick, ticks.this_run()) @@ -713,10 +736,8 @@ pub fn specialize_material2d_meshes( } }; - specialized_material_pipeline_cache.insert( - (*view_entity, *visible_entity), - (ticks.this_run(), pipeline_id), - ); + view_specialized_material_pipeline_cache + .insert(*visible_entity, (ticks.this_run(), pipeline_id)); } } } @@ -741,6 +762,12 @@ pub fn queue_material2d_meshes( } for (view_entity, view, visible_entities) in &views { + let Some(view_specialized_material_pipeline_cache) = + specialized_material_pipeline_cache.get(view_entity) + else { + continue; + }; + let Some(transparent_phase) = transparent_render_phases.get_mut(&view.retained_view_entity) else { continue; @@ -754,8 +781,8 @@ pub fn queue_material2d_meshes( }; for (render_entity, visible_entity) in visible_entities.iter::() { - let Some((current_change_tick, pipeline_id)) = specialized_material_pipeline_cache - .get(&(*view_entity, *visible_entity)) + let Some((current_change_tick, pipeline_id)) = view_specialized_material_pipeline_cache + .get(visible_entity) .map(|(current_change_tick, pipeline_id)| (*current_change_tick, *pipeline_id)) else { continue;