diff options
| author | HampusM <hampus@hampusmat.com> | 2026-09-28 01:37:37 +0200 |
|---|---|---|
| committer | HampusM <hampus@hampusmat.com> | 2026-09-28 17:46:38 +0200 |
| commit | 5c4178f43d9eb0d9d268786c90e52f510a2cc182 (patch) | |
| tree | c5ac28412b8084bf43a4433b47b8bad056d8ce99 | |
| parent | c38ab1ed3b6453d67642dde5c6401c1657c7f025 (diff) | |
feat(engine): add dependencies for assets
| -rw-r--r-- | engine/src/asset.rs | 344 | ||||
| -rw-r--r-- | engine/src/material.rs | 8 | ||||
| -rw-r--r-- | engine/src/mesh.rs | 8 | ||||
| -rw-r--r-- | engine/src/model.rs | 8 | ||||
| -rw-r--r-- | engine/src/rendering/shader.rs | 8 | ||||
| -rw-r--r-- | engine/src/texture.rs | 8 |
6 files changed, 281 insertions, 103 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)] diff --git a/engine/src/material.rs b/engine/src/material.rs index bf07b72..5f77950 100644 --- a/engine/src/material.rs +++ b/engine/src/material.rs @@ -44,7 +44,13 @@ impl Default for Material } } -impl Asset for Material {} +impl Asset for Material +{ + fn dependencies(&self) -> impl Iterator<Item = crate::asset::Dependency<'_>> + { + [].into_iter() + } +} /// [`Material`] builder. #[derive(Debug, Clone)] diff --git a/engine/src/mesh.rs b/engine/src/mesh.rs index 994387a..b52edb1 100644 --- a/engine/src/mesh.rs +++ b/engine/src/mesh.rs @@ -313,7 +313,13 @@ impl Mesh } } -impl Asset for Mesh {} +impl Asset for Mesh +{ + fn dependencies(&self) -> impl Iterator<Item = crate::asset::Dependency<'_>> + { + [].into_iter() + } +} /// Mesh builder #[derive(Debug, Clone, Default)] diff --git a/engine/src/model.rs b/engine/src/model.rs index 88d2622..220e4f6 100644 --- a/engine/src/model.rs +++ b/engine/src/model.rs @@ -55,7 +55,13 @@ impl Spec } } -impl Asset for Spec {} +impl Asset for Spec +{ + fn dependencies(&self) -> impl Iterator<Item = crate::asset::Dependency<'_>> + { + [].into_iter() + } +} #[derive(Debug, Default, Clone)] pub struct SpecBuilder diff --git a/engine/src/rendering/shader.rs b/engine/src/rendering/shader.rs index 061d9ac..2528471 100644 --- a/engine/src/rendering/shader.rs +++ b/engine/src/rendering/shader.rs @@ -54,7 +54,13 @@ pub struct ModuleSource pub link_entrypoints: EntrypointFlags, } -impl Asset for ModuleSource {} +impl Asset for ModuleSource +{ + fn dependencies(&self) -> impl Iterator<Item = crate::asset::Dependency<'_>> + { + [].into_iter() + } +} bitflags! { #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] diff --git a/engine/src/texture.rs b/engine/src/texture.rs index 54646e8..2a20581 100644 --- a/engine/src/texture.rs +++ b/engine/src/texture.rs @@ -12,7 +12,13 @@ pub enum Texture CubeMap(TexCubeMap), } -impl Asset for Texture {} +impl Asset for Texture +{ + fn dependencies(&self) -> impl Iterator<Item = crate::asset::Dependency<'_>> + { + [].into_iter() + } +} #[derive(Debug, Clone)] pub struct Tex2D |
