Asset re-loading while it's being deleted (#2011)
fixes #824 fixes #1956 * marked asset loading methods as `must_use` * fixed asset re-loading while asset is still loading to work as comment is describing code * introduced a 1 frame delay between unused asset marking and actual asset removal
This commit is contained in:
		
							parent
							
								
									2390bee647
								
							
						
					
					
						commit
						4f0499b91f
					
				| @ -10,7 +10,7 @@ use bevy_log::warn; | |||||||
| use bevy_tasks::TaskPool; | use bevy_tasks::TaskPool; | ||||||
| use bevy_utils::{HashMap, Uuid}; | use bevy_utils::{HashMap, Uuid}; | ||||||
| use crossbeam_channel::TryRecvError; | use crossbeam_channel::TryRecvError; | ||||||
| use parking_lot::RwLock; | use parking_lot::{Mutex, RwLock}; | ||||||
| use std::{collections::hash_map::Entry, path::Path, sync::Arc}; | use std::{collections::hash_map::Entry, path::Path, sync::Arc}; | ||||||
| use thiserror::Error; | use thiserror::Error; | ||||||
| 
 | 
 | ||||||
| @ -45,6 +45,7 @@ fn format_missing_asset_ext(exts: &[String]) -> String { | |||||||
| pub(crate) struct AssetRefCounter { | pub(crate) struct AssetRefCounter { | ||||||
|     pub(crate) channel: Arc<RefChangeChannel>, |     pub(crate) channel: Arc<RefChangeChannel>, | ||||||
|     pub(crate) ref_counts: Arc<RwLock<HashMap<HandleId, usize>>>, |     pub(crate) ref_counts: Arc<RwLock<HashMap<HandleId, usize>>>, | ||||||
|  |     pub(crate) mark_unused_assets: Arc<Mutex<Vec<HandleId>>>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub struct AssetServerInternal { | pub struct AssetServerInternal { | ||||||
| @ -224,6 +225,7 @@ impl AssetServer { | |||||||
|     /// The name of the asset folder is set inside the
 |     /// The name of the asset folder is set inside the
 | ||||||
|     /// [`AssetServerSettings`](crate::AssetServerSettings) resource. The default name is
 |     /// [`AssetServerSettings`](crate::AssetServerSettings) resource. The default name is
 | ||||||
|     /// `"assets"`.
 |     /// `"assets"`.
 | ||||||
|  |     #[must_use = "not using the returned strong handle may result in the unexpected release of the asset"] | ||||||
|     pub fn load<'a, T: Asset, P: Into<AssetPath<'a>>>(&self, path: P) -> Handle<T> { |     pub fn load<'a, T: Asset, P: Into<AssetPath<'a>>>(&self, path: P) -> Handle<T> { | ||||||
|         self.load_untyped(path).typed() |         self.load_untyped(path).typed() | ||||||
|     } |     } | ||||||
| @ -253,11 +255,12 @@ impl AssetServer { | |||||||
|                 }), |                 }), | ||||||
|             }; |             }; | ||||||
| 
 | 
 | ||||||
|             // if asset is already loaded (or is loading), don't load again
 |             // if asset is already loaded or is loading, don't load again
 | ||||||
|             if !force |             if !force | ||||||
|                 && source_info |                 && (source_info | ||||||
|                     .committed_assets |                     .committed_assets | ||||||
|                     .contains(&asset_path_id.label_id()) |                     .contains(&asset_path_id.label_id()) | ||||||
|  |                     || source_info.load_state == LoadState::Loading) | ||||||
|             { |             { | ||||||
|                 return Ok(asset_path_id); |                 return Ok(asset_path_id); | ||||||
|             } |             } | ||||||
| @ -325,7 +328,8 @@ impl AssetServer { | |||||||
|             let type_uuid = loaded_asset.value.as_ref().unwrap().type_uuid(); |             let type_uuid = loaded_asset.value.as_ref().unwrap().type_uuid(); | ||||||
|             source_info.asset_types.insert(label_id, type_uuid); |             source_info.asset_types.insert(label_id, type_uuid); | ||||||
|             for dependency in loaded_asset.dependencies.iter() { |             for dependency in loaded_asset.dependencies.iter() { | ||||||
|                 self.load_untyped(dependency.clone()); |                 // another handle already exists created from the asset path
 | ||||||
|  |                 let _ = self.load_untyped(dependency.clone()); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -337,6 +341,7 @@ impl AssetServer { | |||||||
|         Ok(asset_path_id) |         Ok(asset_path_id) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     #[must_use = "not using the returned strong handle may result in the unexpected release of the asset"] | ||||||
|     pub fn load_untyped<'a, P: Into<AssetPath<'a>>>(&self, path: P) -> HandleUntyped { |     pub fn load_untyped<'a, P: Into<AssetPath<'a>>>(&self, path: P) -> HandleUntyped { | ||||||
|         let handle_id = self.load_untracked(path.into(), false); |         let handle_id = self.load_untracked(path.into(), false); | ||||||
|         self.get_handle_untyped(handle_id) |         self.get_handle_untyped(handle_id) | ||||||
| @ -356,6 +361,7 @@ impl AssetServer { | |||||||
|         asset_path.into() |         asset_path.into() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     #[must_use = "not using the returned strong handles may result in the unexpected release of the assets"] | ||||||
|     pub fn load_folder<P: AsRef<Path>>( |     pub fn load_folder<P: AsRef<Path>>( | ||||||
|         &self, |         &self, | ||||||
|         path: P, |         path: P, | ||||||
| @ -385,33 +391,14 @@ impl AssetServer { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn free_unused_assets(&self) { |     pub fn free_unused_assets(&self) { | ||||||
|         let receiver = &self.server.asset_ref_counter.channel.receiver; |         let mut potential_frees = self.server.asset_ref_counter.mark_unused_assets.lock(); | ||||||
|         let mut ref_counts = self.server.asset_ref_counter.ref_counts.write(); |  | ||||||
|         let asset_sources = self.server.asset_sources.read(); |  | ||||||
|         let mut potential_frees = Vec::new(); |  | ||||||
|         loop { |  | ||||||
|             let ref_change = match receiver.try_recv() { |  | ||||||
|                 Ok(ref_change) => ref_change, |  | ||||||
|                 Err(TryRecvError::Empty) => break, |  | ||||||
|                 Err(TryRecvError::Disconnected) => panic!("RefChange channel disconnected."), |  | ||||||
|             }; |  | ||||||
|             match ref_change { |  | ||||||
|                 RefChange::Increment(handle_id) => *ref_counts.entry(handle_id).or_insert(0) += 1, |  | ||||||
|                 RefChange::Decrement(handle_id) => { |  | ||||||
|                     let entry = ref_counts.entry(handle_id).or_insert(0); |  | ||||||
|                     *entry -= 1; |  | ||||||
|                     if *entry == 0 { |  | ||||||
|                         potential_frees.push(handle_id); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         if !potential_frees.is_empty() { |         if !potential_frees.is_empty() { | ||||||
|  |             let ref_counts = self.server.asset_ref_counter.ref_counts.read(); | ||||||
|  |             let asset_sources = self.server.asset_sources.read(); | ||||||
|             let asset_lifecycles = self.server.asset_lifecycles.read(); |             let asset_lifecycles = self.server.asset_lifecycles.read(); | ||||||
|             for potential_free in potential_frees { |             for potential_free in potential_frees.drain(..) { | ||||||
|                 if let Some(i) = ref_counts.get(&potential_free).cloned() { |                 if let Some(&0) = ref_counts.get(&potential_free) { | ||||||
|                     if i == 0 { |  | ||||||
|                     let type_uuid = match potential_free { |                     let type_uuid = match potential_free { | ||||||
|                         HandleId::Id(type_uuid, _) => Some(type_uuid), |                         HandleId::Id(type_uuid, _) => Some(type_uuid), | ||||||
|                         HandleId::AssetPathId(id) => asset_sources |                         HandleId::AssetPathId(id) => asset_sources | ||||||
| @ -428,6 +415,32 @@ impl AssetServer { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     pub fn mark_unused_assets(&self) { | ||||||
|  |         let receiver = &self.server.asset_ref_counter.channel.receiver; | ||||||
|  |         let mut ref_counts = self.server.asset_ref_counter.ref_counts.write(); | ||||||
|  |         let mut potential_frees = None; | ||||||
|  |         loop { | ||||||
|  |             let ref_change = match receiver.try_recv() { | ||||||
|  |                 Ok(ref_change) => ref_change, | ||||||
|  |                 Err(TryRecvError::Empty) => break, | ||||||
|  |                 Err(TryRecvError::Disconnected) => panic!("RefChange channel disconnected."), | ||||||
|  |             }; | ||||||
|  |             match ref_change { | ||||||
|  |                 RefChange::Increment(handle_id) => *ref_counts.entry(handle_id).or_insert(0) += 1, | ||||||
|  |                 RefChange::Decrement(handle_id) => { | ||||||
|  |                     let entry = ref_counts.entry(handle_id).or_insert(0); | ||||||
|  |                     *entry -= 1; | ||||||
|  |                     if *entry == 0 { | ||||||
|  |                         potential_frees | ||||||
|  |                             .get_or_insert_with(|| { | ||||||
|  |                                 self.server.asset_ref_counter.mark_unused_assets.lock() | ||||||
|  |                             }) | ||||||
|  |                             .push(handle_id); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn create_assets_in_load_context(&self, load_context: &mut LoadContext) { |     fn create_assets_in_load_context(&self, load_context: &mut LoadContext) { | ||||||
| @ -504,6 +517,7 @@ impl AssetServer { | |||||||
| 
 | 
 | ||||||
| pub fn free_unused_assets_system(asset_server: Res<AssetServer>) { | pub fn free_unused_assets_system(asset_server: Res<AssetServer>) { | ||||||
|     asset_server.free_unused_assets(); |     asset_server.free_unused_assets(); | ||||||
|  |     asset_server.mark_unused_assets(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 François
						François