Skip to content

Commit

Permalink
Depreciate LoadAndSave Asset Processor (#15090)
Browse files Browse the repository at this point in the history
# Objective

- Fixes #15060

## Solution

- Added `IdentityAssetTransformer<A>` which is an `AssetTransformer`
which infallibly returns the input `Asset` unmodified.
- Replaced `LoadAndSave` and `LoadAndSaveSettings` with type definitions
linking back to `LoadTransformAndSave` and
`LoadTransformAndSaveSettings` respectively.
- Marked `LoadAndSave` and `LoadAndSaveSettings` as depreciated with a
migration guide included, hinting to the user to use the underlying type
instead.

## Testing

- Ran CI locally

---

## Migration Guide

- Replace `LoadAndSave<L, S>` with `LoadTransformAndSave<L,
IdentityAssetTransformer<<L as AssetLoader>::Asset>, S>`
- Replace `LoadAndSaveSettings<L, S>` with
`LoadTransformAndSaveSettings<L, (), S>`
  • Loading branch information
bushrat011899 authored Sep 9, 2024
1 parent ce32b5c commit dac4a5b
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 55 deletions.
74 changes: 24 additions & 50 deletions crates/bevy_asset/src/processor/process.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::io::SliceReader;
use crate::transformer::IdentityAssetTransformer;
use crate::{
io::{
AssetReaderError, AssetWriterError, MissingAssetWriterError,
Expand Down Expand Up @@ -47,6 +48,11 @@ pub trait Process: Send + Sync + Sized + 'static {
/// an [`AssetSaver`] that allows you save any `S` asset. However you can
/// also implement [`Process`] directly if [`LoadTransformAndSave`] feels limiting or unnecessary.
///
/// If your [`Process`] does not need to transform the [`Asset`], you can use [`IdentityAssetTransformer`] as `T`.
/// This will directly return the input [`Asset`], allowing your [`Process`] to directly load and then save an [`Asset`].
/// However, this pattern should only be used for cases such as file format conversion.
/// Otherwise, consider refactoring your [`AssetLoader`] and [`AssetSaver`] to isolate the transformation step into an explicit [`AssetTransformer`].
///
/// This uses [`LoadTransformAndSaveSettings`] to configure the processor.
///
/// [`Asset`]: crate::Asset
Expand All @@ -60,6 +66,18 @@ pub struct LoadTransformAndSave<
marker: PhantomData<fn() -> L>,
}

impl<L: AssetLoader, S: AssetSaver<Asset = L::Asset>> From<S>
for LoadTransformAndSave<L, IdentityAssetTransformer<L::Asset>, S>
{
fn from(value: S) -> Self {
LoadTransformAndSave {
transformer: IdentityAssetTransformer::new(),
saver: value,
marker: PhantomData,
}
}
}

/// Settings for the [`LoadTransformAndSave`] [`Process::Settings`] implementation.
///
/// `LoaderSettings` corresponds to [`AssetLoader::Settings`], `TransformerSettings` corresponds to [`AssetTransformer::Settings`],
Expand Down Expand Up @@ -98,30 +116,16 @@ impl<
/// This uses [`LoadAndSaveSettings`] to configure the processor.
///
/// [`Asset`]: crate::Asset
pub struct LoadAndSave<L: AssetLoader, S: AssetSaver<Asset = L::Asset>> {
saver: S,
marker: PhantomData<fn() -> L>,
}

impl<L: AssetLoader, S: AssetSaver<Asset = L::Asset>> From<S> for LoadAndSave<L, S> {
fn from(value: S) -> Self {
LoadAndSave {
saver: value,
marker: PhantomData,
}
}
}
#[deprecated = "Use `LoadTransformAndSave<L, IdentityAssetTransformer<<L as AssetLoader>::Asset>, S>` instead"]
pub type LoadAndSave<L, S> =
LoadTransformAndSave<L, IdentityAssetTransformer<<L as AssetLoader>::Asset>, S>;

/// Settings for the [`LoadAndSave`] [`Process::Settings`] implementation.
///
/// `LoaderSettings` corresponds to [`AssetLoader::Settings`] and `SaverSettings` corresponds to [`AssetSaver::Settings`].
#[derive(Serialize, Deserialize, Default)]
pub struct LoadAndSaveSettings<LoaderSettings, SaverSettings> {
/// The [`AssetLoader::Settings`] for [`LoadAndSave`].
pub loader_settings: LoaderSettings,
/// The [`AssetSaver::Settings`] for [`LoadAndSave`].
pub saver_settings: SaverSettings,
}
#[deprecated = "Use `LoadTransformAndSaveSettings<LoaderSettings, (), SaverSettings>` instead"]
pub type LoadAndSaveSettings<LoaderSettings, SaverSettings> =
LoadTransformAndSaveSettings<LoaderSettings, (), SaverSettings>;

/// An error that is encountered during [`Process::process`].
#[derive(Error, Debug)]
Expand Down Expand Up @@ -213,36 +217,6 @@ where
}
}

impl<Loader: AssetLoader, Saver: AssetSaver<Asset = Loader::Asset>> Process
for LoadAndSave<Loader, Saver>
{
type Settings = LoadAndSaveSettings<Loader::Settings, Saver::Settings>;
type OutputLoader = Saver::OutputLoader;

async fn process<'a>(
&'a self,
context: &'a mut ProcessContext<'_>,
meta: AssetMeta<(), Self>,
writer: &'a mut Writer,
) -> Result<<Self::OutputLoader as AssetLoader>::Settings, ProcessError> {
let AssetAction::Process { settings, .. } = meta.asset else {
return Err(ProcessError::WrongMetaType);
};
let loader_meta = AssetMeta::<Loader, ()>::new(AssetAction::Load {
loader: std::any::type_name::<Loader>().to_string(),
settings: settings.loader_settings,
});
let loaded_asset = context.load_source_asset(loader_meta).await?;
let saved_asset = SavedAsset::<Loader::Asset>::from_loaded(&loaded_asset).unwrap();
let output_settings = self
.saver
.save(writer, saved_asset, &settings.saver_settings)
.await
.map_err(|error| ProcessError::AssetSaveError(error.into()))?;
Ok(output_settings)
}
}

/// A type-erased variant of [`Process`] that enables interacting with processor implementations without knowing
/// their type.
pub trait ErasedProcessor: Send + Sync {
Expand Down
36 changes: 36 additions & 0 deletions crates/bevy_asset/src/transformer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ use bevy_utils::{ConditionalSendFuture, HashMap};
use serde::{Deserialize, Serialize};
use std::{
borrow::Borrow,
convert::Infallible,
hash::Hash,
marker::PhantomData,
ops::{Deref, DerefMut},
};

Expand Down Expand Up @@ -241,3 +243,37 @@ impl<'a, A: Asset> TransformedSubAsset<'a, A> {
self.labeled_assets.keys().map(|s| &**s)
}
}

/// An identity [`AssetTransformer`] which infallibly returns the input [`Asset`] on transformation.]
pub struct IdentityAssetTransformer<A: Asset> {
_phantom: PhantomData<fn(A) -> A>,
}

impl<A: Asset> IdentityAssetTransformer<A> {
pub const fn new() -> Self {
Self {
_phantom: PhantomData,
}
}
}

impl<A: Asset> Default for IdentityAssetTransformer<A> {
fn default() -> Self {
Self::new()
}
}

impl<A: Asset> AssetTransformer for IdentityAssetTransformer<A> {
type AssetInput = A;
type AssetOutput = A;
type Settings = ();
type Error = Infallible;

async fn transform<'a>(
&'a self,
asset: TransformedAsset<Self::AssetInput>,
_settings: &'a Self::Settings,
) -> Result<TransformedAsset<Self::AssetOutput>, Self::Error> {
Ok(asset)
}
}
15 changes: 10 additions & 5 deletions crates/bevy_render/src/texture/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,16 @@ impl Plugin for ImagePlugin {
.world()
.get_resource::<bevy_asset::processor::AssetProcessor>()
{
processor.register_processor::<bevy_asset::processor::LoadAndSave<ImageLoader, CompressedImageSaver>>(
CompressedImageSaver.into(),
);
processor
.set_default_processor::<bevy_asset::processor::LoadAndSave<ImageLoader, CompressedImageSaver>>("png");
processor.register_processor::<bevy_asset::processor::LoadTransformAndSave<
ImageLoader,
bevy_asset::transformer::IdentityAssetTransformer<Image>,
CompressedImageSaver,
>>(CompressedImageSaver.into());
processor.set_default_processor::<bevy_asset::processor::LoadTransformAndSave<
ImageLoader,
bevy_asset::transformer::IdentityAssetTransformer<Image>,
CompressedImageSaver,
>>("png");
}

if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
Expand Down

0 comments on commit dac4a5b

Please sign in to comment.