diff --git a/crates/bevy_render/src/mesh/allocator.rs b/crates/bevy_render/src/mesh/allocator.rs index 3653fc1e87..c3a0139216 100644 --- a/crates/bevy_render/src/mesh/allocator.rs +++ b/crates/bevy_render/src/mesh/allocator.rs @@ -358,7 +358,10 @@ pub fn allocate_and_free_meshes( render_device: Res, render_queue: Res, ) { - // Process newly-added meshes. + // Process removed or modified meshes. + mesh_allocator.free_meshes(&extracted_meshes); + + // Process newly-added or modified meshes. mesh_allocator.allocate_meshes( &mesh_allocator_settings, &extracted_meshes, @@ -366,9 +369,6 @@ pub fn allocate_and_free_meshes( &render_device, &render_queue, ); - - // Process removed meshes. - mesh_allocator.free_meshes(&extracted_meshes); } impl MeshAllocator { @@ -607,9 +607,17 @@ impl MeshAllocator { } } + /// Frees allocations for meshes that were removed or modified this frame. fn free_meshes(&mut self, extracted_meshes: &ExtractedAssets) { let mut empty_slabs = >::default(); - for mesh_id in &extracted_meshes.removed { + + // TODO: Consider explicitly reusing allocations for changed meshes of the same size + let meshes_to_free = extracted_meshes + .removed + .iter() + .chain(extracted_meshes.modified.iter()); + + for mesh_id in meshes_to_free { if let Some(slab_id) = self.mesh_id_to_vertex_slab.remove(mesh_id) { self.free_allocation_in_slab(mesh_id, slab_id, &mut empty_slabs); } diff --git a/crates/bevy_render/src/render_asset.rs b/crates/bevy_render/src/render_asset.rs index acc299b3d7..66f0b2b23c 100644 --- a/crates/bevy_render/src/render_asset.rs +++ b/crates/bevy_render/src/render_asset.rs @@ -151,14 +151,19 @@ impl RenderAssetDependency for A { #[derive(Resource)] pub struct ExtractedAssets { /// The assets extracted this frame. + /// + /// These are assets that were either added or modified this frame. pub extracted: Vec<(AssetId, A::SourceAsset)>, - /// IDs of the assets removed this frame. + /// IDs of the assets that were removed this frame. /// /// These assets will not be present in [`ExtractedAssets::extracted`]. pub removed: HashSet>, - /// IDs of the assets added this frame. + /// IDs of the assets that were modified this frame. + pub modified: HashSet>, + + /// IDs of the assets that were added this frame. pub added: HashSet>, } @@ -167,6 +172,7 @@ impl Default for ExtractedAssets { Self { extracted: Default::default(), removed: Default::default(), + modified: Default::default(), added: Default::default(), } } @@ -235,8 +241,9 @@ pub(crate) fn extract_render_asset( |world, mut cached_state: Mut>| { let (mut events, mut assets) = cached_state.state.get_mut(world); - let mut changed_assets = >::default(); + let mut needs_extracting = >::default(); let mut removed = >::default(); + let mut modified = >::default(); for event in events.read() { #[expect( @@ -244,12 +251,20 @@ pub(crate) fn extract_render_asset( reason = "LoadedWithDependencies is marked as a TODO, so it's likely this will no longer lint soon." )] match event { - AssetEvent::Added { id } | AssetEvent::Modified { id } => { - changed_assets.insert(*id); + AssetEvent::Added { id } => { + needs_extracting.insert(*id); + } + AssetEvent::Modified { id } => { + needs_extracting.insert(*id); + modified.insert(*id); + } + AssetEvent::Removed { .. } => { + // We don't care that the asset was removed from Assets in the main world. + // An asset is only removed from RenderAssets when its last handle is dropped (AssetEvent::Unused). } - AssetEvent::Removed { .. } => {} AssetEvent::Unused { id } => { - changed_assets.remove(id); + needs_extracting.remove(id); + modified.remove(id); removed.insert(*id); } AssetEvent::LoadedWithDependencies { .. } => { @@ -260,7 +275,7 @@ pub(crate) fn extract_render_asset( let mut extracted_assets = Vec::new(); let mut added = >::default(); - for id in changed_assets.drain() { + for id in needs_extracting.drain() { if let Some(asset) = assets.get(id) { let asset_usage = A::asset_usage(asset); if asset_usage.contains(RenderAssetUsages::RENDER_WORLD) { @@ -280,6 +295,7 @@ pub(crate) fn extract_render_asset( commands.insert_resource(ExtractedAssets:: { extracted: extracted_assets, removed, + modified, added, }); cached_state.state.apply(world);