Skip to content

Commit

Permalink
wasm assets
Browse files Browse the repository at this point in the history
  • Loading branch information
mrk-its committed Oct 19, 2020
1 parent 9b35d01 commit 05bf8ae
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 25 deletions.
9 changes: 8 additions & 1 deletion crates/bevy_asset/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ license = "MIT"
keywords = ["bevy"]

[features]
default = ["filesystem_watcher"]
default = []
filesystem_watcher = ["notify"]

[dependencies]
Expand All @@ -37,3 +37,10 @@ log = { version = "0.4", features = ["release_max_level_info"] }
notify = { version = "5.0.0-pre.2", optional = true }
parking_lot = "0.11.0"
rand = "0.7.3"
async-trait = "0.1.41"

web-sys = { version = "0.3", features = ["Request", "Window", "Response"]}
wasm-bindgen = "0.2.68"
wasm-bindgen-futures = "0.4"
js-sys = "0.3"
futures-lite = "1.11.1"
20 changes: 14 additions & 6 deletions crates/bevy_asset/src/asset_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,19 @@ pub enum AssetServerError {
PathLoaderError(#[from] AssetIoError),
}

#[cfg(not(target_arch="wasm32"))]
type AssetIoImpl = FileAssetIo;

#[cfg(target_arch="wasm32")]
type AssetIoImpl = crate::wasm_io::WebAssetIo;

#[derive(Default)]
pub(crate) struct AssetRefCounter {
pub(crate) channel: Arc<RefChangeChannel>,
pub(crate) ref_counts: Arc<RwLock<HashMap<HandleId, usize>>>,
}

pub struct AssetServerInternal<TAssetIo: AssetIo = FileAssetIo> {
pub struct AssetServerInternal<TAssetIo: AssetIo = AssetIoImpl> {
pub(crate) asset_io: TAssetIo,
pub(crate) asset_ref_counter: AssetRefCounter,
pub(crate) asset_sources: Arc<RwLock<HashMap<SourcePathId, SourceInfo>>>,
Expand All @@ -46,8 +52,9 @@ pub struct AssetServerInternal<TAssetIo: AssetIo = FileAssetIo> {
task_pool: TaskPool,
}


/// Loads assets from the filesystem on background threads
pub struct AssetServer<TAssetIo: AssetIo = FileAssetIo> {
pub struct AssetServer<TAssetIo: AssetIo = AssetIoImpl> {
pub(crate) server: Arc<AssetServerInternal<TAssetIo>>,
}

Expand Down Expand Up @@ -180,7 +187,7 @@ impl<TAssetIo: AssetIo> AssetServer<TAssetIo> {
}

// TODO: properly set failed LoadState in all failure cases
fn load_sync<'a, P: Into<AssetPath<'a>>>(
async fn load_async<'a, P: Into<AssetPath<'a>>>(
&self,
path: P,
force: bool,
Expand Down Expand Up @@ -221,7 +228,7 @@ impl<TAssetIo: AssetIo> AssetServer<TAssetIo> {
};

// load the asset bytes
let bytes = self.server.asset_io.load_path(asset_path.path())?;
let bytes = self.server.asset_io.load_path(asset_path.path()).await?;

// load the asset source using the corresponding AssetLoader
let mut load_context = LoadContext::new(
Expand All @@ -231,7 +238,8 @@ impl<TAssetIo: AssetIo> AssetServer<TAssetIo> {
version,
);
asset_loader
.load(&bytes, &mut load_context)
.load_async(&bytes, &mut load_context)
.await
.map_err(AssetServerError::AssetLoaderError)?;

// if version has changed since we loaded and grabbed a lock, return. theres is a newer version being loaded
Expand Down Expand Up @@ -291,7 +299,7 @@ impl<TAssetIo: AssetIo> AssetServer<TAssetIo> {
self.server
.task_pool
.spawn(async move {
server.load_sync(owned_path, force).unwrap();
server.load_async(owned_path, force).await.unwrap();
})
.detach();
asset_path.into()
Expand Down
17 changes: 11 additions & 6 deletions crates/bevy_asset/src/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ use std::{
sync::Arc,
};
use thiserror::Error;

use crate::{filesystem_watcher::FilesystemWatcher, AssetServer};
#[cfg(feature = "filesystem_watcher")]
use crate::{filesystem_watcher::FilesystemWatcher};
use crate::AssetServer;
use async_trait::async_trait;

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

/// Handles load requests from an AssetServer
#[async_trait]
pub trait AssetIo: Send + Sync + 'static {
fn load_path(&self, path: &Path) -> Result<Vec<u8>, AssetIoError>;
fn save_path(&self, path: &Path, bytes: &[u8]) -> Result<(), AssetIoError>;
async fn load_path(&self, path: &Path) -> Result<Vec<u8>, AssetIoError>;
async fn save_path(&self, path: &Path, bytes: &[u8]) -> Result<(), AssetIoError>;
fn read_directory(
&self,
path: &Path,
Expand All @@ -47,6 +50,7 @@ pub struct FileAssetIo {
impl FileAssetIo {
pub fn new<P: AsRef<Path>>(path: P) -> Self {
FileAssetIo {
#[cfg(feature = "filesystem_watcher")]
filesystem_watcher: Default::default(),
root_path: Self::get_root_path().join(path.as_ref()),
}
Expand All @@ -67,8 +71,9 @@ impl FileAssetIo {
}
}

#[async_trait]
impl AssetIo for FileAssetIo {
fn load_path(&self, path: &Path) -> Result<Vec<u8>, AssetIoError> {
async fn load_path(&self, path: &Path) -> Result<Vec<u8>, AssetIoError> {
let mut bytes = Vec::new();
match File::open(self.root_path.join(path)) {
Ok(mut file) => {
Expand Down Expand Up @@ -98,7 +103,7 @@ impl AssetIo for FileAssetIo {
)))
}

fn save_path(&self, path: &Path, bytes: &[u8]) -> Result<(), AssetIoError> {
async fn save_path(&self, path: &Path, bytes: &[u8]) -> Result<(), AssetIoError> {
let path = self.root_path.join(path);
if let Some(parent_path) = path.parent() {
fs::create_dir_all(parent_path)?;
Expand Down
6 changes: 6 additions & 0 deletions crates/bevy_asset/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod info;
mod io;
mod loader;
mod path;
pub mod wasm_io;

pub use asset_server::*;
pub use assets::*;
Expand Down Expand Up @@ -61,7 +62,12 @@ impl Plugin for AssetPlugin {
let settings = app
.resources_mut()
.get_or_insert_with(AssetServerSettings::default);

#[cfg(not(target_arch="wasm32"))]
let source = FileAssetIo::new(&settings.asset_folder);
#[cfg(target_arch="wasm32")]
let source = wasm_io::WebAssetIo::new(&settings.asset_folder);

AssetServer::new(source, task_pool)
};

Expand Down
9 changes: 7 additions & 2 deletions crates/bevy_asset/src/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,14 @@ use bevy_utils::HashMap;
use crossbeam_channel::{Receiver, Sender};
use downcast_rs::{impl_downcast, Downcast};
use std::path::Path;
use async_trait::async_trait;

/// A loader for an asset source
#[async_trait]
pub trait AssetLoader: Send + Sync + 'static {
async fn load_async<'a>(&self, bytes: &[u8], load_context: &mut LoadContext<'a>) -> Result<(), anyhow::Error> {
self.load(bytes, load_context)
}
fn load(&self, bytes: &[u8], load_context: &mut LoadContext) -> Result<(), anyhow::Error>;
fn extensions(&self) -> &[&str];
}
Expand Down Expand Up @@ -94,8 +99,8 @@ impl<'a> LoadContext<'a> {
Handle::strong(id.into(), self.ref_change_channel.sender.clone())
}

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

pub fn get_asset_metas(&self) -> Vec<AssetMeta> {
Expand Down
109 changes: 109 additions & 0 deletions crates/bevy_asset/src/wasm_io.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
use crate::io::{AssetIo, AssetIoError};
use anyhow::Result;
use async_trait::async_trait;
use std::{
path::{Path, PathBuf},
};

use js_sys::Uint8Array;
use wasm_bindgen::{JsCast, JsValue};
use wasm_bindgen_futures::JsFuture;
use web_sys::Response;

pub struct WebAssetIo {
pub root_path: PathBuf,
}

impl WebAssetIo {
pub fn new<P: AsRef<Path>>(path: P) -> Self {
WebAssetIo {
root_path: path.as_ref().into()
}
}
}

#[derive(Debug)]
struct SafeJsFuture(JsFuture);

impl SafeJsFuture {
pub fn new(js_future: JsFuture) -> SafeJsFuture {
SafeJsFuture(js_future)
}
}

unsafe impl Send for SafeJsFuture {}

#[derive(Debug)]
pub struct SafeJsValue(JsValue);
unsafe impl Send for SafeJsValue {}

impl SafeJsValue {
pub fn dyn_into<T>(self) -> std::result::Result<T, JsValue>
where
T: JsCast,
{
self.0.dyn_into()
}
}

use futures_lite::FutureExt;

impl std::future::Future for SafeJsFuture {
type Output = Result<SafeJsValue, SafeJsValue>;

fn poll(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
let ret = self.0.poll(cx);
ret.map(|r| r.map(|f| SafeJsValue(f)).map_err(|f| SafeJsValue(f)))
}
}

#[async_trait]
impl AssetIo for WebAssetIo {
async fn load_path(&self, path: &Path) -> Result<Vec<u8>, AssetIoError> {
log::info!("root_path: {:?} path: {:?}", self.root_path, path);
let path = self.root_path.join(Path::new(path));
log::info!("path: {:?}", path);
let resp_value = SafeJsFuture::new(JsFuture::from(
web_sys::window()
.unwrap()
.fetch_with_str(path.to_str().unwrap())
));
let response_data = SafeJsFuture::new(JsFuture::from(
resp_value
.await
.unwrap()
.dyn_into::<Response>()
.unwrap()
.array_buffer()
.unwrap(),
));
let data = response_data.await.unwrap();
Ok(Uint8Array::new(&data.0).to_vec())
}

fn read_directory(
&self,
_path: &Path,
) -> Result<Box<dyn Iterator<Item = PathBuf>>, AssetIoError> {
panic!("not supported on this platform");
}

async fn save_path(&self, _path: &Path, _bytes: &[u8]) -> Result<(), AssetIoError> {
panic!("not supported on this platform");
}

fn watch_path_for_changes(&self, _path: &Path) -> Result<(), AssetIoError> {
Ok(())
}

fn watch_for_changes(&self) -> Result<(), AssetIoError> {
Ok(())
}

fn is_directory(&self, _path: &Path) -> bool {
false
}
}
1 change: 1 addition & 0 deletions crates/bevy_gltf/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@ image = { version = "0.23", default-features = false }
thiserror = "1.0"
anyhow = "1.0"
base64 = "0.12.3"
async-trait = "0.1.41"
17 changes: 11 additions & 6 deletions crates/bevy_gltf/src/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use gltf::{mesh::Mode, Primitive};
use image::{GenericImageView, ImageFormat};
use std::path::Path;
use thiserror::Error;
use async_trait::async_trait;

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

#[async_trait]
impl AssetLoader for GltfLoader {
async fn load_async<'a>(&self, bytes: &[u8], load_context: &mut LoadContext<'a>) -> Result<()> {
Ok(load_gltf(bytes, load_context).await?)
}
fn load(&self, bytes: &[u8], load_context: &mut LoadContext) -> Result<()> {
Ok(load_gltf(bytes, load_context)?)
panic!("use async_load")
}

fn extensions(&self) -> &[&str] {
Expand All @@ -57,10 +62,10 @@ impl AssetLoader for GltfLoader {
}
}

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

let world_builder = &mut world.build();

Expand Down Expand Up @@ -267,9 +272,9 @@ fn get_primitive_topology(mode: Mode) -> Result<PrimitiveTopology, GltfError> {
}
}

fn load_buffers(
async fn load_buffers<'a>(
gltf: &gltf::Gltf,
load_context: &LoadContext,
load_context: &LoadContext<'a>,
asset_path: &Path,
) -> Result<Vec<Vec<u8>>, GltfError> {
const OCTET_STREAM_URI: &str = "data:application/octet-stream;base64,";
Expand All @@ -287,7 +292,7 @@ fn load_buffers(
} else {
// TODO: Remove this and add dep
let buffer_path = asset_path.parent().unwrap().join(uri);
let buffer_bytes = load_context.read_asset_bytes(buffer_path)?;
let buffer_bytes = load_context.read_asset_bytes(buffer_path).await?;
buffer_data.push(buffer_bytes);
}
}
Expand Down
8 changes: 4 additions & 4 deletions crates/bevy_winit/src/winit_windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,10 @@ impl WinitWindows {

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

winit_window
.set_cursor_grab(window.cursor_locked())
.unwrap();
winit_window.set_cursor_visible(window.cursor_visible());
// winit_window
// .set_cursor_grab(window.cursor_locked())
// .unwrap();
// winit_window.set_cursor_visible(window.cursor_visible());

self.window_id_to_winit
.insert(window.id(), winit_window.id());
Expand Down

0 comments on commit 05bf8ae

Please sign in to comment.