Reuse and hot reload folder handles (#10210)
# Objective - Folder handles are not shared. Loading the same folder multiple times will result in different handles. - Once folder handles are shared, they can no longer be manually reloaded, so we should add support for hot-reloading them ## Solution - Reuse folder handles based on their path - Trigger a reload of a folder if a file contained in it (or a sub folder) is added or removed - This also covers adding/removing/moving sub folders containing files --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
		
							parent
							
								
									77309ba5d8
								
							
						
					
					
						commit
						bfca4384cc
					
				| @ -86,23 +86,6 @@ impl std::fmt::Debug for AssetInfos { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl AssetInfos { | impl AssetInfos { | ||||||
|     pub(crate) fn create_loading_handle<A: Asset>(&mut self) -> Handle<A> { |  | ||||||
|         unwrap_with_context( |  | ||||||
|             Self::create_handle_internal( |  | ||||||
|                 &mut self.infos, |  | ||||||
|                 &self.handle_providers, |  | ||||||
|                 &mut self.living_labeled_assets, |  | ||||||
|                 self.watching_for_changes, |  | ||||||
|                 TypeId::of::<A>(), |  | ||||||
|                 None, |  | ||||||
|                 None, |  | ||||||
|                 true, |  | ||||||
|             ), |  | ||||||
|             std::any::type_name::<A>(), |  | ||||||
|         ) |  | ||||||
|         .typed_debug_checked() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub(crate) fn create_loading_handle_untyped( |     pub(crate) fn create_loading_handle_untyped( | ||||||
|         &mut self, |         &mut self, | ||||||
|         type_id: TypeId, |         type_id: TypeId, | ||||||
|  | |||||||
| @ -23,6 +23,7 @@ use crossbeam_channel::{Receiver, Sender}; | |||||||
| use futures_lite::StreamExt; | use futures_lite::StreamExt; | ||||||
| use info::*; | use info::*; | ||||||
| use parking_lot::RwLock; | use parking_lot::RwLock; | ||||||
|  | use std::path::PathBuf; | ||||||
| use std::{any::TypeId, path::Path, sync::Arc}; | use std::{any::TypeId, path::Path, sync::Arc}; | ||||||
| use thiserror::Error; | use thiserror::Error; | ||||||
| 
 | 
 | ||||||
| @ -525,15 +526,33 @@ impl AssetServer { | |||||||
|     /// Loads all assets from the specified folder recursively. The [`LoadedFolder`] asset (when it loads) will
 |     /// Loads all assets from the specified folder recursively. The [`LoadedFolder`] asset (when it loads) will
 | ||||||
|     /// contain handles to all assets in the folder. You can wait for all assets to load by checking the [`LoadedFolder`]'s
 |     /// contain handles to all assets in the folder. You can wait for all assets to load by checking the [`LoadedFolder`]'s
 | ||||||
|     /// [`RecursiveDependencyLoadState`].
 |     /// [`RecursiveDependencyLoadState`].
 | ||||||
|  |     ///
 | ||||||
|  |     /// Loading the same folder multiple times will return the same handle. If the `file_watcher`
 | ||||||
|  |     /// feature is enabled, [`LoadedFolder`] handles will reload when a file in the folder is
 | ||||||
|  |     /// removed, added or moved. This includes files in subdirectories and moving, adding,
 | ||||||
|  |     /// or removing complete subdirectories.
 | ||||||
|     #[must_use = "not using the returned strong handle may result in the unexpected release of the assets"] |     #[must_use = "not using the returned strong handle may result in the unexpected release of the assets"] | ||||||
|     pub fn load_folder<'a>(&self, path: impl Into<AssetPath<'a>>) -> Handle<LoadedFolder> { |     pub fn load_folder<'a>(&self, path: impl Into<AssetPath<'a>>) -> Handle<LoadedFolder> { | ||||||
|         let handle = { |  | ||||||
|             let mut infos = self.data.infos.write(); |  | ||||||
|             infos.create_loading_handle::<LoadedFolder>() |  | ||||||
|         }; |  | ||||||
|         let id = handle.id().untyped(); |  | ||||||
|         let path = path.into().into_owned(); |         let path = path.into().into_owned(); | ||||||
|  |         let (handle, should_load) = self | ||||||
|  |             .data | ||||||
|  |             .infos | ||||||
|  |             .write() | ||||||
|  |             .get_or_create_path_handle::<LoadedFolder>( | ||||||
|  |                 path.clone(), | ||||||
|  |                 HandleLoadingMode::Request, | ||||||
|  |                 None, | ||||||
|  |             ); | ||||||
|  |         if !should_load { | ||||||
|  |             return handle; | ||||||
|  |         } | ||||||
|  |         let id = handle.id().untyped(); | ||||||
|  |         self.load_folder_internal(id, path); | ||||||
| 
 | 
 | ||||||
|  |         handle | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub(crate) fn load_folder_internal(&self, id: UntypedAssetId, path: AssetPath) { | ||||||
|         fn load_folder<'a>( |         fn load_folder<'a>( | ||||||
|             source: AssetSourceId<'static>, |             source: AssetSourceId<'static>, | ||||||
|             path: &'a Path, |             path: &'a Path, | ||||||
| @ -568,6 +587,7 @@ impl AssetServer { | |||||||
|             }) |             }) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         let path = path.into_owned(); | ||||||
|         let server = self.clone(); |         let server = self.clone(); | ||||||
|         IoTaskPool::get() |         IoTaskPool::get() | ||||||
|             .spawn(async move { |             .spawn(async move { | ||||||
| @ -607,8 +627,6 @@ impl AssetServer { | |||||||
|                 } |                 } | ||||||
|             }) |             }) | ||||||
|             .detach(); |             .detach(); | ||||||
| 
 |  | ||||||
|         handle |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn send_asset_event(&self, event: InternalAssetEvent) { |     fn send_asset_event(&self, event: InternalAssetEvent) { | ||||||
| @ -860,6 +878,19 @@ pub fn handle_internal_asset_events(world: &mut World) { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         let reload_parent_folders = |path: PathBuf, source: &AssetSourceId<'static>| { | ||||||
|  |             let mut current_folder = path; | ||||||
|  |             while let Some(parent) = current_folder.parent() { | ||||||
|  |                 current_folder = parent.to_path_buf(); | ||||||
|  |                 let parent_asset_path = | ||||||
|  |                     AssetPath::from(current_folder.clone()).with_source(source.clone()); | ||||||
|  |                 if let Some(folder_handle) = infos.get_path_handle(parent_asset_path.clone()) { | ||||||
|  |                     info!("Reloading folder {parent_asset_path} because the content has changed"); | ||||||
|  |                     server.load_folder_internal(folder_handle.id(), parent_asset_path); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|         let mut paths_to_reload = HashSet::new(); |         let mut paths_to_reload = HashSet::new(); | ||||||
|         let mut handle_event = |source: AssetSourceId<'static>, event: AssetSourceEvent| { |         let mut handle_event = |source: AssetSourceId<'static>, event: AssetSourceEvent| { | ||||||
|             match event { |             match event { | ||||||
| @ -870,6 +901,16 @@ pub fn handle_internal_asset_events(world: &mut World) { | |||||||
|                     queue_ancestors(&path, &infos, &mut paths_to_reload); |                     queue_ancestors(&path, &infos, &mut paths_to_reload); | ||||||
|                     paths_to_reload.insert(path); |                     paths_to_reload.insert(path); | ||||||
|                 } |                 } | ||||||
|  |                 AssetSourceEvent::RenamedFolder { old, new } => { | ||||||
|  |                     reload_parent_folders(old, &source); | ||||||
|  |                     reload_parent_folders(new, &source); | ||||||
|  |                 } | ||||||
|  |                 AssetSourceEvent::AddedAsset(path) | ||||||
|  |                 | AssetSourceEvent::RemovedAsset(path) | ||||||
|  |                 | AssetSourceEvent::RemovedFolder(path) | ||||||
|  |                 | AssetSourceEvent::AddedFolder(path) => { | ||||||
|  |                     reload_parent_folders(path, &source); | ||||||
|  |                 } | ||||||
|                 _ => {} |                 _ => {} | ||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Niklas Eicker
						Niklas Eicker