Skip to content

Commit 05bf8ae

Browse files
committed
wasm assets
1 parent 9b35d01 commit 05bf8ae

File tree

9 files changed

+171
-25
lines changed

9 files changed

+171
-25
lines changed

crates/bevy_asset/Cargo.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ license = "MIT"
1313
keywords = ["bevy"]
1414

1515
[features]
16-
default = ["filesystem_watcher"]
16+
default = []
1717
filesystem_watcher = ["notify"]
1818

1919
[dependencies]
@@ -37,3 +37,10 @@ log = { version = "0.4", features = ["release_max_level_info"] }
3737
notify = { version = "5.0.0-pre.2", optional = true }
3838
parking_lot = "0.11.0"
3939
rand = "0.7.3"
40+
async-trait = "0.1.41"
41+
42+
web-sys = { version = "0.3", features = ["Request", "Window", "Response"]}
43+
wasm-bindgen = "0.2.68"
44+
wasm-bindgen-futures = "0.4"
45+
js-sys = "0.3"
46+
futures-lite = "1.11.1"

crates/bevy_asset/src/asset_server.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,19 @@ pub enum AssetServerError {
2929
PathLoaderError(#[from] AssetIoError),
3030
}
3131

32+
#[cfg(not(target_arch="wasm32"))]
33+
type AssetIoImpl = FileAssetIo;
34+
35+
#[cfg(target_arch="wasm32")]
36+
type AssetIoImpl = crate::wasm_io::WebAssetIo;
37+
3238
#[derive(Default)]
3339
pub(crate) struct AssetRefCounter {
3440
pub(crate) channel: Arc<RefChangeChannel>,
3541
pub(crate) ref_counts: Arc<RwLock<HashMap<HandleId, usize>>>,
3642
}
3743

38-
pub struct AssetServerInternal<TAssetIo: AssetIo = FileAssetIo> {
44+
pub struct AssetServerInternal<TAssetIo: AssetIo = AssetIoImpl> {
3945
pub(crate) asset_io: TAssetIo,
4046
pub(crate) asset_ref_counter: AssetRefCounter,
4147
pub(crate) asset_sources: Arc<RwLock<HashMap<SourcePathId, SourceInfo>>>,
@@ -46,8 +52,9 @@ pub struct AssetServerInternal<TAssetIo: AssetIo = FileAssetIo> {
4652
task_pool: TaskPool,
4753
}
4854

55+
4956
/// Loads assets from the filesystem on background threads
50-
pub struct AssetServer<TAssetIo: AssetIo = FileAssetIo> {
57+
pub struct AssetServer<TAssetIo: AssetIo = AssetIoImpl> {
5158
pub(crate) server: Arc<AssetServerInternal<TAssetIo>>,
5259
}
5360

@@ -180,7 +187,7 @@ impl<TAssetIo: AssetIo> AssetServer<TAssetIo> {
180187
}
181188

182189
// TODO: properly set failed LoadState in all failure cases
183-
fn load_sync<'a, P: Into<AssetPath<'a>>>(
190+
async fn load_async<'a, P: Into<AssetPath<'a>>>(
184191
&self,
185192
path: P,
186193
force: bool,
@@ -221,7 +228,7 @@ impl<TAssetIo: AssetIo> AssetServer<TAssetIo> {
221228
};
222229

223230
// load the asset bytes
224-
let bytes = self.server.asset_io.load_path(asset_path.path())?;
231+
let bytes = self.server.asset_io.load_path(asset_path.path()).await?;
225232

226233
// load the asset source using the corresponding AssetLoader
227234
let mut load_context = LoadContext::new(
@@ -231,7 +238,8 @@ impl<TAssetIo: AssetIo> AssetServer<TAssetIo> {
231238
version,
232239
);
233240
asset_loader
234-
.load(&bytes, &mut load_context)
241+
.load_async(&bytes, &mut load_context)
242+
.await
235243
.map_err(AssetServerError::AssetLoaderError)?;
236244

237245
// if version has changed since we loaded and grabbed a lock, return. theres is a newer version being loaded
@@ -291,7 +299,7 @@ impl<TAssetIo: AssetIo> AssetServer<TAssetIo> {
291299
self.server
292300
.task_pool
293301
.spawn(async move {
294-
server.load_sync(owned_path, force).unwrap();
302+
server.load_async(owned_path, force).await.unwrap();
295303
})
296304
.detach();
297305
asset_path.into()

crates/bevy_asset/src/io.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ use std::{
1111
sync::Arc,
1212
};
1313
use thiserror::Error;
14-
15-
use crate::{filesystem_watcher::FilesystemWatcher, AssetServer};
14+
#[cfg(feature = "filesystem_watcher")]
15+
use crate::{filesystem_watcher::FilesystemWatcher};
16+
use crate::AssetServer;
17+
use async_trait::async_trait;
1618

1719
/// Errors that occur while loading assets
1820
#[derive(Error, Debug)]
@@ -26,9 +28,10 @@ pub enum AssetIoError {
2628
}
2729

2830
/// Handles load requests from an AssetServer
31+
#[async_trait]
2932
pub trait AssetIo: Send + Sync + 'static {
30-
fn load_path(&self, path: &Path) -> Result<Vec<u8>, AssetIoError>;
31-
fn save_path(&self, path: &Path, bytes: &[u8]) -> Result<(), AssetIoError>;
33+
async fn load_path(&self, path: &Path) -> Result<Vec<u8>, AssetIoError>;
34+
async fn save_path(&self, path: &Path, bytes: &[u8]) -> Result<(), AssetIoError>;
3235
fn read_directory(
3336
&self,
3437
path: &Path,
@@ -47,6 +50,7 @@ pub struct FileAssetIo {
4750
impl FileAssetIo {
4851
pub fn new<P: AsRef<Path>>(path: P) -> Self {
4952
FileAssetIo {
53+
#[cfg(feature = "filesystem_watcher")]
5054
filesystem_watcher: Default::default(),
5155
root_path: Self::get_root_path().join(path.as_ref()),
5256
}
@@ -67,8 +71,9 @@ impl FileAssetIo {
6771
}
6872
}
6973

74+
#[async_trait]
7075
impl AssetIo for FileAssetIo {
71-
fn load_path(&self, path: &Path) -> Result<Vec<u8>, AssetIoError> {
76+
async fn load_path(&self, path: &Path) -> Result<Vec<u8>, AssetIoError> {
7277
let mut bytes = Vec::new();
7378
match File::open(self.root_path.join(path)) {
7479
Ok(mut file) => {
@@ -98,7 +103,7 @@ impl AssetIo for FileAssetIo {
98103
)))
99104
}
100105

101-
fn save_path(&self, path: &Path, bytes: &[u8]) -> Result<(), AssetIoError> {
106+
async fn save_path(&self, path: &Path, bytes: &[u8]) -> Result<(), AssetIoError> {
102107
let path = self.root_path.join(path);
103108
if let Some(parent_path) = path.parent() {
104109
fs::create_dir_all(parent_path)?;

crates/bevy_asset/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ mod info;
77
mod io;
88
mod loader;
99
mod path;
10+
pub mod wasm_io;
1011

1112
pub use asset_server::*;
1213
pub use assets::*;
@@ -61,7 +62,12 @@ impl Plugin for AssetPlugin {
6162
let settings = app
6263
.resources_mut()
6364
.get_or_insert_with(AssetServerSettings::default);
65+
66+
#[cfg(not(target_arch="wasm32"))]
6467
let source = FileAssetIo::new(&settings.asset_folder);
68+
#[cfg(target_arch="wasm32")]
69+
let source = wasm_io::WebAssetIo::new(&settings.asset_folder);
70+
6571
AssetServer::new(source, task_pool)
6672
};
6773

crates/bevy_asset/src/loader.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,14 @@ use bevy_utils::HashMap;
99
use crossbeam_channel::{Receiver, Sender};
1010
use downcast_rs::{impl_downcast, Downcast};
1111
use std::path::Path;
12+
use async_trait::async_trait;
1213

1314
/// A loader for an asset source
15+
#[async_trait]
1416
pub trait AssetLoader: Send + Sync + 'static {
17+
async fn load_async<'a>(&self, bytes: &[u8], load_context: &mut LoadContext<'a>) -> Result<(), anyhow::Error> {
18+
self.load(bytes, load_context)
19+
}
1520
fn load(&self, bytes: &[u8], load_context: &mut LoadContext) -> Result<(), anyhow::Error>;
1621
fn extensions(&self) -> &[&str];
1722
}
@@ -94,8 +99,8 @@ impl<'a> LoadContext<'a> {
9499
Handle::strong(id.into(), self.ref_change_channel.sender.clone())
95100
}
96101

97-
pub fn read_asset_bytes<P: AsRef<Path>>(&self, path: P) -> Result<Vec<u8>, AssetIoError> {
98-
self.asset_io.load_path(path.as_ref())
102+
pub async fn read_asset_bytes<P: AsRef<Path>>(&self, path: P) -> Result<Vec<u8>, AssetIoError> {
103+
self.asset_io.load_path(path.as_ref()).await
99104
}
100105

101106
pub fn get_asset_metas(&self) -> Vec<AssetMeta> {

crates/bevy_asset/src/wasm_io.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
use crate::io::{AssetIo, AssetIoError};
2+
use anyhow::Result;
3+
use async_trait::async_trait;
4+
use std::{
5+
path::{Path, PathBuf},
6+
};
7+
8+
use js_sys::Uint8Array;
9+
use wasm_bindgen::{JsCast, JsValue};
10+
use wasm_bindgen_futures::JsFuture;
11+
use web_sys::Response;
12+
13+
pub struct WebAssetIo {
14+
pub root_path: PathBuf,
15+
}
16+
17+
impl WebAssetIo {
18+
pub fn new<P: AsRef<Path>>(path: P) -> Self {
19+
WebAssetIo {
20+
root_path: path.as_ref().into()
21+
}
22+
}
23+
}
24+
25+
#[derive(Debug)]
26+
struct SafeJsFuture(JsFuture);
27+
28+
impl SafeJsFuture {
29+
pub fn new(js_future: JsFuture) -> SafeJsFuture {
30+
SafeJsFuture(js_future)
31+
}
32+
}
33+
34+
unsafe impl Send for SafeJsFuture {}
35+
36+
#[derive(Debug)]
37+
pub struct SafeJsValue(JsValue);
38+
unsafe impl Send for SafeJsValue {}
39+
40+
impl SafeJsValue {
41+
pub fn dyn_into<T>(self) -> std::result::Result<T, JsValue>
42+
where
43+
T: JsCast,
44+
{
45+
self.0.dyn_into()
46+
}
47+
}
48+
49+
use futures_lite::FutureExt;
50+
51+
impl std::future::Future for SafeJsFuture {
52+
type Output = Result<SafeJsValue, SafeJsValue>;
53+
54+
fn poll(
55+
mut self: std::pin::Pin<&mut Self>,
56+
cx: &mut std::task::Context<'_>,
57+
) -> std::task::Poll<Self::Output> {
58+
let ret = self.0.poll(cx);
59+
ret.map(|r| r.map(|f| SafeJsValue(f)).map_err(|f| SafeJsValue(f)))
60+
}
61+
}
62+
63+
#[async_trait]
64+
impl AssetIo for WebAssetIo {
65+
async fn load_path(&self, path: &Path) -> Result<Vec<u8>, AssetIoError> {
66+
log::info!("root_path: {:?} path: {:?}", self.root_path, path);
67+
let path = self.root_path.join(Path::new(path));
68+
log::info!("path: {:?}", path);
69+
let resp_value = SafeJsFuture::new(JsFuture::from(
70+
web_sys::window()
71+
.unwrap()
72+
.fetch_with_str(path.to_str().unwrap())
73+
));
74+
let response_data = SafeJsFuture::new(JsFuture::from(
75+
resp_value
76+
.await
77+
.unwrap()
78+
.dyn_into::<Response>()
79+
.unwrap()
80+
.array_buffer()
81+
.unwrap(),
82+
));
83+
let data = response_data.await.unwrap();
84+
Ok(Uint8Array::new(&data.0).to_vec())
85+
}
86+
87+
fn read_directory(
88+
&self,
89+
_path: &Path,
90+
) -> Result<Box<dyn Iterator<Item = PathBuf>>, AssetIoError> {
91+
panic!("not supported on this platform");
92+
}
93+
94+
async fn save_path(&self, _path: &Path, _bytes: &[u8]) -> Result<(), AssetIoError> {
95+
panic!("not supported on this platform");
96+
}
97+
98+
fn watch_path_for_changes(&self, _path: &Path) -> Result<(), AssetIoError> {
99+
Ok(())
100+
}
101+
102+
fn watch_for_changes(&self) -> Result<(), AssetIoError> {
103+
Ok(())
104+
}
105+
106+
fn is_directory(&self, _path: &Path) -> bool {
107+
false
108+
}
109+
}

crates/bevy_gltf/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,4 @@ image = { version = "0.23", default-features = false }
3030
thiserror = "1.0"
3131
anyhow = "1.0"
3232
base64 = "0.12.3"
33+
async-trait = "0.1.41"

crates/bevy_gltf/src/loader.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use gltf::{mesh::Mode, Primitive};
1818
use image::{GenericImageView, ImageFormat};
1919
use std::path::Path;
2020
use thiserror::Error;
21+
use async_trait::async_trait;
2122

2223
/// An error that occurs when loading a GLTF file
2324
#[derive(Error, Debug)]
@@ -46,9 +47,13 @@ pub enum GltfError {
4647
#[derive(Default)]
4748
pub struct GltfLoader;
4849

50+
#[async_trait]
4951
impl AssetLoader for GltfLoader {
52+
async fn load_async<'a>(&self, bytes: &[u8], load_context: &mut LoadContext<'a>) -> Result<()> {
53+
Ok(load_gltf(bytes, load_context).await?)
54+
}
5055
fn load(&self, bytes: &[u8], load_context: &mut LoadContext) -> Result<()> {
51-
Ok(load_gltf(bytes, load_context)?)
56+
panic!("use async_load")
5257
}
5358

5459
fn extensions(&self) -> &[&str] {
@@ -57,10 +62,10 @@ impl AssetLoader for GltfLoader {
5762
}
5863
}
5964

60-
fn load_gltf(bytes: &[u8], load_context: &mut LoadContext) -> Result<(), GltfError> {
65+
async fn load_gltf<'a>(bytes: &[u8], load_context: &mut LoadContext<'a>) -> Result<(), GltfError> {
6166
let gltf = gltf::Gltf::from_slice(bytes)?;
6267
let mut world = World::default();
63-
let buffer_data = load_buffers(&gltf, load_context, load_context.path())?;
68+
let buffer_data = load_buffers(&gltf, load_context, load_context.path()).await?;
6469

6570
let world_builder = &mut world.build();
6671

@@ -267,9 +272,9 @@ fn get_primitive_topology(mode: Mode) -> Result<PrimitiveTopology, GltfError> {
267272
}
268273
}
269274

270-
fn load_buffers(
275+
async fn load_buffers<'a>(
271276
gltf: &gltf::Gltf,
272-
load_context: &LoadContext,
277+
load_context: &LoadContext<'a>,
273278
asset_path: &Path,
274279
) -> Result<Vec<Vec<u8>>, GltfError> {
275280
const OCTET_STREAM_URI: &str = "data:application/octet-stream;base64,";
@@ -287,7 +292,7 @@ fn load_buffers(
287292
} else {
288293
// TODO: Remove this and add dep
289294
let buffer_path = asset_path.parent().unwrap().join(uri);
290-
let buffer_bytes = load_context.read_asset_bytes(buffer_path)?;
295+
let buffer_bytes = load_context.read_asset_bytes(buffer_path).await?;
291296
buffer_data.push(buffer_bytes);
292297
}
293298
}

crates/bevy_winit/src/winit_windows.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,10 @@ impl WinitWindows {
7171

7272
let winit_window = winit_window_builder.build(&event_loop).unwrap();
7373

74-
winit_window
75-
.set_cursor_grab(window.cursor_locked())
76-
.unwrap();
77-
winit_window.set_cursor_visible(window.cursor_visible());
74+
// winit_window
75+
// .set_cursor_grab(window.cursor_locked())
76+
// .unwrap();
77+
// winit_window.set_cursor_visible(window.cursor_visible());
7878

7979
self.window_id_to_winit
8080
.insert(window.id(), winit_window.id());

0 commit comments

Comments
 (0)