Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
68 changes: 66 additions & 2 deletions crates/bevy_asset/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -710,14 +710,14 @@ mod tests {
handle::Handle,
io::{
gated::{GateOpener, GatedReader},
memory::{Dir, MemoryAssetReader},
memory::{Dir, MemoryAssetReader, MemoryAssetWriter},
AssetReader, AssetReaderError, AssetSourceBuilder, AssetSourceEvent, AssetSourceId,
AssetWatcher, Reader, ReaderRequiredFeatures,
},
loader::{AssetLoader, LoadContext},
Asset, AssetApp, AssetEvent, AssetId, AssetLoadError, AssetLoadFailedEvent, AssetPath,
AssetPlugin, AssetServer, Assets, InvalidGenerationError, LoadState, LoadedAsset,
UnapprovedPathMode, UntypedHandle,
UnapprovedPathMode, UntypedHandle, WriteDefaultMetaError,
};
use alloc::{
boxed::Box,
Expand Down Expand Up @@ -909,12 +909,18 @@ mod tests {
let mut app = App::new();
let dir = Dir::default();
let dir_clone = dir.clone();
let dir_clone2 = dir.clone();
app.register_asset_source(
AssetSourceId::Default,
AssetSourceBuilder::new(move || {
Box::new(MemoryAssetReader {
root: dir_clone.clone(),
})
})
.with_writer(move |_| {
Some(Box::new(MemoryAssetWriter {
root: dir_clone2.clone(),
}))
}),
)
.add_plugins((
Expand Down Expand Up @@ -2766,4 +2772,62 @@ mod tests {
Some(())
});
}

pub(crate) fn read_asset_as_string(dir: &Dir, path: &Path) -> String {
let bytes = dir.get_asset(path).unwrap();
str::from_utf8(bytes.value()).unwrap().to_string()
}

pub(crate) fn read_meta_as_string(dir: &Dir, path: &Path) -> String {
let bytes = dir.get_metadata(path).unwrap();
str::from_utf8(bytes.value()).unwrap().to_string()
}

#[test]
fn writes_default_meta_for_loader() {
let (mut app, source) = create_app();

app.register_asset_loader(CoolTextLoader);

const ASSET_PATH: &str = "abc.cool.ron";
source.insert_asset_text(Path::new(ASSET_PATH), "blah");

let asset_server = app.world().resource::<AssetServer>().clone();
bevy_tasks::block_on(asset_server.write_default_loader_meta_file_for_path(ASSET_PATH))
.unwrap();

assert_eq!(
read_meta_as_string(&source, Path::new(ASSET_PATH)),
r#"(
meta_format_version: "1.0",
asset: Load(
loader: "bevy_asset::tests::CoolTextLoader",
settings: (),
),
)"#
);
}

#[test]
fn write_default_meta_does_not_overwrite() {
let (mut app, source) = create_app();

app.register_asset_loader(CoolTextLoader);

const ASSET_PATH: &str = "abc.cool.ron";
source.insert_asset_text(Path::new(ASSET_PATH), "blah");
const META_TEXT: &str = "hey i'm walkin here!";
source.insert_meta_text(Path::new(ASSET_PATH), META_TEXT);

let asset_server = app.world().resource::<AssetServer>().clone();
assert!(matches!(
bevy_tasks::block_on(asset_server.write_default_loader_meta_file_for_path(ASSET_PATH)),
Err(WriteDefaultMetaError::MetaAlreadyExists)
));

assert_eq!(
read_meta_as_string(&source, Path::new(ASSET_PATH)),
META_TEXT
);
}
}
11 changes: 8 additions & 3 deletions crates/bevy_asset/src/meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,14 @@ impl<L: AssetLoader, P: Process> AssetMetaDyn for AssetMeta<L, P> {
}
}
fn serialize(&self) -> Vec<u8> {
ron::ser::to_string_pretty(&self, PrettyConfig::default())
.expect("type is convertible to ron")
.into_bytes()
ron::ser::to_string_pretty(
&self,
// This defaults to \r\n on Windows, so hard-code it to \n so it's consistent for
// testing.
PrettyConfig::default().new_line("\n"),
)
.expect("type is convertible to ron")
.into_bytes()
}
fn processed_info(&self) -> &Option<ProcessedInfo> {
&self.processed_info
Expand Down
60 changes: 25 additions & 35 deletions crates/bevy_asset/src/processor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,6 @@ use tracing::{debug, error, trace, warn};
#[cfg(feature = "trace")]
use {
alloc::string::ToString,
bevy_reflect::TypePath,
bevy_tasks::ConditionalSendFuture,
tracing::{info_span, instrument::Instrument},
};

Expand Down Expand Up @@ -447,7 +445,16 @@ impl AssetProcessor {
.await;
};

let meta = processor.default_meta();
let short_type_path = processor.short_type_path();
// Try to get the processor using the short type - if it fails, that must mean that the
// short type path is insufficient, so we'll have to fall back to the long path.
let processor_path_kind = if self.get_processor(short_type_path).is_ok() {
MetaProcessorPathKind::Short
} else {
MetaProcessorPathKind::Long
};

let meta = processor.default_meta(processor_path_kind);
let serialized_meta = meta.serialize();

let source = self.get_source(path.source())?;
Expand Down Expand Up @@ -754,8 +761,6 @@ impl AssetProcessor {
.processors
.write()
.unwrap_or_else(PoisonError::into_inner);
#[cfg(feature = "trace")]
let processor = InstrumentedAssetProcessor(processor);
let processor = Arc::new(processor);
processors
.type_path_to_processor
Expand Down Expand Up @@ -1082,7 +1087,10 @@ impl AssetProcessor {
.get_full_extension()
.and_then(|ext| self.get_default_processor(&ext))
{
let meta = processor.default_meta();
// Note: It doesn't matter whether we use the Long or Short kind, since we're
// returning the processor here anyway, and we're only using this meta to pass
// along the processor settings.
let meta = processor.default_meta(MetaProcessorPathKind::Long);
(meta, Some(processor))
} else {
match server.get_path_asset_loader(asset_path.clone()).await {
Expand Down Expand Up @@ -1195,9 +1203,17 @@ impl AssetProcessor {
reader_for_process,
&mut new_processed_info,
);
processor
.process(&mut context, settings, &mut *writer)
.await?
let process = processor.process(&mut context, settings, &mut *writer);
#[cfg(feature = "trace")]
let process = {
let span = info_span!(
"asset processing",
processor = processor.type_path(),
asset = asset_path.to_string(),
);
process.instrument(span)
};
process.await?
};

writer
Expand Down Expand Up @@ -1504,32 +1520,6 @@ impl ProcessingState {
}
}

#[cfg(feature = "trace")]
#[derive(TypePath)]
struct InstrumentedAssetProcessor<T>(T);

#[cfg(feature = "trace")]
impl<T: Process> Process for InstrumentedAssetProcessor<T> {
type Settings = T::Settings;
type OutputLoader = T::OutputLoader;

fn process(
&self,
context: &mut ProcessContext,
settings: &Self::Settings,
writer: &mut crate::io::Writer,
) -> impl ConditionalSendFuture<
Output = Result<<Self::OutputLoader as crate::AssetLoader>::Settings, ProcessError>,
> {
let span = info_span!(
"asset processing",
processor = T::type_path(),
asset = context.path().to_string(),
);
self.0.process(context, settings, writer).instrument(span)
}
}

/// The (successful) result of processing an asset
#[derive(Debug, Clone)]
pub enum ProcessResult {
Expand Down
30 changes: 27 additions & 3 deletions crates/bevy_asset/src/processor/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,8 +245,20 @@ pub trait ErasedProcessor: Send + Sync {
/// Deserialized `meta` as type-erased [`AssetMeta`], operating under the assumption that it matches the meta
/// for the underlying [`Process`] impl.
fn deserialize_meta(&self, meta: &[u8]) -> Result<Box<dyn AssetMetaDyn>, DeserializeMetaError>;
/// Returns the type-path of the original [`Process`].
fn type_path(&self) -> &'static str;
/// Returns the short type path of this processor.
fn short_type_path(&self) -> &'static str;
/// Returns the default type-erased [`AssetMeta`] for the underlying [`Process`] impl.
fn default_meta(&self) -> Box<dyn AssetMetaDyn>;
fn default_meta(&self, processor_path_kind: MetaProcessorPathKind) -> Box<dyn AssetMetaDyn>;
}

/// Specifies which kind of path to use to specify the processor's type.
pub enum MetaProcessorPathKind {
/// Use the short type path.
Short,
/// Use the fully-qualified type path.
Long,
}

impl<P: Process> ErasedProcessor for P {
Expand Down Expand Up @@ -281,9 +293,21 @@ impl<P: Process> ErasedProcessor for P {
Ok(Box::new(meta))
}

fn default_meta(&self) -> Box<dyn AssetMetaDyn> {
fn type_path(&self) -> &'static str {
P::type_path()
}

fn short_type_path(&self) -> &'static str {
P::short_type_path()
}

fn default_meta(&self, processor_path_kind: MetaProcessorPathKind) -> Box<dyn AssetMetaDyn> {
let type_path = match processor_path_kind {
MetaProcessorPathKind::Short => P::short_type_path(),
MetaProcessorPathKind::Long => P::type_path(),
};
Box::new(AssetMeta::<(), P>::new(AssetAction::Process {
processor: P::type_path().to_string(),
processor: type_path.to_string(),
settings: P::Settings::default(),
}))
}
Expand Down
Loading