diff options
Diffstat (limited to 'engine')
| -rw-r--r-- | engine/Cargo.toml | 1 | ||||
| -rw-r--r-- | engine/src/asset.rs | 204 | ||||
| -rw-r--r-- | engine/src/material.rs | 4 | ||||
| -rw-r--r-- | engine/src/material/asset.rs | 21 | ||||
| -rw-r--r-- | engine/src/mesh.rs | 3 | ||||
| -rw-r--r-- | engine/src/model.rs | 4 | ||||
| -rw-r--r-- | engine/src/model/asset.rs | 11 | ||||
| -rw-r--r-- | engine/src/rendering.rs | 4 | ||||
| -rw-r--r-- | engine/src/rendering/shader.rs | 14 | ||||
| -rw-r--r-- | engine/src/texture.rs | 6 |
10 files changed, 146 insertions, 126 deletions
diff --git a/engine/Cargo.toml b/engine/Cargo.toml index 1e2e84d..9fa20a6 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -16,6 +16,7 @@ crossbeam-queue = "0.3.12" parking_lot = "0.12.3" circular-buffer = "2.0.0" intmap = "3.1.3" +hashbrown = "0.15.2" engine-macros = { workspace = true } engine-ecs = { workspace = true } engine-reflection = { workspace = true } diff --git a/engine/src/asset.rs b/engine/src/asset.rs index 8337f5f..781ca22 100644 --- a/engine/src/asset.rs +++ b/engine/src/asset.rs @@ -1,9 +1,8 @@ -use std::any::{type_name, Any}; +use std::any::{type_name, Any, TypeId}; use std::borrow::Cow; use std::cell::RefCell; use std::collections::HashMap; use std::convert::Infallible; -use std::ffi::{OsStr, OsString}; use std::fmt::{Debug, Display}; use std::hash::{DefaultHasher, Hash, Hasher}; use std::hint::cold_path; @@ -28,6 +27,8 @@ declare_entity! { pub HANDLE_ASSETS_PHASE: (Phase, pair!(ChildOf, { *PRE_UPDATE_PHASE })); } +pub trait Asset: Send + Sync + 'static {} + /// Asset label. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Label<'a> @@ -129,8 +130,7 @@ pub struct Assets { store: Vec<StoredAsset>, asset_lookup: RefCell<HashMap<LabelHash, LookupEntry>>, - importers: Vec<WrappedImporterFn>, - importer_lookup: HashMap<OsString, usize>, + importers: hashbrown::HashMap<AssetTypeId, WrappedImporterFn>, import_work_queue: WorkQueue<ImportWorkUserData>, import_work_msg_receiver: MpscReceiver<ImportWorkMessage>, import_work_msg_sender: MpscSender<ImportWorkMessage>, @@ -148,8 +148,7 @@ impl Assets Self { store: Vec::with_capacity(capacity), asset_lookup: RefCell::new(HashMap::with_capacity(capacity)), - importers: Vec::new(), - importer_lookup: HashMap::new(), + importers: hashbrown::HashMap::new(), import_work_queue: WorkQueue::new("asset_importing_work_queue"), import_work_msg_receiver, import_work_msg_sender, @@ -157,34 +156,25 @@ impl Assets } } - pub fn set_importer<'file_ext, AssetSettings, Err>( + pub fn set_importer<AssetT, AssetSettings, Err>( &mut self, - file_extensions: impl IntoIterator<Item: Into<Cow<'file_ext, str>>>, func: impl Fn(&mut Submitter<'_>, &Path, Option<&AssetSettings>) -> Result<(), Err>, ) where + AssetT: Asset, AssetSettings: 'static, Err: std::error::Error + Send + Sync + 'static, { - self.importers.push(WrappedImporterFn::new(func)); - - let importer_index = self.importers.len() - 1; - - self.importer_lookup - .extend(file_extensions.into_iter().map(|file_ext| { - let file_ext: Cow<str> = file_ext.into(); - - (file_ext.into_owned().into(), importer_index) - })); + self.importers + .insert(AssetTypeId::of::<AssetT>(), WrappedImporterFn::new(func)); } #[tracing::instrument( skip_all, - fields(asset_label, asset_type = type_name::<Asset>()) + fields(asset_label, asset_type = type_name::<AssetT>()) )] - pub fn get<Asset: 'static + Send + Sync>( - &self, - handle: &Handle<Asset>, - ) -> Option<&Asset> + pub fn get<AssetT>(&self, handle: &Handle<AssetT>) -> Option<&AssetT> + where + AssetT: Asset, { let id = handle.id.into_valid()?; @@ -203,7 +193,7 @@ impl Assets unreachable!(); }; - let Some(asset) = stored_asset.strong.downcast_ref::<Asset>() else { + let Some(asset) = stored_asset.strong.downcast_ref::<AssetT>() else { tracing::error!("Wrong asset type"); return None; }; @@ -211,11 +201,13 @@ impl Assets Some(asset) } - #[tracing::instrument(skip_all, fields(asset_type=type_name::<Asset>()))] - pub fn get_handle_to_loaded<'label, Asset: 'static + Send + Sync>( + #[tracing::instrument(skip_all, fields(asset_type=type_name::<AssetT>()))] + pub fn get_handle_to_loaded<'label, AssetT>( &self, label: impl Into<Label<'label>>, - ) -> Option<Handle<Asset>> + ) -> Option<Handle<AssetT>> + where + AssetT: Asset, { let label = label.into(); @@ -231,7 +223,7 @@ impl Assets unreachable!(); }; - if stored_asset.strong.downcast_ref::<Asset>().is_none() { + if stored_asset.strong.downcast_ref::<AssetT>().is_none() { tracing::error!("Wrong asset type"); return None; } @@ -239,10 +231,9 @@ impl Assets Some(Handle::new(label_hash)) } - pub fn is_loaded_and_has_type<Asset: 'static + Send + Sync>( - &self, - handle: &Handle<Asset>, - ) -> bool + pub fn is_loaded_and_has_type<AssetT>(&self, handle: &Handle<AssetT>) -> bool + where + AssetT: Asset, { let Some(id) = handle.id.into_valid() else { return false; @@ -260,13 +251,12 @@ impl Assets unreachable!(); }; - stored_asset.strong.downcast_ref::<Asset>().is_some() + stored_asset.strong.downcast_ref::<AssetT>().is_some() } - pub fn get_label<Asset: 'static + Send + Sync>( - &self, - handle: &Handle<Asset>, - ) -> Option<LabelOwned> + pub fn get_label<AssetT>(&self, handle: &Handle<AssetT>) -> Option<LabelOwned> + where + AssetT: Asset, { let id = handle.id.into_valid()?; @@ -293,10 +283,9 @@ impl Assets } #[tracing::instrument(skip(self))] - pub fn load<'i, Asset: 'static + Send + Sync>( - &self, - label: impl Into<Label<'i>> + Debug, - ) -> Handle<Asset> + pub fn load<'i, AssetT>(&self, label: impl Into<Label<'i>> + Debug) -> Handle<AssetT> + where + AssetT: Asset, { let label = label.into(); @@ -309,7 +298,7 @@ impl Assets } let Some(lookup_entry) = asset_lookup.get(&label_hash) else { - self.add_import_work::<Infallible>( + self.add_import_work::<AssetT, Infallible>( &label, label_hash, None, @@ -325,8 +314,8 @@ impl Assets unreachable!(); }; - if stored_asset.strong.downcast_ref::<Asset>().is_none() { - tracing::error!("Wrong asset type {}", type_name::<Asset>()); + if stored_asset.strong.downcast_ref::<AssetT>().is_none() { + tracing::error!("Wrong asset type {}", type_name::<AssetT>()); } } LookupEntry::Pending => {} @@ -336,13 +325,13 @@ impl Assets } #[tracing::instrument(skip(self))] - pub fn load_with_settings<'i, Asset, AssetSettings>( + pub fn load_with_settings<'i, AssetT, AssetSettings>( &self, label: impl Into<Label<'i>> + Debug, asset_settings: AssetSettings, - ) -> Handle<Asset> + ) -> Handle<AssetT> where - Asset: Send + Sync + 'static, + AssetT: Asset, AssetSettings: Send + Sync + Debug + 'static, { let label = label.into(); @@ -356,7 +345,7 @@ impl Assets } let Some(lookup_entry) = asset_lookup.get(&label_hash) else { - self.add_import_work::<AssetSettings>( + self.add_import_work::<AssetT, AssetSettings>( &label, label_hash, Some(asset_settings), @@ -372,10 +361,10 @@ impl Assets unreachable!(); }; - if stored_asset.strong.downcast_ref::<Asset>().is_none() { + if stored_asset.strong.downcast_ref::<AssetT>().is_none() { tracing::error!( "Wrong asset type {} for asset", - type_name::<Asset>() + type_name::<AssetT>() ); } } @@ -385,11 +374,13 @@ impl Assets Handle::new(label_hash) } - pub fn store_with_name<'name, Asset: 'static + Send + Sync>( + pub fn store_with_name<'name, AssetT>( &mut self, name: impl Into<Cow<'name, str>>, - asset: Asset, - ) -> Handle<Asset> + asset: AssetT, + ) -> Handle<AssetT> + where + AssetT: Asset, { self.store_with_label( Label { @@ -400,11 +391,13 @@ impl Assets ) } - pub fn store_with_name_with<'name, Asset: 'static + Send + Sync>( + pub fn store_with_name_with<'name, AssetT>( &mut self, name: impl Into<Cow<'name, str>>, - func: impl FnOnce(&mut Self) -> Asset, - ) -> Handle<Asset> + func: impl FnOnce(&mut Self) -> AssetT, + ) -> Handle<AssetT> + where + AssetT: Asset, { let asset = func(self); @@ -417,12 +410,14 @@ impl Assets ) } - #[tracing::instrument(skip(self, asset), fields(asset_type=type_name::<Asset>()))] - pub fn store_with_label<'i, Asset: 'static + Send + Sync>( + #[tracing::instrument(skip(self, asset), fields(asset_type=type_name::<AssetT>()))] + pub fn store_with_label<'i, AssetT>( &mut self, label: impl Into<Label<'i>> + Debug, - asset: Asset, - ) -> Handle<Asset> + asset: AssetT, + ) -> Handle<AssetT> + where + AssetT: Asset, { let label = label.into(); @@ -483,25 +478,18 @@ impl Assets false } - fn add_import_work<AssetSettings>( + fn add_import_work<AssetT, AssetSettings>( &self, label: &Label<'_>, label_hash: LabelHash, asset_settings: Option<AssetSettings>, asset_lookup: &mut HashMap<LabelHash, LookupEntry>, ) where + AssetT: Asset, AssetSettings: Any + Send + Sync, { - let Some(file_ext) = label.path.extension() else { - tracing::error!("Asset file is missing a file extension"); - return; - }; - - let Some(importer) = self.get_importer(file_ext) else { - tracing::error!( - "No importer exists for asset file extension {}", - file_ext.to_string_lossy() - ); + let Some(importer) = self.importers.get(&AssetTypeId::of::<AssetT>()) else { + tracing::error!("No importer exists for asset"); return; }; @@ -546,17 +534,6 @@ impl Assets ); } } - - fn get_importer(&self, file_ext: &OsStr) -> Option<&WrappedImporterFn> - { - let index = *self.importer_lookup.get(file_ext)?; - - let Some(importer) = self.importers.get(index) else { - unreachable!(); - }; - - Some(importer) - } } impl Default for Assets @@ -575,16 +552,18 @@ pub struct Submitter<'path> impl Submitter<'_> { - pub fn submit_load_other<'label, Asset: Send + Sync + 'static>( + pub fn submit_load_other<'label, AssetT>( &self, label: impl Into<Label<'label>>, - ) -> Handle<Asset> + ) -> Handle<AssetT> + where + AssetT: Asset, { let label = label.into(); let _ = self.import_work_msg_sender.send(ImportWorkMessage::Load { do_load: |assets, label, _asset_settings| { - let _ = assets.load::<Asset>(label); + let _ = assets.load::<AssetT>(label); }, label: label.to_owned(), asset_settings: None, @@ -593,13 +572,13 @@ impl Submitter<'_> Handle::new(LabelHash::new(&label)) } - pub fn submit_load_other_with_settings<'label, Asset, AssetSettings>( + pub fn submit_load_other_with_settings<'label, AssetT, AssetSettings>( &self, label: impl Into<Label<'label>>, asset_settings: AssetSettings, - ) -> Handle<Asset> + ) -> Handle<AssetT> where - Asset: Send + Sync + 'static, + AssetT: Asset, AssetSettings: Send + Sync + Debug + 'static, { let label = label.into(); @@ -612,7 +591,7 @@ impl Submitter<'_> .expect("Not possible"); let _ = assets - .load_with_settings::<Asset, AssetSettings>(label, asset_settings); + .load_with_settings::<AssetT, AssetSettings>(label, asset_settings); }, label: label.to_owned(), asset_settings: Some(Box::new(asset_settings)), @@ -621,10 +600,9 @@ impl Submitter<'_> Handle::new(LabelHash::new(&label)) } - pub fn submit_store<Asset: Send + Sync + 'static>( - &self, - asset: Asset, - ) -> Handle<Asset> + pub fn submit_store<AssetT>(&self, asset: AssetT) -> Handle<AssetT> + where + AssetT: Asset, { let label = LabelOwned { path: self.asset_path.into(), @@ -635,11 +613,11 @@ impl Submitter<'_> let _ = self.import_work_msg_sender.send(ImportWorkMessage::Store { do_store: |assets, label, boxed_asset| { - let Ok(asset) = boxed_asset.downcast::<Asset>() else { + let Ok(asset) = boxed_asset.downcast::<AssetT>() else { unreachable!(); }; - assets.store_with_label::<Asset>(&label, *asset); + assets.store_with_label::<AssetT>(&label, *asset); }, label, asset: Box::new(asset), @@ -648,11 +626,13 @@ impl Submitter<'_> Handle::new(label_hash) } - pub fn submit_store_named<Asset: Send + Sync + 'static>( + pub fn submit_store_named<AssetT>( &self, name: impl AsRef<str>, - asset: Asset, - ) -> Handle<Asset> + asset: AssetT, + ) -> Handle<AssetT> + where + AssetT: Asset, { let label = LabelOwned { path: self.asset_path.into(), @@ -663,11 +643,11 @@ impl Submitter<'_> let _ = self.import_work_msg_sender.send(ImportWorkMessage::Store { do_store: |assets, label, boxed_asset| { - let Ok(asset) = boxed_asset.downcast::<Asset>() else { + let Ok(asset) = boxed_asset.downcast::<AssetT>() else { unreachable!(); }; - assets.store_with_label::<Asset>(&label, *asset); + assets.store_with_label::<AssetT>(&label, *asset); }, label, asset: Box::new(asset), @@ -679,13 +659,13 @@ impl Submitter<'_> /// Asset handle. #[derive(Debug)] -pub struct Handle<Asset: 'static> +pub struct Handle<AssetT: Asset> { id: Id, - _pd: PhantomData<Asset>, + _pd: PhantomData<AssetT>, } -impl<Asset: 'static> Handle<Asset> +impl<AssetT: Asset> Handle<AssetT> { pub fn invalid() -> Self { @@ -712,7 +692,7 @@ impl<Asset: 'static> Handle<Asset> } } -impl<Asset: 'static> Clone for Handle<Asset> +impl<AssetT: Asset> Clone for Handle<AssetT> { fn clone(&self) -> Self { @@ -974,10 +954,24 @@ struct StoredAsset impl StoredAsset { - fn new<Asset: Any + Send + Sync>(asset: Asset) -> Self + fn new<AssetT: Asset>(asset: AssetT) -> Self { let strong = Arc::new(asset); Self { strong } } } + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +struct AssetTypeId +{ + inner: TypeId, +} + +impl AssetTypeId +{ + fn of<AssetT: Asset>() -> Self + { + Self { inner: TypeId::of::<AssetT>() } + } +} diff --git a/engine/src/material.rs b/engine/src/material.rs index fc9fcd7..bf07b72 100644 --- a/engine/src/material.rs +++ b/engine/src/material.rs @@ -1,4 +1,4 @@ -use crate::asset::Handle as AssetHandle; +use crate::asset::{Asset, Handle as AssetHandle}; use crate::builder; use crate::color::{Color, Rgb}; use crate::ecs::Component; @@ -44,6 +44,8 @@ impl Default for Material } } +impl Asset for Material {} + /// [`Material`] builder. #[derive(Debug, Clone)] pub struct Builder diff --git a/engine/src/material/asset.rs b/engine/src/material/asset.rs index f87b768..f452f54 100644 --- a/engine/src/material/asset.rs +++ b/engine/src/material/asset.rs @@ -1,17 +1,9 @@ -use std::borrow::Cow; -use std::collections::HashMap; use std::fs::read_to_string; use std::path::{Path, PathBuf}; -use crate::asset::{Assets, Handle as AssetHandle, Submitter as AssetSubmitter}; +use crate::asset::{Assets, Submitter as AssetSubmitter}; use crate::material::Material; -#[derive(Debug, Clone)] -pub struct Map -{ - pub assets: HashMap<Cow<'static, str>, AssetHandle<Material>>, -} - /// Material asset import settings. #[derive(Debug)] #[non_exhaustive] @@ -19,15 +11,19 @@ pub struct Settings {} pub fn add_importers(assets: &mut Assets) { - assets.set_importer(["mtl"], import_wavefront_mtl_asset); + assets.set_importer::<Material, Settings, Error>(import); } -fn import_wavefront_mtl_asset( +fn import( asset_submitter: &mut AssetSubmitter<'_>, path: &Path, _settings: Option<&'_ Settings>, ) -> Result<(), Error> { + if path.extension().is_none_or(|ext| ext != "mtl") { + return Err(Error::UnsupportedFileType); + } + let parent_path = path .parent() .ok_or_else(|| Error::InvalidPath(path.to_path_buf()))?; @@ -71,6 +67,9 @@ fn import_wavefront_mtl_asset( #[derive(Debug, thiserror::Error)] enum Error { + #[error("File type is unsupported")] + UnsupportedFileType, + #[error("Invalid path '{}'", .0.display())] InvalidPath(PathBuf), diff --git a/engine/src/mesh.rs b/engine/src/mesh.rs index 771f7bd..994387a 100644 --- a/engine/src/mesh.rs +++ b/engine/src/mesh.rs @@ -2,6 +2,7 @@ use std::alloc::Layout; use zerocopy::IntoBytes; +use crate::asset::Asset; use crate::data_types::dimens::Dimens3; use crate::mesh::vertex_buffer::{VertexAttrProperties, VertexLabel}; use crate::vector::{Vec2, Vec3}; @@ -312,6 +313,8 @@ impl Mesh } } +impl Asset for Mesh {} + /// Mesh builder #[derive(Debug, Clone, Default)] pub struct Builder diff --git a/engine/src/model.rs b/engine/src/model.rs index 560c43d..88d2622 100644 --- a/engine/src/model.rs +++ b/engine/src/model.rs @@ -1,4 +1,4 @@ -use crate::asset::{Assets, Handle as AssetHandle}; +use crate::asset::{Asset, Assets, Handle as AssetHandle}; use crate::ecs::Component; use crate::material::Material; use crate::mesh::Mesh; @@ -55,6 +55,8 @@ impl Spec } } +impl Asset for Spec {} + #[derive(Debug, Default, Clone)] pub struct SpecBuilder { diff --git a/engine/src/model/asset.rs b/engine/src/model/asset.rs index ff5eddf..af3e1b2 100644 --- a/engine/src/model/asset.rs +++ b/engine/src/model/asset.rs @@ -42,15 +42,19 @@ impl Default for Settings pub fn add_importers(assets: &mut Assets) { - assets.set_importer(["obj"], import_wavefront_obj_asset); + assets.set_importer::<Spec, Settings, Error>(import); } -fn import_wavefront_obj_asset( +fn import( asset_submitter: &mut AssetSubmitter<'_>, path: &Path, settings: Option<&Settings>, ) -> Result<(), Error> { + if path.extension().is_none_or(|ext| ext != "obj") { + return Err(Error::UnsupportedFileType); + } + let settings = match settings { Some(settings) => settings, None => &Settings::default(), @@ -107,6 +111,9 @@ fn import_wavefront_obj_asset( #[derive(Debug, thiserror::Error)] enum Error { + #[error("File type is unsupported")] + UnsupportedFileType, + #[error("Invalid path '{}'", .0.display())] InvalidPath(PathBuf), diff --git a/engine/src/rendering.rs b/engine/src/rendering.rs index bf0ab4e..0e78356 100644 --- a/engine/src/rendering.rs +++ b/engine/src/rendering.rs @@ -4,7 +4,7 @@ use std::sync::atomic::{AtomicU64, Ordering}; use bitflags::bitflags; use engine_macros::Reflection; -use crate::asset::Handle as AssetHandle; +use crate::asset::{Asset, Handle as AssetHandle}; use crate::builder; use crate::data_types::dimens::Dimens; use crate::draw_flags::PolygonModeConfig; @@ -265,7 +265,7 @@ pub enum Command } #[derive(Debug)] -pub enum AssetOrValue<T: 'static> +pub enum AssetOrValue<T: Asset> { Asset(AssetHandle<T>), Value(T), diff --git a/engine/src/rendering/shader.rs b/engine/src/rendering/shader.rs index 085773b..061d9ac 100644 --- a/engine/src/rendering/shader.rs +++ b/engine/src/rendering/shader.rs @@ -22,6 +22,7 @@ use shader_slang::{ }; use crate::asset::{ + Asset, Assets, Event as AssetEvent, Handle as AssetHandle, @@ -53,6 +54,8 @@ pub struct ModuleSource pub link_entrypoints: EntrypointFlags, } +impl Asset for ModuleSource {} + bitflags! { #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] pub struct EntrypointFlags: usize @@ -1262,12 +1265,16 @@ struct VarInput<'a> index: usize, } -fn import_slang_asset( +fn import( asset_submitter: &mut AssetSubmitter<'_>, file_path: &Path, settings: Option<&'_ Settings>, ) -> Result<(), ImportError> { + if file_path.extension().is_none_or(|ext| ext != "slang") { + return Err(ImportError::UnsupportedFileType); + } + let file_name = file_path .file_name() .ok_or(ImportError::NoPathFileName)? @@ -1295,6 +1302,9 @@ fn import_slang_asset( #[derive(Debug, thiserror::Error)] enum ImportError { + #[error("File type is unsupported")] + UnsupportedFileType, + #[error("Failed to read file")] ReadFileFailed(#[source] std::io::Error), @@ -1362,7 +1372,7 @@ fn initialize(mut assets: Single<Assets>) -> Result<(), crate::Error> { let assets = assets.get_mut()?; - assets.set_importer::<_, _>(["slang"], import_slang_asset); + assets.set_importer::<ModuleSource, Settings, ImportError>(import); Ok(()) } diff --git a/engine/src/texture.rs b/engine/src/texture.rs index 844d9eb..54646e8 100644 --- a/engine/src/texture.rs +++ b/engine/src/texture.rs @@ -1,6 +1,6 @@ use std::path::Path; -use crate::asset::{Assets, Submitter as AssetSubmitter}; +use crate::asset::{Asset, Assets, Submitter as AssetSubmitter}; use crate::builder; use crate::color::Rgba; use crate::image::{Error as ImageError, Image}; @@ -12,6 +12,8 @@ pub enum Texture CubeMap(TexCubeMap), } +impl Asset for Texture {} + #[derive(Debug, Clone)] pub struct Tex2D { @@ -136,7 +138,7 @@ impl Default for ImportSettingsBuilder pub(crate) fn initialize(assets: &mut Assets) { - assets.set_importer::<_, _>(["png", "jpg"], import); + assets.set_importer::<Texture, ImportSettings, ImageError>(import); } fn import( |
