Skip to content

Commit

Permalink
Reuse and hot reload folder handles (bevyengine#10210)
Browse files Browse the repository at this point in the history
# 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 <[email protected]>
  • Loading branch information
2 people authored and ameknite committed Nov 6, 2023
1 parent f893d08 commit 72698fb
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 24 deletions.
17 changes: 0 additions & 17 deletions crates/bevy_asset/src/server/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,23 +86,6 @@ impl std::fmt::Debug for 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(
&mut self,
type_id: TypeId,
Expand Down
55 changes: 48 additions & 7 deletions crates/bevy_asset/src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use crossbeam_channel::{Receiver, Sender};
use futures_lite::StreamExt;
use info::*;
use parking_lot::RwLock;
use std::path::PathBuf;
use std::{any::TypeId, path::Path, sync::Arc};
use thiserror::Error;

Expand Down Expand Up @@ -525,15 +526,33 @@ impl AssetServer {
/// 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
/// [`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"]
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 (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>(
source: AssetSourceId<'static>,
path: &'a Path,
Expand Down Expand Up @@ -568,6 +587,7 @@ impl AssetServer {
})
}

let path = path.into_owned();
let server = self.clone();
IoTaskPool::get()
.spawn(async move {
Expand Down Expand Up @@ -607,8 +627,6 @@ impl AssetServer {
}
})
.detach();

handle
}

fn send_asset_event(&self, event: InternalAssetEvent) {
Expand Down Expand Up @@ -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 handle_event = |source: AssetSourceId<'static>, event: AssetSourceEvent| {
match event {
Expand All @@ -870,6 +901,16 @@ pub fn handle_internal_asset_events(world: &mut World) {
queue_ancestors(&path, &infos, &mut paths_to_reload);
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);
}
_ => {}
}
};
Expand Down

0 comments on commit 72698fb

Please sign in to comment.