Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Support for Extension-less Assets #10153

Merged
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
a484322
Added Support for Extenstion-less Assets
bushrat011899 Oct 17, 2023
4a47930
Explicit Error Handling in Asset Loading
bushrat011899 Oct 17, 2023
cf3a9c6
Update assets/data/asset_no_extension
bushrat011899 Oct 17, 2023
097937e
Update crates/bevy_asset/src/server/loaders.rs
bushrat011899 Oct 17, 2023
dd172dc
Added Example of Multiple Assets Using the Same Path
bushrat011899 Oct 17, 2023
4598ae2
Removed Debug Message Accidentally Left Behind
bushrat011899 Oct 17, 2023
8010640
Merge remote-tracking branch 'upstream/main' into AssetLoadingWithout…
bushrat011899 Nov 16, 2023
e3fde60
Fixes for Upstream Merge
bushrat011899 Nov 16, 2023
83e0a25
Fixed up some naming
bushrat011899 Nov 19, 2023
9a43f3b
Fixed `AssetLoader` Resolution and Added Inferred Direct Loading
bushrat011899 Nov 19, 2023
7a10b54
Removed Redundant Error Case in `load_direct_with_reader`
bushrat011899 Nov 22, 2023
5069e31
Updated `TODO` in `AssetInfos::get_or_create_path_handle_internal`
bushrat011899 Nov 22, 2023
85e3915
Updated `AssetInofs::get_path_handles` to return `impl Iterator`
bushrat011899 Nov 22, 2023
80e9287
Updated `AssetInfos::path_to_id` to be a 2-stage `HashMap`
bushrat011899 Nov 23, 2023
45448e9
Updated `AssetServer::get_handle` to avoid iteration
bushrat011899 Nov 23, 2023
190befd
Merge remote-tracking branch 'upstream/main' into AssetLoadingWithout…
bushrat011899 Jan 30, 2024
13a9ead
Resolved Merge Conflicts
bushrat011899 Jan 30, 2024
19f6789
Delete loaders.rs
bushrat011899 Jan 30, 2024
b75dbe4
Clarified File Extension Recommendation
bushrat011899 Jan 31, 2024
63379e8
Fixed Incompatibility with Hot Reloading
bushrat011899 Jan 31, 2024
476ca45
Merge remote-tracking branch 'upstream/main' into AssetLoadingWithout…
bushrat011899 Jan 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions assets/data/asset_no_extension
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CustomAsset (
value: 13
)
5 changes: 1 addition & 4 deletions crates/bevy_asset/src/io/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,10 +248,7 @@ impl AsyncRead for VecReader {
/// Appends `.meta` to the given path.
pub(crate) fn get_meta_path(path: &Path) -> PathBuf {
let mut meta_path = path.to_path_buf();
let mut extension = path
.extension()
.expect("asset paths must have extensions")
.to_os_string();
let mut extension = path.extension().unwrap_or_default().to_os_string();
extension.push(".meta");
meta_path.set_extension(extension);
bushrat011899 marked this conversation as resolved.
Show resolved Hide resolved
meta_path
Expand Down
90 changes: 87 additions & 3 deletions crates/bevy_asset/src/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ impl<'a> LoadContext<'a> {
/// See [`AssetPath`] for more on labeled assets.
pub fn has_labeled_asset<'b>(&self, label: impl Into<CowArc<'b, str>>) -> bool {
let path = self.asset_path.clone().with_label(label.into());
self.asset_server.get_handle_untyped(&path).is_some()
!self.asset_server.get_handles_untyped(&path).is_empty()
}

/// "Finishes" this context by populating the final [`Asset`] value (and the erased [`AssetMeta`] value, if it exists).
Expand Down Expand Up @@ -539,7 +539,7 @@ impl<'a> LoadContext<'a> {
let loaded_asset = {
let (meta, loader, mut reader) = self
.asset_server
.get_meta_loader_and_reader(&path)
.get_meta_loader_and_reader(&path, None)
.await
.map_err(to_error)?;
self.asset_server
Expand Down Expand Up @@ -573,7 +573,7 @@ impl<'a> LoadContext<'a> {
///
/// [`Process`]: crate::processor::Process
/// [`LoadAndSave`]: crate::processor::LoadAndSave
pub async fn load_direct_with_reader<'b>(
pub async fn load_direct_with_reader_and_path<'b>(
&mut self,
reader: &mut Reader<'_>,
path: impl Into<AssetPath<'b>>,
Expand Down Expand Up @@ -618,6 +618,90 @@ impl<'a> LoadContext<'a> {

Ok(loaded_asset)
}

/// Loads the asset from the given `reader` directly as the asset type `type_id`. This is an async function that will wait until the asset is fully loaded before
/// returning. Use this if you need the _value_ of another asset in order to load the current asset, and that value comes from your [`Reader`].
/// For example, if you are deriving a new asset from the referenced asset, or you are building a collection of assets.
pub async fn load_direct_with_reader_and_type_id<'b>(
&mut self,
reader: &mut Reader<'_>,
type_id: TypeId,
) -> Result<ErasedLoadedAsset, LoadDirectError> {
let loader = self
.asset_server
.get_asset_loader_with_asset_type_id(type_id)
.await
.map_err(|error| LoadDirectError {
dependency: self.asset_path.clone_owned(),
error: error.into(),
})?;

let meta = loader.default_meta();

let load_context = LoadContext::new(
self.asset_server,
self.asset_path.clone_owned(),
self.should_load_dependencies,
self.populate_hashes,
);

let loaded_asset = loader
.load(reader, meta, load_context)
.await
.map_err(|error| LoadDirectError {
dependency: self.asset_path.clone_owned(),
error: AssetLoadError::AssetLoaderError {
path: self.asset_path.clone_owned(),
loader_name: loader.type_name(),
error,
},
})?;

Ok(loaded_asset)
}

/// Loads the asset from the given `reader` directly as the asset type `type_id`. This is an async function that will wait until the asset is fully loaded before
/// returning. Use this if you need the _value_ of another asset in order to load the current asset, and that value comes from your [`Reader`].
/// For example, if you are deriving a new asset from the referenced asset, or you are building a collection of assets.
pub async fn load_direct_with_reader<'b, A: Asset>(
&mut self,
reader: &mut Reader<'_>,
) -> Result<A, LoadDirectError> {
let loader = self
.asset_server
.get_asset_loader_with_asset_type::<A>()
.await
.map_err(|error| LoadDirectError {
dependency: self.asset_path.clone_owned(),
error: error.into(),
})?;

let meta = loader.default_meta();

let load_context = LoadContext::new(
self.asset_server,
self.asset_path.clone_owned(),
self.should_load_dependencies,
self.populate_hashes,
);

let loaded_asset = loader
.load(reader, meta, load_context)
.await
.map_err(|error| LoadDirectError {
dependency: self.asset_path.clone_owned(),
error: AssetLoadError::AssetLoaderError {
path: self.asset_path.clone_owned(),
loader_name: loader.type_name(),
error,
},
})?
.take::<A>()
// AssetServer::get_asset_loader_with_asset_type::<A> can only return an `AssetLoader<Asset = A>`
.unwrap();

Ok(loaded_asset)
}
bushrat011899 marked this conversation as resolved.
Show resolved Hide resolved
}

/// An error produced when calling [`LoadContext::read_asset_bytes`]
Expand Down
93 changes: 75 additions & 18 deletions crates/bevy_asset/src/server/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ impl AssetInfo {

#[derive(Default)]
pub(crate) struct AssetInfos {
path_to_id: HashMap<AssetPath<'static>, UntypedAssetId>,
path_to_id: HashMap<AssetPath<'static>, HashMap<TypeId, UntypedAssetId>>,
bushrat011899 marked this conversation as resolved.
Show resolved Hide resolved
infos: HashMap<UntypedAssetId, AssetInfo>,
/// If set to `true`, this informs [`AssetInfos`] to track data relevant to watching for changes (such as `load_dependants`)
/// This should only be set at startup.
Expand Down Expand Up @@ -189,7 +189,17 @@ impl AssetInfos {
loading_mode: HandleLoadingMode,
meta_transform: Option<MetaTransform>,
) -> Result<(UntypedHandle, bool), GetOrCreateHandleInternalError> {
match self.path_to_id.entry(path.clone()) {
// TODO: It is not currently possible to create or get a `Handle` without the `type_id` parameter.
// This function should
bushrat011899 marked this conversation as resolved.
Show resolved Hide resolved
let type_id =
bushrat011899 marked this conversation as resolved.
Show resolved Hide resolved
type_id.ok_or(GetOrCreateHandleInternalError::HandleMissingButTypeIdNotSpecified)?;

match self
.path_to_id
.entry(path.clone())
.or_default()
.entry(type_id)
{
Entry::Occupied(entry) => {
let id = *entry.get();
// if there is a path_to_id entry, info always exists
Expand Down Expand Up @@ -219,9 +229,6 @@ impl AssetInfos {
// We must create a new strong handle for the existing id and ensure that the drop of the old
// strong handle doesn't remove the asset from the Assets collection
info.handle_drops_to_skip += 1;
let type_id = type_id.ok_or(
GetOrCreateHandleInternalError::HandleMissingButTypeIdNotSpecified,
)?;
let provider = self
.handle_providers
.get(&type_id)
Expand All @@ -238,8 +245,6 @@ impl AssetInfos {
HandleLoadingMode::NotLoading => false,
HandleLoadingMode::Request | HandleLoadingMode::Force => true,
};
let type_id = type_id
.ok_or(GetOrCreateHandleInternalError::HandleMissingButTypeIdNotSpecified)?;
let handle = Self::create_handle_internal(
&mut self.infos,
&self.handle_providers,
Expand All @@ -264,9 +269,48 @@ impl AssetInfos {
self.infos.get_mut(&id)
}

pub(crate) fn get_path_handle(&self, path: AssetPath) -> Option<UntypedHandle> {
let id = *self.path_to_id.get(&path)?;
self.get_id_handle(id)
pub(crate) fn get_path_and_type_id_handle(
&self,
path: AssetPath<'_>,
type_id: TypeId,
) -> Option<UntypedHandle> {
let id = self.path_to_id.get(&path)?.get(&type_id)?;
self.get_id_handle(*id)
}

pub(crate) fn get_path_handles<'a>(
&'a self,
path: AssetPath<'a>,
) -> impl Iterator<Item = UntypedHandle> + 'a {
/// Concrete type to allow returning an `impl Iterator` even if `self.path_to_id.get(&path)` is `None`
enum HandlesByPathIterator<T> {
None,
Some(T),
}

impl<T> Iterator for HandlesByPathIterator<T>
where
T: Iterator<Item = UntypedHandle>,
{
type Item = UntypedHandle;

fn next(&mut self) -> Option<Self::Item> {
match self {
HandlesByPathIterator::None => None,
HandlesByPathIterator::Some(iter) => iter.next(),
}
}
}

if let Some(type_id_to_id) = self.path_to_id.get(&path) {
HandlesByPathIterator::Some(
type_id_to_id
.values()
.filter_map(|id| self.get_id_handle(*id)),
)
} else {
HandlesByPathIterator::None
}
}

pub(crate) fn get_id_handle(&self, id: UntypedAssetId) -> Option<UntypedHandle> {
Expand All @@ -278,12 +322,15 @@ impl AssetInfos {
/// Returns `true` if the asset this path points to is still alive
pub(crate) fn is_path_alive<'a>(&self, path: impl Into<AssetPath<'a>>) -> bool {
let path = path.into();
if let Some(id) = self.path_to_id.get(&path) {
if let Some(info) = self.infos.get(id) {
return info.weak_handle.strong_count() > 0;
}
}
false

let Some(type_id_to_id) = self.path_to_id.get(&path) else {
return false;
};

type_id_to_id
.values()
.filter_map(|id| self.infos.get(id))
.any(|info| info.weak_handle.strong_count() > 0)
}

/// Returns `true` if the asset at this path should be reloaded
Expand Down Expand Up @@ -552,7 +599,7 @@ impl AssetInfos {

fn process_handle_drop_internal(
infos: &mut HashMap<UntypedAssetId, AssetInfo>,
path_to_id: &mut HashMap<AssetPath<'static>, UntypedAssetId>,
path_to_id: &mut HashMap<AssetPath<'static>, HashMap<TypeId, UntypedAssetId>>,
loader_dependants: &mut HashMap<AssetPath<'static>, HashSet<AssetPath<'static>>>,
living_labeled_assets: &mut HashMap<AssetPath<'static>, HashSet<String>>,
watching_for_changes: bool,
Expand Down Expand Up @@ -587,7 +634,17 @@ impl AssetInfos {
};
}
}
path_to_id.remove(&path);

if let Some(type_id_to_id) = path_to_id.get_mut(&path) {
type_id_to_id.remove(&id.type_id());
}

if path_to_id
.get(&path)
.is_some_and(|type_id_to_id| type_id_to_id.is_empty())
{
path_to_id.remove(&path);
}
}
true
}
Expand Down
Loading