summaryrefslogtreecommitdiff
path: root/engine/src/asset.rs
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2026-09-28 01:37:37 +0200
committerHampusM <hampus@hampusmat.com>2026-09-28 17:46:38 +0200
commit5c4178f43d9eb0d9d268786c90e52f510a2cc182 (patch)
treec5ac28412b8084bf43a4433b47b8bad056d8ce99 /engine/src/asset.rs
parentc38ab1ed3b6453d67642dde5c6401c1657c7f025 (diff)
feat(engine): add dependencies for assets
Diffstat (limited to 'engine/src/asset.rs')
-rw-r--r--engine/src/asset.rs344
1 files changed, 246 insertions, 98 deletions
diff --git a/engine/src/asset.rs b/engine/src/asset.rs
index 08c92bd..9d69727 100644
--- a/engine/src/asset.rs
+++ b/engine/src/asset.rs
@@ -27,7 +27,29 @@ declare_entity! {
pub HANDLE_ASSETS_PHASE: (Phase, pair!(ChildOf, { *PRE_UPDATE_PHASE }));
}
-pub trait Asset: Send + Sync + 'static {}
+pub trait Asset: Send + Sync + 'static
+{
+ fn dependencies(&self) -> impl Iterator<Item = Dependency<'_>>;
+}
+
+#[derive(Debug)]
+pub struct Dependency<'a>
+{
+ label: Label<'a>,
+ asset_ty_id: AssetTypeId,
+}
+
+impl<'a> Dependency<'a>
+{
+ #[must_use]
+ pub fn new<AssetT: Asset>(label: Label<'a>) -> Self
+ {
+ Self {
+ label,
+ asset_ty_id: AssetTypeId::of::<AssetT>(),
+ }
+ }
+}
/// Asset label.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -135,8 +157,8 @@ impl Assets
let asset_lookup = self.asset_lookup.borrow();
- let LookupEntry::Occupied(asset_index, asset_label) =
- asset_lookup.get(&id.label_hash)?
+ let AssetState::Ready(asset_index, asset_label) =
+ &asset_lookup.get(&id.label_hash)?.state
else {
return None;
};
@@ -172,7 +194,8 @@ impl Assets
let asset_lookup = self.asset_lookup.borrow();
- let LookupEntry::Occupied(asset_index, _) = asset_lookup.get(&label_hash)? else {
+ let AssetState::Ready(asset_index, _) = &asset_lookup.get(&label_hash)?.state
+ else {
return None;
};
@@ -198,8 +221,8 @@ impl Assets
let asset_lookup = self.asset_lookup.borrow();
- let Some(LookupEntry::Occupied(asset_index, _)) =
- asset_lookup.get(&id.label_hash)
+ let Some(AssetState::Ready(asset_index, _)) =
+ asset_lookup.get(&id.label_hash).map(|entry| &entry.state)
else {
return false;
};
@@ -222,9 +245,9 @@ impl Assets
{
let id = id.into_valid()?;
- let lookup_entry = self.asset_lookup.borrow().get(&id.label_hash)?.clone();
+ let asset_lookup = self.asset_lookup.borrow();
- let LookupEntry::Occupied(_, label) = lookup_entry else {
+ let AssetState::Ready(_, label) = &asset_lookup.get(&id.label_hash)?.state else {
return None;
};
@@ -242,35 +265,12 @@ impl Assets
let label_hash = LabelHash::new(&label);
- let mut asset_lookup = self.asset_lookup.borrow_mut();
-
- if Self::is_pending(&asset_lookup, &label) {
- return Handle::new(label_hash);
- }
-
- let Some(lookup_entry) = asset_lookup.get(&label_hash) else {
- self.add_import_work::<AssetT, Infallible>(
- &label,
- label_hash,
- None,
- &mut asset_lookup,
- );
-
- return Handle::new(label_hash);
- };
-
- match lookup_entry {
- LookupEntry::Occupied(asset_index, _) => {
- let Some(stored_asset) = self.store.get(*asset_index) else {
- unreachable!();
- };
-
- if stored_asset.strong.downcast_ref::<AssetT>().is_none() {
- tracing::error!("Wrong asset type {}", type_name::<AssetT>());
- }
- }
- LookupEntry::Pending => {}
- }
+ self.load_asset::<Infallible>(
+ AssetTypeId::of::<AssetT>(),
+ label,
+ label_hash,
+ None,
+ );
Handle::new(label_hash)
}
@@ -291,38 +291,12 @@ impl Assets
let label_hash = LabelHash::new(&label);
- let mut asset_lookup = self.asset_lookup.borrow_mut();
-
- if Self::is_pending(&asset_lookup, &label) {
- return Handle::new(label_hash);
- }
-
- let Some(lookup_entry) = asset_lookup.get(&label_hash) else {
- self.add_import_work::<AssetT, AssetSettings>(
- &label,
- label_hash,
- Some(asset_settings),
- &mut asset_lookup,
- );
-
- return Handle::new(label_hash);
- };
-
- match lookup_entry {
- LookupEntry::Occupied(asset_index, _) => {
- let Some(stored_asset) = self.store.get(*asset_index) else {
- unreachable!();
- };
-
- if stored_asset.strong.downcast_ref::<AssetT>().is_none() {
- tracing::error!(
- "Wrong asset type {} for asset",
- type_name::<AssetT>()
- );
- }
- }
- LookupEntry::Pending => {}
- }
+ self.load_asset(
+ AssetTypeId::of::<AssetT>(),
+ label,
+ label_hash,
+ Some(asset_settings),
+ );
Handle::new(label_hash)
}
@@ -378,15 +352,64 @@ impl Assets
let label_hash = LabelHash::new(&label);
+ let id = Id {
+ inner: IdInner::Valid(IdValid { label_hash }),
+ };
+
if matches!(
- self.asset_lookup.get_mut().get(&label_hash),
- Some(LookupEntry::Occupied(_, _))
+ self.asset_lookup
+ .get_mut()
+ .get(&label_hash)
+ .map(|entry| &entry.state),
+ Some(AssetState::Ready(..) | AssetState::DependenciesPending { .. })
) {
tracing::error!("Asset already exists");
return Handle::new(label_hash);
}
+ let mut missing_dependencies = 0;
+
+ for dependency in asset.dependencies() {
+ match self
+ .asset_lookup
+ .get_mut()
+ .get_mut(&LabelHash::new(&dependency.label))
+ {
+ Some(LookupEntry { state: AssetState::Ready(..), .. }) => {}
+ Some(LookupEntry {
+ state: AssetState::Pending | AssetState::DependenciesPending { .. },
+ waiting_dependants,
+ }) => {
+ missing_dependencies += 1;
+
+ waiting_dependants.push(id);
+ }
+ None => {
+ missing_dependencies += 1;
+
+ let dependency_label_hash = LabelHash::new(&dependency.label);
+
+ self.load_asset::<Infallible>(
+ dependency.asset_ty_id,
+ dependency.label.clone(),
+ dependency_label_hash,
+ None,
+ );
+
+ self.asset_lookup
+ .get_mut()
+ .entry(LabelHash::new(&dependency.label))
+ .or_insert_with(|| LookupEntry {
+ state: AssetState::Pending,
+ waiting_dependants: Vec::new(),
+ })
+ .waiting_dependants
+ .push(id);
+ }
+ }
+ }
+
tracing::debug!("Storing asset");
self.store.push(StoredAsset::new(asset));
@@ -395,17 +418,32 @@ impl Assets
let label = label.to_owned();
- self.asset_lookup
+ let entry = self
+ .asset_lookup
.get_mut()
- .insert(label_hash, LookupEntry::Occupied(index, label.clone()));
+ .entry(label_hash)
+ .or_insert_with(|| LookupEntry {
+ state: AssetState::Pending,
+ waiting_dependants: Vec::new(),
+ });
+
+ if missing_dependencies != 0 {
+ entry.state = AssetState::DependenciesPending {
+ index,
+ label: label.clone(),
+ remaining_dependencies: missing_dependencies,
+ };
+ } else {
+ entry.state = AssetState::Ready(index, label.clone());
- let id = Id {
- inner: IdInner::Valid(IdValid { label_hash }),
- };
+ self.events
+ .curr_tick_events
+ .push(Event::Stored(id, label.into_label()));
+
+ let waiting_dependants = std::mem::take(&mut entry.waiting_dependants);
- self.events
- .curr_tick_events
- .push(Event::Stored(id, label.into_label()));
+ self.recurse_update_dependants(waiting_dependants);
+ }
Handle::from_id(id)
}
@@ -415,37 +453,119 @@ impl Assets
&self.events
}
+ fn recurse_update_dependants(&mut self, initial_dependants: Vec<Id>)
+ {
+ let mut all_dependants = initial_dependants;
+
+ loop {
+ let dependants = std::mem::take(&mut all_dependants);
+
+ if dependants.is_empty() {
+ break;
+ }
+
+ for dependant_id in dependants {
+ let dependant_label_hash = dependant_id.into_valid().unwrap().label_hash;
+
+ let Some(LookupEntry { state, waiting_dependants }) =
+ self.asset_lookup.get_mut().get_mut(&dependant_label_hash)
+ else {
+ continue;
+ };
+
+ {
+ let AssetState::DependenciesPending {
+ remaining_dependencies, ..
+ } = state
+ else {
+ continue;
+ };
+
+ *remaining_dependencies = remaining_dependencies.saturating_sub(1);
+
+ if *remaining_dependencies > 0 {
+ continue;
+ }
+ }
+
+ let AssetState::DependenciesPending { index, label, .. } =
+ std::mem::replace(state, AssetState::Pending)
+ else {
+ unreachable!();
+ };
+
+ *state = AssetState::Ready(index, label.clone());
+
+ self.events
+ .curr_tick_events
+ .push(Event::Stored(dependant_id, label.into_label()));
+
+ all_dependants.append(waiting_dependants);
+ }
+ }
+ }
+
fn is_pending(asset_lookup: &HashMap<LabelHash, LookupEntry>, label: &Label) -> bool
{
if label.name.is_some() {
- if let Some(LookupEntry::Pending) =
- asset_lookup.get(&LabelHash::new(&Label {
- path: label.path.as_ref().into(),
- name: None,
- }))
+ if let Some(AssetState::Pending | AssetState::DependenciesPending { .. }) =
+ asset_lookup
+ .get(&LabelHash::new(&Label {
+ path: label.path.as_ref().into(),
+ name: None,
+ }))
+ .map(|entry| &entry.state)
{
return true;
}
}
- if let Some(LookupEntry::Pending) = asset_lookup.get(&LabelHash::new(label)) {
+ if let Some(AssetState::Pending | AssetState::DependenciesPending { .. }) =
+ asset_lookup
+ .get(&LabelHash::new(label))
+ .map(|entry| &entry.state)
+ {
return true;
}
false
}
- fn add_import_work<AssetT, AssetSettings>(
+ fn load_asset<AssetSettings>(
+ &self,
+ asset_ty_id: AssetTypeId,
+ label: Label<'_>,
+ label_hash: LabelHash,
+ asset_settings: Option<AssetSettings>,
+ ) where
+ AssetSettings: Send + Sync + Debug + 'static,
+ {
+ let mut asset_lookup = self.asset_lookup.borrow_mut();
+
+ if Self::is_pending(&asset_lookup, &label) {
+ return;
+ }
+
+ self.add_import_work::<AssetSettings>(
+ asset_ty_id,
+ &label,
+ label_hash,
+ asset_settings,
+ &mut asset_lookup,
+ );
+ }
+
+ fn add_import_work<AssetSettings>(
&self,
+ asset_ty_id: AssetTypeId,
label: &Label<'_>,
label_hash: LabelHash,
asset_settings: Option<AssetSettings>,
asset_lookup: &mut HashMap<LabelHash, LookupEntry>,
) where
- AssetT: Asset,
AssetSettings: Any + Send + Sync,
{
- let Some(importer) = self.importers.get(&AssetTypeId::of::<AssetT>()) else {
+ let Some(importer) = self.importers.get(&asset_ty_id) else {
tracing::error!("No importer exists for asset");
return;
};
@@ -479,15 +599,30 @@ impl Assets
},
});
- asset_lookup.insert(label_hash, LookupEntry::Pending);
+ assert!(!asset_lookup.contains_key(&label_hash));
+
+ asset_lookup.insert(
+ label_hash,
+ LookupEntry {
+ state: AssetState::Pending,
+ waiting_dependants: Vec::new(),
+ },
+ );
if label.name.is_some() {
+ let parent_label_hash = LabelHash::new(&Label {
+ path: label.path.as_ref().into(),
+ name: None,
+ });
+
+ assert!(!asset_lookup.contains_key(&parent_label_hash));
+
asset_lookup.insert(
- LabelHash::new(&Label {
- path: label.path.as_ref().into(),
- name: None,
- }),
- LookupEntry::Pending,
+ parent_label_hash,
+ LookupEntry {
+ state: AssetState::Pending,
+ waiting_dependants: Vec::new(),
+ },
);
}
}
@@ -847,7 +982,7 @@ fn add_received_assets(mut assets: Single<Assets>)
assets,
Label {
path: label.path.as_path().into(),
- name: label.name.as_deref().map(std::convert::Into::into),
+ name: label.name.as_deref().map(Into::into),
},
asset_settings,
);
@@ -896,11 +1031,24 @@ enum ImportWorkMessage
},
}
-#[derive(Debug, Clone)]
-enum LookupEntry
+#[derive(Debug)]
+struct LookupEntry
{
- Occupied(usize, LabelOwned),
+ state: AssetState,
+ waiting_dependants: Vec<Id>,
+}
+
+#[derive(Debug)]
+enum AssetState
+{
+ Ready(usize, LabelOwned),
Pending,
+ DependenciesPending
+ {
+ index: usize,
+ label: LabelOwned,
+ remaining_dependencies: usize,
+ },
}
#[derive(Debug)]