Skip to content

Commit 5d86d64

Browse files
committed
Hot reload labeled assets whose source asset is not loaded
1 parent 1980ac8 commit 5d86d64

File tree

2 files changed

+53
-2
lines changed

2 files changed

+53
-2
lines changed

crates/bevy_asset/src/server/info.rs

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ pub(crate) struct AssetInfos {
6969
/// Tracks assets that depend on the "key" asset path inside their asset loaders ("loader dependencies")
7070
/// This should only be set when watching for changes to avoid unnecessary work.
7171
pub(crate) loader_dependants: HashMap<AssetPath<'static>, HashSet<AssetPath<'static>>>,
72+
/// Tracks living labeled assets for a given source asset.
73+
/// This should only be set when watching for changes to avoid unnecessary work.
74+
pub(crate) living_labeled_assets: HashMap<AssetPath<'static>, HashSet<String>>,
7275
pub(crate) handle_providers: HashMap<TypeId, AssetHandleProvider>,
7376
pub(crate) dependency_loaded_event_sender: HashMap<TypeId, fn(&mut World, UntypedAssetId)>,
7477
}
@@ -88,6 +91,8 @@ impl AssetInfos {
8891
Self::create_handle_internal(
8992
&mut self.infos,
9093
&self.handle_providers,
94+
&mut self.living_labeled_assets,
95+
self.watching_for_changes,
9196
TypeId::of::<A>(),
9297
None,
9398
None,
@@ -107,6 +112,8 @@ impl AssetInfos {
107112
Self::create_handle_internal(
108113
&mut self.infos,
109114
&self.handle_providers,
115+
&mut self.living_labeled_assets,
116+
self.watching_for_changes,
110117
type_id,
111118
None,
112119
None,
@@ -116,9 +123,12 @@ impl AssetInfos {
116123
)
117124
}
118125

126+
#[allow(clippy::too_many_arguments)]
119127
fn create_handle_internal(
120128
infos: &mut HashMap<UntypedAssetId, AssetInfo>,
121129
handle_providers: &HashMap<TypeId, AssetHandleProvider>,
130+
living_labeled_assets: &mut HashMap<AssetPath<'static>, HashSet<String>>,
131+
watching_for_changes: bool,
122132
type_id: TypeId,
123133
path: Option<AssetPath<'static>>,
124134
meta_transform: Option<MetaTransform>,
@@ -128,6 +138,16 @@ impl AssetInfos {
128138
.get(&type_id)
129139
.ok_or(MissingHandleProviderError(type_id))?;
130140

141+
if watching_for_changes {
142+
if let Some(path) = &path {
143+
let mut without_label = path.to_owned();
144+
if let Some(label) = without_label.remove_label() {
145+
let labels = living_labeled_assets.entry(without_label).or_default();
146+
labels.insert(label.to_string());
147+
}
148+
}
149+
}
150+
131151
let handle = provider.reserve_handle_internal(true, path.clone(), meta_transform);
132152
let mut info = AssetInfo::new(Arc::downgrade(&handle), path);
133153
if loading {
@@ -136,6 +156,7 @@ impl AssetInfos {
136156
info.rec_dep_load_state = RecursiveDependencyLoadState::Loading;
137157
}
138158
infos.insert(handle.id, info);
159+
139160
Ok(UntypedHandle::Strong(handle))
140161
}
141162

@@ -226,6 +247,8 @@ impl AssetInfos {
226247
let handle = Self::create_handle_internal(
227248
&mut self.infos,
228249
&self.handle_providers,
250+
&mut self.living_labeled_assets,
251+
self.watching_for_changes,
229252
type_id,
230253
Some(path),
231254
meta_transform,
@@ -256,7 +279,7 @@ impl AssetInfos {
256279
Some(UntypedHandle::Strong(strong_handle))
257280
}
258281

259-
/// Returns `true` if this path has
282+
/// Returns `true` if the asset this path points to is still alive
260283
pub(crate) fn is_path_alive(&self, path: &AssetPath) -> bool {
261284
if let Some(id) = self.path_to_id.get(path) {
262285
if let Some(info) = self.infos.get(id) {
@@ -266,12 +289,26 @@ impl AssetInfos {
266289
false
267290
}
268291

292+
/// Returns `true` if the asset at this path should be reloaded
293+
pub(crate) fn should_reload(&self, path: &AssetPath) -> bool {
294+
if self.is_path_alive(path) {
295+
return true;
296+
}
297+
298+
if let Some(living) = self.living_labeled_assets.get(path) {
299+
!living.is_empty()
300+
} else {
301+
false
302+
}
303+
}
304+
269305
// Returns `true` if the asset should be removed from the collection
270306
pub(crate) fn process_handle_drop(&mut self, id: UntypedAssetId) -> bool {
271307
Self::process_handle_drop_internal(
272308
&mut self.infos,
273309
&mut self.path_to_id,
274310
&mut self.loader_dependants,
311+
&mut self.living_labeled_assets,
275312
self.watching_for_changes,
276313
id,
277314
)
@@ -520,6 +557,7 @@ impl AssetInfos {
520557
infos: &mut HashMap<UntypedAssetId, AssetInfo>,
521558
path_to_id: &mut HashMap<AssetPath<'static>, UntypedAssetId>,
522559
loader_dependants: &mut HashMap<AssetPath<'static>, HashSet<AssetPath<'static>>>,
560+
living_labeled_assets: &mut HashMap<AssetPath<'static>, HashSet<String>>,
523561
watching_for_changes: bool,
524562
id: UntypedAssetId,
525563
) -> bool {
@@ -539,6 +577,18 @@ impl AssetInfos {
539577
dependants.remove(&path);
540578
}
541579
}
580+
if let Some(label) = path.label() {
581+
let mut without_label = path.to_owned();
582+
without_label.remove_label();
583+
if let Entry::Occupied(mut entry) =
584+
living_labeled_assets.entry(without_label)
585+
{
586+
entry.get_mut().remove(label);
587+
if entry.get().is_empty() {
588+
entry.remove();
589+
}
590+
};
591+
}
542592
}
543593
path_to_id.remove(&path);
544594
}
@@ -565,6 +615,7 @@ impl AssetInfos {
565615
&mut self.infos,
566616
&mut self.path_to_id,
567617
&mut self.loader_dependants,
618+
&mut self.living_labeled_assets,
568619
self.watching_for_changes,
569620
id.untyped(provider.type_id),
570621
);

crates/bevy_asset/src/server/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ impl AssetServer {
394394
let owned_path = path.to_owned();
395395
IoTaskPool::get()
396396
.spawn(async move {
397-
if server.data.infos.read().is_path_alive(&owned_path) {
397+
if server.data.infos.read().should_reload(&owned_path) {
398398
info!("Reloading {owned_path} because it has changed");
399399
if let Err(err) = server.load_internal(None, owned_path, true, None).await {
400400
error!("{}", err);

0 commit comments

Comments
 (0)