diff --git a/crates/bevy_asset/src/asset_server.rs b/crates/bevy_asset/src/asset_server.rs index be185e515ba96..b2abadf6f0ac9 100644 --- a/crates/bevy_asset/src/asset_server.rs +++ b/crates/bevy_asset/src/asset_server.rs @@ -276,6 +276,7 @@ impl AssetServer { &self.server.asset_ref_counter.channel, &*self.server.asset_io, version, + &self.server.task_pool, ); asset_loader .load(&bytes, &mut load_context) diff --git a/crates/bevy_asset/src/loader.rs b/crates/bevy_asset/src/loader.rs index aa0f3cd009b5a..921939fc4bbef 100644 --- a/crates/bevy_asset/src/loader.rs +++ b/crates/bevy_asset/src/loader.rs @@ -8,6 +8,7 @@ use bevy_ecs::{ system::{Res, ResMut}, }; use bevy_reflect::{TypeUuid, TypeUuidDynamic}; +use bevy_tasks::TaskPool; use bevy_utils::{BoxedFuture, HashMap}; use crossbeam_channel::{Receiver, Sender}; use downcast_rs::{impl_downcast, Downcast}; @@ -78,6 +79,7 @@ pub struct LoadContext<'a> { pub(crate) labeled_assets: HashMap, BoxedLoadedAsset>, pub(crate) path: &'a Path, pub(crate) version: usize, + pub(crate) task_pool: &'a TaskPool, } impl<'a> LoadContext<'a> { @@ -86,6 +88,7 @@ impl<'a> LoadContext<'a> { ref_change_channel: &'a RefChangeChannel, asset_io: &'a dyn AssetIo, version: usize, + task_pool: &'a TaskPool, ) -> Self { Self { ref_change_channel, @@ -93,6 +96,7 @@ impl<'a> LoadContext<'a> { labeled_assets: Default::default(), version, path, + task_pool, } } @@ -134,6 +138,10 @@ impl<'a> LoadContext<'a> { } asset_metas } + + pub fn task_pool(&self) -> &TaskPool { + self.task_pool + } } /// The result of loading an asset of type `T` diff --git a/crates/bevy_gltf/src/loader.rs b/crates/bevy_gltf/src/loader.rs index 5021eff00cac2..52b8e83625725 100644 --- a/crates/bevy_gltf/src/loader.rs +++ b/crates/bevy_gltf/src/loader.rs @@ -220,49 +220,33 @@ async fn load_gltf<'a, 'b>( }) .collect(); + // TODO: use the threaded impl on wasm once wasm thread pool doesn't deadlock on it + #[cfg(target_arch = "wasm32")] for gltf_texture in gltf.textures() { - let mut texture = match gltf_texture.source().source() { - gltf::image::Source::View { view, mime_type } => { - let start = view.offset() as usize; - let end = (view.offset() + view.length()) as usize; - let buffer = &buffer_data[view.buffer().index()][start..end]; - Texture::from_buffer(buffer, ImageType::MimeType(mime_type))? - } - gltf::image::Source::Uri { uri, mime_type } => { - let uri = percent_encoding::percent_decode_str(uri) - .decode_utf8() - .unwrap(); - let uri = uri.as_ref(); - let (bytes, image_type) = match DataUri::parse(uri) { - Ok(data_uri) => (data_uri.decode()?, ImageType::MimeType(data_uri.mime_type)), - Err(()) => { - let parent = load_context.path().parent().unwrap(); - let image_path = parent.join(uri); - let bytes = load_context.read_asset_bytes(image_path.clone()).await?; - - let extension = Path::new(uri).extension().unwrap().to_str().unwrap(); - let image_type = ImageType::Extension(extension); - - (bytes, image_type) - } - }; - - Texture::from_buffer( - &bytes, - mime_type - .map(|mt| ImageType::MimeType(mt)) - .unwrap_or(image_type), - )? - } - }; - let texture_label = texture_label(&gltf_texture); - texture.sampler = texture_sampler(&gltf_texture); - if linear_textures.contains(&gltf_texture.index()) { - texture.format = TextureFormat::Rgba8Unorm; - } - load_context.set_labeled_asset::(&texture_label, LoadedAsset::new(texture)); + let (texture, label) = + load_texture(gltf_texture, &buffer_data, &linear_textures, &load_context).await?; + load_context.set_labeled_asset(&label, LoadedAsset::new(texture)); } + #[cfg(not(target_arch = "wasm32"))] + load_context + .task_pool() + .scope(|scope| { + gltf.textures().for_each(|gltf_texture| { + let linear_textures = &linear_textures; + let load_context: &LoadContext = load_context; + let buffer_data = &buffer_data; + scope.spawn(async move { + load_texture(gltf_texture, buffer_data, linear_textures, load_context).await + }); + }); + }) + .into_iter() + .filter_map(|result| result.ok()) + .for_each(|(texture, label)| { + load_context.set_labeled_asset(&label, LoadedAsset::new(texture)); + }); + let mut scenes = vec![]; let mut named_scenes = HashMap::new(); for scene in gltf.scenes() { @@ -310,6 +294,54 @@ async fn load_gltf<'a, 'b>( Ok(()) } +async fn load_texture<'a>( + gltf_texture: gltf::Texture<'a>, + buffer_data: &[Vec], + linear_textures: &HashSet, + load_context: &LoadContext<'a>, +) -> Result<(Texture, String), GltfError> { + let mut texture = match gltf_texture.source().source() { + gltf::image::Source::View { view, mime_type } => { + let start = view.offset() as usize; + let end = (view.offset() + view.length()) as usize; + let buffer = &buffer_data[view.buffer().index()][start..end]; + Texture::from_buffer(buffer, ImageType::MimeType(mime_type))? + } + gltf::image::Source::Uri { uri, mime_type } => { + let uri = percent_encoding::percent_decode_str(uri) + .decode_utf8() + .unwrap(); + let uri = uri.as_ref(); + let (bytes, image_type) = match DataUri::parse(uri) { + Ok(data_uri) => (data_uri.decode()?, ImageType::MimeType(data_uri.mime_type)), + Err(()) => { + let parent = load_context.path().parent().unwrap(); + let image_path = parent.join(uri); + let bytes = load_context.read_asset_bytes(image_path.clone()).await?; + + let extension = Path::new(uri).extension().unwrap().to_str().unwrap(); + let image_type = ImageType::Extension(extension); + + (bytes, image_type) + } + }; + + Texture::from_buffer( + &bytes, + mime_type + .map(|mt| ImageType::MimeType(mt)) + .unwrap_or(image_type), + )? + } + }; + texture.sampler = texture_sampler(&gltf_texture); + if (linear_textures).contains(&gltf_texture.index()) { + texture.format = TextureFormat::Rgba8Unorm; + } + + Ok((texture, texture_label(&gltf_texture))) +} + fn load_material(material: &Material, load_context: &mut LoadContext) -> Handle { let material_label = material_label(&material);