diff --git a/crates/bevy_animation/src/graph.rs b/crates/bevy_animation/src/graph.rs index aba53f1d7a..01a270ed53 100644 --- a/crates/bevy_animation/src/graph.rs +++ b/crates/bevy_animation/src/graph.rs @@ -6,7 +6,6 @@ use std::ops::{Index, IndexMut}; use bevy_asset::io::Reader; use bevy_asset::{Asset, AssetId, AssetLoader, AssetPath, AsyncReadExt as _, Handle, LoadContext}; use bevy_reflect::{Reflect, ReflectSerialize}; -use bevy_utils::BoxedFuture; use petgraph::graph::{DiGraph, NodeIndex}; use ron::de::SpannedError; use serde::{Deserialize, Serialize}; @@ -336,40 +335,37 @@ impl AssetLoader for AnimationGraphAssetLoader { type Error = AnimationGraphLoadError; - fn load<'a>( + async fn load<'a>( &'a self, - reader: &'a mut Reader, + reader: &'a mut Reader<'_>, _: &'a Self::Settings, - load_context: &'a mut LoadContext, - ) -> BoxedFuture<'a, Result> { - Box::pin(async move { - let mut bytes = Vec::new(); - reader.read_to_end(&mut bytes).await?; + load_context: &'a mut LoadContext<'_>, + ) -> Result { + let mut bytes = Vec::new(); + reader.read_to_end(&mut bytes).await?; - // Deserialize a `SerializedAnimationGraph` directly, so that we can - // get the list of the animation clips it refers to and load them. - let mut deserializer = ron::de::Deserializer::from_bytes(&bytes)?; - let serialized_animation_graph = - SerializedAnimationGraph::deserialize(&mut deserializer) - .map_err(|err| deserializer.span_error(err))?; + // Deserialize a `SerializedAnimationGraph` directly, so that we can + // get the list of the animation clips it refers to and load them. + let mut deserializer = ron::de::Deserializer::from_bytes(&bytes)?; + let serialized_animation_graph = SerializedAnimationGraph::deserialize(&mut deserializer) + .map_err(|err| deserializer.span_error(err))?; - // Load all `AssetPath`s to convert from a - // `SerializedAnimationGraph` to a real `AnimationGraph`. - Ok(AnimationGraph { - graph: serialized_animation_graph.graph.map( - |_, serialized_node| AnimationGraphNode { - clip: serialized_node.clip.as_ref().map(|clip| match clip { - SerializedAnimationClip::AssetId(asset_id) => Handle::Weak(*asset_id), - SerializedAnimationClip::AssetPath(asset_path) => { - load_context.load(asset_path) - } - }), - weight: serialized_node.weight, - }, - |_, _| (), - ), - root: serialized_animation_graph.root, - }) + // Load all `AssetPath`s to convert from a + // `SerializedAnimationGraph` to a real `AnimationGraph`. + Ok(AnimationGraph { + graph: serialized_animation_graph.graph.map( + |_, serialized_node| AnimationGraphNode { + clip: serialized_node.clip.as_ref().map(|clip| match clip { + SerializedAnimationClip::AssetId(asset_id) => Handle::Weak(*asset_id), + SerializedAnimationClip::AssetPath(asset_path) => { + load_context.load(asset_path) + } + }), + weight: serialized_node.weight, + }, + |_, _| (), + ), + root: serialized_animation_graph.root, }) } diff --git a/crates/bevy_asset/src/io/android.rs b/crates/bevy_asset/src/io/android.rs index 38791b35e0..18daac5949 100644 --- a/crates/bevy_asset/src/io/android.rs +++ b/crates/bevy_asset/src/io/android.rs @@ -2,7 +2,6 @@ use crate::io::{ get_meta_path, AssetReader, AssetReaderError, EmptyPathStream, PathStream, Reader, VecReader, }; use bevy_utils::tracing::error; -use bevy_utils::BoxedFuture; use std::{ffi::CString, path::Path}; /// [`AssetReader`] implementation for Android devices, built on top of Android's [`AssetManager`]. @@ -17,57 +16,47 @@ use std::{ffi::CString, path::Path}; pub struct AndroidAssetReader; impl AssetReader for AndroidAssetReader { - fn read<'a>( - &'a self, - path: &'a Path, - ) -> BoxedFuture<'a, Result>, AssetReaderError>> { - Box::pin(async move { - let asset_manager = bevy_winit::ANDROID_APP - .get() - .expect("Bevy must be setup with the #[bevy_main] macro on Android") - .asset_manager(); - let mut opened_asset = asset_manager - .open(&CString::new(path.to_str().unwrap()).unwrap()) - .ok_or(AssetReaderError::NotFound(path.to_path_buf()))?; - let bytes = opened_asset.buffer()?; - let reader: Box = Box::new(VecReader::new(bytes.to_vec())); - Ok(reader) - }) + async fn read<'a>(&'a self, path: &'a Path) -> Result>, AssetReaderError> { + let asset_manager = bevy_winit::ANDROID_APP + .get() + .expect("Bevy must be setup with the #[bevy_main] macro on Android") + .asset_manager(); + let mut opened_asset = asset_manager + .open(&CString::new(path.to_str().unwrap()).unwrap()) + .ok_or(AssetReaderError::NotFound(path.to_path_buf()))?; + let bytes = opened_asset.buffer()?; + let reader: Box = Box::new(VecReader::new(bytes.to_vec())); + Ok(reader) } - fn read_meta<'a>( - &'a self, - path: &'a Path, - ) -> BoxedFuture<'a, Result>, AssetReaderError>> { - Box::pin(async move { - let meta_path = get_meta_path(path); - let asset_manager = bevy_winit::ANDROID_APP - .get() - .expect("Bevy must be setup with the #[bevy_main] macro on Android") - .asset_manager(); - let mut opened_asset = asset_manager - .open(&CString::new(meta_path.to_str().unwrap()).unwrap()) - .ok_or(AssetReaderError::NotFound(meta_path))?; - let bytes = opened_asset.buffer()?; - let reader: Box = Box::new(VecReader::new(bytes.to_vec())); - Ok(reader) - }) + async fn read_meta<'a>(&'a self, path: &'a Path) -> Result>, AssetReaderError> { + let meta_path = get_meta_path(path); + let asset_manager = bevy_winit::ANDROID_APP + .get() + .expect("Bevy must be setup with the #[bevy_main] macro on Android") + .asset_manager(); + let mut opened_asset = asset_manager + .open(&CString::new(meta_path.to_str().unwrap()).unwrap()) + .ok_or(AssetReaderError::NotFound(meta_path))?; + let bytes = opened_asset.buffer()?; + let reader: Box = Box::new(VecReader::new(bytes.to_vec())); + Ok(reader) } - fn read_directory<'a>( + async fn read_directory<'a>( &'a self, _path: &'a Path, - ) -> BoxedFuture<'a, Result, AssetReaderError>> { + ) -> Result, AssetReaderError> { let stream: Box = Box::new(EmptyPathStream); error!("Reading directories is not supported with the AndroidAssetReader"); - Box::pin(async move { Ok(stream) }) + Ok(stream) } - fn is_directory<'a>( + async fn is_directory<'a>( &'a self, _path: &'a Path, - ) -> BoxedFuture<'a, std::result::Result> { + ) -> std::result::Result { error!("Reading directories is not supported with the AndroidAssetReader"); - Box::pin(async move { Ok(false) }) + Ok(false) } } diff --git a/crates/bevy_asset/src/io/file/file_asset.rs b/crates/bevy_asset/src/io/file/file_asset.rs index aa20913140..5826fe097d 100644 --- a/crates/bevy_asset/src/io/file/file_asset.rs +++ b/crates/bevy_asset/src/io/file/file_asset.rs @@ -3,7 +3,6 @@ use crate::io::{ Reader, Writer, }; use async_fs::{read_dir, File}; -use bevy_utils::BoxedFuture; use futures_lite::StreamExt; use std::path::Path; @@ -11,215 +10,168 @@ use std::path::Path; use super::{FileAssetReader, FileAssetWriter}; impl AssetReader for FileAssetReader { - fn read<'a>( - &'a self, - path: &'a Path, - ) -> BoxedFuture<'a, Result>, AssetReaderError>> { - Box::pin(async move { - let full_path = self.root_path.join(path); - match File::open(&full_path).await { - Ok(file) => { - let reader: Box = Box::new(file); - Ok(reader) - } - Err(e) => { - if e.kind() == std::io::ErrorKind::NotFound { - Err(AssetReaderError::NotFound(full_path)) - } else { - Err(e.into()) - } + async fn read<'a>(&'a self, path: &'a Path) -> Result>, AssetReaderError> { + let full_path = self.root_path.join(path); + match File::open(&full_path).await { + Ok(file) => { + let reader: Box = Box::new(file); + Ok(reader) + } + Err(e) => { + if e.kind() == std::io::ErrorKind::NotFound { + Err(AssetReaderError::NotFound(full_path)) + } else { + Err(e.into()) } } - }) + } } - fn read_meta<'a>( - &'a self, - path: &'a Path, - ) -> BoxedFuture<'a, Result>, AssetReaderError>> { + async fn read_meta<'a>(&'a self, path: &'a Path) -> Result>, AssetReaderError> { let meta_path = get_meta_path(path); - Box::pin(async move { - let full_path = self.root_path.join(meta_path); - match File::open(&full_path).await { - Ok(file) => { - let reader: Box = Box::new(file); - Ok(reader) - } - Err(e) => { - if e.kind() == std::io::ErrorKind::NotFound { - Err(AssetReaderError::NotFound(full_path)) - } else { - Err(e.into()) - } + let full_path = self.root_path.join(meta_path); + match File::open(&full_path).await { + Ok(file) => { + let reader: Box = Box::new(file); + Ok(reader) + } + Err(e) => { + if e.kind() == std::io::ErrorKind::NotFound { + Err(AssetReaderError::NotFound(full_path)) + } else { + Err(e.into()) } } - }) + } } - fn read_directory<'a>( + async fn read_directory<'a>( &'a self, path: &'a Path, - ) -> BoxedFuture<'a, Result, AssetReaderError>> { - Box::pin(async move { - let full_path = self.root_path.join(path); - match read_dir(&full_path).await { - Ok(read_dir) => { - let root_path = self.root_path.clone(); - let mapped_stream = read_dir.filter_map(move |f| { - f.ok().and_then(|dir_entry| { - let path = dir_entry.path(); - // filter out meta files as they are not considered assets - if let Some(ext) = path.extension().and_then(|e| e.to_str()) { - if ext.eq_ignore_ascii_case("meta") { - return None; - } + ) -> Result, AssetReaderError> { + let full_path = self.root_path.join(path); + match read_dir(&full_path).await { + Ok(read_dir) => { + let root_path = self.root_path.clone(); + let mapped_stream = read_dir.filter_map(move |f| { + f.ok().and_then(|dir_entry| { + let path = dir_entry.path(); + // filter out meta files as they are not considered assets + if let Some(ext) = path.extension().and_then(|e| e.to_str()) { + if ext.eq_ignore_ascii_case("meta") { + return None; } - let relative_path = path.strip_prefix(&root_path).unwrap(); - Some(relative_path.to_owned()) - }) - }); - let read_dir: Box = Box::new(mapped_stream); - Ok(read_dir) - } - Err(e) => { - if e.kind() == std::io::ErrorKind::NotFound { - Err(AssetReaderError::NotFound(full_path)) - } else { - Err(e.into()) - } + } + let relative_path = path.strip_prefix(&root_path).unwrap(); + Some(relative_path.to_owned()) + }) + }); + let read_dir: Box = Box::new(mapped_stream); + Ok(read_dir) + } + Err(e) => { + if e.kind() == std::io::ErrorKind::NotFound { + Err(AssetReaderError::NotFound(full_path)) + } else { + Err(e.into()) } } - }) + } } - fn is_directory<'a>( - &'a self, - path: &'a Path, - ) -> BoxedFuture<'a, Result> { - Box::pin(async move { - let full_path = self.root_path.join(path); - let metadata = full_path - .metadata() - .map_err(|_e| AssetReaderError::NotFound(path.to_owned()))?; - Ok(metadata.file_type().is_dir()) - }) + async fn is_directory<'a>(&'a self, path: &'a Path) -> Result { + let full_path = self.root_path.join(path); + let metadata = full_path + .metadata() + .map_err(|_e| AssetReaderError::NotFound(path.to_owned()))?; + Ok(metadata.file_type().is_dir()) } } impl AssetWriter for FileAssetWriter { - fn write<'a>( - &'a self, - path: &'a Path, - ) -> BoxedFuture<'a, Result, AssetWriterError>> { - Box::pin(async move { - let full_path = self.root_path.join(path); - if let Some(parent) = full_path.parent() { - async_fs::create_dir_all(parent).await?; - } - let file = File::create(&full_path).await?; - let writer: Box = Box::new(file); - Ok(writer) - }) + async fn write<'a>(&'a self, path: &'a Path) -> Result, AssetWriterError> { + let full_path = self.root_path.join(path); + if let Some(parent) = full_path.parent() { + async_fs::create_dir_all(parent).await?; + } + let file = File::create(&full_path).await?; + let writer: Box = Box::new(file); + Ok(writer) } - fn write_meta<'a>( - &'a self, - path: &'a Path, - ) -> BoxedFuture<'a, Result, AssetWriterError>> { - Box::pin(async move { - let meta_path = get_meta_path(path); - let full_path = self.root_path.join(meta_path); - if let Some(parent) = full_path.parent() { - async_fs::create_dir_all(parent).await?; - } - let file = File::create(&full_path).await?; - let writer: Box = Box::new(file); - Ok(writer) - }) + async fn write_meta<'a>(&'a self, path: &'a Path) -> Result, AssetWriterError> { + let meta_path = get_meta_path(path); + let full_path = self.root_path.join(meta_path); + if let Some(parent) = full_path.parent() { + async_fs::create_dir_all(parent).await?; + } + let file = File::create(&full_path).await?; + let writer: Box = Box::new(file); + Ok(writer) } - fn remove<'a>(&'a self, path: &'a Path) -> BoxedFuture<'a, Result<(), AssetWriterError>> { - Box::pin(async move { - let full_path = self.root_path.join(path); - async_fs::remove_file(full_path).await?; - Ok(()) - }) + async fn remove<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> { + let full_path = self.root_path.join(path); + async_fs::remove_file(full_path).await?; + Ok(()) } - fn remove_meta<'a>(&'a self, path: &'a Path) -> BoxedFuture<'a, Result<(), AssetWriterError>> { - Box::pin(async move { - let meta_path = get_meta_path(path); - let full_path = self.root_path.join(meta_path); - async_fs::remove_file(full_path).await?; - Ok(()) - }) + async fn remove_meta<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> { + let meta_path = get_meta_path(path); + let full_path = self.root_path.join(meta_path); + async_fs::remove_file(full_path).await?; + Ok(()) } - fn rename<'a>( + async fn rename<'a>( &'a self, old_path: &'a Path, new_path: &'a Path, - ) -> BoxedFuture<'a, Result<(), AssetWriterError>> { - Box::pin(async move { - let full_old_path = self.root_path.join(old_path); - let full_new_path = self.root_path.join(new_path); - if let Some(parent) = full_new_path.parent() { - async_fs::create_dir_all(parent).await?; - } - async_fs::rename(full_old_path, full_new_path).await?; - Ok(()) - }) + ) -> Result<(), AssetWriterError> { + let full_old_path = self.root_path.join(old_path); + let full_new_path = self.root_path.join(new_path); + if let Some(parent) = full_new_path.parent() { + async_fs::create_dir_all(parent).await?; + } + async_fs::rename(full_old_path, full_new_path).await?; + Ok(()) } - fn rename_meta<'a>( + async fn rename_meta<'a>( &'a self, old_path: &'a Path, new_path: &'a Path, - ) -> BoxedFuture<'a, Result<(), AssetWriterError>> { - Box::pin(async move { - let old_meta_path = get_meta_path(old_path); - let new_meta_path = get_meta_path(new_path); - let full_old_path = self.root_path.join(old_meta_path); - let full_new_path = self.root_path.join(new_meta_path); - if let Some(parent) = full_new_path.parent() { - async_fs::create_dir_all(parent).await?; - } - async_fs::rename(full_old_path, full_new_path).await?; - Ok(()) - }) + ) -> Result<(), AssetWriterError> { + let old_meta_path = get_meta_path(old_path); + let new_meta_path = get_meta_path(new_path); + let full_old_path = self.root_path.join(old_meta_path); + let full_new_path = self.root_path.join(new_meta_path); + if let Some(parent) = full_new_path.parent() { + async_fs::create_dir_all(parent).await?; + } + async_fs::rename(full_old_path, full_new_path).await?; + Ok(()) } - fn remove_directory<'a>( - &'a self, - path: &'a Path, - ) -> BoxedFuture<'a, Result<(), AssetWriterError>> { - Box::pin(async move { - let full_path = self.root_path.join(path); - async_fs::remove_dir_all(full_path).await?; - Ok(()) - }) + async fn remove_directory<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> { + let full_path = self.root_path.join(path); + async_fs::remove_dir_all(full_path).await?; + Ok(()) } - fn remove_empty_directory<'a>( - &'a self, - path: &'a Path, - ) -> BoxedFuture<'a, Result<(), AssetWriterError>> { - Box::pin(async move { - let full_path = self.root_path.join(path); - async_fs::remove_dir(full_path).await?; - Ok(()) - }) + async fn remove_empty_directory<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> { + let full_path = self.root_path.join(path); + async_fs::remove_dir(full_path).await?; + Ok(()) } - fn remove_assets_in_directory<'a>( + async fn remove_assets_in_directory<'a>( &'a self, path: &'a Path, - ) -> BoxedFuture<'a, Result<(), AssetWriterError>> { - Box::pin(async move { - let full_path = self.root_path.join(path); - async_fs::remove_dir_all(&full_path).await?; - async_fs::create_dir_all(&full_path).await?; - Ok(()) - }) + ) -> Result<(), AssetWriterError> { + let full_path = self.root_path.join(path); + async_fs::remove_dir_all(&full_path).await?; + async_fs::create_dir_all(&full_path).await?; + Ok(()) } } diff --git a/crates/bevy_asset/src/io/file/sync_file_asset.rs b/crates/bevy_asset/src/io/file/sync_file_asset.rs index a8bf573a7a..4264721501 100644 --- a/crates/bevy_asset/src/io/file/sync_file_asset.rs +++ b/crates/bevy_asset/src/io/file/sync_file_asset.rs @@ -5,7 +5,6 @@ use crate::io::{ get_meta_path, AssetReader, AssetReaderError, AssetWriter, AssetWriterError, PathStream, Reader, Writer, }; -use bevy_utils::BoxedFuture; use std::{ fs::{read_dir, File}, @@ -76,221 +75,180 @@ impl Stream for DirReader { } impl AssetReader for FileAssetReader { - fn read<'a>( - &'a self, - path: &'a Path, - ) -> BoxedFuture<'a, Result>, AssetReaderError>> { - Box::pin(async move { - let full_path = self.root_path.join(path); - match File::open(&full_path) { - Ok(file) => { - let reader: Box = Box::new(FileReader(file)); - Ok(reader) - } - Err(e) => { - if e.kind() == std::io::ErrorKind::NotFound { - Err(AssetReaderError::NotFound(full_path)) - } else { - Err(e.into()) - } + async fn read<'a>(&'a self, path: &'a Path) -> Result>, AssetReaderError> { + let full_path = self.root_path.join(path); + match File::open(&full_path) { + Ok(file) => { + let reader: Box = Box::new(FileReader(file)); + Ok(reader) + } + Err(e) => { + if e.kind() == std::io::ErrorKind::NotFound { + Err(AssetReaderError::NotFound(full_path)) + } else { + Err(e.into()) } } - }) + } } - fn read_meta<'a>( - &'a self, - path: &'a Path, - ) -> BoxedFuture<'a, Result>, AssetReaderError>> { + async fn read_meta<'a>(&'a self, path: &'a Path) -> Result>, AssetReaderError> { let meta_path = get_meta_path(path); - Box::pin(async move { - let full_path = self.root_path.join(meta_path); - match File::open(&full_path) { - Ok(file) => { - let reader: Box = Box::new(FileReader(file)); - Ok(reader) - } - Err(e) => { - if e.kind() == std::io::ErrorKind::NotFound { - Err(AssetReaderError::NotFound(full_path)) - } else { - Err(e.into()) - } + let full_path = self.root_path.join(meta_path); + match File::open(&full_path) { + Ok(file) => { + let reader: Box = Box::new(FileReader(file)); + Ok(reader) + } + Err(e) => { + if e.kind() == std::io::ErrorKind::NotFound { + Err(AssetReaderError::NotFound(full_path)) + } else { + Err(e.into()) } } - }) + } } - fn read_directory<'a>( + async fn read_directory<'a>( &'a self, path: &'a Path, - ) -> BoxedFuture<'a, Result, AssetReaderError>> { - Box::pin(async move { - let full_path = self.root_path.join(path); - match read_dir(&full_path) { - Ok(read_dir) => { - let root_path = self.root_path.clone(); - let mapped_stream = read_dir.filter_map(move |f| { - f.ok().and_then(|dir_entry| { - let path = dir_entry.path(); - // filter out meta files as they are not considered assets - if let Some(ext) = path.extension().and_then(|e| e.to_str()) { - if ext.eq_ignore_ascii_case("meta") { - return None; - } + ) -> Result, AssetReaderError> { + let full_path = self.root_path.join(path); + match read_dir(&full_path) { + Ok(read_dir) => { + let root_path = self.root_path.clone(); + let mapped_stream = read_dir.filter_map(move |f| { + f.ok().and_then(|dir_entry| { + let path = dir_entry.path(); + // filter out meta files as they are not considered assets + if let Some(ext) = path.extension().and_then(|e| e.to_str()) { + if ext.eq_ignore_ascii_case("meta") { + return None; } - let relative_path = path.strip_prefix(&root_path).unwrap(); - Some(relative_path.to_owned()) - }) - }); - let read_dir: Box = Box::new(DirReader(mapped_stream.collect())); - Ok(read_dir) - } - Err(e) => { - if e.kind() == std::io::ErrorKind::NotFound { - Err(AssetReaderError::NotFound(full_path)) - } else { - Err(e.into()) - } + } + let relative_path = path.strip_prefix(&root_path).unwrap(); + Some(relative_path.to_owned()) + }) + }); + let read_dir: Box = Box::new(DirReader(mapped_stream.collect())); + Ok(read_dir) + } + Err(e) => { + if e.kind() == std::io::ErrorKind::NotFound { + Err(AssetReaderError::NotFound(full_path)) + } else { + Err(e.into()) } } - }) + } } - fn is_directory<'a>( + async fn is_directory<'a>( &'a self, path: &'a Path, - ) -> BoxedFuture<'a, std::result::Result> { - Box::pin(async move { - let full_path = self.root_path.join(path); - let metadata = full_path - .metadata() - .map_err(|_e| AssetReaderError::NotFound(path.to_owned()))?; - Ok(metadata.file_type().is_dir()) - }) + ) -> std::result::Result { + let full_path = self.root_path.join(path); + let metadata = full_path + .metadata() + .map_err(|_e| AssetReaderError::NotFound(path.to_owned()))?; + Ok(metadata.file_type().is_dir()) } } impl AssetWriter for FileAssetWriter { - fn write<'a>( - &'a self, - path: &'a Path, - ) -> BoxedFuture<'a, Result, AssetWriterError>> { - Box::pin(async move { - let full_path = self.root_path.join(path); - if let Some(parent) = full_path.parent() { - std::fs::create_dir_all(parent)?; - } - let file = File::create(&full_path)?; - let writer: Box = Box::new(FileWriter(file)); - Ok(writer) - }) + async fn write<'a>(&'a self, path: &'a Path) -> Result, AssetWriterError> { + let full_path = self.root_path.join(path); + if let Some(parent) = full_path.parent() { + std::fs::create_dir_all(parent)?; + } + let file = File::create(&full_path)?; + let writer: Box = Box::new(FileWriter(file)); + Ok(writer) } - fn write_meta<'a>( - &'a self, - path: &'a Path, - ) -> BoxedFuture<'a, Result, AssetWriterError>> { - Box::pin(async move { - let meta_path = get_meta_path(path); - let full_path = self.root_path.join(meta_path); - if let Some(parent) = full_path.parent() { - std::fs::create_dir_all(parent)?; - } - let file = File::create(&full_path)?; - let writer: Box = Box::new(FileWriter(file)); - Ok(writer) - }) + async fn write_meta<'a>(&'a self, path: &'a Path) -> Result, AssetWriterError> { + let meta_path = get_meta_path(path); + let full_path = self.root_path.join(meta_path); + if let Some(parent) = full_path.parent() { + std::fs::create_dir_all(parent)?; + } + let file = File::create(&full_path)?; + let writer: Box = Box::new(FileWriter(file)); + Ok(writer) } - fn remove<'a>( - &'a self, - path: &'a Path, - ) -> BoxedFuture<'a, std::result::Result<(), AssetWriterError>> { - Box::pin(async move { - let full_path = self.root_path.join(path); - std::fs::remove_file(full_path)?; - Ok(()) - }) + async fn remove<'a>(&'a self, path: &'a Path) -> std::result::Result<(), AssetWriterError> { + let full_path = self.root_path.join(path); + std::fs::remove_file(full_path)?; + Ok(()) } - fn remove_meta<'a>( + async fn remove_meta<'a>( &'a self, path: &'a Path, - ) -> BoxedFuture<'a, std::result::Result<(), AssetWriterError>> { - Box::pin(async move { - let meta_path = get_meta_path(path); - let full_path = self.root_path.join(meta_path); - std::fs::remove_file(full_path)?; - Ok(()) - }) + ) -> std::result::Result<(), AssetWriterError> { + let meta_path = get_meta_path(path); + let full_path = self.root_path.join(meta_path); + std::fs::remove_file(full_path)?; + Ok(()) } - fn remove_directory<'a>( + async fn remove_directory<'a>( &'a self, path: &'a Path, - ) -> BoxedFuture<'a, std::result::Result<(), AssetWriterError>> { - Box::pin(async move { - let full_path = self.root_path.join(path); - std::fs::remove_dir_all(full_path)?; - Ok(()) - }) + ) -> std::result::Result<(), AssetWriterError> { + let full_path = self.root_path.join(path); + std::fs::remove_dir_all(full_path)?; + Ok(()) } - fn remove_empty_directory<'a>( + async fn remove_empty_directory<'a>( &'a self, path: &'a Path, - ) -> BoxedFuture<'a, std::result::Result<(), AssetWriterError>> { - Box::pin(async move { - let full_path = self.root_path.join(path); - std::fs::remove_dir(full_path)?; - Ok(()) - }) + ) -> std::result::Result<(), AssetWriterError> { + let full_path = self.root_path.join(path); + std::fs::remove_dir(full_path)?; + Ok(()) } - fn remove_assets_in_directory<'a>( + async fn remove_assets_in_directory<'a>( &'a self, path: &'a Path, - ) -> BoxedFuture<'a, std::result::Result<(), AssetWriterError>> { - Box::pin(async move { - let full_path = self.root_path.join(path); - std::fs::remove_dir_all(&full_path)?; - std::fs::create_dir_all(&full_path)?; - Ok(()) - }) + ) -> std::result::Result<(), AssetWriterError> { + let full_path = self.root_path.join(path); + std::fs::remove_dir_all(&full_path)?; + std::fs::create_dir_all(&full_path)?; + Ok(()) } - fn rename<'a>( + async fn rename<'a>( &'a self, old_path: &'a Path, new_path: &'a Path, - ) -> BoxedFuture<'a, std::result::Result<(), AssetWriterError>> { - Box::pin(async move { - let full_old_path = self.root_path.join(old_path); - let full_new_path = self.root_path.join(new_path); - if let Some(parent) = full_new_path.parent() { - std::fs::create_dir_all(parent)?; - } - std::fs::rename(full_old_path, full_new_path)?; - Ok(()) - }) + ) -> std::result::Result<(), AssetWriterError> { + let full_old_path = self.root_path.join(old_path); + let full_new_path = self.root_path.join(new_path); + if let Some(parent) = full_new_path.parent() { + std::fs::create_dir_all(parent)?; + } + std::fs::rename(full_old_path, full_new_path)?; + Ok(()) } - fn rename_meta<'a>( + async fn rename_meta<'a>( &'a self, old_path: &'a Path, new_path: &'a Path, - ) -> BoxedFuture<'a, std::result::Result<(), AssetWriterError>> { - Box::pin(async move { - let old_meta_path = get_meta_path(old_path); - let new_meta_path = get_meta_path(new_path); - let full_old_path = self.root_path.join(old_meta_path); - let full_new_path = self.root_path.join(new_meta_path); - if let Some(parent) = full_new_path.parent() { - std::fs::create_dir_all(parent)?; - } - std::fs::rename(full_old_path, full_new_path)?; - Ok(()) - }) + ) -> std::result::Result<(), AssetWriterError> { + let old_meta_path = get_meta_path(old_path); + let new_meta_path = get_meta_path(new_path); + let full_old_path = self.root_path.join(old_meta_path); + let full_new_path = self.root_path.join(new_meta_path); + if let Some(parent) = full_new_path.parent() { + std::fs::create_dir_all(parent)?; + } + std::fs::rename(full_old_path, full_new_path)?; + Ok(()) } } diff --git a/crates/bevy_asset/src/io/gated.rs b/crates/bevy_asset/src/io/gated.rs index 76f531a04c..d3d2b35f1f 100644 --- a/crates/bevy_asset/src/io/gated.rs +++ b/crates/bevy_asset/src/io/gated.rs @@ -1,5 +1,5 @@ use crate::io::{AssetReader, AssetReaderError, PathStream, Reader}; -use bevy_utils::{BoxedFuture, HashMap}; +use bevy_utils::HashMap; use crossbeam_channel::{Receiver, Sender}; use parking_lot::RwLock; use std::{path::Path, sync::Arc}; @@ -55,10 +55,7 @@ impl GatedReader { } impl AssetReader for GatedReader { - fn read<'a>( - &'a self, - path: &'a Path, - ) -> BoxedFuture<'a, Result>, AssetReaderError>> { + async fn read<'a>(&'a self, path: &'a Path) -> Result>, AssetReaderError> { let receiver = { let mut gates = self.gates.write(); let gates = gates @@ -66,31 +63,23 @@ impl AssetReader for GatedReader { .or_insert_with(crossbeam_channel::unbounded); gates.1.clone() }; - Box::pin(async move { - receiver.recv().unwrap(); - let result = self.reader.read(path).await?; - Ok(result) - }) + receiver.recv().unwrap(); + let result = self.reader.read(path).await?; + Ok(result) } - fn read_meta<'a>( - &'a self, - path: &'a Path, - ) -> BoxedFuture<'a, Result>, AssetReaderError>> { - self.reader.read_meta(path) + async fn read_meta<'a>(&'a self, path: &'a Path) -> Result>, AssetReaderError> { + self.reader.read_meta(path).await } - fn read_directory<'a>( + async fn read_directory<'a>( &'a self, path: &'a Path, - ) -> BoxedFuture<'a, Result, AssetReaderError>> { - self.reader.read_directory(path) + ) -> Result, AssetReaderError> { + self.reader.read_directory(path).await } - fn is_directory<'a>( - &'a self, - path: &'a Path, - ) -> BoxedFuture<'a, Result> { - self.reader.is_directory(path) + async fn is_directory<'a>(&'a self, path: &'a Path) -> Result { + self.reader.is_directory(path).await } } diff --git a/crates/bevy_asset/src/io/memory.rs b/crates/bevy_asset/src/io/memory.rs index cc13d04820..563086f7b0 100644 --- a/crates/bevy_asset/src/io/memory.rs +++ b/crates/bevy_asset/src/io/memory.rs @@ -1,5 +1,5 @@ use crate::io::{AssetReader, AssetReaderError, PathStream, Reader}; -use bevy_utils::{BoxedFuture, HashMap}; +use bevy_utils::HashMap; use futures_io::AsyncRead; use futures_lite::{ready, Stream}; use parking_lot::RwLock; @@ -237,62 +237,47 @@ impl AsyncRead for DataReader { } impl AssetReader for MemoryAssetReader { - fn read<'a>( - &'a self, - path: &'a Path, - ) -> BoxedFuture<'a, Result>, AssetReaderError>> { - Box::pin(async move { - self.root - .get_asset(path) - .map(|data| { - let reader: Box = Box::new(DataReader { - data, - bytes_read: 0, - }); - reader - }) - .ok_or_else(|| AssetReaderError::NotFound(path.to_path_buf())) - }) + async fn read<'a>(&'a self, path: &'a Path) -> Result>, AssetReaderError> { + self.root + .get_asset(path) + .map(|data| { + let reader: Box = Box::new(DataReader { + data, + bytes_read: 0, + }); + reader + }) + .ok_or_else(|| AssetReaderError::NotFound(path.to_path_buf())) } - fn read_meta<'a>( - &'a self, - path: &'a Path, - ) -> BoxedFuture<'a, Result>, AssetReaderError>> { - Box::pin(async move { - self.root - .get_metadata(path) - .map(|data| { - let reader: Box = Box::new(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>, AssetReaderError> { + self.root + .get_metadata(path) + .map(|data| { + let reader: Box = Box::new(DataReader { + data, + bytes_read: 0, + }); + reader + }) + .ok_or_else(|| AssetReaderError::NotFound(path.to_path_buf())) } - fn read_directory<'a>( + async fn read_directory<'a>( &'a self, path: &'a Path, - ) -> BoxedFuture<'a, Result, AssetReaderError>> { - Box::pin(async move { - self.root - .get_dir(path) - .map(|dir| { - let stream: Box = Box::new(DirStream::new(dir)); - stream - }) - .ok_or_else(|| AssetReaderError::NotFound(path.to_path_buf())) - }) + ) -> Result, AssetReaderError> { + self.root + .get_dir(path) + .map(|dir| { + let stream: Box = Box::new(DirStream::new(dir)); + stream + }) + .ok_or_else(|| AssetReaderError::NotFound(path.to_path_buf())) } - fn is_directory<'a>( - &'a self, - path: &'a Path, - ) -> BoxedFuture<'a, Result> { - Box::pin(async move { Ok(self.root.get_dir(path).is_some()) }) + async fn is_directory<'a>(&'a self, path: &'a Path) -> Result { + Ok(self.root.get_dir(path).is_some()) } } diff --git a/crates/bevy_asset/src/io/mod.rs b/crates/bevy_asset/src/io/mod.rs index 9b8f83b0ee..766e536455 100644 --- a/crates/bevy_asset/src/io/mod.rs +++ b/crates/bevy_asset/src/io/mod.rs @@ -21,7 +21,7 @@ mod source; pub use futures_lite::{AsyncReadExt, AsyncWriteExt}; pub use source::*; -use bevy_utils::BoxedFuture; +use bevy_utils::{BoxedFuture, ConditionalSendFuture}; use futures_io::{AsyncRead, AsyncWrite}; use futures_lite::{ready, Stream}; use std::{ @@ -59,7 +59,7 @@ pub type Reader<'a> = dyn AsyncRead + Unpin + Send + Sync + 'a; /// 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 -/// `path`. +/// `path`. This trait is not object safe, if needed use a dyn [`ErasedAssetReader`] instead. /// /// Also see [`AssetWriter`]. pub trait AssetReader: Send + Sync + 'static { @@ -67,35 +67,90 @@ pub trait AssetReader: Send + Sync + 'static { fn read<'a>( &'a self, path: &'a Path, - ) -> BoxedFuture<'a, Result>, AssetReaderError>>; + ) -> impl ConditionalSendFuture>, AssetReaderError>>; /// Returns a future to load the full file data at the provided path. fn read_meta<'a>( &'a self, path: &'a Path, - ) -> BoxedFuture<'a, Result>, AssetReaderError>>; + ) -> impl ConditionalSendFuture>, AssetReaderError>>; /// Returns an iterator of directory entry names at the provided path. fn read_directory<'a>( &'a self, path: &'a Path, - ) -> BoxedFuture<'a, Result, AssetReaderError>>; - /// Returns true if the provided path points to a directory. + ) -> impl ConditionalSendFuture, AssetReaderError>>; + /// Returns an iterator of directory entry names at the provided path. fn is_directory<'a>( &'a self, path: &'a Path, - ) -> BoxedFuture<'a, Result>; - + ) -> impl ConditionalSendFuture>; /// Reads asset metadata bytes at the given `path` into a [`Vec`]. This is a convenience /// function that wraps [`AssetReader::read_meta`] by default. fn read_meta_bytes<'a>( &'a self, path: &'a Path, - ) -> BoxedFuture<'a, Result, AssetReaderError>> { - Box::pin(async move { + ) -> impl ConditionalSendFuture, AssetReaderError>> { + async { let mut meta_reader = self.read_meta(path).await?; let mut meta_bytes = Vec::new(); meta_reader.read_to_end(&mut meta_bytes).await?; Ok(meta_bytes) - }) + } + } +} + +/// Equivalent to an [`AssetReader`] but using boxed futures, necessary eg. when using a `dyn AssetReader`, +/// 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>, AssetReaderError>>; + /// Returns a future to load the full file data at the provided path. + fn read_meta<'a>( + &'a self, + path: &'a Path, + ) -> BoxedFuture>, AssetReaderError>>; + /// Returns an iterator of directory entry names at the provided path. + fn read_directory<'a>( + &'a self, + path: &'a Path, + ) -> BoxedFuture, AssetReaderError>>; + /// Returns true if the provided path points to a directory. + fn is_directory<'a>(&'a self, path: &'a Path) -> BoxedFuture>; + /// Reads asset metadata bytes at the given `path` into a [`Vec`]. This is a convenience + /// function that wraps [`ErasedAssetReader::read_meta`] by default. + fn read_meta_bytes<'a>( + &'a self, + path: &'a Path, + ) -> BoxedFuture, AssetReaderError>>; +} + +impl ErasedAssetReader for T { + fn read<'a>( + &'a self, + path: &'a Path, + ) -> BoxedFuture>, AssetReaderError>> { + Box::pin(Self::read(self, path)) + } + fn read_meta<'a>( + &'a self, + path: &'a Path, + ) -> BoxedFuture>, AssetReaderError>> { + Box::pin(Self::read_meta(self, path)) + } + fn read_directory<'a>( + &'a self, + path: &'a Path, + ) -> BoxedFuture, AssetReaderError>> { + Box::pin(Self::read_directory(self, path)) + } + fn is_directory<'a>(&'a self, path: &'a Path) -> BoxedFuture> { + Box::pin(Self::is_directory(self, path)) + } + fn read_meta_bytes<'a>( + &'a self, + path: &'a Path, + ) -> BoxedFuture, AssetReaderError>> { + Box::pin(Self::read_meta_bytes(self, path)) } } @@ -113,7 +168,7 @@ pub enum AssetWriterError { /// Preforms write operations on an asset storage. [`AssetWriter`] exposes a "virtual filesystem" /// API, where asset bytes and asset metadata bytes are both stored and accessible for a given -/// `path`. +/// `path`. This trait is not object safe, if needed use a dyn [`ErasedAssetWriter`] instead. /// /// Also see [`AssetReader`]. pub trait AssetWriter: Send + Sync + 'static { @@ -121,72 +176,195 @@ pub trait AssetWriter: Send + Sync + 'static { fn write<'a>( &'a self, path: &'a Path, - ) -> BoxedFuture<'a, Result, AssetWriterError>>; + ) -> impl ConditionalSendFuture, AssetWriterError>>; /// Writes the full asset meta bytes at the provided path. /// This _should not_ include storage specific extensions like `.meta`. fn write_meta<'a>( &'a self, path: &'a Path, - ) -> BoxedFuture<'a, Result, AssetWriterError>>; + ) -> impl ConditionalSendFuture, AssetWriterError>>; /// Removes the asset stored at the given path. - fn remove<'a>(&'a self, path: &'a Path) -> BoxedFuture<'a, Result<(), AssetWriterError>>; + fn remove<'a>( + &'a self, + path: &'a Path, + ) -> impl ConditionalSendFuture>; /// Removes the asset meta stored at the given path. /// This _should not_ include storage specific extensions like `.meta`. - fn remove_meta<'a>(&'a self, path: &'a Path) -> BoxedFuture<'a, Result<(), AssetWriterError>>; + fn remove_meta<'a>( + &'a self, + path: &'a Path, + ) -> impl ConditionalSendFuture>; /// Renames the asset at `old_path` to `new_path` fn rename<'a>( &'a self, old_path: &'a Path, new_path: &'a Path, - ) -> BoxedFuture<'a, Result<(), AssetWriterError>>; + ) -> impl ConditionalSendFuture>; /// Renames the asset meta for the asset at `old_path` to `new_path`. /// This _should not_ include storage specific extensions like `.meta`. fn rename_meta<'a>( &'a self, old_path: &'a Path, new_path: &'a Path, - ) -> BoxedFuture<'a, Result<(), AssetWriterError>>; + ) -> impl ConditionalSendFuture>; /// Removes the directory at the given path, including all assets _and_ directories in that directory. fn remove_directory<'a>( &'a self, path: &'a Path, - ) -> BoxedFuture<'a, Result<(), AssetWriterError>>; + ) -> impl ConditionalSendFuture>; /// Removes the directory at the given path, but only if it is completely empty. This will return an error if the /// directory is not empty. fn remove_empty_directory<'a>( &'a self, path: &'a Path, - ) -> BoxedFuture<'a, Result<(), AssetWriterError>>; + ) -> impl ConditionalSendFuture>; /// Removes all assets (and directories) in this directory, resulting in an empty directory. fn remove_assets_in_directory<'a>( &'a self, path: &'a Path, - ) -> BoxedFuture<'a, Result<(), AssetWriterError>>; + ) -> impl ConditionalSendFuture>; /// Writes the asset `bytes` to the given `path`. fn write_bytes<'a>( &'a self, path: &'a Path, bytes: &'a [u8], - ) -> BoxedFuture<'a, Result<(), AssetWriterError>> { - Box::pin(async move { + ) -> impl ConditionalSendFuture> { + async { let mut writer = self.write(path).await?; writer.write_all(bytes).await?; writer.flush().await?; Ok(()) - }) + } } /// Writes the asset meta `bytes` to the given `path`. fn write_meta_bytes<'a>( &'a self, path: &'a Path, bytes: &'a [u8], - ) -> BoxedFuture<'a, Result<(), AssetWriterError>> { - Box::pin(async move { + ) -> impl ConditionalSendFuture> { + async { let mut meta_writer = self.write_meta(path).await?; meta_writer.write_all(bytes).await?; meta_writer.flush().await?; Ok(()) - }) + } + } +} + +/// Equivalent to an [`AssetWriter`] but using boxed futures, necessary eg. when using a `dyn AssetWriter`, +/// as [`AssetWriter`] isn't currently object safe. +pub trait ErasedAssetWriter: Send + Sync + 'static { + /// Writes the full asset bytes at the provided path. + fn write<'a>(&'a self, path: &'a Path) -> BoxedFuture, AssetWriterError>>; + /// Writes the full asset meta bytes at the provided path. + /// This _should not_ include storage specific extensions like `.meta`. + fn write_meta<'a>( + &'a self, + path: &'a Path, + ) -> BoxedFuture, AssetWriterError>>; + /// Removes the asset stored at the given path. + fn remove<'a>(&'a self, path: &'a Path) -> BoxedFuture>; + /// Removes the asset meta stored at the given path. + /// This _should not_ include storage specific extensions like `.meta`. + fn remove_meta<'a>(&'a self, path: &'a Path) -> BoxedFuture>; + /// Renames the asset at `old_path` to `new_path` + fn rename<'a>( + &'a self, + old_path: &'a Path, + new_path: &'a Path, + ) -> BoxedFuture>; + /// Renames the asset meta for the asset at `old_path` to `new_path`. + /// This _should not_ include storage specific extensions like `.meta`. + fn rename_meta<'a>( + &'a self, + old_path: &'a Path, + new_path: &'a Path, + ) -> BoxedFuture>; + /// Removes the directory at the given path, including all assets _and_ directories in that directory. + fn remove_directory<'a>(&'a self, path: &'a Path) -> BoxedFuture>; + /// Removes the directory at the given path, but only if it is completely empty. This will return an error if the + /// directory is not empty. + fn remove_empty_directory<'a>( + &'a self, + path: &'a Path, + ) -> BoxedFuture>; + /// Removes all assets (and directories) in this directory, resulting in an empty directory. + fn remove_assets_in_directory<'a>( + &'a self, + path: &'a Path, + ) -> BoxedFuture>; + /// Writes the asset `bytes` to the given `path`. + fn write_bytes<'a>( + &'a self, + path: &'a Path, + bytes: &'a [u8], + ) -> BoxedFuture>; + /// Writes the asset meta `bytes` to the given `path`. + fn write_meta_bytes<'a>( + &'a self, + path: &'a Path, + bytes: &'a [u8], + ) -> BoxedFuture>; +} + +impl ErasedAssetWriter for T { + fn write<'a>(&'a self, path: &'a Path) -> BoxedFuture, AssetWriterError>> { + Box::pin(Self::write(self, path)) + } + fn write_meta<'a>( + &'a self, + path: &'a Path, + ) -> BoxedFuture, AssetWriterError>> { + Box::pin(Self::write_meta(self, path)) + } + fn remove<'a>(&'a self, path: &'a Path) -> BoxedFuture> { + Box::pin(Self::remove(self, path)) + } + fn remove_meta<'a>(&'a self, path: &'a Path) -> BoxedFuture> { + Box::pin(Self::remove_meta(self, path)) + } + fn rename<'a>( + &'a self, + old_path: &'a Path, + new_path: &'a Path, + ) -> BoxedFuture> { + Box::pin(Self::rename(self, old_path, new_path)) + } + fn rename_meta<'a>( + &'a self, + old_path: &'a Path, + new_path: &'a Path, + ) -> BoxedFuture> { + Box::pin(Self::rename_meta(self, old_path, new_path)) + } + fn remove_directory<'a>(&'a self, path: &'a Path) -> BoxedFuture> { + Box::pin(Self::remove_directory(self, path)) + } + fn remove_empty_directory<'a>( + &'a self, + path: &'a Path, + ) -> BoxedFuture> { + Box::pin(Self::remove_empty_directory(self, path)) + } + fn remove_assets_in_directory<'a>( + &'a self, + path: &'a Path, + ) -> BoxedFuture> { + Box::pin(Self::remove_assets_in_directory(self, path)) + } + fn write_bytes<'a>( + &'a self, + path: &'a Path, + bytes: &'a [u8], + ) -> BoxedFuture> { + Box::pin(Self::write_bytes(self, path, bytes)) + } + fn write_meta_bytes<'a>( + &'a self, + path: &'a Path, + bytes: &'a [u8], + ) -> BoxedFuture> { + Box::pin(Self::write_meta_bytes(self, path, bytes)) } } diff --git a/crates/bevy_asset/src/io/processor_gated.rs b/crates/bevy_asset/src/io/processor_gated.rs index b86460f04b..5f3bfe1467 100644 --- a/crates/bevy_asset/src/io/processor_gated.rs +++ b/crates/bevy_asset/src/io/processor_gated.rs @@ -5,16 +5,17 @@ use crate::{ }; use async_lock::RwLockReadGuardArc; use bevy_utils::tracing::trace; -use bevy_utils::BoxedFuture; use futures_io::AsyncRead; use std::{path::Path, pin::Pin, sync::Arc}; +use super::ErasedAssetReader; + /// An [`AssetReader`] that will prevent asset (and asset metadata) read futures from returning for a /// given path until that path has been processed by [`AssetProcessor`]. /// /// [`AssetProcessor`]: crate::processor::AssetProcessor pub struct ProcessorGatedReader { - reader: Box, + reader: Box, source: AssetSourceId<'static>, processor_data: Arc, } @@ -23,7 +24,7 @@ impl ProcessorGatedReader { /// Creates a new [`ProcessorGatedReader`]. pub fn new( source: AssetSourceId<'static>, - reader: Box, + reader: Box, processor_data: Arc, ) -> Self { Self { @@ -48,87 +49,69 @@ impl ProcessorGatedReader { } impl AssetReader for ProcessorGatedReader { - fn read<'a>( - &'a self, - path: &'a Path, - ) -> BoxedFuture<'a, Result>, AssetReaderError>> { - Box::pin(async move { - 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 - .processor_data - .wait_until_processed(asset_path.clone()) - .await; - match process_result { - ProcessStatus::Processed => {} - ProcessStatus::Failed | ProcessStatus::NonExistent => { - return Err(AssetReaderError::NotFound(path.to_owned())); - } + async fn read<'a>(&'a self, path: &'a Path) -> Result>, 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 + .processor_data + .wait_until_processed(asset_path.clone()) + .await; + match process_result { + ProcessStatus::Processed => {} + ProcessStatus::Failed | ProcessStatus::NonExistent => { + return Err(AssetReaderError::NotFound(path.to_owned())); } - 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> = - Box::new(TransactionLockedReader::new(asset_reader, lock)); - Ok(reader) - }) + } + 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> = Box::new(TransactionLockedReader::new(asset_reader, lock)); + Ok(reader) } - fn read_meta<'a>( - &'a self, - path: &'a Path, - ) -> BoxedFuture<'a, Result>, AssetReaderError>> { - Box::pin(async move { - 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 - .processor_data - .wait_until_processed(asset_path.clone()) - .await; - match process_result { - ProcessStatus::Processed => {} - ProcessStatus::Failed | ProcessStatus::NonExistent => { - return Err(AssetReaderError::NotFound(path.to_owned())); - } + async fn read_meta<'a>(&'a self, path: &'a Path) -> Result>, 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 + .processor_data + .wait_until_processed(asset_path.clone()) + .await; + match process_result { + ProcessStatus::Processed => {} + ProcessStatus::Failed | ProcessStatus::NonExistent => { + return Err(AssetReaderError::NotFound(path.to_owned())); } - 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> = Box::new(TransactionLockedReader::new(meta_reader, lock)); - Ok(reader) - }) + } + 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> = Box::new(TransactionLockedReader::new(meta_reader, lock)); + Ok(reader) } - fn read_directory<'a>( + async fn read_directory<'a>( &'a self, path: &'a Path, - ) -> BoxedFuture<'a, Result, AssetReaderError>> { - Box::pin(async move { - trace!( - "Waiting for processing to finish before reading directory {:?}", - path - ); - self.processor_data.wait_until_finished().await; - trace!("Processing finished, reading directory {:?}", path); - let result = self.reader.read_directory(path).await?; - Ok(result) - }) + ) -> Result, AssetReaderError> { + trace!( + "Waiting for processing to finish before reading directory {:?}", + path + ); + self.processor_data.wait_until_finished().await; + trace!("Processing finished, reading directory {:?}", path); + let result = self.reader.read_directory(path).await?; + Ok(result) } - fn is_directory<'a>( - &'a self, - path: &'a Path, - ) -> BoxedFuture<'a, Result> { - Box::pin(async move { - trace!( - "Waiting for processing to finish before reading directory {:?}", - path - ); - self.processor_data.wait_until_finished().await; - trace!("Processing finished, getting directory status {:?}", path); - let result = self.reader.is_directory(path).await?; - Ok(result) - }) + async fn is_directory<'a>(&'a self, path: &'a Path) -> Result { + trace!( + "Waiting for processing to finish before reading directory {:?}", + path + ); + self.processor_data.wait_until_finished().await; + trace!("Processing finished, getting directory status {:?}", path); + let result = self.reader.is_directory(path).await?; + Ok(result) } } diff --git a/crates/bevy_asset/src/io/source.rs b/crates/bevy_asset/src/io/source.rs index 7293a73bea..21bd29294e 100644 --- a/crates/bevy_asset/src/io/source.rs +++ b/crates/bevy_asset/src/io/source.rs @@ -1,8 +1,5 @@ use crate::{ - io::{ - processor_gated::ProcessorGatedReader, AssetReader, AssetSourceEvent, AssetWatcher, - AssetWriter, - }, + io::{processor_gated::ProcessorGatedReader, AssetSourceEvent, AssetWatcher}, processor::AssetProcessorData, }; use bevy_ecs::system::Resource; @@ -11,6 +8,12 @@ use bevy_utils::{CowArc, Duration, HashMap}; use std::{fmt::Display, hash::Hash, sync::Arc}; use thiserror::Error; +use super::{ErasedAssetReader, ErasedAssetWriter}; + +// Needed for doc strings. +#[allow(unused_imports)] +use crate::io::{AssetReader, AssetWriter}; + /// A reference to an "asset source", which maps to an [`AssetReader`] and/or [`AssetWriter`]. /// /// * [`AssetSourceId::Default`] corresponds to "default asset paths" that don't specify a source: `/path/to/asset.png` @@ -110,8 +113,8 @@ impl<'a> PartialEq for AssetSourceId<'a> { /// and whether or not the source is processed. #[derive(Default)] pub struct AssetSourceBuilder { - pub reader: Option Box + Send + Sync>>, - pub writer: Option Option> + Send + Sync>>, + pub reader: Option Box + Send + Sync>>, + pub writer: Option Option> + Send + Sync>>, pub watcher: Option< Box< dyn FnMut(crossbeam_channel::Sender) -> Option> @@ -119,9 +122,9 @@ pub struct AssetSourceBuilder { + Sync, >, >, - pub processed_reader: Option Box + Send + Sync>>, + pub processed_reader: Option Box + Send + Sync>>, pub processed_writer: - Option Option> + Send + Sync>>, + Option Option> + Send + Sync>>, pub processed_watcher: Option< Box< dyn FnMut(crossbeam_channel::Sender) -> Option> @@ -192,7 +195,7 @@ impl AssetSourceBuilder { /// Will use the given `reader` function to construct unprocessed [`AssetReader`] instances. pub fn with_reader( mut self, - reader: impl FnMut() -> Box + Send + Sync + 'static, + reader: impl FnMut() -> Box + Send + Sync + 'static, ) -> Self { self.reader = Some(Box::new(reader)); self @@ -201,7 +204,7 @@ impl AssetSourceBuilder { /// Will use the given `writer` function to construct unprocessed [`AssetWriter`] instances. pub fn with_writer( mut self, - writer: impl FnMut(bool) -> Option> + Send + Sync + 'static, + writer: impl FnMut(bool) -> Option> + Send + Sync + 'static, ) -> Self { self.writer = Some(Box::new(writer)); self @@ -222,7 +225,7 @@ impl AssetSourceBuilder { /// Will use the given `reader` function to construct processed [`AssetReader`] instances. pub fn with_processed_reader( mut self, - reader: impl FnMut() -> Box + Send + Sync + 'static, + reader: impl FnMut() -> Box + Send + Sync + 'static, ) -> Self { self.processed_reader = Some(Box::new(reader)); self @@ -231,7 +234,7 @@ impl AssetSourceBuilder { /// Will use the given `writer` function to construct processed [`AssetWriter`] instances. pub fn with_processed_writer( mut self, - writer: impl FnMut(bool) -> Option> + Send + Sync + 'static, + writer: impl FnMut(bool) -> Option> + Send + Sync + 'static, ) -> Self { self.processed_writer = Some(Box::new(writer)); self @@ -355,10 +358,10 @@ impl AssetSourceBuilders { /// for a specific asset source, identified by an [`AssetSourceId`]. pub struct AssetSource { id: AssetSourceId<'static>, - reader: Box, - writer: Option>, - processed_reader: Option>, - processed_writer: Option>, + reader: Box, + writer: Option>, + processed_reader: Option>, + processed_writer: Option>, watcher: Option>, processed_watcher: Option>, event_receiver: Option>, @@ -379,13 +382,13 @@ impl AssetSource { /// Return's this source's unprocessed [`AssetReader`]. #[inline] - pub fn reader(&self) -> &dyn AssetReader { + pub fn reader(&self) -> &dyn ErasedAssetReader { &*self.reader } /// Return's this source's unprocessed [`AssetWriter`], if it exists. #[inline] - pub fn writer(&self) -> Result<&dyn AssetWriter, MissingAssetWriterError> { + pub fn writer(&self) -> Result<&dyn ErasedAssetWriter, MissingAssetWriterError> { self.writer .as_deref() .ok_or_else(|| MissingAssetWriterError(self.id.clone_owned())) @@ -393,7 +396,9 @@ impl AssetSource { /// Return's this source's processed [`AssetReader`], if it exists. #[inline] - pub fn processed_reader(&self) -> Result<&dyn AssetReader, MissingProcessedAssetReaderError> { + pub fn processed_reader( + &self, + ) -> Result<&dyn ErasedAssetReader, MissingProcessedAssetReaderError> { self.processed_reader .as_deref() .ok_or_else(|| MissingProcessedAssetReaderError(self.id.clone_owned())) @@ -401,7 +406,9 @@ impl AssetSource { /// Return's this source's processed [`AssetWriter`], if it exists. #[inline] - pub fn processed_writer(&self) -> Result<&dyn AssetWriter, MissingProcessedAssetWriterError> { + pub fn processed_writer( + &self, + ) -> Result<&dyn ErasedAssetWriter, MissingProcessedAssetWriterError> { self.processed_writer .as_deref() .ok_or_else(|| MissingProcessedAssetWriterError(self.id.clone_owned())) @@ -429,7 +436,9 @@ impl AssetSource { /// Returns a builder function for this platform's default [`AssetReader`]. `path` is the relative path to /// the asset root. - pub fn get_default_reader(_path: String) -> impl FnMut() -> Box + Send + Sync { + pub fn get_default_reader( + _path: String, + ) -> impl FnMut() -> Box + Send + Sync { move || { #[cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))] return Box::new(super::file::FileAssetReader::new(&_path)); @@ -444,7 +453,7 @@ impl AssetSource { /// the asset root. This will return [`None`] if this platform does not support writing assets by default. pub fn get_default_writer( _path: String, - ) -> impl FnMut(bool) -> Option> + Send + Sync { + ) -> impl FnMut(bool) -> Option> + Send + Sync { move |_create_root: bool| { #[cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))] return Some(Box::new(super::file::FileAssetWriter::new( diff --git a/crates/bevy_asset/src/io/wasm.rs b/crates/bevy_asset/src/io/wasm.rs index aab497ddfa..95f4905508 100644 --- a/crates/bevy_asset/src/io/wasm.rs +++ b/crates/bevy_asset/src/io/wasm.rs @@ -2,7 +2,6 @@ use crate::io::{ get_meta_path, AssetReader, AssetReaderError, EmptyPathStream, PathStream, Reader, VecReader, }; use bevy_utils::tracing::error; -use bevy_utils::BoxedFuture; use js_sys::{Uint8Array, JSON}; use std::path::{Path, PathBuf}; use wasm_bindgen::{JsCast, JsValue}; @@ -59,40 +58,30 @@ impl HttpWasmAssetReader { } impl AssetReader for HttpWasmAssetReader { - fn read<'a>( - &'a self, - path: &'a Path, - ) -> BoxedFuture<'a, Result>, AssetReaderError>> { - Box::pin(async move { - let path = self.root_path.join(path); - self.fetch_bytes(path).await - }) + async fn read<'a>(&'a self, path: &'a Path) -> Result>, AssetReaderError> { + let path = self.root_path.join(path); + self.fetch_bytes(path).await } - fn read_meta<'a>( - &'a self, - path: &'a Path, - ) -> BoxedFuture<'a, Result>, AssetReaderError>> { - Box::pin(async move { - let meta_path = get_meta_path(&self.root_path.join(path)); - Ok(self.fetch_bytes(meta_path).await?) - }) + async fn read_meta<'a>(&'a self, path: &'a Path) -> Result>, AssetReaderError> { + let meta_path = get_meta_path(&self.root_path.join(path)); + Ok(self.fetch_bytes(meta_path).await?) } - fn read_directory<'a>( + async fn read_directory<'a>( &'a self, _path: &'a Path, - ) -> BoxedFuture<'a, Result, AssetReaderError>> { + ) -> Result, AssetReaderError> { let stream: Box = Box::new(EmptyPathStream); error!("Reading directories is not supported with the HttpWasmAssetReader"); - Box::pin(async move { Ok(stream) }) + Ok(stream) } - fn is_directory<'a>( + async fn is_directory<'a>( &'a self, _path: &'a Path, - ) -> BoxedFuture<'a, std::result::Result> { + ) -> std::result::Result { error!("Reading directories is not supported with the HttpWasmAssetReader"); - Box::pin(async move { Ok(false) }) + Ok(false) } } diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index 4e579a04f2..f15797f619 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -40,8 +40,6 @@ pub use path::*; pub use reflect::*; pub use server::*; -pub use bevy_utils::BoxedFuture; - /// Rusty Object Notation, a crate used to serialize and deserialize bevy assets. pub use ron; @@ -448,7 +446,7 @@ mod tests { }; use bevy_log::LogPlugin; use bevy_reflect::TypePath; - use bevy_utils::{BoxedFuture, Duration, HashMap}; + use bevy_utils::{Duration, HashMap}; use futures_lite::AsyncReadExt; use serde::{Deserialize, Serialize}; use std::{path::Path, sync::Arc}; @@ -497,40 +495,38 @@ mod tests { type Error = CoolTextLoaderError; - fn load<'a>( + async fn load<'a>( &'a self, - reader: &'a mut Reader, + reader: &'a mut Reader<'_>, _settings: &'a Self::Settings, - load_context: &'a mut LoadContext, - ) -> BoxedFuture<'a, Result> { - Box::pin(async move { - let mut bytes = Vec::new(); - reader.read_to_end(&mut bytes).await?; - let mut ron: CoolTextRon = ron::de::from_bytes(&bytes)?; - let mut embedded = String::new(); - for dep in ron.embedded_dependencies { - let loaded = load_context.load_direct(&dep).await.map_err(|_| { - Self::Error::CannotLoadDependency { - dependency: dep.into(), - } - })?; - let cool = loaded.get::().unwrap(); - embedded.push_str(&cool.text); - } - Ok(CoolText { - text: ron.text, - embedded, - dependencies: ron - .dependencies - .iter() - .map(|p| load_context.load(p)) - .collect(), - sub_texts: ron - .sub_texts - .drain(..) - .map(|text| load_context.add_labeled_asset(text.clone(), SubText { text })) - .collect(), - }) + load_context: &'a mut LoadContext<'_>, + ) -> Result { + let mut bytes = Vec::new(); + reader.read_to_end(&mut bytes).await?; + let mut ron: CoolTextRon = ron::de::from_bytes(&bytes)?; + let mut embedded = String::new(); + for dep in ron.embedded_dependencies { + let loaded = load_context.load_direct(&dep).await.map_err(|_| { + Self::Error::CannotLoadDependency { + dependency: dep.into(), + } + })?; + let cool = loaded.get::().unwrap(); + embedded.push_str(&cool.text); + } + Ok(CoolText { + text: ron.text, + embedded, + dependencies: ron + .dependencies + .iter() + .map(|p| load_context.load(p)) + .collect(), + sub_texts: ron + .sub_texts + .drain(..) + .map(|text| load_context.add_labeled_asset(text.clone(), SubText { text })) + .collect(), }) } @@ -560,31 +556,25 @@ mod tests { } impl AssetReader for UnstableMemoryAssetReader { - fn is_directory<'a>( - &'a self, - path: &'a Path, - ) -> BoxedFuture<'a, Result> { - self.memory_reader.is_directory(path) + async fn is_directory<'a>(&'a self, path: &'a Path) -> Result { + self.memory_reader.is_directory(path).await } - fn read_directory<'a>( + async fn read_directory<'a>( &'a self, path: &'a Path, - ) -> BoxedFuture<'a, Result, AssetReaderError>> { - self.memory_reader.read_directory(path) + ) -> Result, AssetReaderError> { + self.memory_reader.read_directory(path).await } - fn read_meta<'a>( + async fn read_meta<'a>( &'a self, path: &'a Path, - ) -> BoxedFuture<'a, Result>, AssetReaderError>> { - self.memory_reader.read_meta(path) + ) -> Result>, AssetReaderError> { + self.memory_reader.read_meta(path).await } - fn read<'a>( + async fn read<'a>( &'a self, path: &'a Path, - ) -> BoxedFuture< - 'a, - Result>, bevy_asset::io::AssetReaderError>, - > { + ) -> Result>, 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) { @@ -605,13 +595,14 @@ mod tests { ), ); let wait = self.load_delay; - return Box::pin(async move { + return async move { std::thread::sleep(wait); Err(AssetReaderError::Io(io_error.into())) - }); + } + .await; } - self.memory_reader.read(path) + self.memory_reader.read(path).await } } diff --git a/crates/bevy_asset/src/loader.rs b/crates/bevy_asset/src/loader.rs index 627b286482..ae3f08511a 100644 --- a/crates/bevy_asset/src/loader.rs +++ b/crates/bevy_asset/src/loader.rs @@ -9,7 +9,7 @@ use crate::{ UntypedAssetId, UntypedHandle, }; use bevy_ecs::world::World; -use bevy_utils::{BoxedFuture, CowArc, HashMap, HashSet}; +use bevy_utils::{BoxedFuture, ConditionalSendFuture, CowArc, HashMap, HashSet}; use downcast_rs::{impl_downcast, Downcast}; use futures_lite::AsyncReadExt; use ron::error::SpannedError; @@ -35,7 +35,7 @@ pub trait AssetLoader: Send + Sync + 'static { reader: &'a mut Reader, settings: &'a Self::Settings, load_context: &'a mut LoadContext, - ) -> BoxedFuture<'a, Result>; + ) -> impl ConditionalSendFuture>; /// Returns a list of extensions supported by this [`AssetLoader`], without the preceding dot. /// Note that users of this [`AssetLoader`] may choose to load files with a non-matching extension. diff --git a/crates/bevy_asset/src/meta.rs b/crates/bevy_asset/src/meta.rs index 2b082e9550..ccc1df9b72 100644 --- a/crates/bevy_asset/src/meta.rs +++ b/crates/bevy_asset/src/meta.rs @@ -171,12 +171,12 @@ impl Process for () { type Settings = (); type OutputLoader = (); - fn process<'a>( + async fn process<'a>( &'a self, - _context: &'a mut bevy_asset::processor::ProcessContext, + _context: &'a mut bevy_asset::processor::ProcessContext<'_>, _meta: AssetMeta<(), Self>, _writer: &'a mut bevy_asset::io::Writer, - ) -> bevy_utils::BoxedFuture<'a, Result<(), bevy_asset::processor::ProcessError>> { + ) -> Result<(), bevy_asset::processor::ProcessError> { unreachable!() } } @@ -194,12 +194,12 @@ impl AssetLoader for () { type Asset = (); type Settings = (); type Error = std::io::Error; - fn load<'a>( + async fn load<'a>( &'a self, - _reader: &'a mut crate::io::Reader, + _reader: &'a mut crate::io::Reader<'_>, _settings: &'a Self::Settings, - _load_context: &'a mut crate::LoadContext, - ) -> bevy_utils::BoxedFuture<'a, Result> { + _load_context: &'a mut crate::LoadContext<'_>, + ) -> Result { unreachable!(); } diff --git a/crates/bevy_asset/src/processor/mod.rs b/crates/bevy_asset/src/processor/mod.rs index ace12c8f73..a507ea6547 100644 --- a/crates/bevy_asset/src/processor/mod.rs +++ b/crates/bevy_asset/src/processor/mod.rs @@ -6,8 +6,9 @@ pub use process::*; use crate::{ io::{ - AssetReader, AssetReaderError, AssetSource, AssetSourceBuilders, AssetSourceEvent, - AssetSourceId, AssetSources, AssetWriter, AssetWriterError, MissingAssetSourceError, + AssetReaderError, AssetSource, AssetSourceBuilders, AssetSourceEvent, AssetSourceId, + AssetSources, AssetWriterError, ErasedAssetReader, ErasedAssetWriter, + MissingAssetSourceError, }, meta::{ get_asset_hash, get_full_asset_hash, AssetAction, AssetActionMinimal, AssetHash, AssetMeta, @@ -30,6 +31,10 @@ use std::{ }; use thiserror::Error; +// Needed for doc strings +#[allow(unused_imports)] +use crate::io::{AssetReader, AssetWriter}; + /// A "background" asset processor that reads asset values from a source [`AssetSource`] (which corresponds to an [`AssetReader`] / [`AssetWriter`] pair), /// processes them in some way, and writes them to a destination [`AssetSource`]. /// @@ -510,8 +515,8 @@ impl AssetProcessor { /// Retrieves asset paths recursively. If `clean_empty_folders_writer` is Some, it will be used to clean up empty /// folders when they are discovered. fn get_asset_paths<'a>( - reader: &'a dyn AssetReader, - clean_empty_folders_writer: Option<&'a dyn AssetWriter>, + reader: &'a dyn ErasedAssetReader, + clean_empty_folders_writer: Option<&'a dyn ErasedAssetWriter>, path: PathBuf, paths: &'a mut Vec, ) -> BoxedFuture<'a, Result> { diff --git a/crates/bevy_asset/src/processor/process.rs b/crates/bevy_asset/src/processor/process.rs index 75b10acfa2..b2fca01c12 100644 --- a/crates/bevy_asset/src/processor/process.rs +++ b/crates/bevy_asset/src/processor/process.rs @@ -10,7 +10,7 @@ use crate::{ AssetLoadError, AssetLoader, AssetPath, DeserializeMetaError, ErasedLoadedAsset, MissingAssetLoaderForExtensionError, MissingAssetLoaderForTypeNameError, }; -use bevy_utils::BoxedFuture; +use bevy_utils::{BoxedFuture, ConditionalSendFuture}; use serde::{Deserialize, Serialize}; use std::marker::PhantomData; use thiserror::Error; @@ -32,7 +32,9 @@ pub trait Process: Send + Sync + Sized + 'static { context: &'a mut ProcessContext, meta: AssetMeta<(), Self>, writer: &'a mut Writer, - ) -> BoxedFuture<'a, Result<::Settings, ProcessError>>; + ) -> impl ConditionalSendFuture< + Output = Result<::Settings, ProcessError>, + >; } /// A flexible [`Process`] implementation that loads the source [`Asset`] using the `L` [`AssetLoader`], then transforms @@ -173,41 +175,38 @@ impl< type Settings = LoadTransformAndSaveSettings; type OutputLoader = Saver::OutputLoader; - fn process<'a>( + async fn process<'a>( &'a self, - context: &'a mut ProcessContext, + context: &'a mut ProcessContext<'_>, meta: AssetMeta<(), Self>, writer: &'a mut Writer, - ) -> BoxedFuture<'a, Result<::Settings, ProcessError>> { - Box::pin(async move { - let AssetAction::Process { settings, .. } = meta.asset else { - return Err(ProcessError::WrongMetaType); - }; - let loader_meta = AssetMeta::::new(AssetAction::Load { - loader: std::any::type_name::().to_string(), - settings: settings.loader_settings, - }); - let pre_transformed_asset = TransformedAsset::::from_loaded( - context.load_source_asset(loader_meta).await?, - ) - .unwrap(); + ) -> Result<::Settings, ProcessError> { + let AssetAction::Process { settings, .. } = meta.asset else { + return Err(ProcessError::WrongMetaType); + }; + let loader_meta = AssetMeta::::new(AssetAction::Load { + loader: std::any::type_name::().to_string(), + settings: settings.loader_settings, + }); + let pre_transformed_asset = TransformedAsset::::from_loaded( + context.load_source_asset(loader_meta).await?, + ) + .unwrap(); - let post_transformed_asset = self - .transformer - .transform(pre_transformed_asset, &settings.transformer_settings) - .await - .map_err(|err| ProcessError::AssetTransformError(err.into()))?; + let post_transformed_asset = self + .transformer + .transform(pre_transformed_asset, &settings.transformer_settings) + .await + .map_err(|err| ProcessError::AssetTransformError(err.into()))?; - let saved_asset = - SavedAsset::::from_transformed(&post_transformed_asset); + let saved_asset = SavedAsset::::from_transformed(&post_transformed_asset); - let output_settings = self - .saver - .save(writer, saved_asset, &settings.saver_settings) - .await - .map_err(|error| ProcessError::AssetSaveError(error.into()))?; - Ok(output_settings) - }) + let output_settings = self + .saver + .save(writer, saved_asset, &settings.saver_settings) + .await + .map_err(|error| ProcessError::AssetSaveError(error.into()))?; + Ok(output_settings) } } @@ -217,29 +216,27 @@ impl> Process type Settings = LoadAndSaveSettings; type OutputLoader = Saver::OutputLoader; - fn process<'a>( + async fn process<'a>( &'a self, - context: &'a mut ProcessContext, + context: &'a mut ProcessContext<'_>, meta: AssetMeta<(), Self>, writer: &'a mut Writer, - ) -> BoxedFuture<'a, Result<::Settings, ProcessError>> { - Box::pin(async move { - let AssetAction::Process { settings, .. } = meta.asset else { - return Err(ProcessError::WrongMetaType); - }; - let loader_meta = AssetMeta::::new(AssetAction::Load { - loader: std::any::type_name::().to_string(), - settings: settings.loader_settings, - }); - let loaded_asset = context.load_source_asset(loader_meta).await?; - let saved_asset = SavedAsset::::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) - }) + ) -> Result<::Settings, ProcessError> { + let AssetAction::Process { settings, .. } = meta.asset else { + return Err(ProcessError::WrongMetaType); + }; + let loader_meta = AssetMeta::::new(AssetAction::Load { + loader: std::any::type_name::().to_string(), + settings: settings.loader_settings, + }); + let loaded_asset = context.load_source_asset(loader_meta).await?; + let saved_asset = SavedAsset::::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) } } diff --git a/crates/bevy_asset/src/saver.rs b/crates/bevy_asset/src/saver.rs index a366338f7c..36408dd125 100644 --- a/crates/bevy_asset/src/saver.rs +++ b/crates/bevy_asset/src/saver.rs @@ -1,7 +1,7 @@ use crate::transformer::TransformedAsset; use crate::{io::Writer, meta::Settings, Asset, ErasedLoadedAsset}; use crate::{AssetLoader, Handle, LabeledAsset, UntypedHandle}; -use bevy_utils::{BoxedFuture, CowArc, HashMap}; +use bevy_utils::{BoxedFuture, ConditionalSendFuture, CowArc, HashMap}; use serde::{Deserialize, Serialize}; use std::{borrow::Borrow, hash::Hash, ops::Deref}; @@ -24,7 +24,9 @@ pub trait AssetSaver: Send + Sync + 'static { writer: &'a mut Writer, asset: SavedAsset<'a, Self::Asset>, settings: &'a Self::Settings, - ) -> BoxedFuture<'a, Result<::Settings, Self::Error>>; + ) -> impl ConditionalSendFuture< + Output = Result<::Settings, Self::Error>, + >; } /// A type-erased dynamic variant of [`AssetSaver`] that allows callers to save assets without knowing the actual type of the [`AssetSaver`]. diff --git a/crates/bevy_asset/src/server/loaders.rs b/crates/bevy_asset/src/server/loaders.rs index 98cc3bce9d..05d2c4873e 100644 --- a/crates/bevy_asset/src/server/loaders.rs +++ b/crates/bevy_asset/src/server/loaders.rs @@ -341,21 +341,19 @@ mod tests { type Error = String; - fn load<'a>( + async fn load<'a>( &'a self, - _: &'a mut crate::io::Reader, + _: &'a mut crate::io::Reader<'_>, _: &'a Self::Settings, - _: &'a mut crate::LoadContext, - ) -> bevy_utils::BoxedFuture<'a, Result> { + _: &'a mut crate::LoadContext<'_>, + ) -> Result { self.sender.send(()).unwrap(); - Box::pin(async move { - Err(format!( - "Loaded {}:{}", - std::any::type_name::(), - N - )) - }) + Err(format!( + "Loaded {}:{}", + std::any::type_name::(), + N + )) } fn extensions(&self) -> &[&str] { diff --git a/crates/bevy_asset/src/server/mod.rs b/crates/bevy_asset/src/server/mod.rs index 2be2848aec..60536cc405 100644 --- a/crates/bevy_asset/src/server/mod.rs +++ b/crates/bevy_asset/src/server/mod.rs @@ -4,8 +4,8 @@ mod loaders; use crate::{ folder::LoadedFolder, io::{ - AssetReader, AssetReaderError, AssetSource, AssetSourceEvent, AssetSourceId, AssetSources, - MissingAssetSourceError, MissingProcessedAssetReaderError, Reader, + AssetReaderError, AssetSource, AssetSourceEvent, AssetSourceId, AssetSources, + ErasedAssetReader, MissingAssetSourceError, MissingProcessedAssetReaderError, Reader, }, loader::{AssetLoader, ErasedAssetLoader, LoadContext, LoadedAsset}, meta::{ @@ -30,6 +30,10 @@ use std::path::PathBuf; use std::{any::TypeId, path::Path, sync::Arc}; use thiserror::Error; +// Needed for doc string +#[allow(unused_imports)] +use crate::io::{AssetReader, AssetWriter}; + /// Loads and tracks the state of [`Asset`] values from a configured [`AssetReader`]. This can be used to kick off new asset loads and /// retrieve their current load states. /// @@ -657,7 +661,7 @@ impl AssetServer { fn load_folder<'a>( source: AssetSourceId<'static>, path: &'a Path, - reader: &'a dyn AssetReader, + reader: &'a dyn ErasedAssetReader, server: &'a AssetServer, handles: &'a mut Vec, ) -> bevy_utils::BoxedFuture<'a, Result<(), AssetLoadError>> { diff --git a/crates/bevy_asset/src/transformer.rs b/crates/bevy_asset/src/transformer.rs index 3b8ae58bc3..0ffddc4658 100644 --- a/crates/bevy_asset/src/transformer.rs +++ b/crates/bevy_asset/src/transformer.rs @@ -1,5 +1,5 @@ use crate::{meta::Settings, Asset, ErasedLoadedAsset, Handle, LabeledAsset, UntypedHandle}; -use bevy_utils::{BoxedFuture, CowArc, HashMap}; +use bevy_utils::{ConditionalSendFuture, CowArc, HashMap}; use serde::{Deserialize, Serialize}; use std::{ borrow::Borrow, @@ -25,7 +25,7 @@ pub trait AssetTransformer: Send + Sync + 'static { &'a self, asset: TransformedAsset, settings: &'a Self::Settings, - ) -> BoxedFuture<'a, Result, Self::Error>>; + ) -> impl ConditionalSendFuture, Self::Error>>; } /// An [`Asset`] (and any "sub assets") intended to be transformed diff --git a/crates/bevy_audio/src/audio_source.rs b/crates/bevy_audio/src/audio_source.rs index 8b0c7090ea..242c6a6a7c 100644 --- a/crates/bevy_audio/src/audio_source.rs +++ b/crates/bevy_audio/src/audio_source.rs @@ -3,7 +3,6 @@ use bevy_asset::{ Asset, AssetLoader, LoadContext, }; use bevy_reflect::TypePath; -use bevy_utils::BoxedFuture; use std::{io::Cursor, sync::Arc}; /// A source of audio data @@ -43,18 +42,16 @@ impl AssetLoader for AudioLoader { type Settings = (); type Error = std::io::Error; - fn load<'a>( + async fn load<'a>( &'a self, - reader: &'a mut Reader, + reader: &'a mut Reader<'_>, _settings: &'a Self::Settings, - _load_context: &'a mut LoadContext, - ) -> BoxedFuture<'a, Result> { - Box::pin(async move { - let mut bytes = Vec::new(); - reader.read_to_end(&mut bytes).await?; - Ok(AudioSource { - bytes: bytes.into(), - }) + _load_context: &'a mut LoadContext<'_>, + ) -> Result { + let mut bytes = Vec::new(); + reader.read_to_end(&mut bytes).await?; + Ok(AudioSource { + bytes: bytes.into(), }) } diff --git a/crates/bevy_gltf/src/loader.rs b/crates/bevy_gltf/src/loader.rs index 6f2f279698..1a8caa4531 100644 --- a/crates/bevy_gltf/src/loader.rs +++ b/crates/bevy_gltf/src/loader.rs @@ -162,17 +162,15 @@ impl AssetLoader for GltfLoader { type Asset = Gltf; type Settings = GltfLoaderSettings; type Error = GltfError; - fn load<'a>( + async fn load<'a>( &'a self, - reader: &'a mut Reader, + reader: &'a mut Reader<'_>, settings: &'a GltfLoaderSettings, - load_context: &'a mut LoadContext, - ) -> bevy_utils::BoxedFuture<'a, Result> { - Box::pin(async move { - let mut bytes = Vec::new(); - reader.read_to_end(&mut bytes).await?; - load_gltf(self, &bytes, load_context, settings).await - }) + load_context: &'a mut LoadContext<'_>, + ) -> Result { + let mut bytes = Vec::new(); + reader.read_to_end(&mut bytes).await?; + load_gltf(self, &bytes, load_context, settings).await } fn extensions(&self) -> &[&str] { diff --git a/crates/bevy_render/src/render_resource/shader.rs b/crates/bevy_render/src/render_resource/shader.rs index 677378cc90..49d61533ea 100644 --- a/crates/bevy_render/src/render_resource/shader.rs +++ b/crates/bevy_render/src/render_resource/shader.rs @@ -2,7 +2,7 @@ use super::ShaderDefVal; use crate::define_atomic_id; use bevy_asset::{io::Reader, Asset, AssetLoader, AssetPath, Handle, LoadContext}; use bevy_reflect::TypePath; -use bevy_utils::{tracing::error, BoxedFuture}; +use bevy_utils::tracing::error; use futures_lite::AsyncReadExt; use std::{borrow::Cow, marker::Copy}; use thiserror::Error; @@ -259,43 +259,39 @@ impl AssetLoader for ShaderLoader { type Asset = Shader; type Settings = (); type Error = ShaderLoaderError; - fn load<'a>( + async fn load<'a>( &'a self, - reader: &'a mut Reader, + reader: &'a mut Reader<'_>, _settings: &'a Self::Settings, - load_context: &'a mut LoadContext, - ) -> BoxedFuture<'a, Result> { - Box::pin(async move { - let ext = load_context.path().extension().unwrap().to_str().unwrap(); - let path = load_context.asset_path().to_string(); - // On windows, the path will inconsistently use \ or /. - // TODO: remove this once AssetPath forces cross-platform "slash" consistency. See #10511 - let path = path.replace(std::path::MAIN_SEPARATOR, "/"); - let mut bytes = Vec::new(); - reader.read_to_end(&mut bytes).await?; - let mut shader = match ext { - "spv" => Shader::from_spirv(bytes, load_context.path().to_string_lossy()), - "wgsl" => Shader::from_wgsl(String::from_utf8(bytes)?, path), - "vert" => { - Shader::from_glsl(String::from_utf8(bytes)?, naga::ShaderStage::Vertex, path) - } - "frag" => { - Shader::from_glsl(String::from_utf8(bytes)?, naga::ShaderStage::Fragment, path) - } - "comp" => { - Shader::from_glsl(String::from_utf8(bytes)?, naga::ShaderStage::Compute, path) - } - _ => panic!("unhandled extension: {ext}"), - }; - - // collect and store file dependencies - for import in &shader.imports { - if let ShaderImport::AssetPath(asset_path) = import { - shader.file_dependencies.push(load_context.load(asset_path)); - } + load_context: &'a mut LoadContext<'_>, + ) -> Result { + let ext = load_context.path().extension().unwrap().to_str().unwrap(); + let path = load_context.asset_path().to_string(); + // On windows, the path will inconsistently use \ or /. + // TODO: remove this once AssetPath forces cross-platform "slash" consistency. See #10511 + let path = path.replace(std::path::MAIN_SEPARATOR, "/"); + let mut bytes = Vec::new(); + reader.read_to_end(&mut bytes).await?; + let mut shader = match ext { + "spv" => Shader::from_spirv(bytes, load_context.path().to_string_lossy()), + "wgsl" => Shader::from_wgsl(String::from_utf8(bytes)?, path), + "vert" => Shader::from_glsl(String::from_utf8(bytes)?, naga::ShaderStage::Vertex, path), + "frag" => { + Shader::from_glsl(String::from_utf8(bytes)?, naga::ShaderStage::Fragment, path) } - Ok(shader) - }) + "comp" => { + Shader::from_glsl(String::from_utf8(bytes)?, naga::ShaderStage::Compute, path) + } + _ => panic!("unhandled extension: {ext}"), + }; + + // collect and store file dependencies + for import in &shader.imports { + if let ShaderImport::AssetPath(asset_path) = import { + shader.file_dependencies.push(load_context.load(asset_path)); + } + } + Ok(shader) } fn extensions(&self) -> &[&str] { diff --git a/crates/bevy_render/src/texture/compressed_image_saver.rs b/crates/bevy_render/src/texture/compressed_image_saver.rs index dde2a900b4..0ab053df33 100644 --- a/crates/bevy_render/src/texture/compressed_image_saver.rs +++ b/crates/bevy_render/src/texture/compressed_image_saver.rs @@ -1,6 +1,6 @@ use crate::texture::{Image, ImageFormat, ImageFormatSetting, ImageLoader, ImageLoaderSettings}; use bevy_asset::saver::{AssetSaver, SavedAsset}; -use futures_lite::{AsyncWriteExt, FutureExt}; +use futures_lite::AsyncWriteExt; use thiserror::Error; pub struct CompressedImageSaver; @@ -19,46 +19,46 @@ impl AssetSaver for CompressedImageSaver { type OutputLoader = ImageLoader; type Error = CompressedImageSaverError; - fn save<'a>( + async fn save<'a>( &'a self, writer: &'a mut bevy_asset::io::Writer, image: SavedAsset<'a, Self::Asset>, _settings: &'a Self::Settings, - ) -> bevy_utils::BoxedFuture<'a, Result> { - // PERF: this should live inside the future, but CompressorParams and Compressor are not Send / can't be owned by the BoxedFuture (which _is_ Send) - let mut compressor_params = basis_universal::CompressorParams::new(); - compressor_params.set_basis_format(basis_universal::BasisTextureFormat::UASTC4x4); - compressor_params.set_generate_mipmaps(true); + ) -> Result { let is_srgb = image.texture_descriptor.format.is_srgb(); - let color_space = if is_srgb { - basis_universal::ColorSpace::Srgb - } else { - basis_universal::ColorSpace::Linear + + let compressed_basis_data = { + let mut compressor_params = basis_universal::CompressorParams::new(); + compressor_params.set_basis_format(basis_universal::BasisTextureFormat::UASTC4x4); + compressor_params.set_generate_mipmaps(true); + let color_space = if is_srgb { + basis_universal::ColorSpace::Srgb + } else { + basis_universal::ColorSpace::Linear + }; + compressor_params.set_color_space(color_space); + compressor_params.set_uastc_quality_level(basis_universal::UASTC_QUALITY_DEFAULT); + + let mut source_image = compressor_params.source_image_mut(0); + let size = image.size(); + source_image.init(&image.data, size.x, size.y, 4); + + let mut compressor = basis_universal::Compressor::new(4); + // SAFETY: the CompressorParams are "valid" to the best of our knowledge. The basis-universal + // library bindings note that invalid params might produce undefined behavior. + unsafe { + compressor.init(&compressor_params); + compressor.process().unwrap(); + } + compressor.basis_file().to_vec() }; - compressor_params.set_color_space(color_space); - compressor_params.set_uastc_quality_level(basis_universal::UASTC_QUALITY_DEFAULT); - let mut source_image = compressor_params.source_image_mut(0); - let size = image.size(); - source_image.init(&image.data, size.x, size.y, 4); - - let mut compressor = basis_universal::Compressor::new(4); - // SAFETY: the CompressorParams are "valid" to the best of our knowledge. The basis-universal - // library bindings note that invalid params might produce undefined behavior. - unsafe { - compressor.init(&compressor_params); - compressor.process().unwrap(); - } - let compressed_basis_data = compressor.basis_file().to_vec(); - async move { - writer.write_all(&compressed_basis_data).await?; - Ok(ImageLoaderSettings { - format: ImageFormatSetting::Format(ImageFormat::Basis), - is_srgb, - sampler: image.sampler.clone(), - asset_usage: image.asset_usage, - }) - } - .boxed() + writer.write_all(&compressed_basis_data).await?; + Ok(ImageLoaderSettings { + format: ImageFormatSetting::Format(ImageFormat::Basis), + is_srgb, + sampler: image.sampler.clone(), + asset_usage: image.asset_usage, + }) } } diff --git a/crates/bevy_render/src/texture/exr_texture_loader.rs b/crates/bevy_render/src/texture/exr_texture_loader.rs index 9f0d670658..d5f39aa3c0 100644 --- a/crates/bevy_render/src/texture/exr_texture_loader.rs +++ b/crates/bevy_render/src/texture/exr_texture_loader.rs @@ -6,7 +6,6 @@ use bevy_asset::{ io::{AsyncReadExt, Reader}, AssetLoader, LoadContext, }; -use bevy_utils::BoxedFuture; use image::ImageDecoder; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -36,45 +35,43 @@ impl AssetLoader for ExrTextureLoader { type Settings = ExrTextureLoaderSettings; type Error = ExrTextureLoaderError; - fn load<'a>( + async fn load<'a>( &'a self, - reader: &'a mut Reader, + reader: &'a mut Reader<'_>, settings: &'a Self::Settings, - _load_context: &'a mut LoadContext, - ) -> BoxedFuture<'a, Result> { - Box::pin(async move { - let format = TextureFormat::Rgba32Float; - debug_assert_eq!( - format.pixel_size(), - 4 * 4, - "Format should have 32bit x 4 size" - ); + _load_context: &'a mut LoadContext<'_>, + ) -> Result { + let format = TextureFormat::Rgba32Float; + debug_assert_eq!( + format.pixel_size(), + 4 * 4, + "Format should have 32bit x 4 size" + ); - let mut bytes = Vec::new(); - reader.read_to_end(&mut bytes).await?; - let decoder = image::codecs::openexr::OpenExrDecoder::with_alpha_preference( - std::io::Cursor::new(bytes), - Some(true), - )?; - let (width, height) = decoder.dimensions(); + let mut bytes = Vec::new(); + reader.read_to_end(&mut bytes).await?; + let decoder = image::codecs::openexr::OpenExrDecoder::with_alpha_preference( + std::io::Cursor::new(bytes), + Some(true), + )?; + let (width, height) = decoder.dimensions(); - let total_bytes = decoder.total_bytes() as usize; + let total_bytes = decoder.total_bytes() as usize; - let mut buf = vec![0u8; total_bytes]; - decoder.read_image(buf.as_mut_slice())?; + let mut buf = vec![0u8; total_bytes]; + decoder.read_image(buf.as_mut_slice())?; - Ok(Image::new( - Extent3d { - width, - height, - depth_or_array_layers: 1, - }, - TextureDimension::D2, - buf, - format, - settings.asset_usage, - )) - }) + Ok(Image::new( + Extent3d { + width, + height, + depth_or_array_layers: 1, + }, + TextureDimension::D2, + buf, + format, + settings.asset_usage, + )) } fn extensions(&self) -> &[&str] { diff --git a/crates/bevy_render/src/texture/hdr_texture_loader.rs b/crates/bevy_render/src/texture/hdr_texture_loader.rs index 1fd0218639..641055690f 100644 --- a/crates/bevy_render/src/texture/hdr_texture_loader.rs +++ b/crates/bevy_render/src/texture/hdr_texture_loader.rs @@ -29,48 +29,46 @@ impl AssetLoader for HdrTextureLoader { type Asset = Image; type Settings = HdrTextureLoaderSettings; type Error = HdrTextureLoaderError; - fn load<'a>( + async fn load<'a>( &'a self, - reader: &'a mut Reader, + reader: &'a mut Reader<'_>, settings: &'a Self::Settings, - _load_context: &'a mut LoadContext, - ) -> bevy_utils::BoxedFuture<'a, Result> { - Box::pin(async move { - let format = TextureFormat::Rgba32Float; - debug_assert_eq!( - format.pixel_size(), - 4 * 4, - "Format should have 32bit x 4 size" - ); + _load_context: &'a mut LoadContext<'_>, + ) -> Result { + let format = TextureFormat::Rgba32Float; + debug_assert_eq!( + format.pixel_size(), + 4 * 4, + "Format should have 32bit x 4 size" + ); - let mut bytes = Vec::new(); - reader.read_to_end(&mut bytes).await?; - let decoder = image::codecs::hdr::HdrDecoder::new(bytes.as_slice())?; - let info = decoder.metadata(); - let rgb_data = decoder.read_image_hdr()?; - let mut rgba_data = Vec::with_capacity(rgb_data.len() * format.pixel_size()); + let mut bytes = Vec::new(); + reader.read_to_end(&mut bytes).await?; + let decoder = image::codecs::hdr::HdrDecoder::new(bytes.as_slice())?; + let info = decoder.metadata(); + let rgb_data = decoder.read_image_hdr()?; + let mut rgba_data = Vec::with_capacity(rgb_data.len() * format.pixel_size()); - for rgb in rgb_data { - let alpha = 1.0f32; + for rgb in rgb_data { + let alpha = 1.0f32; - rgba_data.extend_from_slice(&rgb.0[0].to_ne_bytes()); - rgba_data.extend_from_slice(&rgb.0[1].to_ne_bytes()); - rgba_data.extend_from_slice(&rgb.0[2].to_ne_bytes()); - rgba_data.extend_from_slice(&alpha.to_ne_bytes()); - } + rgba_data.extend_from_slice(&rgb.0[0].to_ne_bytes()); + rgba_data.extend_from_slice(&rgb.0[1].to_ne_bytes()); + rgba_data.extend_from_slice(&rgb.0[2].to_ne_bytes()); + rgba_data.extend_from_slice(&alpha.to_ne_bytes()); + } - Ok(Image::new( - Extent3d { - width: info.width, - height: info.height, - depth_or_array_layers: 1, - }, - TextureDimension::D2, - rgba_data, - format, - settings.asset_usage, - )) - }) + Ok(Image::new( + Extent3d { + width: info.width, + height: info.height, + depth_or_array_layers: 1, + }, + TextureDimension::D2, + rgba_data, + format, + settings.asset_usage, + )) } fn extensions(&self) -> &[&str] { diff --git a/crates/bevy_render/src/texture/image_loader.rs b/crates/bevy_render/src/texture/image_loader.rs index 44a4fdb925..534d064409 100644 --- a/crates/bevy_render/src/texture/image_loader.rs +++ b/crates/bevy_render/src/texture/image_loader.rs @@ -85,37 +85,35 @@ impl AssetLoader for ImageLoader { type Asset = Image; type Settings = ImageLoaderSettings; type Error = ImageLoaderError; - fn load<'a>( + async fn load<'a>( &'a self, - reader: &'a mut Reader, + reader: &'a mut Reader<'_>, settings: &'a ImageLoaderSettings, - load_context: &'a mut LoadContext, - ) -> bevy_utils::BoxedFuture<'a, Result> { - Box::pin(async move { - // use the file extension for the image type - let ext = load_context.path().extension().unwrap().to_str().unwrap(); + load_context: &'a mut LoadContext<'_>, + ) -> Result { + // use the file extension for the image type + let ext = load_context.path().extension().unwrap().to_str().unwrap(); - let mut bytes = Vec::new(); - reader.read_to_end(&mut bytes).await?; - let image_type = match settings.format { - ImageFormatSetting::FromExtension => ImageType::Extension(ext), - ImageFormatSetting::Format(format) => ImageType::Format(format), - }; - Ok(Image::from_buffer( - #[cfg(all(debug_assertions, feature = "dds"))] - load_context.path().display().to_string(), - &bytes, - image_type, - self.supported_compressed_formats, - settings.is_srgb, - settings.sampler.clone(), - settings.asset_usage, - ) - .map_err(|err| FileTextureError { - error: err, - path: format!("{}", load_context.path().display()), - })?) - }) + let mut bytes = Vec::new(); + reader.read_to_end(&mut bytes).await?; + let image_type = match settings.format { + ImageFormatSetting::FromExtension => ImageType::Extension(ext), + ImageFormatSetting::Format(format) => ImageType::Format(format), + }; + Ok(Image::from_buffer( + #[cfg(all(debug_assertions, feature = "dds"))] + load_context.path().display().to_string(), + &bytes, + image_type, + self.supported_compressed_formats, + settings.is_srgb, + settings.sampler.clone(), + settings.asset_usage, + ) + .map_err(|err| FileTextureError { + error: err, + path: format!("{}", load_context.path().display()), + })?) } fn extensions(&self) -> &[&str] { diff --git a/crates/bevy_scene/src/scene_loader.rs b/crates/bevy_scene/src/scene_loader.rs index f4dce7c66a..107d014b2b 100644 --- a/crates/bevy_scene/src/scene_loader.rs +++ b/crates/bevy_scene/src/scene_loader.rs @@ -6,7 +6,6 @@ use bevy_asset::{io::Reader, AssetLoader, AsyncReadExt, LoadContext}; use bevy_ecs::reflect::AppTypeRegistry; use bevy_ecs::world::{FromWorld, World}; use bevy_reflect::TypeRegistryArc; -use bevy_utils::BoxedFuture; #[cfg(feature = "serialize")] use serde::de::DeserializeSeed; use thiserror::Error; @@ -44,23 +43,21 @@ impl AssetLoader for SceneLoader { type Settings = (); type Error = SceneLoaderError; - fn load<'a>( + async fn load<'a>( &'a self, - reader: &'a mut Reader, + reader: &'a mut Reader<'_>, _settings: &'a (), - _load_context: &'a mut LoadContext, - ) -> BoxedFuture<'a, Result> { - Box::pin(async move { - let mut bytes = Vec::new(); - reader.read_to_end(&mut bytes).await?; - let mut deserializer = ron::de::Deserializer::from_bytes(&bytes)?; - let scene_deserializer = SceneDeserializer { - type_registry: &self.type_registry.read(), - }; - Ok(scene_deserializer - .deserialize(&mut deserializer) - .map_err(|e| deserializer.span_error(e))?) - }) + _load_context: &'a mut LoadContext<'_>, + ) -> Result { + let mut bytes = Vec::new(); + reader.read_to_end(&mut bytes).await?; + let mut deserializer = ron::de::Deserializer::from_bytes(&bytes)?; + let scene_deserializer = SceneDeserializer { + type_registry: &self.type_registry.read(), + }; + Ok(scene_deserializer + .deserialize(&mut deserializer) + .map_err(|e| deserializer.span_error(e))?) } fn extensions(&self) -> &[&str] { diff --git a/crates/bevy_text/src/font_loader.rs b/crates/bevy_text/src/font_loader.rs index a47abbd961..45f3e9701e 100644 --- a/crates/bevy_text/src/font_loader.rs +++ b/crates/bevy_text/src/font_loader.rs @@ -21,17 +21,15 @@ impl AssetLoader for FontLoader { type Asset = Font; type Settings = (); type Error = FontLoaderError; - fn load<'a>( + async fn load<'a>( &'a self, - reader: &'a mut Reader, + reader: &'a mut Reader<'_>, _settings: &'a (), - _load_context: &'a mut LoadContext, - ) -> bevy_utils::BoxedFuture<'a, Result> { - Box::pin(async move { - let mut bytes = Vec::new(); - reader.read_to_end(&mut bytes).await?; - Ok(Font::try_from_bytes(bytes)?) - }) + _load_context: &'a mut LoadContext<'_>, + ) -> Result { + let mut bytes = Vec::new(); + reader.read_to_end(&mut bytes).await?; + Ok(Font::try_from_bytes(bytes)?) } fn extensions(&self) -> &[&str] { diff --git a/crates/bevy_utils/src/lib.rs b/crates/bevy_utils/src/lib.rs index 33f5312171..d142f43fbc 100644 --- a/crates/bevy_utils/src/lib.rs +++ b/crates/bevy_utils/src/lib.rs @@ -36,21 +36,36 @@ use hashbrown::hash_map::RawEntryMut; use std::{ any::TypeId, fmt::Debug, - future::Future, hash::{BuildHasher, BuildHasherDefault, Hash, Hasher}, marker::PhantomData, mem::ManuallyDrop, ops::Deref, - pin::Pin, }; -/// An owned and dynamically typed Future used when you can't statically type your result or need to add some indirection. #[cfg(not(target_arch = "wasm32"))] -pub type BoxedFuture<'a, T> = Pin + Send + 'a>>; +mod conditional_send { + /// Use [`ConditionalSend`] to mark an optional Send trait bound. Useful as on certain platforms (eg. WASM), + /// futures aren't Send. + pub trait ConditionalSend: Send {} + impl ConditionalSend for T {} +} -#[allow(missing_docs)] #[cfg(target_arch = "wasm32")] -pub type BoxedFuture<'a, T> = Pin + 'a>>; +#[allow(missing_docs)] +mod conditional_send { + pub trait ConditionalSend {} + impl ConditionalSend for T {} +} + +pub use conditional_send::*; + +/// Use [`ConditionalSendFuture`] for a future with an optional Send trait bound, as on certain platforms (eg. WASM), +/// futures aren't Send. +pub trait ConditionalSendFuture: std::future::Future + ConditionalSend {} +impl ConditionalSendFuture for T {} + +/// An owned and dynamically typed Future used when you can't statically type your result or need to add some indirection. +pub type BoxedFuture<'a, T> = std::pin::Pin + 'a>>; /// A shortcut alias for [`hashbrown::hash_map::Entry`]. pub type Entry<'a, K, V, S = BuildHasherDefault> = hashbrown::hash_map::Entry<'a, K, V, S>; diff --git a/examples/asset/asset_decompression.rs b/examples/asset/asset_decompression.rs index cd17a38a89..f003471a72 100644 --- a/examples/asset/asset_decompression.rs +++ b/examples/asset/asset_decompression.rs @@ -7,7 +7,6 @@ use bevy::{ }, prelude::*, reflect::TypePath, - utils::BoxedFuture, }; use flate2::read::GzDecoder; use std::io::prelude::*; @@ -41,44 +40,42 @@ impl AssetLoader for GzAssetLoader { type Asset = GzAsset; type Settings = (); type Error = GzAssetLoaderError; - fn load<'a>( + async fn load<'a>( &'a self, - reader: &'a mut Reader, + reader: &'a mut Reader<'_>, _settings: &'a (), - load_context: &'a mut LoadContext, - ) -> BoxedFuture<'a, Result> { - Box::pin(async move { - let compressed_path = load_context.path(); - let file_name = compressed_path - .file_name() - .ok_or(GzAssetLoaderError::IndeterminateFilePath)? - .to_string_lossy(); - let uncompressed_file_name = file_name - .strip_suffix(".gz") - .ok_or(GzAssetLoaderError::IndeterminateFilePath)?; - let contained_path = compressed_path.join(uncompressed_file_name); + load_context: &'a mut LoadContext<'_>, + ) -> Result { + let compressed_path = load_context.path(); + let file_name = compressed_path + .file_name() + .ok_or(GzAssetLoaderError::IndeterminateFilePath)? + .to_string_lossy(); + let uncompressed_file_name = file_name + .strip_suffix(".gz") + .ok_or(GzAssetLoaderError::IndeterminateFilePath)?; + let contained_path = compressed_path.join(uncompressed_file_name); - let mut bytes_compressed = Vec::new(); + let mut bytes_compressed = Vec::new(); - reader.read_to_end(&mut bytes_compressed).await?; + reader.read_to_end(&mut bytes_compressed).await?; - let mut decoder = GzDecoder::new(bytes_compressed.as_slice()); + let mut decoder = GzDecoder::new(bytes_compressed.as_slice()); - let mut bytes_uncompressed = Vec::new(); + let mut bytes_uncompressed = Vec::new(); - decoder.read_to_end(&mut bytes_uncompressed)?; + decoder.read_to_end(&mut bytes_uncompressed)?; - // Now that we have decompressed the asset, let's pass it back to the - // context to continue loading + // Now that we have decompressed the asset, let's pass it back to the + // context to continue loading - let mut reader = VecReader::new(bytes_uncompressed); + let mut reader = VecReader::new(bytes_uncompressed); - let uncompressed = load_context - .load_direct_with_reader(&mut reader, contained_path) - .await?; + let uncompressed = load_context + .load_direct_with_reader(&mut reader, contained_path) + .await?; - Ok(GzAsset { uncompressed }) - }) + Ok(GzAsset { uncompressed }) } fn extensions(&self) -> &[&str] { diff --git a/examples/asset/custom_asset.rs b/examples/asset/custom_asset.rs index c10e297c38..b3167bbd1d 100644 --- a/examples/asset/custom_asset.rs +++ b/examples/asset/custom_asset.rs @@ -4,7 +4,6 @@ use bevy::{ asset::{io::Reader, ron, AssetLoader, AsyncReadExt, LoadContext}, prelude::*, reflect::TypePath, - utils::BoxedFuture, }; use serde::Deserialize; use thiserror::Error; @@ -34,18 +33,16 @@ impl AssetLoader for CustomAssetLoader { type Asset = CustomAsset; type Settings = (); type Error = CustomAssetLoaderError; - fn load<'a>( + async fn load<'a>( &'a self, - reader: &'a mut Reader, + reader: &'a mut Reader<'_>, _settings: &'a (), - _load_context: &'a mut LoadContext, - ) -> BoxedFuture<'a, Result> { - Box::pin(async move { - let mut bytes = Vec::new(); - reader.read_to_end(&mut bytes).await?; - let custom_asset = ron::de::from_bytes::(&bytes)?; - Ok(custom_asset) - }) + _load_context: &'a mut LoadContext<'_>, + ) -> Result { + let mut bytes = Vec::new(); + reader.read_to_end(&mut bytes).await?; + let custom_asset = ron::de::from_bytes::(&bytes)?; + Ok(custom_asset) } fn extensions(&self) -> &[&str] { @@ -75,19 +72,17 @@ impl AssetLoader for BlobAssetLoader { type Settings = (); type Error = BlobAssetLoaderError; - fn load<'a>( + async fn load<'a>( &'a self, - reader: &'a mut Reader, + reader: &'a mut Reader<'_>, _settings: &'a (), - _load_context: &'a mut LoadContext, - ) -> BoxedFuture<'a, Result> { - Box::pin(async move { - info!("Loading Blob..."); - let mut bytes = Vec::new(); - reader.read_to_end(&mut bytes).await?; + _load_context: &'a mut LoadContext<'_>, + ) -> Result { + info!("Loading Blob..."); + let mut bytes = Vec::new(); + reader.read_to_end(&mut bytes).await?; - Ok(Blob { bytes }) - }) + Ok(Blob { bytes }) } } diff --git a/examples/asset/custom_asset_reader.rs b/examples/asset/custom_asset_reader.rs index 4e4b0eede6..d302ebbf7f 100644 --- a/examples/asset/custom_asset_reader.rs +++ b/examples/asset/custom_asset_reader.rs @@ -3,42 +3,35 @@ //! It does not know anything about the asset formats, only how to talk to the underlying storage. use bevy::{ - asset::io::{AssetReader, AssetReaderError, AssetSource, AssetSourceId, PathStream, Reader}, + asset::io::{ + AssetReader, AssetReaderError, AssetSource, AssetSourceId, ErasedAssetReader, PathStream, + Reader, + }, prelude::*, - utils::BoxedFuture, }; use std::path::Path; /// A custom asset reader implementation that wraps a given asset reader implementation -struct CustomAssetReader(Box); +struct CustomAssetReader(Box); impl AssetReader for CustomAssetReader { - fn read<'a>( - &'a self, - path: &'a Path, - ) -> BoxedFuture<'a, Result>, AssetReaderError>> { + async fn read<'a>(&'a self, path: &'a Path) -> Result>, AssetReaderError> { info!("Reading {:?}", path); - self.0.read(path) + self.0.read(path).await } - fn read_meta<'a>( - &'a self, - path: &'a Path, - ) -> BoxedFuture<'a, Result>, AssetReaderError>> { - self.0.read_meta(path) + async fn read_meta<'a>(&'a self, path: &'a Path) -> Result>, AssetReaderError> { + self.0.read_meta(path).await } - fn read_directory<'a>( + async fn read_directory<'a>( &'a self, path: &'a Path, - ) -> BoxedFuture<'a, Result, AssetReaderError>> { - self.0.read_directory(path) + ) -> Result, AssetReaderError> { + self.0.read_directory(path).await } - fn is_directory<'a>( - &'a self, - path: &'a Path, - ) -> BoxedFuture<'a, Result> { - self.0.is_directory(path) + async fn is_directory<'a>(&'a self, path: &'a Path) -> Result { + self.0.is_directory(path).await } } diff --git a/examples/asset/processing/asset_processing.rs b/examples/asset/processing/asset_processing.rs index 3f660ef85f..f99fc4769a 100644 --- a/examples/asset/processing/asset_processing.rs +++ b/examples/asset/processing/asset_processing.rs @@ -12,7 +12,6 @@ use bevy::{ }, prelude::*, reflect::TypePath, - utils::BoxedFuture, }; use serde::{Deserialize, Serialize}; use std::convert::Infallible; @@ -83,22 +82,20 @@ impl AssetLoader for TextLoader { type Asset = Text; type Settings = TextSettings; type Error = std::io::Error; - fn load<'a>( + async fn load<'a>( &'a self, - reader: &'a mut Reader, + reader: &'a mut Reader<'_>, settings: &'a TextSettings, - _load_context: &'a mut LoadContext, - ) -> BoxedFuture<'a, Result> { - Box::pin(async move { - let mut bytes = Vec::new(); - reader.read_to_end(&mut bytes).await?; - let value = if let Some(ref text) = settings.text_override { - text.clone() - } else { - String::from_utf8(bytes).unwrap() - }; - Ok(Text(value)) - }) + _load_context: &'a mut LoadContext<'_>, + ) -> Result { + let mut bytes = Vec::new(); + reader.read_to_end(&mut bytes).await?; + let value = if let Some(ref text) = settings.text_override { + text.clone() + } else { + String::from_utf8(bytes).unwrap() + }; + Ok(Text(value)) } fn extensions(&self) -> &[&str] { @@ -138,30 +135,28 @@ impl AssetLoader for CoolTextLoader { type Settings = (); type Error = CoolTextLoaderError; - fn load<'a>( + async fn load<'a>( &'a self, - reader: &'a mut Reader, + reader: &'a mut Reader<'_>, _settings: &'a Self::Settings, - load_context: &'a mut LoadContext, - ) -> BoxedFuture<'a, Result> { - Box::pin(async move { - let mut bytes = Vec::new(); - reader.read_to_end(&mut bytes).await?; - 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(&embedded).await?; - let text = loaded.get::().unwrap(); - base_text.push_str(&text.0); - } - Ok(CoolText { - text: base_text, - dependencies: ron - .dependencies - .iter() - .map(|p| load_context.load(p)) - .collect(), - }) + load_context: &'a mut LoadContext<'_>, + ) -> Result { + let mut bytes = Vec::new(); + reader.read_to_end(&mut bytes).await?; + 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(&embedded).await?; + let text = loaded.get::().unwrap(); + base_text.push_str(&text.0); + } + Ok(CoolText { + text: base_text, + dependencies: ron + .dependencies + .iter() + .map(|p| load_context.load(p)) + .collect(), }) } @@ -184,15 +179,13 @@ impl AssetTransformer for CoolTextTransformer { type Settings = CoolTextTransformerSettings; type Error = Infallible; - fn transform<'a>( + async fn transform<'a>( &'a self, mut asset: TransformedAsset, settings: &'a Self::Settings, - ) -> BoxedFuture<'a, Result, Self::Error>> { - Box::pin(async move { - asset.text = format!("{}{}", asset.text, settings.appended); - Ok(asset) - }) + ) -> Result, Self::Error> { + asset.text = format!("{}{}", asset.text, settings.appended); + Ok(asset) } } @@ -204,16 +197,14 @@ impl AssetSaver for CoolTextSaver { type OutputLoader = TextLoader; type Error = std::io::Error; - fn save<'a>( + async fn save<'a>( &'a self, writer: &'a mut Writer, asset: SavedAsset<'a, Self::Asset>, _settings: &'a Self::Settings, - ) -> BoxedFuture<'a, Result> { - Box::pin(async move { - writer.write_all(asset.text.as_bytes()).await?; - Ok(TextSettings::default()) - }) + ) -> Result { + writer.write_all(asset.text.as_bytes()).await?; + Ok(TextSettings::default()) } }