Optimize common usages of AssetReader (#14082)

# Objective

The `AssetReader` trait allows customizing the behavior of fetching
bytes for an `AssetPath`, and expects implementors to return `dyn
AsyncRead + AsyncSeek`. This gives implementors of `AssetLoader` great
flexibility to tightly integrate their asset loading behavior with the
asynchronous task system.

However, almost all implementors of `AssetLoader` don't use the async
functionality at all, and just call `AsyncReadExt::read_to_end(&mut
Vec<u8>)`. This is incredibly inefficient, as this method repeatedly
calls `poll_read` on the trait object, filling the vector 32 bytes at a
time. At my work we have assets that are hundreds of megabytes which
makes this a meaningful overhead.

## Solution

Turn the `Reader` type alias into an actual trait, with a provided
method `read_to_end`. This provided method should be more efficient than
the existing extension method, as the compiler will know the underlying
type of `Reader` when generating this function, which removes the
repeated dynamic dispatches and allows the compiler to make further
optimizations after inlining. Individual implementors are able to
override the provided implementation -- for simple asset readers that
just copy bytes from one buffer to another, this allows removing a large
amount of overhead from the provided implementation.

Now that `Reader` is an actual trait, I also improved the ergonomics for
implementing `AssetReader`. Currently, implementors are expected to box
their reader and return it as a trait object, which adds unnecessary
boilerplate to implementations. This PR changes that trait method to
return a pseudo trait alias, which allows implementors to return `impl
Reader` instead of `Box<dyn Reader>`. Now, the boilerplate for boxing
occurs in `ErasedAssetReader`.

## Testing

I made identical changes to my company's fork of bevy. Our app, which
makes heavy use of `read_to_end` for asset loading, still worked
properly after this. I am not aware if we have a more systematic way of
testing asset loading for correctness.

---

## Migration Guide

The trait method `bevy_asset::io::AssetReader::read` (and `read_meta`)
now return an opaque type instead of a boxed trait object. Implementors
of these methods should change the type signatures appropriately

```rust
impl AssetReader for MyReader {
    // Before
    async fn read<'a>(&'a self, path: &'a Path) -> Result<Box<Reader<'a>>, AssetReaderError> {
        let reader = // construct a reader
        Box::new(reader) as Box<Reader<'a>>
    }

    // After
    async fn read<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
        // create a reader
    }
}
```

`bevy::asset::io::Reader` is now a trait, rather than a type alias for a
trait object. Implementors of `AssetLoader::load` will need to adjust
the method signature accordingly

```rust
impl AssetLoader for MyLoader {
    async fn load<'a>(
        &'a self,
        // Before:
        reader: &'a mut bevy::asset::io::Reader,
        // After:
        reader: &'a mut dyn bevy::asset::io::Reader,
        _: &'a Self::Settings,
        load_context: &'a mut LoadContext<'_>,
    ) -> Result<Self::Asset, Self::Error> {
}
```

Additionally, implementors of `AssetReader` that return a type
implementing `futures_io::AsyncRead` and `AsyncSeek` might need to
explicitly implement `bevy::asset::io::Reader` for that type.

```rust
impl bevy::asset::io::Reader for MyAsyncReadAndSeek {}
```
This commit is contained in:
Joseph 2024-07-01 12:59:42 -07:00 committed by GitHub
parent bd7dcd3f6d
commit 5876352206
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 260 additions and 149 deletions

View file

@ -4,7 +4,7 @@ use std::io::{self, Write};
use std::ops::{Index, IndexMut};
use bevy_asset::io::Reader;
use bevy_asset::{Asset, AssetId, AssetLoader, AssetPath, AsyncReadExt as _, Handle, LoadContext};
use bevy_asset::{Asset, AssetId, AssetLoader, AssetPath, Handle, LoadContext};
use bevy_reflect::{Reflect, ReflectSerialize};
use petgraph::graph::{DiGraph, NodeIndex};
use ron::de::SpannedError;
@ -337,7 +337,7 @@ impl AssetLoader for AnimationGraphAssetLoader {
async fn load<'a>(
&'a self,
reader: &'a mut Reader<'_>,
reader: &'a mut dyn Reader,
_: &'a Self::Settings,
load_context: &'a mut LoadContext<'_>,
) -> Result<Self::Asset, Self::Error> {

View file

@ -28,6 +28,7 @@ bevy_reflect = { path = "../bevy_reflect", version = "0.14.0-dev", features = [
bevy_tasks = { path = "../bevy_tasks", version = "0.14.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.14.0-dev" }
stackfuture = "0.3"
async-broadcast = "0.5"
async-fs = "2.0"
async-lock = "3.0"

View file

@ -16,7 +16,7 @@ use std::{ffi::CString, path::Path};
pub struct AndroidAssetReader;
impl AssetReader for AndroidAssetReader {
async fn read<'a>(&'a self, path: &'a Path) -> Result<Box<Reader<'a>>, AssetReaderError> {
async fn read<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
let asset_manager = bevy_winit::ANDROID_APP
.get()
.expect("Bevy must be setup with the #[bevy_main] macro on Android")
@ -25,11 +25,11 @@ impl AssetReader for AndroidAssetReader {
.open(&CString::new(path.to_str().unwrap()).unwrap())
.ok_or(AssetReaderError::NotFound(path.to_path_buf()))?;
let bytes = opened_asset.buffer()?;
let reader: Box<Reader> = Box::new(VecReader::new(bytes.to_vec()));
let reader = VecReader::new(bytes.to_vec());
Ok(reader)
}
async fn read_meta<'a>(&'a self, path: &'a Path) -> Result<Box<Reader<'a>>, AssetReaderError> {
async fn read_meta<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
let meta_path = get_meta_path(path);
let asset_manager = bevy_winit::ANDROID_APP
.get()
@ -39,7 +39,7 @@ impl AssetReader for AndroidAssetReader {
.open(&CString::new(meta_path.to_str().unwrap()).unwrap())
.ok_or(AssetReaderError::NotFound(meta_path))?;
let bytes = opened_asset.buffer()?;
let reader: Box<Reader> = Box::new(VecReader::new(bytes.to_vec()));
let reader = VecReader::new(bytes.to_vec());
Ok(reader)
}

View file

@ -9,40 +9,30 @@ use std::path::Path;
use super::{FileAssetReader, FileAssetWriter};
impl Reader for File {}
impl AssetReader for FileAssetReader {
async fn read<'a>(&'a self, path: &'a Path) -> Result<Box<Reader<'a>>, AssetReaderError> {
async fn read<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
let full_path = self.root_path.join(path);
match File::open(&full_path).await {
Ok(file) => {
let reader: Box<Reader> = Box::new(file);
Ok(reader)
}
Err(e) => {
File::open(&full_path).await.map_err(|e| {
if e.kind() == std::io::ErrorKind::NotFound {
Err(AssetReaderError::NotFound(full_path))
AssetReaderError::NotFound(full_path)
} else {
Err(e.into())
}
}
e.into()
}
})
}
async fn read_meta<'a>(&'a self, path: &'a Path) -> Result<Box<Reader<'a>>, AssetReaderError> {
async fn read_meta<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
let meta_path = get_meta_path(path);
let full_path = self.root_path.join(meta_path);
match File::open(&full_path).await {
Ok(file) => {
let reader: Box<Reader> = Box::new(file);
Ok(reader)
}
Err(e) => {
File::open(&full_path).await.map_err(|e| {
if e.kind() == std::io::ErrorKind::NotFound {
Err(AssetReaderError::NotFound(full_path))
AssetReaderError::NotFound(full_path)
} else {
Err(e.into())
}
}
e.into()
}
})
}
async fn read_directory<'a>(

View file

@ -42,6 +42,16 @@ impl AsyncSeek for FileReader {
}
}
impl Reader for FileReader {
fn read_to_end<'a>(
&'a mut self,
buf: &'a mut Vec<u8>,
) -> stackfuture::StackFuture<'a, std::io::Result<usize>, { crate::io::STACK_FUTURE_SIZE }>
{
stackfuture::StackFuture::from(async { self.0.read_to_end(buf) })
}
}
struct FileWriter(File);
impl AsyncWrite for FileWriter {
@ -87,13 +97,10 @@ impl Stream for DirReader {
}
impl AssetReader for FileAssetReader {
async fn read<'a>(&'a self, path: &'a Path) -> Result<Box<Reader<'a>>, AssetReaderError> {
async fn read<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
let full_path = self.root_path.join(path);
match File::open(&full_path) {
Ok(file) => {
let reader: Box<Reader> = Box::new(FileReader(file));
Ok(reader)
}
Ok(file) => Ok(FileReader(file)),
Err(e) => {
if e.kind() == std::io::ErrorKind::NotFound {
Err(AssetReaderError::NotFound(full_path))
@ -104,14 +111,11 @@ impl AssetReader for FileAssetReader {
}
}
async fn read_meta<'a>(&'a self, path: &'a Path) -> Result<Box<Reader<'a>>, AssetReaderError> {
async fn read_meta<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
let meta_path = get_meta_path(path);
let full_path = self.root_path.join(meta_path);
match File::open(&full_path) {
Ok(file) => {
let reader: Box<Reader> = Box::new(FileReader(file));
Ok(reader)
}
Ok(file) => Ok(FileReader(file)),
Err(e) => {
if e.kind() == std::io::ErrorKind::NotFound {
Err(AssetReaderError::NotFound(full_path))

View file

@ -55,7 +55,7 @@ impl<R: AssetReader> GatedReader<R> {
}
impl<R: AssetReader> AssetReader for GatedReader<R> {
async fn read<'a>(&'a self, path: &'a Path) -> Result<Box<Reader<'a>>, AssetReaderError> {
async fn read<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
let receiver = {
let mut gates = self.gates.write();
let gates = gates
@ -68,7 +68,7 @@ impl<R: AssetReader> AssetReader for GatedReader<R> {
Ok(result)
}
async fn read_meta<'a>(&'a self, path: &'a Path) -> Result<Box<Reader<'a>>, AssetReaderError> {
async fn read_meta<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
self.reader.read_meta(path).await
}

View file

@ -277,29 +277,41 @@ impl AsyncSeek for DataReader {
}
}
impl Reader for DataReader {
fn read_to_end<'a>(
&'a mut self,
buf: &'a mut Vec<u8>,
) -> stackfuture::StackFuture<'a, std::io::Result<usize>, { super::STACK_FUTURE_SIZE }> {
stackfuture::StackFuture::from(async {
if self.bytes_read >= self.data.value().len() {
Ok(0)
} else {
buf.extend_from_slice(&self.data.value()[self.bytes_read..]);
let n = self.data.value().len() - self.bytes_read;
self.bytes_read = self.data.value().len();
Ok(n)
}
})
}
}
impl AssetReader for MemoryAssetReader {
async fn read<'a>(&'a self, path: &'a Path) -> Result<Box<Reader<'a>>, AssetReaderError> {
async fn read<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
self.root
.get_asset(path)
.map(|data| {
let reader: Box<Reader> = Box::new(DataReader {
.map(|data| DataReader {
data,
bytes_read: 0,
});
reader
})
.ok_or_else(|| AssetReaderError::NotFound(path.to_path_buf()))
}
async fn read_meta<'a>(&'a self, path: &'a Path) -> Result<Box<Reader<'a>>, AssetReaderError> {
async fn read_meta<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
self.root
.get_metadata(path)
.map(|data| {
let reader: Box<Reader> = Box::new(DataReader {
.map(|data| DataReader {
data,
bytes_read: 0,
});
reader
})
.ok_or_else(|| AssetReaderError::NotFound(path.to_path_buf()))
}

View file

@ -18,7 +18,7 @@ pub mod wasm;
mod source;
pub use futures_lite::{AsyncReadExt, AsyncWriteExt};
pub use futures_lite::AsyncWriteExt;
pub use source::*;
use bevy_utils::{BoxedFuture, ConditionalSendFuture};
@ -72,11 +72,59 @@ impl From<std::io::Error> for AssetReaderError {
}
}
pub trait AsyncReadAndSeek: AsyncRead + AsyncSeek {}
/// The maximum size of a future returned from [`Reader::read_to_end`].
/// This is large enough to fit ten references.
// Ideally this would be even smaller (ReadToEndFuture only needs space for two references based on its definition),
// but compiler optimizations can apparently inflate the stack size of futures due to inlining, which makes
// a higher maximum necessary.
pub const STACK_FUTURE_SIZE: usize = 10 * std::mem::size_of::<&()>();
impl<T: AsyncRead + AsyncSeek> AsyncReadAndSeek for T {}
pub use stackfuture::StackFuture;
pub type Reader<'a> = dyn AsyncReadAndSeek + Unpin + Send + Sync + 'a;
/// A type returned from [`AssetReader::read`], which is used to read the contents of a file
/// (or virtual file) corresponding to an asset.
///
/// This is essentially a trait alias for types implementing [`AsyncRead`] and [`AsyncSeek`].
/// The only reason a blanket implementation is not provided for applicable types is to allow
/// implementors to override the provided implementation of [`Reader::read_to_end`].
pub trait Reader: AsyncRead + AsyncSeek + Unpin + Send + Sync {
/// Reads the entire contents of this reader and appends them to a vec.
///
/// # Note for implementors
/// You should override the provided implementation if you can fill up the buffer more
/// efficiently than the default implementation, which calls `poll_read` repeatedly to
/// fill up the buffer 32 bytes at a time.
fn read_to_end<'a>(
&'a mut self,
buf: &'a mut Vec<u8>,
) -> StackFuture<'a, std::io::Result<usize>, STACK_FUTURE_SIZE> {
let future = futures_lite::AsyncReadExt::read_to_end(self, buf);
StackFuture::from(future)
}
}
impl Reader for Box<dyn Reader + '_> {
fn read_to_end<'a>(
&'a mut self,
buf: &'a mut Vec<u8>,
) -> StackFuture<'a, std::io::Result<usize>, STACK_FUTURE_SIZE> {
(**self).read_to_end(buf)
}
}
/// A future that returns a value or an [`AssetReaderError`]
pub trait AssetReaderFuture:
ConditionalSendFuture<Output = Result<Self::Value, AssetReaderError>>
{
type Value;
}
impl<F, T> AssetReaderFuture for F
where
F: ConditionalSendFuture<Output = Result<T, AssetReaderError>>,
{
type Value = T;
}
/// Performs read operations on an asset storage. [`AssetReader`] exposes a "virtual filesystem"
/// API, where asset bytes and asset metadata bytes are both stored and accessible for a given
@ -85,15 +133,29 @@ pub type Reader<'a> = dyn AsyncReadAndSeek + Unpin + Send + Sync + 'a;
/// Also see [`AssetWriter`].
pub trait AssetReader: Send + Sync + 'static {
/// Returns a future to load the full file data at the provided path.
fn read<'a>(
&'a self,
path: &'a Path,
) -> impl ConditionalSendFuture<Output = Result<Box<Reader<'a>>, AssetReaderError>>;
///
/// # Note for implementors
/// The preferred style for implementing this method is an `async fn` returning an opaque type.
///
/// ```no_run
/// # use std::path::Path;
/// # use bevy_asset::{prelude::*, io::{AssetReader, PathStream, Reader, AssetReaderError}};
/// # struct MyReader;
/// impl AssetReader for MyReader {
/// async fn read<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
/// // ...
/// # let val: Box<dyn Reader> = unimplemented!(); Ok(val)
/// }
/// # async fn read_meta<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
/// # let val: Box<dyn Reader> = unimplemented!(); Ok(val) }
/// # async fn read_directory<'a>(&'a self, path: &'a Path) -> Result<Box<PathStream>, AssetReaderError> { unimplemented!() }
/// # async fn is_directory<'a>(&'a self, path: &'a Path) -> Result<bool, AssetReaderError> { unimplemented!() }
/// # async fn read_meta_bytes<'a>(&'a self, path: &'a Path) -> Result<Vec<u8>, AssetReaderError> { unimplemented!() }
/// }
/// ```
fn read<'a>(&'a self, path: &'a Path) -> impl AssetReaderFuture<Value: Reader + 'a>;
/// Returns a future to load the full file data at the provided path.
fn read_meta<'a>(
&'a self,
path: &'a Path,
) -> impl ConditionalSendFuture<Output = Result<Box<Reader<'a>>, AssetReaderError>>;
fn read_meta<'a>(&'a self, path: &'a Path) -> impl AssetReaderFuture<Value: Reader + 'a>;
/// Returns an iterator of directory entry names at the provided path.
fn read_directory<'a>(
&'a self,
@ -123,13 +185,15 @@ pub trait AssetReader: Send + Sync + 'static {
/// as [`AssetReader`] isn't currently object safe.
pub trait ErasedAssetReader: Send + Sync + 'static {
/// Returns a future to load the full file data at the provided path.
fn read<'a>(&'a self, path: &'a Path)
-> BoxedFuture<Result<Box<Reader<'a>>, AssetReaderError>>;
fn read<'a>(
&'a self,
path: &'a Path,
) -> BoxedFuture<Result<Box<dyn Reader + 'a>, AssetReaderError>>;
/// Returns a future to load the full file data at the provided path.
fn read_meta<'a>(
&'a self,
path: &'a Path,
) -> BoxedFuture<Result<Box<Reader<'a>>, AssetReaderError>>;
) -> BoxedFuture<Result<Box<dyn Reader + 'a>, AssetReaderError>>;
/// Returns an iterator of directory entry names at the provided path.
fn read_directory<'a>(
&'a self,
@ -149,14 +213,20 @@ impl<T: AssetReader> ErasedAssetReader for T {
fn read<'a>(
&'a self,
path: &'a Path,
) -> BoxedFuture<Result<Box<Reader<'a>>, AssetReaderError>> {
Box::pin(Self::read(self, path))
) -> BoxedFuture<Result<Box<dyn Reader + 'a>, AssetReaderError>> {
Box::pin(async {
let reader = Self::read(self, path).await?;
Ok(Box::new(reader) as Box<dyn Reader>)
})
}
fn read_meta<'a>(
&'a self,
path: &'a Path,
) -> BoxedFuture<Result<Box<Reader<'a>>, AssetReaderError>> {
Box::pin(Self::read_meta(self, path))
) -> BoxedFuture<Result<Box<dyn Reader + 'a>, AssetReaderError>> {
Box::pin(async {
let reader = Self::read_meta(self, path).await?;
Ok(Box::new(reader) as Box<dyn Reader>)
})
}
fn read_directory<'a>(
&'a self,
@ -498,6 +568,24 @@ impl AsyncSeek for VecReader {
}
}
impl Reader for VecReader {
fn read_to_end<'a>(
&'a mut self,
buf: &'a mut Vec<u8>,
) -> StackFuture<'a, std::io::Result<usize>, STACK_FUTURE_SIZE> {
StackFuture::from(async {
if self.bytes_read >= self.bytes.len() {
Ok(0)
} else {
buf.extend_from_slice(&self.bytes[self.bytes_read..]);
let n = self.bytes.len() - self.bytes_read;
self.bytes_read = self.bytes.len();
Ok(n)
}
})
}
}
/// An [`AsyncRead`] implementation capable of reading a [`&[u8]`].
pub struct SliceReader<'a> {
bytes: &'a [u8],
@ -565,6 +653,24 @@ impl<'a> AsyncSeek for SliceReader<'a> {
}
}
impl Reader for SliceReader<'_> {
fn read_to_end<'a>(
&'a mut self,
buf: &'a mut Vec<u8>,
) -> StackFuture<'a, std::io::Result<usize>, STACK_FUTURE_SIZE> {
StackFuture::from(async {
if self.bytes_read >= self.bytes.len() {
Ok(0)
} else {
buf.extend_from_slice(&self.bytes[self.bytes_read..]);
let n = self.bytes.len() - self.bytes_read;
self.bytes_read = self.bytes.len();
Ok(n)
}
})
}
}
/// Appends `.meta` to the given path.
pub(crate) fn get_meta_path(path: &Path) -> PathBuf {
let mut meta_path = path.to_path_buf();

View file

@ -51,7 +51,7 @@ impl ProcessorGatedReader {
}
impl AssetReader for ProcessorGatedReader {
async fn read<'a>(&'a self, path: &'a Path) -> Result<Box<Reader<'a>>, AssetReaderError> {
async fn read<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
let asset_path = AssetPath::from(path.to_path_buf()).with_source(self.source.clone());
trace!("Waiting for processing to finish before reading {asset_path}");
let process_result = self
@ -67,11 +67,11 @@ impl AssetReader for ProcessorGatedReader {
trace!("Processing finished with {asset_path}, reading {process_result:?}",);
let lock = self.get_transaction_lock(&asset_path).await?;
let asset_reader = self.reader.read(path).await?;
let reader: Box<Reader<'a>> = Box::new(TransactionLockedReader::new(asset_reader, lock));
let reader = TransactionLockedReader::new(asset_reader, lock);
Ok(reader)
}
async fn read_meta<'a>(&'a self, path: &'a Path) -> Result<Box<Reader<'a>>, AssetReaderError> {
async fn read_meta<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
let asset_path = AssetPath::from(path.to_path_buf()).with_source(self.source.clone());
trace!("Waiting for processing to finish before reading meta for {asset_path}",);
let process_result = self
@ -87,7 +87,7 @@ impl AssetReader for ProcessorGatedReader {
trace!("Processing finished with {process_result:?}, reading meta for {asset_path}",);
let lock = self.get_transaction_lock(&asset_path).await?;
let meta_reader = self.reader.read_meta(path).await?;
let reader: Box<Reader<'a>> = Box::new(TransactionLockedReader::new(meta_reader, lock));
let reader = TransactionLockedReader::new(meta_reader, lock);
Ok(reader)
}
@ -119,12 +119,12 @@ impl AssetReader for ProcessorGatedReader {
/// An [`AsyncRead`] impl that will hold its asset's transaction lock until [`TransactionLockedReader`] is dropped.
pub struct TransactionLockedReader<'a> {
reader: Box<Reader<'a>>,
reader: Box<dyn Reader + 'a>,
_file_transaction_lock: RwLockReadGuardArc<()>,
}
impl<'a> TransactionLockedReader<'a> {
fn new(reader: Box<Reader<'a>>, file_transaction_lock: RwLockReadGuardArc<()>) -> Self {
fn new(reader: Box<dyn Reader + 'a>, file_transaction_lock: RwLockReadGuardArc<()>) -> Self {
Self {
reader,
_file_transaction_lock: file_transaction_lock,
@ -132,7 +132,7 @@ impl<'a> TransactionLockedReader<'a> {
}
}
impl<'a> AsyncRead for TransactionLockedReader<'a> {
impl AsyncRead for TransactionLockedReader<'_> {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
@ -142,7 +142,7 @@ impl<'a> AsyncRead for TransactionLockedReader<'a> {
}
}
impl<'a> AsyncSeek for TransactionLockedReader<'a> {
impl AsyncSeek for TransactionLockedReader<'_> {
fn poll_seek(
mut self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
@ -151,3 +151,12 @@ impl<'a> AsyncSeek for TransactionLockedReader<'a> {
Pin::new(&mut self.reader).poll_seek(cx, pos)
}
}
impl Reader for TransactionLockedReader<'_> {
fn read_to_end<'a>(
&'a mut self,
buf: &'a mut Vec<u8>,
) -> stackfuture::StackFuture<'a, std::io::Result<usize>, { super::STACK_FUTURE_SIZE }> {
self.reader.read_to_end(buf)
}
}

View file

@ -51,7 +51,7 @@ fn js_value_to_err(context: &str) -> impl FnOnce(JsValue) -> std::io::Error + '_
}
impl HttpWasmAssetReader {
async fn fetch_bytes<'a>(&self, path: PathBuf) -> Result<Box<Reader<'a>>, AssetReaderError> {
async fn fetch_bytes<'a>(&self, path: PathBuf) -> Result<impl Reader, AssetReaderError> {
// The JS global scope includes a self-reference via a specialising name, which can be used to determine the type of global context available.
let global: Global = js_sys::global().unchecked_into();
let promise = if !global.window().is_undefined() {
@ -77,7 +77,7 @@ impl HttpWasmAssetReader {
200 => {
let data = JsFuture::from(resp.array_buffer().unwrap()).await.unwrap();
let bytes = Uint8Array::new(&data).to_vec();
let reader: Box<Reader> = Box::new(VecReader::new(bytes));
let reader = VecReader::new(bytes);
Ok(reader)
}
404 => Err(AssetReaderError::NotFound(path)),
@ -87,12 +87,12 @@ impl HttpWasmAssetReader {
}
impl AssetReader for HttpWasmAssetReader {
async fn read<'a>(&'a self, path: &'a Path) -> Result<Box<Reader<'a>>, AssetReaderError> {
async fn read<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
let path = self.root_path.join(path);
self.fetch_bytes(path).await
}
async fn read_meta<'a>(&'a self, path: &'a Path) -> Result<Box<Reader<'a>>, AssetReaderError> {
async fn read_meta<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
let meta_path = get_meta_path(&self.root_path.join(path));
self.fetch_bytes(meta_path).await
}

View file

@ -460,7 +460,6 @@ mod tests {
use bevy_log::LogPlugin;
use bevy_reflect::TypePath;
use bevy_utils::{Duration, HashMap};
use futures_lite::AsyncReadExt;
use serde::{Deserialize, Serialize};
use std::{path::Path, sync::Arc};
use thiserror::Error;
@ -510,7 +509,7 @@ mod tests {
async fn load<'a>(
&'a self,
reader: &'a mut Reader<'_>,
reader: &'a mut dyn Reader,
_settings: &'a Self::Settings,
load_context: &'a mut LoadContext<'_>,
) -> Result<Self::Asset, Self::Error> {
@ -584,13 +583,13 @@ mod tests {
async fn read_meta<'a>(
&'a self,
path: &'a Path,
) -> Result<Box<bevy_asset::io::Reader<'a>>, AssetReaderError> {
) -> Result<impl bevy_asset::io::Reader + 'a, AssetReaderError> {
self.memory_reader.read_meta(path).await
}
async fn read<'a>(
&'a self,
path: &'a Path,
) -> Result<Box<bevy_asset::io::Reader<'a>>, bevy_asset::io::AssetReaderError> {
) -> Result<impl bevy_asset::io::Reader + 'a, bevy_asset::io::AssetReaderError> {
let attempt_number = {
let mut attempt_counters = self.attempt_counters.lock().unwrap();
if let Some(existing) = attempt_counters.get_mut(path) {

View file

@ -9,7 +9,6 @@ use crate::{
use bevy_ecs::world::World;
use bevy_utils::{BoxedFuture, ConditionalSendFuture, CowArc, HashMap, HashSet};
use downcast_rs::{impl_downcast, Downcast};
use futures_lite::AsyncReadExt;
use ron::error::SpannedError;
use serde::{Deserialize, Serialize};
use std::{
@ -30,7 +29,7 @@ pub trait AssetLoader: Send + Sync + 'static {
/// Asynchronously loads [`AssetLoader::Asset`] (and any other labeled assets) from the bytes provided by [`Reader`].
fn load<'a>(
&'a self,
reader: &'a mut Reader,
reader: &'a mut dyn Reader,
settings: &'a Self::Settings,
load_context: &'a mut LoadContext,
) -> impl ConditionalSendFuture<Output = Result<Self::Asset, Self::Error>>;
@ -47,7 +46,7 @@ pub trait ErasedAssetLoader: Send + Sync + 'static {
/// Asynchronously loads the asset(s) from the bytes provided by [`Reader`].
fn load<'a>(
&'a self,
reader: &'a mut Reader,
reader: &'a mut dyn Reader,
meta: Box<dyn AssetMetaDyn>,
load_context: LoadContext<'a>,
) -> BoxedFuture<
@ -78,7 +77,7 @@ where
/// Processes the asset in an asynchronous closure.
fn load<'a>(
&'a self,
reader: &'a mut Reader,
reader: &'a mut dyn Reader,
meta: Box<dyn AssetMetaDyn>,
mut load_context: LoadContext<'a>,
) -> BoxedFuture<
@ -519,7 +518,7 @@ impl<'a> LoadContext<'a> {
path: AssetPath<'static>,
meta: Box<dyn AssetMetaDyn>,
loader: &dyn ErasedAssetLoader,
reader: &mut Reader<'_>,
reader: &mut dyn Reader,
) -> Result<ErasedLoadedAsset, LoadDirectError> {
let loaded_asset = self
.asset_server

View file

@ -11,16 +11,16 @@ 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>>),
enum ReaderRef<'a> {
Borrowed(&'a mut dyn Reader),
Boxed(Box<dyn Reader + 'a>),
}
impl<'a, 'b> ReaderRef<'a, 'b> {
pub fn as_mut(&mut self) -> &mut Reader {
impl ReaderRef<'_> {
pub fn as_mut(&mut self) -> &mut dyn Reader {
match self {
ReaderRef::Borrowed(r) => r,
ReaderRef::Boxed(b) => &mut *b,
ReaderRef::Borrowed(r) => &mut **r,
ReaderRef::Boxed(b) => &mut **b,
}
}
}
@ -168,13 +168,13 @@ impl<'ctx, 'builder> UntypedNestedLoader<'ctx, 'builder> {
/// - `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>>,
reader: Option<&'builder mut (dyn 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 {
pub fn with_reader(mut self, reader: &'builder mut (dyn Reader + 'reader)) -> Self {
self.reader = Some(reader);
self
}

View file

@ -196,7 +196,7 @@ impl AssetLoader for () {
type Error = std::io::Error;
async fn load<'a>(
&'a self,
_reader: &'a mut crate::io::Reader<'_>,
_reader: &'a mut dyn crate::io::Reader,
_settings: &'a Self::Settings,
_load_context: &'a mut crate::LoadContext<'_>,
) -> Result<Self::Asset, Self::Error> {

View file

@ -310,7 +310,7 @@ impl<T: AssetLoader> AssetLoader for InstrumentedAssetLoader<T> {
fn load<'a>(
&'a self,
reader: &'a mut crate::io::Reader,
reader: &'a mut dyn crate::io::Reader,
settings: &'a Self::Settings,
load_context: &'a mut crate::LoadContext,
) -> impl ConditionalSendFuture<Output = Result<Self::Asset, Self::Error>> {
@ -382,7 +382,7 @@ mod tests {
async fn load<'a>(
&'a self,
_: &'a mut crate::io::Reader<'_>,
_: &'a mut dyn crate::io::Reader,
_: &'a Self::Settings,
_: &'a mut crate::LoadContext<'_>,
) -> Result<Self::Asset, Self::Error> {

View file

@ -1065,7 +1065,7 @@ impl AssetServer {
(
Box<dyn AssetMetaDyn>,
Arc<dyn ErasedAssetLoader>,
Box<Reader<'a>>,
Box<dyn Reader + 'a>,
),
AssetLoadError,
> {
@ -1169,7 +1169,7 @@ impl AssetServer {
asset_path: &AssetPath<'_>,
meta: Box<dyn AssetMetaDyn>,
loader: &dyn ErasedAssetLoader,
reader: &mut Reader<'_>,
reader: &mut dyn Reader,
load_dependencies: bool,
populate_hashes: bool,
) -> Result<ErasedLoadedAsset, AssetLoadError> {

View file

@ -1,7 +1,4 @@
use bevy_asset::{
io::{AsyncReadExt, Reader},
Asset, AssetLoader, LoadContext,
};
use bevy_asset::{io::Reader, Asset, AssetLoader, LoadContext};
use bevy_reflect::TypePath;
use std::{io::Cursor, sync::Arc};
@ -46,7 +43,7 @@ impl AssetLoader for AudioLoader {
async fn load<'a>(
&'a self,
reader: &'a mut Reader<'_>,
reader: &'a mut dyn Reader,
_settings: &'a Self::Settings,
_load_context: &'a mut LoadContext<'_>,
) -> Result<AudioSource, Self::Error> {

View file

@ -6,7 +6,7 @@ use crate::{
#[cfg(feature = "bevy_animation")]
use bevy_animation::{AnimationTarget, AnimationTargetId};
use bevy_asset::{
io::Reader, AssetLoadError, AssetLoader, AsyncReadExt, Handle, LoadContext, ReadAssetBytesError,
io::Reader, AssetLoadError, AssetLoader, Handle, LoadContext, ReadAssetBytesError,
};
use bevy_color::{Color, LinearRgba};
use bevy_core::Name;
@ -176,7 +176,7 @@ impl AssetLoader for GltfLoader {
type Error = GltfError;
async fn load<'a>(
&'a self,
reader: &'a mut Reader<'_>,
reader: &'a mut dyn Reader,
settings: &'a GltfLoaderSettings,
load_context: &'a mut LoadContext<'_>,
) -> Result<Gltf, Self::Error> {

View file

@ -1,5 +1,5 @@
use bevy_asset::{
io::{AsyncReadAndSeek, Reader, Writer},
io::{Reader, Writer},
saver::{AssetSaver, SavedAsset},
Asset, AssetLoader, AsyncReadExt, AsyncWriteExt, LoadContext,
};
@ -85,7 +85,7 @@ impl AssetLoader for MeshletMeshSaverLoad {
async fn load<'a>(
&'a self,
reader: &'a mut Reader<'_>,
reader: &'a mut dyn Reader,
_settings: &'a Self::Settings,
_load_context: &'a mut LoadContext<'_>,
) -> Result<Self::Asset, Self::Error> {
@ -144,9 +144,7 @@ pub enum MeshletMeshSaveOrLoadError {
Io(#[from] std::io::Error),
}
async fn read_u64(
reader: &mut (dyn AsyncReadAndSeek + Sync + Send + Unpin),
) -> Result<u64, bincode::Error> {
async fn read_u64(reader: &mut dyn Reader) -> Result<u64, bincode::Error> {
let mut bytes = [0u8; 8];
reader.read_exact(&mut bytes).await?;
Ok(u64::from_le_bytes(bytes))

View file

@ -3,7 +3,6 @@ use crate::define_atomic_id;
use bevy_asset::{io::Reader, Asset, AssetLoader, AssetPath, Handle, LoadContext};
use bevy_reflect::TypePath;
use bevy_utils::tracing::error;
use futures_lite::AsyncReadExt;
use std::{borrow::Cow, marker::Copy};
use thiserror::Error;
@ -261,7 +260,7 @@ impl AssetLoader for ShaderLoader {
type Error = ShaderLoaderError;
async fn load<'a>(
&'a self,
reader: &'a mut Reader<'_>,
reader: &'a mut dyn Reader,
_settings: &'a Self::Settings,
load_context: &'a mut LoadContext<'_>,
) -> Result<Shader, Self::Error> {

View file

@ -2,10 +2,7 @@ use crate::{
render_asset::RenderAssetUsages,
texture::{Image, TextureFormatPixelInfo},
};
use bevy_asset::{
io::{AsyncReadExt, Reader},
AssetLoader, LoadContext,
};
use bevy_asset::{io::Reader, AssetLoader, LoadContext};
use image::ImageDecoder;
use serde::{Deserialize, Serialize};
use thiserror::Error;
@ -37,7 +34,7 @@ impl AssetLoader for ExrTextureLoader {
async fn load<'a>(
&'a self,
reader: &'a mut Reader<'_>,
reader: &'a mut dyn Reader,
settings: &'a Self::Settings,
_load_context: &'a mut LoadContext<'_>,
) -> Result<Image, Self::Error> {

View file

@ -2,7 +2,7 @@ use crate::{
render_asset::RenderAssetUsages,
texture::{Image, TextureFormatPixelInfo},
};
use bevy_asset::{io::Reader, AssetLoader, AsyncReadExt, LoadContext};
use bevy_asset::{io::Reader, AssetLoader, LoadContext};
use image::DynamicImage;
use serde::{Deserialize, Serialize};
use thiserror::Error;
@ -32,7 +32,7 @@ impl AssetLoader for HdrTextureLoader {
type Error = HdrTextureLoaderError;
async fn load<'a>(
&'a self,
reader: &'a mut Reader<'_>,
reader: &'a mut dyn Reader,
settings: &'a Self::Settings,
_load_context: &'a mut LoadContext<'_>,
) -> Result<Image, Self::Error> {

View file

@ -1,4 +1,4 @@
use bevy_asset::{io::Reader, AssetLoader, AsyncReadExt, LoadContext};
use bevy_asset::{io::Reader, AssetLoader, LoadContext};
use bevy_ecs::prelude::{FromWorld, World};
use thiserror::Error;
@ -88,7 +88,7 @@ impl AssetLoader for ImageLoader {
type Error = ImageLoaderError;
async fn load<'a>(
&'a self,
reader: &'a mut Reader<'_>,
reader: &'a mut dyn Reader,
settings: &'a ImageLoaderSettings,
load_context: &'a mut LoadContext<'_>,
) -> Result<Image, Self::Error> {

View file

@ -2,7 +2,7 @@ use crate::ron;
#[cfg(feature = "serialize")]
use crate::serde::SceneDeserializer;
use crate::DynamicScene;
use bevy_asset::{io::Reader, AssetLoader, AsyncReadExt, LoadContext};
use bevy_asset::{io::Reader, AssetLoader, LoadContext};
use bevy_ecs::reflect::AppTypeRegistry;
use bevy_ecs::world::{FromWorld, World};
use bevy_reflect::TypeRegistryArc;
@ -47,7 +47,7 @@ impl AssetLoader for SceneLoader {
async fn load<'a>(
&'a self,
reader: &'a mut Reader<'_>,
reader: &'a mut dyn Reader,
_settings: &'a (),
_load_context: &'a mut LoadContext<'_>,
) -> Result<Self::Asset, Self::Error> {

View file

@ -1,5 +1,5 @@
use crate::Font;
use bevy_asset::{io::Reader, AssetLoader, AsyncReadExt, LoadContext};
use bevy_asset::{io::Reader, AssetLoader, LoadContext};
use thiserror::Error;
#[derive(Default)]
@ -23,7 +23,7 @@ impl AssetLoader for FontLoader {
type Error = FontLoaderError;
async fn load<'a>(
&'a self,
reader: &'a mut Reader<'_>,
reader: &'a mut dyn Reader,
_settings: &'a (),
_load_context: &'a mut LoadContext<'_>,
) -> Result<Font, Self::Error> {

View file

@ -3,7 +3,7 @@
use bevy::{
asset::{
io::{Reader, VecReader},
AssetLoader, AsyncReadExt, ErasedLoadedAsset, LoadContext, LoadDirectError,
AssetLoader, ErasedLoadedAsset, LoadContext, LoadDirectError,
},
prelude::*,
reflect::TypePath,
@ -42,7 +42,7 @@ impl AssetLoader for GzAssetLoader {
type Error = GzAssetLoaderError;
async fn load<'a>(
&'a self,
reader: &'a mut Reader<'_>,
reader: &'a mut dyn Reader,
_settings: &'a (),
load_context: &'a mut LoadContext<'_>,
) -> Result<Self::Asset, Self::Error> {

View file

@ -1,7 +1,7 @@
//! Implements loader for a custom asset type.
use bevy::{
asset::{io::Reader, AssetLoader, AsyncReadExt, LoadContext},
asset::{io::Reader, AssetLoader, LoadContext},
prelude::*,
reflect::TypePath,
};
@ -35,7 +35,7 @@ impl AssetLoader for CustomAssetLoader {
type Error = CustomAssetLoaderError;
async fn load<'a>(
&'a self,
reader: &'a mut Reader<'_>,
reader: &'a mut dyn Reader,
_settings: &'a (),
_load_context: &'a mut LoadContext<'_>,
) -> Result<Self::Asset, Self::Error> {
@ -74,7 +74,7 @@ impl AssetLoader for BlobAssetLoader {
async fn load<'a>(
&'a self,
reader: &'a mut Reader<'_>,
reader: &'a mut dyn Reader,
_settings: &'a (),
_load_context: &'a mut LoadContext<'_>,
) -> Result<Self::Asset, Self::Error> {

View file

@ -15,11 +15,11 @@ use std::path::Path;
struct CustomAssetReader(Box<dyn ErasedAssetReader>);
impl AssetReader for CustomAssetReader {
async fn read<'a>(&'a self, path: &'a Path) -> Result<Box<Reader<'a>>, AssetReaderError> {
async fn read<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
info!("Reading {:?}", path);
self.0.read(path).await
}
async fn read_meta<'a>(&'a self, path: &'a Path) -> Result<Box<Reader<'a>>, AssetReaderError> {
async fn read_meta<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
self.0.read_meta(path).await
}

View file

@ -7,7 +7,7 @@ use bevy::{
processor::LoadTransformAndSave,
saver::{AssetSaver, SavedAsset},
transformer::{AssetTransformer, TransformedAsset},
AssetLoader, AsyncReadExt, AsyncWriteExt, LoadContext,
AssetLoader, AsyncWriteExt, LoadContext,
},
prelude::*,
reflect::TypePath,
@ -83,7 +83,7 @@ impl AssetLoader for TextLoader {
type Error = std::io::Error;
async fn load<'a>(
&'a self,
reader: &'a mut Reader<'_>,
reader: &'a mut dyn Reader,
settings: &'a TextSettings,
_load_context: &'a mut LoadContext<'_>,
) -> Result<Text, Self::Error> {
@ -137,7 +137,7 @@ impl AssetLoader for CoolTextLoader {
async fn load<'a>(
&'a self,
reader: &'a mut Reader<'_>,
reader: &'a mut dyn Reader,
_settings: &'a Self::Settings,
load_context: &'a mut LoadContext<'_>,
) -> Result<CoolText, Self::Error> {