Make LoadContext use the builder pattern for loading dependent assets (#13465)

# Objective
- Fixes #13445.

## Solution
- Removes all `load_` methods from `LoadContext`.
- Introduces `fn loader()` which returns a builder.

## Testing
- I've tested with `cargo test --package=bevy_asset` and run the two
relevant examples (`asset_processing` & `asset_decompression`).

---

## Changelog
- Replaced all `load_` methods on `LoadContext` with the new `loader()`
pattern.

## Migration Guide
- Several LoadContext method calls will need to be updated:
- `load_context.load_with_settings(path, settings)` =>
`load_context.loader().with_settings(settings).load(path)`
- `load_context.load_untyped(path)` =>
`load_context.loader().untyped().load(path)`
- `load_context.load_direct(path)` =>
`load_context.loader().direct().load(path)`
- `load_context.load_direct_untyped(path)` =>
`load_context.loader().direct().untyped().load(path)`
- `load_context.load_direct_with_settings(path, settings)` =>
`load_context.loader().with_settings(settings).direct().load(path)`
- `load_context.load_direct_with_reader(reader, path)` =>
`load_context.loader().direct().with_reader(reader).load(path)`
- `load_context.load_direct_with_reader_and_settings(reader, path,
settings)` =>
`load_context.loader().with_settings(settings).direct().with_reader(reader).load(path)`
- `load_context.load_direct_untyped_with_reader(reader, path)` =>
`load_context.loader().direct().with_reader(reader).untyped().load(path)`

---

CC @alice-i-cecile / @bushrat011899 

Examples:
```rust
load_context.loader()
    .with_asset_type::<A>()
    .with_asset_type_id(TypeId::of::<A>())
    .with_settings(|mut settings| { settings.key = value; })
    // Then, for a Handle<A>:
    .load::<A>()
    // Or, for a Handle<LoadedUntypedAsset>:
    .untyped()
    .load()
    // Or, to load an `A` directly:
    .direct()
    .load::<A>()
    .await
    // Or, to load an `ErasedLoadedAsset` directly:
    .direct()
    .untyped()
    .load()
    .await
```
This commit is contained in:
Ricky Taylor 2024-05-23 00:35:41 +01:00 committed by GitHub
parent faf003fc9d
commit efcb6d6c11
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 387 additions and 311 deletions

View File

@ -27,6 +27,7 @@ mod folder;
mod handle;
mod id;
mod loader;
mod loader_builders;
mod path;
mod reflect;
mod server;
@ -40,6 +41,9 @@ pub use futures_lite::{AsyncReadExt, AsyncWriteExt};
pub use handle::*;
pub use id::*;
pub use loader::*;
pub use loader_builders::{
DirectNestedLoader, NestedLoader, UntypedDirectNestedLoader, UntypedNestedLoader,
};
pub use path::*;
pub use reflect::*;
pub use server::*;
@ -511,7 +515,9 @@ mod tests {
let mut embedded = String::new();
for dep in ron.embedded_dependencies {
let loaded = load_context
.load_direct::<CoolText>(&dep)
.loader()
.direct()
.load::<CoolText>(&dep)
.await
.map_err(|_| Self::Error::CannotLoadDependency {
dependency: dep.into(),

View File

@ -1,12 +1,10 @@
use crate::{
io::{AssetReaderError, MissingAssetSourceError, MissingProcessedAssetReaderError, Reader},
meta::{
loader_settings_meta_transform, meta_transform_settings, AssetHash, AssetMeta,
AssetMetaDyn, ProcessedInfoMinimal, Settings,
},
loader_builders::NestedLoader,
meta::{AssetHash, AssetMeta, AssetMetaDyn, ProcessedInfoMinimal, Settings},
path::AssetPath,
Asset, AssetLoadError, AssetServer, AssetServerMode, Assets, Handle, LoadedUntypedAsset,
UntypedAssetId, UntypedHandle,
Asset, AssetLoadError, AssetServer, AssetServerMode, Assets, Handle, UntypedAssetId,
UntypedHandle,
};
use bevy_ecs::world::World;
use bevy_utils::{BoxedFuture, ConditionalSendFuture, CowArc, HashMap, HashSet};
@ -310,14 +308,14 @@ pub enum DeserializeMetaError {
/// A context that provides access to assets in [`AssetLoader`]s, tracks dependencies, and collects asset load state.
/// Any asset state accessed by [`LoadContext`] will be tracked and stored for use in dependency events and asset preprocessing.
pub struct LoadContext<'a> {
asset_server: &'a AssetServer,
should_load_dependencies: bool,
pub(crate) asset_server: &'a AssetServer,
pub(crate) should_load_dependencies: bool,
populate_hashes: bool,
asset_path: AssetPath<'static>,
dependencies: HashSet<UntypedAssetId>,
pub(crate) dependencies: HashSet<UntypedAssetId>,
/// Direct dependencies used by this loader.
loader_dependencies: HashMap<AssetPath<'static>, AssetHash>,
labeled_assets: HashMap<CowArc<'static, str>, LabeledAsset>,
pub(crate) loader_dependencies: HashMap<AssetPath<'static>, AssetHash>,
pub(crate) labeled_assets: HashMap<CowArc<'static, str>, LabeledAsset>,
}
impl<'a> LoadContext<'a> {
@ -503,57 +501,6 @@ impl<'a> LoadContext<'a> {
Ok(bytes)
}
/// Retrieves a handle for the asset at the given path and adds that path as a dependency of the asset.
/// If the current context is a normal [`AssetServer::load`], an actual asset load will be kicked off immediately, which ensures the load happens
/// as soon as possible.
/// "Normal loads" kicked from within a normal Bevy App will generally configure the context to kick off loads immediately.
/// If the current context is configured to not load dependencies automatically (ex: [`AssetProcessor`](crate::processor::AssetProcessor)),
/// a load will not be kicked off automatically. It is then the calling context's responsibility to begin a load if necessary.
pub fn load<'b, A: Asset>(&mut self, path: impl Into<AssetPath<'b>>) -> Handle<A> {
let path = path.into().to_owned();
let handle = if self.should_load_dependencies {
self.asset_server.load(path)
} else {
self.asset_server.get_or_create_path_handle(path, None)
};
self.dependencies.insert(handle.id().untyped());
handle
}
/// Retrieves a handle for the asset at the given path and adds that path as a dependency of the asset without knowing its type.
pub fn load_untyped<'b>(
&mut self,
path: impl Into<AssetPath<'b>>,
) -> Handle<LoadedUntypedAsset> {
let path = path.into().to_owned();
let handle = if self.should_load_dependencies {
self.asset_server.load_untyped(path)
} else {
self.asset_server.get_or_create_path_handle(path, None)
};
self.dependencies.insert(handle.id().untyped());
handle
}
/// Loads the [`Asset`] of type `A` at the given `path` with the given [`AssetLoader::Settings`] settings `S`. This is a "deferred"
/// load. If the settings type `S` does not match the settings expected by `A`'s asset loader, an error will be printed to the log
/// and the asset load will fail.
pub fn load_with_settings<'b, A: Asset, S: Settings + Default>(
&mut self,
path: impl Into<AssetPath<'b>>,
settings: impl Fn(&mut S) + Send + Sync + 'static,
) -> Handle<A> {
let path = path.into();
let handle = if self.should_load_dependencies {
self.asset_server.load_with_settings(path.clone(), settings)
} else {
self.asset_server
.get_or_create_path_handle(path, Some(loader_settings_meta_transform(settings)))
};
self.dependencies.insert(handle.id().untyped());
handle
}
/// Returns a handle to an asset of type `A` with the label `label`. This [`LoadContext`] must produce an asset of the
/// given type and the given label or the dependencies of this asset will never be considered "fully loaded". However you
/// can call this method before _or_ after adding the labeled asset.
@ -567,7 +514,7 @@ impl<'a> LoadContext<'a> {
handle
}
async fn load_direct_untyped_internal(
pub(crate) async fn load_direct_internal(
&mut self,
path: AssetPath<'static>,
meta: Box<dyn AssetMetaDyn>,
@ -598,223 +545,22 @@ impl<'a> LoadContext<'a> {
Ok(loaded_asset)
}
async fn load_direct_internal<A: Asset>(
&mut self,
path: AssetPath<'static>,
meta: Box<dyn AssetMetaDyn>,
loader: &dyn ErasedAssetLoader,
reader: &mut Reader<'_>,
) -> Result<LoadedAsset<A>, LoadDirectError> {
self.load_direct_untyped_internal(path.clone(), meta, loader, &mut *reader)
.await
.and_then(move |untyped_asset| {
untyped_asset.downcast::<A>().map_err(|_| LoadDirectError {
dependency: path.clone(),
error: AssetLoadError::RequestedHandleTypeMismatch {
path,
requested: TypeId::of::<A>(),
actual_asset_name: loader.asset_type_name(),
loader_name: loader.type_name(),
},
})
})
/// Create a builder for loading nested assets in this context.
#[must_use]
pub fn loader(&mut self) -> NestedLoader<'a, '_> {
NestedLoader::new(self)
}
async fn load_direct_untyped_with_transform(
&mut self,
path: AssetPath<'static>,
meta_transform: impl FnOnce(&mut dyn AssetMetaDyn),
) -> Result<ErasedLoadedAsset, LoadDirectError> {
let (mut meta, loader, mut reader) = self
.asset_server
.get_meta_loader_and_reader(&path, None)
.await
.map_err(|error| LoadDirectError {
dependency: path.clone(),
error,
})?;
meta_transform(&mut *meta);
self.load_direct_untyped_internal(path.clone(), meta, &*loader, &mut *reader)
.await
}
async fn load_direct_with_transform<A: Asset>(
&mut self,
path: AssetPath<'static>,
meta_transform: impl FnOnce(&mut dyn AssetMetaDyn),
) -> Result<LoadedAsset<A>, LoadDirectError> {
let (mut meta, loader, mut reader) = self
.asset_server
.get_meta_loader_and_reader(&path, Some(TypeId::of::<A>()))
.await
.map_err(|error| LoadDirectError {
dependency: path.clone(),
error,
})?;
meta_transform(&mut *meta);
self.load_direct_internal(path.clone(), meta, &*loader, &mut *reader)
.await
}
/// Loads the asset at the given `path` directly. 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. For example, if you are
/// deriving a new asset from the referenced asset, or you are building a collection of assets. This will add the `path` as a
/// "load dependency".
/// Retrieves a handle for the asset at the given path and adds that path as a dependency of the asset.
/// If the current context is a normal [`AssetServer::load`], an actual asset load will be kicked off immediately, which ensures the load happens
/// as soon as possible.
/// "Normal loads" kicked from within a normal Bevy App will generally configure the context to kick off loads immediately.
/// If the current context is configured to not load dependencies automatically (ex: [`AssetProcessor`](crate::processor::AssetProcessor)),
/// a load will not be kicked off automatically. It is then the calling context's responsibility to begin a load if necessary.
///
/// If the current loader is used in a [`Process`] "asset preprocessor", such as a [`LoadTransformAndSave`] preprocessor,
/// changing a "load dependency" will result in re-processing of the asset.
///
/// [`Process`]: crate::processor::Process
/// [`LoadTransformAndSave`]: crate::processor::LoadTransformAndSave
pub async fn load_direct<'b, A: Asset>(
&mut self,
path: impl Into<AssetPath<'b>>,
) -> Result<LoadedAsset<A>, LoadDirectError> {
self.load_direct_with_transform(path.into().into_owned(), |_| {})
.await
}
/// Loads the asset at the given `path` directly. 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. For example, if you are
/// deriving a new asset from the referenced asset, or you are building a collection of assets. This will add the `path` as a
/// "load dependency".
///
/// If the current loader is used in a [`Process`] "asset preprocessor", such as a [`LoadTransformAndSave`] preprocessor,
/// changing a "load dependency" will result in re-processing of the asset.
///
/// If the settings type `S` does not match the settings expected by `A`'s asset loader, an error will be printed to the log
/// and the asset load will fail.
///
/// [`Process`]: crate::processor::Process
/// [`LoadTransformAndSave`]: crate::processor::LoadTransformAndSave
pub async fn load_direct_with_settings<'b, A: Asset, S: Settings + Default>(
&mut self,
path: impl Into<AssetPath<'b>>,
settings: impl Fn(&mut S) + Send + Sync + 'static,
) -> Result<LoadedAsset<A>, LoadDirectError> {
self.load_direct_with_transform(path.into().into_owned(), move |meta| {
meta_transform_settings(meta, &settings);
})
.await
}
/// Loads the asset at the given `path` directly. 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. For example, if you are
/// deriving a new asset from the referenced asset, or you are building a collection of assets. This will add the `path` as a
/// "load dependency".
///
/// If the current loader is used in a [`Process`] "asset preprocessor", such as a [`LoadTransformAndSave`] preprocessor,
/// changing a "load dependency" will result in re-processing of the asset.
///
/// [`Process`]: crate::processor::Process
/// [`LoadTransformAndSave`]: crate::processor::LoadTransformAndSave
pub async fn load_direct_untyped<'b>(
&mut self,
path: impl Into<AssetPath<'b>>,
) -> Result<ErasedLoadedAsset, LoadDirectError> {
self.load_direct_untyped_with_transform(path.into().into_owned(), |_| {})
.await
}
/// Loads the asset at the given `path` directly from the provided `reader`. 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. This will add the `path` as a
/// "load dependency".
///
/// If the current loader is used in a [`Process`] "asset preprocessor", such as a [`LoadTransformAndSave`] preprocessor,
/// changing a "load dependency" will result in re-processing of the asset.
///
/// [`Process`]: crate::processor::Process
/// [`LoadTransformAndSave`]: crate::processor::LoadTransformAndSave
pub async fn load_direct_with_reader<'b, A: Asset>(
&mut self,
reader: &mut Reader<'_>,
path: impl Into<AssetPath<'b>>,
) -> Result<LoadedAsset<A>, LoadDirectError> {
let path = path.into().into_owned();
let loader = self
.asset_server
.get_asset_loader_with_asset_type::<A>()
.await
.map_err(|error| LoadDirectError {
dependency: path.clone(),
error: error.into(),
})?;
let meta = loader.default_meta();
self.load_direct_internal(path, meta, &*loader, reader)
.await
}
/// Loads the asset at the given `path` directly from the provided `reader`. 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. This will add the `path` as a
/// "load dependency".
///
/// If the current loader is used in a [`Process`] "asset preprocessor", such as a [`LoadTransformAndSave`] preprocessor,
/// changing a "load dependency" will result in re-processing of the asset.
///
/// If the settings type `S` does not match the settings expected by `A`'s asset loader, an error will be printed to the log
/// and the asset load will fail.
///
/// [`Process`]: crate::processor::Process
/// [`LoadTransformAndSave`]: crate::processor::LoadTransformAndSave
pub async fn load_direct_with_reader_and_settings<'b, A: Asset, S: Settings + Default>(
&mut self,
reader: &mut Reader<'_>,
path: impl Into<AssetPath<'b>>,
settings: impl Fn(&mut S) + Send + Sync + 'static,
) -> Result<LoadedAsset<A>, LoadDirectError> {
let path = path.into().into_owned();
let loader = self
.asset_server
.get_asset_loader_with_asset_type::<A>()
.await
.map_err(|error| LoadDirectError {
dependency: path.clone(),
error: error.into(),
})?;
let mut meta = loader.default_meta();
meta_transform_settings(&mut *meta, &settings);
self.load_direct_internal(path, meta, &*loader, reader)
.await
}
/// Loads the asset at the given `path` directly from the provided `reader`. 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. This will add the `path` as a
/// "load dependency".
///
/// If the current loader is used in a [`Process`] "asset preprocessor", such as a [`LoadTransformAndSave`] preprocessor,
/// changing a "load dependency" will result in re-processing of the asset.
///
/// [`Process`]: crate::processor::Process
/// [`LoadTransformAndSave`]: crate::processor::LoadTransformAndSave
pub async fn load_direct_untyped_with_reader<'b>(
&mut self,
reader: &mut Reader<'_>,
path: impl Into<AssetPath<'b>>,
) -> Result<ErasedLoadedAsset, LoadDirectError> {
let path = path.into().into_owned();
let loader = self
.asset_server
.get_path_asset_loader(&path)
.await
.map_err(|error| LoadDirectError {
dependency: path.clone(),
error: error.into(),
})?;
let meta = loader.default_meta();
self.load_direct_untyped_internal(path, meta, &*loader, reader)
.await
/// If you need to override asset settings, asset type, or load directly, please see [`LoadContext::loader`].
pub fn load<'b, A: Asset>(&mut self, path: impl Into<AssetPath<'b>>) -> Handle<A> {
self.loader().load(path)
}
}

View File

@ -0,0 +1,305 @@
//! Implementations of the builder-pattern used for loading dependent assets via
//! [`LoadContext::loader`].
use crate::{
io::Reader,
meta::{meta_transform_settings, AssetMetaDyn, MetaTransform, Settings},
Asset, AssetLoadError, AssetPath, ErasedAssetLoader, ErasedLoadedAsset, Handle, LoadContext,
LoadDirectError, LoadedAsset, LoadedUntypedAsset,
};
use std::any::TypeId;
use std::sync::Arc;
// Utility type for handling the sources of reader references
enum ReaderRef<'a, 'b> {
Borrowed(&'a mut Reader<'b>),
Boxed(Box<Reader<'b>>),
}
impl<'a, 'b> ReaderRef<'a, 'b> {
pub fn as_mut(&mut self) -> &mut Reader {
match self {
ReaderRef::Borrowed(r) => r,
ReaderRef::Boxed(b) => &mut *b,
}
}
}
/// A builder for loading nested assets inside a `LoadContext`.
///
/// # Lifetimes
/// - `ctx`: the lifetime of the associated [`AssetServer`] reference
/// - `builder`: the lifetime of the temporary builder structs
pub struct NestedLoader<'ctx, 'builder> {
load_context: &'builder mut LoadContext<'ctx>,
meta_transform: Option<MetaTransform>,
asset_type_id: Option<TypeId>,
}
impl<'ctx, 'builder> NestedLoader<'ctx, 'builder> {
pub(crate) fn new(
load_context: &'builder mut LoadContext<'ctx>,
) -> NestedLoader<'ctx, 'builder> {
NestedLoader {
load_context,
meta_transform: None,
asset_type_id: None,
}
}
fn with_transform(
mut self,
transform: impl Fn(&mut dyn AssetMetaDyn) + Send + Sync + 'static,
) -> Self {
if let Some(prev_transform) = self.meta_transform {
self.meta_transform = Some(Box::new(move |meta| {
prev_transform(meta);
transform(meta);
}));
} else {
self.meta_transform = Some(Box::new(transform));
}
self
}
/// Configure the settings used to load the asset.
///
/// If the settings type `S` does not match the settings expected by `A`'s asset loader, an error will be printed to the log
/// and the asset load will fail.
#[must_use]
pub fn with_settings<S: Settings>(
self,
settings: impl Fn(&mut S) + Send + Sync + 'static,
) -> Self {
self.with_transform(move |meta| meta_transform_settings(meta, &settings))
}
/// Specify the output asset type.
#[must_use]
pub fn with_asset_type<A: Asset>(mut self) -> Self {
self.asset_type_id = Some(TypeId::of::<A>());
self
}
/// Specify the output asset type.
#[must_use]
pub fn with_asset_type_id(mut self, asset_type_id: TypeId) -> Self {
self.asset_type_id = Some(asset_type_id);
self
}
/// Load assets directly, rather than creating handles.
#[must_use]
pub fn direct<'c>(self) -> DirectNestedLoader<'ctx, 'builder, 'c> {
DirectNestedLoader {
base: self,
reader: None,
}
}
/// Load assets without static type information.
///
/// If you need to specify the type of asset, but cannot do it statically,
/// use `.with_asset_type_id()`.
#[must_use]
pub fn untyped(self) -> UntypedNestedLoader<'ctx, 'builder> {
UntypedNestedLoader { base: self }
}
/// Retrieves a handle for the asset at the given path and adds that path as a dependency of the asset.
/// If the current context is a normal [`AssetServer::load`], an actual asset load will be kicked off immediately, which ensures the load happens
/// as soon as possible.
/// "Normal loads" kicked from within a normal Bevy App will generally configure the context to kick off loads immediately.
/// If the current context is configured to not load dependencies automatically (ex: [`AssetProcessor`](crate::processor::AssetProcessor)),
/// a load will not be kicked off automatically. It is then the calling context's responsibility to begin a load if necessary.
pub fn load<'c, A: Asset>(self, path: impl Into<AssetPath<'c>>) -> Handle<A> {
let path = path.into().to_owned();
let handle = if self.load_context.should_load_dependencies {
self.load_context
.asset_server
.load_with_meta_transform(path, self.meta_transform)
} else {
self.load_context
.asset_server
.get_or_create_path_handle(path, None)
};
self.load_context.dependencies.insert(handle.id().untyped());
handle
}
}
/// A builder for loading untyped nested assets inside a [`LoadContext`].
///
/// # Lifetimes
/// - `ctx`: the lifetime of the associated [`AssetServer`] reference
/// - `builder`: the lifetime of the temporary builder structs
pub struct UntypedNestedLoader<'ctx, 'builder> {
base: NestedLoader<'ctx, 'builder>,
}
impl<'ctx, 'builder> UntypedNestedLoader<'ctx, 'builder> {
/// Retrieves a handle for the asset at the given path and adds that path as a dependency of the asset without knowing its type.
pub fn load<'p>(self, path: impl Into<AssetPath<'p>>) -> Handle<LoadedUntypedAsset> {
let path = path.into().to_owned();
let handle = if self.base.load_context.should_load_dependencies {
self.base
.load_context
.asset_server
.load_untyped_with_meta_transform(path, self.base.meta_transform)
} else {
self.base
.load_context
.asset_server
.get_or_create_path_handle(path, self.base.meta_transform)
};
self.base
.load_context
.dependencies
.insert(handle.id().untyped());
handle
}
}
/// A builder for directly loading nested assets inside a `LoadContext`.
///
/// # Lifetimes
/// - `ctx`: the lifetime of the associated [`AssetServer`] reference
/// - `builder`: the lifetime of the temporary builder structs
/// - `reader`: the lifetime of the [`Reader`] reference used to read the asset data
pub struct DirectNestedLoader<'ctx, 'builder, 'reader> {
base: NestedLoader<'ctx, 'builder>,
reader: Option<&'builder mut Reader<'reader>>,
}
impl<'ctx: 'reader, 'builder, 'reader> DirectNestedLoader<'ctx, 'builder, 'reader> {
/// Specify the reader to use to read the asset data.
#[must_use]
pub fn with_reader(mut self, reader: &'builder mut Reader<'reader>) -> Self {
self.reader = Some(reader);
self
}
/// Load the asset without providing static type information.
///
/// If you need to specify the type of asset, but cannot do it statically,
/// use `.with_asset_type_id()`.
#[must_use]
pub fn untyped(self) -> UntypedDirectNestedLoader<'ctx, 'builder, 'reader> {
UntypedDirectNestedLoader { base: self }
}
async fn load_internal(
self,
path: &AssetPath<'static>,
) -> Result<(Arc<dyn ErasedAssetLoader>, ErasedLoadedAsset), LoadDirectError> {
let (mut meta, loader, mut reader) = if let Some(reader) = self.reader {
let loader = if let Some(asset_type_id) = self.base.asset_type_id {
self.base
.load_context
.asset_server
.get_asset_loader_with_asset_type_id(asset_type_id)
.await
.map_err(|error| LoadDirectError {
dependency: path.clone(),
error: error.into(),
})?
} else {
self.base
.load_context
.asset_server
.get_path_asset_loader(path)
.await
.map_err(|error| LoadDirectError {
dependency: path.clone(),
error: error.into(),
})?
};
let meta = loader.default_meta();
(meta, loader, ReaderRef::Borrowed(reader))
} else {
let (meta, loader, reader) = self
.base
.load_context
.asset_server
.get_meta_loader_and_reader(path, self.base.asset_type_id)
.await
.map_err(|error| LoadDirectError {
dependency: path.clone(),
error,
})?;
(meta, loader, ReaderRef::Boxed(reader))
};
if let Some(meta_transform) = self.base.meta_transform {
meta_transform(&mut *meta);
}
let asset = self
.base
.load_context
.load_direct_internal(path.clone(), meta, &*loader, reader.as_mut())
.await?;
Ok((loader, asset))
}
/// Loads the asset at the given `path` directly. 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. For example, if you are
/// deriving a new asset from the referenced asset, or you are building a collection of assets. This will add the `path` as a
/// "load dependency".
///
/// If the current loader is used in a [`Process`] "asset preprocessor", such as a [`LoadTransformAndSave`] preprocessor,
/// changing a "load dependency" will result in re-processing of the asset.
///
/// [`Process`]: crate::processor::Process
/// [`LoadTransformAndSave`]: crate::processor::LoadTransformAndSave
pub async fn load<'p, A: Asset>(
mut self,
path: impl Into<AssetPath<'p>>,
) -> Result<LoadedAsset<A>, LoadDirectError> {
self.base.asset_type_id = Some(TypeId::of::<A>());
let path = path.into().into_owned();
self.load_internal(&path)
.await
.and_then(move |(loader, untyped_asset)| {
untyped_asset.downcast::<A>().map_err(|_| LoadDirectError {
dependency: path.clone(),
error: AssetLoadError::RequestedHandleTypeMismatch {
path,
requested: TypeId::of::<A>(),
actual_asset_name: loader.asset_type_name(),
loader_name: loader.type_name(),
},
})
})
}
}
/// A builder for directly loading untyped nested assets inside a `LoadContext`.
///
/// # Lifetimes
/// - `ctx`: the lifetime of the associated [`AssetServer`] reference
/// - `builder`: the lifetime of the temporary builder structs
/// - `reader`: the lifetime of the [`Reader`] reference used to read the asset data
pub struct UntypedDirectNestedLoader<'ctx, 'builder, 'reader> {
base: DirectNestedLoader<'ctx, 'builder, 'reader>,
}
impl<'ctx: 'reader, 'builder, 'reader> UntypedDirectNestedLoader<'ctx, 'builder, 'reader> {
/// Loads the asset at the given `path` directly. 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. For example, if you are
/// deriving a new asset from the referenced asset, or you are building a collection of assets. This will add the `path` as a
/// "load dependency".
///
/// If the current loader is used in a [`Process`] "asset preprocessor", such as a [`LoadTransformAndSave`] preprocessor,
/// changing a "load dependency" will result in re-processing of the asset.
///
/// [`Process`]: crate::processor::Process
/// [`LoadTransformAndSave`]: crate::processor::LoadTransformAndSave
pub async fn load<'p>(
self,
path: impl Into<AssetPath<'p>>,
) -> Result<ErasedLoadedAsset, LoadDirectError> {
let path = path.into().into_owned();
self.base.load_internal(&path).await.map(|(_, asset)| asset)
}
}

View File

@ -285,7 +285,7 @@ impl AssetServer {
self.load_with_meta_transform(path, Some(loader_settings_meta_transform(settings)))
}
fn load_with_meta_transform<'a, A: Asset>(
pub(crate) fn load_with_meta_transform<'a, A: Asset>(
&self,
path: impl Into<AssetPath<'a>>,
meta_transform: Option<MetaTransform>,
@ -324,30 +324,11 @@ impl AssetServer {
self.load_internal(None, path, false, None).await
}
/// Load an asset without knowing its type. The method returns a handle to a [`LoadedUntypedAsset`].
///
/// Once the [`LoadedUntypedAsset`] is loaded, an untyped handle for the requested path can be
/// retrieved from it.
///
/// ```
/// use bevy_asset::{Assets, Handle, LoadedUntypedAsset};
/// use bevy_ecs::system::{Res, Resource};
///
/// #[derive(Resource)]
/// struct LoadingUntypedHandle(Handle<LoadedUntypedAsset>);
///
/// fn resolve_loaded_untyped_handle(loading_handle: Res<LoadingUntypedHandle>, loaded_untyped_assets: Res<Assets<LoadedUntypedAsset>>) {
/// if let Some(loaded_untyped_asset) = loaded_untyped_assets.get(&loading_handle.0) {
/// let handle = loaded_untyped_asset.handle.clone();
/// // continue working with `handle` which points to the asset at the originally requested path
/// }
/// }
/// ```
///
/// This indirection enables a non blocking load of an untyped asset, since I/O is
/// required to figure out the asset type before a handle can be created.
#[must_use = "not using the returned strong handle may result in the unexpected release of the assets"]
pub fn load_untyped<'a>(&self, path: impl Into<AssetPath<'a>>) -> Handle<LoadedUntypedAsset> {
pub(crate) fn load_untyped_with_meta_transform<'a>(
&self,
path: impl Into<AssetPath<'a>>,
meta_transform: Option<MetaTransform>,
) -> Handle<LoadedUntypedAsset> {
let path = path.into().into_owned();
let untyped_source = AssetSourceId::Name(match path.source() {
AssetSourceId::Default => CowArc::Borrowed(UNTYPED_SOURCE_SUFFIX),
@ -362,7 +343,7 @@ impl AssetServer {
.get_or_create_path_handle::<LoadedUntypedAsset>(
path.clone().with_source(untyped_source),
HandleLoadingMode::Request,
None,
meta_transform,
);
if !should_load {
return handle;
@ -393,10 +374,36 @@ impl AssetServer {
}
})
.detach();
handle
}
/// Load an asset without knowing its type. The method returns a handle to a [`LoadedUntypedAsset`].
///
/// Once the [`LoadedUntypedAsset`] is loaded, an untyped handle for the requested path can be
/// retrieved from it.
///
/// ```
/// use bevy_asset::{Assets, Handle, LoadedUntypedAsset};
/// use bevy_ecs::system::{Res, Resource};
///
/// #[derive(Resource)]
/// struct LoadingUntypedHandle(Handle<LoadedUntypedAsset>);
///
/// fn resolve_loaded_untyped_handle(loading_handle: Res<LoadingUntypedHandle>, loaded_untyped_assets: Res<Assets<LoadedUntypedAsset>>) {
/// if let Some(loaded_untyped_asset) = loaded_untyped_assets.get(&loading_handle.0) {
/// let handle = loaded_untyped_asset.handle.clone();
/// // continue working with `handle` which points to the asset at the originally requested path
/// }
/// }
/// ```
///
/// This indirection enables a non blocking load of an untyped asset, since I/O is
/// required to figure out the asset type before a handle can be created.
#[must_use = "not using the returned strong handle may result in the unexpected release of the assets"]
pub fn load_untyped<'a>(&self, path: impl Into<AssetPath<'a>>) -> Handle<LoadedUntypedAsset> {
self.load_untyped_with_meta_transform(path, None)
}
/// Performs an async asset load.
///
/// `input_handle` must only be [`Some`] if `should_load` was true when retrieving `input_handle`. This is an optimization to

View File

@ -342,12 +342,13 @@ async fn load_gltf<'a, 'b, 'c>(
path,
is_srgb,
sampler_descriptor,
} => {
load_context.load_with_settings(path, move |settings: &mut ImageLoaderSettings| {
} => load_context
.loader()
.with_settings(move |settings: &mut ImageLoaderSettings| {
settings.is_srgb = is_srgb;
settings.sampler = ImageSampler::Descriptor(sampler_descriptor.clone());
})
}
.load(path),
};
handles.push(handle);
}

View File

@ -72,7 +72,11 @@ impl AssetLoader for GzAssetLoader {
let mut reader = VecReader::new(bytes_uncompressed);
let uncompressed = load_context
.load_direct_untyped_with_reader(&mut reader, contained_path)
.loader()
.direct()
.with_reader(&mut reader)
.untyped()
.load(contained_path)
.await?;
Ok(GzAsset { uncompressed })

View File

@ -146,14 +146,21 @@ impl AssetLoader for CoolTextLoader {
let ron: CoolTextRon = ron::de::from_bytes(&bytes)?;
let mut base_text = ron.text;
for embedded in ron.embedded_dependencies {
let loaded = load_context.load_direct::<Text>(&embedded).await?;
let loaded = load_context
.loader()
.direct()
.load::<Text>(&embedded)
.await?;
base_text.push_str(&loaded.get().0);
}
for (path, settings_override) in ron.dependencies_with_settings {
let loaded = load_context
.load_direct_with_settings::<Text, _>(&path, move |settings| {
.loader()
.with_settings(move |settings| {
*settings = settings_override.clone();
})
.direct()
.load::<Text>(&path)
.await?;
base_text.push_str(&loaded.get().0);
}