From 5aa77979d15a13cf8b532f3eb03abb1c29981309 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Mon, 21 Sep 2020 22:13:40 +0200 Subject: [PATCH 001/294] Remove some unsafe code (#540) --- .../src/single_threaded_task_pool.rs | 19 ++++--------------- crates/bevy_tasks/src/task.rs | 5 ++--- crates/bevy_tasks/src/task_pool.rs | 10 +++------- 3 files changed, 9 insertions(+), 25 deletions(-) diff --git a/crates/bevy_tasks/src/single_threaded_task_pool.rs b/crates/bevy_tasks/src/single_threaded_task_pool.rs index 6076b2a120b58..a7e4c7f6999f3 100644 --- a/crates/bevy_tasks/src/single_threaded_task_pool.rs +++ b/crates/bevy_tasks/src/single_threaded_task_pool.rs @@ -65,11 +65,8 @@ impl TaskPool { { let executor = async_executor::LocalExecutor::new(); - let executor: &async_executor::LocalExecutor = &executor; - let executor: &'scope async_executor::LocalExecutor = unsafe { mem::transmute(executor) }; - let mut scope = Scope { - executor, + executor: &executor, results: Vec::new(), }; @@ -87,26 +84,18 @@ impl TaskPool { } pub struct Scope<'scope, T> { - executor: &'scope async_executor::LocalExecutor, + executor: &'scope async_executor::LocalExecutor<'scope>, // Vector to gather results of all futures spawned during scope run results: Vec>>>, } -impl<'scope, T: Send + 'static> Scope<'scope, T> { +impl<'scope, T: Send + 'scope> Scope<'scope, T> { pub fn spawn + 'scope + Send>(&mut self, f: Fut) { let result = Arc::new(Mutex::new(None)); self.results.push(result.clone()); let f = async move { result.lock().unwrap().replace(f.await); }; - - // SAFETY: This function blocks until all futures complete, so we do not read/write the - // data from futures outside of the 'scope lifetime. However, rust has no way of knowing - // this so we must convert to 'static here to appease the compiler as it is unable to - // validate safety. - let fut: Pin + 'scope>> = Box::pin(f); - let fut: Pin + 'static>> = unsafe { mem::transmute(fut) }; - - self.executor.spawn(fut).detach(); + self.executor.spawn(f).detach(); } } diff --git a/crates/bevy_tasks/src/task.rs b/crates/bevy_tasks/src/task.rs index 834b3eb61e247..6087a050ae1f3 100644 --- a/crates/bevy_tasks/src/task.rs +++ b/crates/bevy_tasks/src/task.rs @@ -38,8 +38,7 @@ impl Task { impl Future for Task { type Output = T; - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - // Safe because Task is pinned and contains async_executor::Task by value - unsafe { self.map_unchecked_mut(|x| &mut x.0).poll(cx) } + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + Pin::new(&mut self.0).poll(cx) } } diff --git a/crates/bevy_tasks/src/task_pool.rs b/crates/bevy_tasks/src/task_pool.rs index e8a0b559f2b61..2b101437e3875 100644 --- a/crates/bevy_tasks/src/task_pool.rs +++ b/crates/bevy_tasks/src/task_pool.rs @@ -6,7 +6,7 @@ use std::{ thread::{self, JoinHandle}, }; -use futures_lite::future; +use futures_lite::{future, pin}; /// Used to create a TaskPool #[derive(Debug, Default, Clone)] @@ -180,12 +180,8 @@ impl TaskPool { results }; - // Move the value to ensure that it is owned - let mut fut = fut; - - // Shadow the original binding so that it can't be directly accessed - // ever again. - let fut = unsafe { Pin::new_unchecked(&mut fut) }; + // Pin the future on the stack. + pin!(fut); // SAFETY: This function blocks until all futures complete, so we do not read/write the // data from futures outside of the 'scope lifetime. However, rust has no way of knowing From 18a8532f320c0236ddf2b91a87c29fca1941fff5 Mon Sep 17 00:00:00 2001 From: Zooce Date: Mon, 21 Sep 2020 13:16:17 -0700 Subject: [PATCH 002/294] Add `libudev-dev` to Ubuntu dependencies (#538) --- docs/linux_dependencies.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/linux_dependencies.md b/docs/linux_dependencies.md index a60752a961d23..0922fdfc8e599 100644 --- a/docs/linux_dependencies.md +++ b/docs/linux_dependencies.md @@ -6,7 +6,7 @@ If you don't see your distro present in the list, feel free to add the instructi ## Ubuntu 20.04 ```bash -sudo apt-get install pkg-config libx11-dev libasound2-dev +sudo apt-get install pkg-config libx11-dev libasound2-dev libudev-dev ``` ## Fedora 32 From 295e1f0a18ef579139890f33f81d4e8043ca9415 Mon Sep 17 00:00:00 2001 From: HyperLightKitsune <71573365+HyperLightKitsune@users.noreply.github.com> Date: Mon, 21 Sep 2020 16:51:38 -0400 Subject: [PATCH 003/294] use FnOnce in Commands and ChildBuilder where possible (#535) use FnOnce in Commands and ChildBuilder --- crates/bevy_ecs/src/system/commands.rs | 2 +- crates/bevy_transform/src/hierarchy/child_builder.rs | 8 ++++---- .../src/hierarchy/world_child_builder.rs | 12 +++--------- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/crates/bevy_ecs/src/system/commands.rs b/crates/bevy_ecs/src/system/commands.rs index 56e2a684f001e..be0c4570a1da2 100644 --- a/crates/bevy_ecs/src/system/commands.rs +++ b/crates/bevy_ecs/src/system/commands.rs @@ -298,7 +298,7 @@ impl Commands { commands.current_entity } - pub fn for_current_entity(&mut self, mut f: impl FnMut(Entity)) -> &mut Self { + pub fn for_current_entity(&mut self, f: impl FnOnce(Entity)) -> &mut Self { { let commands = self.commands.lock(); let current_entity = commands diff --git a/crates/bevy_transform/src/hierarchy/child_builder.rs b/crates/bevy_transform/src/hierarchy/child_builder.rs index 5bfb7afd0f1da..b3c4d5d4e6c36 100644 --- a/crates/bevy_transform/src/hierarchy/child_builder.rs +++ b/crates/bevy_transform/src/hierarchy/child_builder.rs @@ -94,7 +94,7 @@ impl<'a> ChildBuilder<'a> { self } - pub fn for_current_entity(&mut self, mut func: impl FnMut(Entity)) -> &mut Self { + pub fn for_current_entity(&mut self, func: impl FnOnce(Entity)) -> &mut Self { let current_entity = self .commands .current_entity @@ -105,13 +105,13 @@ impl<'a> ChildBuilder<'a> { } pub trait BuildChildren { - fn with_children(&mut self, f: impl FnMut(&mut ChildBuilder)) -> &mut Self; + fn with_children(&mut self, f: impl FnOnce(&mut ChildBuilder)) -> &mut Self; fn push_children(&mut self, parent: Entity, children: &[Entity]) -> &mut Self; fn insert_children(&mut self, parent: Entity, index: usize, children: &[Entity]) -> &mut Self; } impl BuildChildren for Commands { - fn with_children(&mut self, mut parent: impl FnMut(&mut ChildBuilder)) -> &mut Self { + fn with_children(&mut self, parent: impl FnOnce(&mut ChildBuilder)) -> &mut Self { { let mut commands = self.commands.lock(); let current_entity = commands.current_entity.expect("Cannot add children because the 'current entity' is not set. You should spawn an entity first."); @@ -159,7 +159,7 @@ impl BuildChildren for Commands { } impl<'a> BuildChildren for ChildBuilder<'a> { - fn with_children(&mut self, mut spawn_children: impl FnMut(&mut ChildBuilder)) -> &mut Self { + fn with_children(&mut self, spawn_children: impl FnOnce(&mut ChildBuilder)) -> &mut Self { let current_entity = self.commands.current_entity.expect("Cannot add children because the 'current entity' is not set. You should spawn an entity first."); self.commands.current_entity = None; let push_children = { diff --git a/crates/bevy_transform/src/hierarchy/world_child_builder.rs b/crates/bevy_transform/src/hierarchy/world_child_builder.rs index 4982df8aa3bde..ce12192afd9ad 100644 --- a/crates/bevy_transform/src/hierarchy/world_child_builder.rs +++ b/crates/bevy_transform/src/hierarchy/world_child_builder.rs @@ -50,14 +50,11 @@ impl<'a, 'b> WorldChildBuilder<'a, 'b> { } pub trait BuildWorldChildren { - fn with_children(&mut self, spawn_children: impl FnMut(&mut WorldChildBuilder)) -> &mut Self; + fn with_children(&mut self, spawn_children: impl FnOnce(&mut WorldChildBuilder)) -> &mut Self; } impl<'a> BuildWorldChildren for WorldBuilder<'a> { - fn with_children( - &mut self, - mut spawn_children: impl FnMut(&mut WorldChildBuilder), - ) -> &mut Self { + fn with_children(&mut self, spawn_children: impl FnOnce(&mut WorldChildBuilder)) -> &mut Self { { let current_entity = self.current_entity.expect("Cannot add children because the 'current entity' is not set. You should spawn an entity first."); let mut builder = WorldChildBuilder { @@ -72,10 +69,7 @@ impl<'a> BuildWorldChildren for WorldBuilder<'a> { } impl<'a, 'b> BuildWorldChildren for WorldChildBuilder<'a, 'b> { - fn with_children( - &mut self, - mut spawn_children: impl FnMut(&mut WorldChildBuilder), - ) -> &mut Self { + fn with_children(&mut self, spawn_children: impl FnOnce(&mut WorldChildBuilder)) -> &mut Self { let current_entity = self .world_builder .current_entity From d562063abf19dee9b9da9dee6054fb425de1e94d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Gillot-Lamure?= Date: Mon, 21 Sep 2020 22:53:17 +0200 Subject: [PATCH 004/294] Add systemd-devel for Fedora Linux dependencies (#528) It is required for the dependency crate libudev-sys (error about missing libudev.pc) --- docs/linux_dependencies.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/linux_dependencies.md b/docs/linux_dependencies.md index 0922fdfc8e599..bce08512b0a14 100644 --- a/docs/linux_dependencies.md +++ b/docs/linux_dependencies.md @@ -11,7 +11,7 @@ sudo apt-get install pkg-config libx11-dev libasound2-dev libudev-dev ## Fedora 32 ```bash -sudo dnf install gcc-c++ libX11-devel alsa-lib-devel +sudo dnf install gcc-c++ libX11-devel alsa-lib-devel systemd-devel ``` ## Arch / Manjaro @@ -70,4 +70,4 @@ At this point, projects should successfully compile but fail on execution. This 1. `find target -type f -name glslang_validator` in order to find glslang_validator in `target/debug/build/bevy-glsl-to-spirv-/out/glslang_validator`. The directory containing glslang_validator will be referenced again, so save it for later: `export OUT_DIR="$(dirname $(find target -type f -name glslang_validator))"`. 2. Running `ldd $OUT_DIR/glslang_validator` may show `libstdc++.so.6` is not found. If all dependencies are found, then bevy should work. If not, install (globally or in nix-shell) any of the results found by `nix-locate -w libstdc++.so.6`. For example purposes, consider `nixos.gcc-unwrapped`. In theory, any of the ones in `find -L /nix/store -type f -name libstdc++.so.6` will work. 3. `patchelf --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" --set-rpath /nix/store/784rh7jrfhagbkydjfrv68h9x3g4gqmk-gcc-8.3.0-lib/lib $OUT_DIR/glslang_validator` -4. Bevy should now be working properly! \ No newline at end of file +4. Bevy should now be working properly! From b5f3585d2e9093028e4c9e243545aa85dca6240e Mon Sep 17 00:00:00 2001 From: Logan Magee Date: Mon, 21 Sep 2020 12:54:47 -0800 Subject: [PATCH 005/294] Merge some imports in bevy_winit/src/lib.rs (#522) --- crates/bevy_winit/src/lib.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index 25153b034a88e..f95544cdcd949 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -14,10 +14,8 @@ use bevy_math::Vec2; use bevy_window::{ CreateWindow, CursorMoved, Window, WindowCloseRequested, WindowCreated, WindowResized, Windows, }; -use event::Event; use winit::{ - event, - event::{DeviceEvent, WindowEvent}, + event::{self, DeviceEvent, Event, WindowEvent}, event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget}, }; From 74f881f20d6c3e8588be8e523424a2be99ed6bf5 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Tue, 22 Sep 2020 00:47:38 +0200 Subject: [PATCH 006/294] Fix compilation error on wasm (#549) Fix compilation error on wasm --- .github/workflows/ci.yml | 7 +++++++ crates/bevy_tasks/src/single_threaded_task_pool.rs | 7 ++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ab83dbfb3694e..065a7e3d50d34 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,6 +55,13 @@ jobs: CARGO_INCREMENTAL: 0 RUSTFLAGS: "-C debuginfo=0 -D warnings" + - name: Check wasm + uses: actions-rs/cargo@v1 + with: + command: check + target: wasm32-unknown-unknown + override: true + clean: runs-on: ubuntu-latest steps: diff --git a/crates/bevy_tasks/src/single_threaded_task_pool.rs b/crates/bevy_tasks/src/single_threaded_task_pool.rs index a7e4c7f6999f3..8899feb9edd34 100644 --- a/crates/bevy_tasks/src/single_threaded_task_pool.rs +++ b/crates/bevy_tasks/src/single_threaded_task_pool.rs @@ -1,7 +1,6 @@ use std::{ future::Future, mem, - pin::Pin, sync::{Arc, Mutex}, }; @@ -63,10 +62,12 @@ impl TaskPool { F: FnOnce(&mut Scope<'scope, T>) + 'scope + Send, T: Send + 'static, { - let executor = async_executor::LocalExecutor::new(); + let executor = &async_executor::LocalExecutor::new(); + let executor: &'scope async_executor::LocalExecutor<'scope> = + unsafe { mem::transmute(executor) }; let mut scope = Scope { - executor: &executor, + executor, results: Vec::new(), }; From dd6f0b5e046fbf78d13b8478b5356a4e3c53e13f Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Tue, 22 Sep 2020 01:12:34 +0200 Subject: [PATCH 007/294] Option to give existing canvas element as winit window (#515) --- crates/bevy_window/Cargo.toml | 1 + crates/bevy_window/src/window.rs | 8 +++++ crates/bevy_winit/Cargo.toml | 1 + crates/bevy_winit/src/winit_windows.rs | 42 ++++++++++++++++++++------ 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/crates/bevy_window/Cargo.toml b/crates/bevy_window/Cargo.toml index 5ee03dc701680..2323e9f0ce2b2 100644 --- a/crates/bevy_window/Cargo.toml +++ b/crates/bevy_window/Cargo.toml @@ -24,3 +24,4 @@ uuid = { version = "0.8", features = ["v4", "serde"] } [target.'cfg(target_arch = "wasm32")'.dependencies] uuid = { version = "0.8", features = ["wasm-bindgen"] } +web-sys = "0.3" diff --git a/crates/bevy_window/src/window.rs b/crates/bevy_window/src/window.rs index 9cfcdf581fbff..b5cdf9aede243 100644 --- a/crates/bevy_window/src/window.rs +++ b/crates/bevy_window/src/window.rs @@ -40,6 +40,8 @@ pub struct Window { pub vsync: bool, pub resizable: bool, pub mode: WindowMode, + #[cfg(target_arch = "wasm32")] + pub canvas: Option, } /// Defines the way a window is displayed @@ -64,6 +66,8 @@ impl Window { vsync: window_descriptor.vsync, resizable: window_descriptor.resizable, mode: window_descriptor.mode, + #[cfg(target_arch = "wasm32")] + canvas: window_descriptor.canvas.clone(), } } } @@ -77,6 +81,8 @@ pub struct WindowDescriptor { pub vsync: bool, pub resizable: bool, pub mode: WindowMode, + #[cfg(target_arch = "wasm32")] + pub canvas: Option, // this is a manual implementation of the non exhaustive pattern, // especially made to allow ..Default::default() @@ -93,6 +99,8 @@ impl Default for WindowDescriptor { vsync: true, resizable: true, mode: WindowMode::Windowed, + #[cfg(target_arch = "wasm32")] + canvas: None, __non_exhaustive: (), } } diff --git a/crates/bevy_winit/Cargo.toml b/crates/bevy_winit/Cargo.toml index ac286e0300706..466acb2276b08 100644 --- a/crates/bevy_winit/Cargo.toml +++ b/crates/bevy_winit/Cargo.toml @@ -31,4 +31,5 @@ log = { version = "0.4", features = ["release_max_level_info"] } [target.'cfg(target_arch = "wasm32")'.dependencies] winit = { version = "0.22.2", package = "cart-tmp-winit", features = ["web-sys"] } +wasm-bindgen = { version = "0.2" } web-sys = "0.3" diff --git a/crates/bevy_winit/src/winit_windows.rs b/crates/bevy_winit/src/winit_windows.rs index 9ec2c88e15fa2..93a21dfe05cdf 100644 --- a/crates/bevy_winit/src/winit_windows.rs +++ b/crates/bevy_winit/src/winit_windows.rs @@ -38,10 +38,30 @@ impl WinitWindows { .with_resizable(window.resizable), }; - let winit_window = winit_window_builder - .with_title(&window.title) - .build(&event_loop) - .unwrap(); + #[allow(unused_mut)] + let mut winit_window_builder = winit_window_builder.with_title(&window.title); + + #[cfg(target_arch = "wasm32")] + { + use wasm_bindgen::JsCast; + use winit::platform::web::WindowBuilderExtWebSys; + + if let Some(selector) = &window.canvas { + let window = web_sys::window().unwrap(); + let document = window.document().unwrap(); + let canvas = document + .query_selector(&selector) + .expect("Cannot query for canvas element"); + if let Some(canvas) = canvas { + let canvas = canvas.dyn_into::().ok(); + winit_window_builder = winit_window_builder.with_canvas(canvas); + } else { + panic!("Cannot find element: {}", selector); + } + } + } + + let winit_window = winit_window_builder.build(&event_loop).unwrap(); self.window_id_to_winit.insert(window.id, winit_window.id()); self.winit_to_window_id.insert(winit_window.id(), window.id); @@ -50,14 +70,16 @@ impl WinitWindows { { use winit::platform::web::WindowExtWebSys; - let canvas = winit_window.canvas(); + if window.canvas.is_none() { + let canvas = winit_window.canvas(); - let window = web_sys::window().unwrap(); - let document = window.document().unwrap(); - let body = document.body().unwrap(); + let window = web_sys::window().unwrap(); + let document = window.document().unwrap(); + let body = document.body().unwrap(); - body.append_child(&canvas) - .expect("Append canvas to HTML body"); + body.append_child(&canvas) + .expect("Append canvas to HTML body"); + } } self.windows.insert(winit_window.id(), winit_window); From 028a22b12936797d3a7ca1ad5d715a98bdf27907 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Mon, 21 Sep 2020 20:23:09 -0700 Subject: [PATCH 008/294] asset: use bevy_tasks in AssetServer (#550) --- crates/bevy_app/Cargo.toml | 1 - crates/bevy_app/src/app.rs | 10 +- crates/bevy_app/src/lib.rs | 2 - crates/bevy_asset/Cargo.toml | 1 + crates/bevy_asset/src/asset_server.rs | 167 ++++++------------ crates/bevy_asset/src/lib.rs | 11 +- crates/bevy_core/Cargo.toml | 3 + crates/bevy_core/src/lib.rs | 10 +- .../src/task_pool_options.rs | 6 +- .../src/schedule/parallel_executor.rs | 6 + crates/bevy_tasks/src/lib.rs | 4 +- crates/bevy_tasks/src/task.rs | 5 + crates/bevy_tasks/src/task_pool.rs | 9 +- crates/bevy_tasks/src/usages.rs | 4 +- crates/bevy_ui/src/widget/text.rs | 75 ++++---- examples/app/thread_pool_resources.rs | 1 - 16 files changed, 141 insertions(+), 174 deletions(-) rename crates/{bevy_app => bevy_core}/src/task_pool_options.rs (97%) diff --git a/crates/bevy_app/Cargo.toml b/crates/bevy_app/Cargo.toml index b50acf709ea7a..49066100019c5 100644 --- a/crates/bevy_app/Cargo.toml +++ b/crates/bevy_app/Cargo.toml @@ -19,7 +19,6 @@ dynamic_plugins = ["libloading"] # bevy bevy_derive = { path = "../bevy_derive", version = "0.2.1" } bevy_ecs = { path = "../bevy_ecs", version = "0.2.1" } -bevy_tasks = { path = "../bevy_tasks", version = "0.2.1" } bevy_math = { path = "../bevy_math", version = "0.2.1" } # other diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 76798d06809f2..3a6bb066cfe8e 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -1,4 +1,4 @@ -use crate::{app_builder::AppBuilder, DefaultTaskPoolOptions}; +use crate::app_builder::AppBuilder; use bevy_ecs::{ParallelExecutor, Resources, Schedule, World}; #[allow(clippy::needless_doctest_main)] @@ -64,20 +64,16 @@ impl App { } pub fn run(mut self) { - // Setup the default bevy task pools - self.resources - .get_cloned::() - .unwrap_or_else(DefaultTaskPoolOptions::default) - .create_default_pools(&mut self.resources); - self.startup_schedule .initialize(&mut self.world, &mut self.resources); + self.startup_executor.initialize(&mut self.resources); self.startup_executor.run( &mut self.startup_schedule, &mut self.world, &mut self.resources, ); + self.executor.initialize(&mut self.resources); let runner = std::mem::replace(&mut self.runner, Box::new(run_once)); (runner)(self); } diff --git a/crates/bevy_app/src/lib.rs b/crates/bevy_app/src/lib.rs index a409edbfafae3..d2e241616b55d 100644 --- a/crates/bevy_app/src/lib.rs +++ b/crates/bevy_app/src/lib.rs @@ -8,7 +8,6 @@ mod app_builder; mod event; mod plugin; mod schedule_runner; -mod task_pool_options; pub use app::*; pub use app_builder::*; @@ -16,7 +15,6 @@ pub use bevy_derive::DynamicPlugin; pub use event::*; pub use plugin::*; pub use schedule_runner::*; -pub use task_pool_options::*; pub mod prelude { pub use crate::{ diff --git a/crates/bevy_asset/Cargo.toml b/crates/bevy_asset/Cargo.toml index a74abe54a8d1c..1a183273b3163 100644 --- a/crates/bevy_asset/Cargo.toml +++ b/crates/bevy_asset/Cargo.toml @@ -20,6 +20,7 @@ filesystem_watcher = ["notify"] # bevy bevy_app = { path = "../bevy_app", version = "0.2.1" } bevy_ecs = { path = "../bevy_ecs", version = "0.2.1" } +bevy_tasks = { path = "../bevy_tasks", version = "0.2.1" } bevy_type_registry = { path = "../bevy_type_registry", version = "0.2.1" } bevy_property = { path = "../bevy_property", version = "0.2.1" } bevy_utils = { path = "../bevy_utils", version = "0.2.1" } diff --git a/crates/bevy_asset/src/asset_server.rs b/crates/bevy_asset/src/asset_server.rs index e473476acf0a7..210343a2fd55b 100644 --- a/crates/bevy_asset/src/asset_server.rs +++ b/crates/bevy_asset/src/asset_server.rs @@ -4,6 +4,7 @@ use crate::{ }; use anyhow::Result; use bevy_ecs::{Res, Resource, Resources}; +use bevy_tasks::TaskPool; use bevy_utils::{HashMap, HashSet}; use crossbeam_channel::TryRecvError; use parking_lot::RwLock; @@ -11,7 +12,6 @@ use std::{ env, fs, io, path::{Path, PathBuf}, sync::Arc, - thread, }; use thiserror::Error; @@ -38,12 +38,6 @@ pub enum AssetServerError { AssetWatchError { path: PathBuf }, } -struct LoaderThread { - // NOTE: these must remain private. the LoaderThread Arc counters are used to determine thread liveness - // if there is one reference, the loader thread is dead. if there are two references, the loader thread is active - requests: Arc>>, -} - /// Info about a specific asset, such as its path and its current load state #[derive(Clone, Debug)] pub struct AssetInfo { @@ -73,11 +67,10 @@ impl LoadState { /// Loads assets from the filesystem on background threads pub struct AssetServer { asset_folders: RwLock>, - loader_threads: RwLock>, - max_loader_threads: usize, asset_handlers: Arc>>>, // TODO: this is a hack to enable retrieving generic AssetLoaders. there must be a better way! loaders: Vec, + task_pool: TaskPool, extension_to_handler_index: HashMap, extension_to_loader_index: HashMap, asset_info: RwLock>, @@ -86,25 +79,22 @@ pub struct AssetServer { filesystem_watcher: Arc>>, } -impl Default for AssetServer { - fn default() -> Self { +impl AssetServer { + pub fn new(task_pool: TaskPool) -> Self { AssetServer { - #[cfg(feature = "filesystem_watcher")] - filesystem_watcher: Arc::new(RwLock::new(None)), - max_loader_threads: 4, asset_folders: Default::default(), - loader_threads: Default::default(), asset_handlers: Default::default(), loaders: Default::default(), extension_to_handler_index: Default::default(), extension_to_loader_index: Default::default(), asset_info_paths: Default::default(), asset_info: Default::default(), + task_pool, + #[cfg(feature = "filesystem_watcher")] + filesystem_watcher: Arc::new(RwLock::new(None)), } } -} -impl AssetServer { pub fn add_handler(&mut self, asset_handler: T) where T: AssetLoadRequestHandler, @@ -183,46 +173,6 @@ impl AssetServer { Ok(()) } - #[cfg(feature = "filesystem_watcher")] - pub fn filesystem_watcher_system(asset_server: Res) { - let mut changed = HashSet::default(); - - loop { - let result = { - let rwlock_guard = asset_server.filesystem_watcher.read(); - if let Some(filesystem_watcher) = rwlock_guard.as_ref() { - filesystem_watcher.receiver.try_recv() - } else { - break; - } - }; - let event = match result { - Ok(result) => result.unwrap(), - Err(TryRecvError::Empty) => break, - Err(TryRecvError::Disconnected) => panic!("FilesystemWatcher disconnected"), - }; - if let notify::event::Event { - kind: notify::event::EventKind::Modify(_), - paths, - .. - } = event - { - for path in paths.iter() { - if !changed.contains(path) { - let root_path = asset_server.get_root_path().unwrap(); - let relative_path = path.strip_prefix(root_path).unwrap(); - match asset_server.load_untyped(relative_path) { - Ok(_) => {} - Err(AssetServerError::AssetLoadError(error)) => panic!("{:?}", error), - Err(_) => {} - } - } - } - changed.extend(paths); - } - } - } - fn get_root_path(&self) -> Result { if let Ok(manifest_dir) = env::var("CARGO_MANIFEST_DIR") { Ok(PathBuf::from(manifest_dir)) @@ -315,12 +265,21 @@ impl AssetServer { } }; - self.send_request_to_loader_thread(LoadRequest { + let load_request = LoadRequest { handle_id, path: path.to_owned(), handler_index: *index, version: new_version, - }); + }; + + let asset_handlers = self.asset_handlers.clone(); + self.task_pool + .spawn(async move { + let handlers = asset_handlers.read(); + let request_handler = &handlers[load_request.handler_index]; + request_handler.handle_request(&load_request); + }) + .detach(); // TODO: watching each asset explicitly is a simpler implementation, its possible it would be more efficient to watch // folders instead (when possible) @@ -370,56 +329,6 @@ impl AssetServer { Some(load_state) } - fn send_request_to_loader_thread(&self, load_request: LoadRequest) { - // NOTE: This lock makes the call to Arc::strong_count safe. Removing (or reordering) it could result in undefined behavior - let mut loader_threads = self.loader_threads.write(); - if loader_threads.len() < self.max_loader_threads { - let loader_thread = LoaderThread { - requests: Arc::new(RwLock::new(vec![load_request])), - }; - let requests = loader_thread.requests.clone(); - loader_threads.push(loader_thread); - Self::start_thread(self.asset_handlers.clone(), requests); - } else { - let most_free_thread = loader_threads - .iter() - .min_by_key(|l| l.requests.read().len()) - .unwrap(); - let mut requests = most_free_thread.requests.write(); - requests.push(load_request); - // if most free thread only has one reference, the thread as spun down. if so, we need to spin it back up! - if Arc::strong_count(&most_free_thread.requests) == 1 { - Self::start_thread( - self.asset_handlers.clone(), - most_free_thread.requests.clone(), - ); - } - } - } - - fn start_thread( - request_handlers: Arc>>>, - requests: Arc>>, - ) { - thread::spawn(move || { - loop { - let request = { - let mut current_requests = requests.write(); - if current_requests.len() == 0 { - // if there are no requests, spin down the thread - break; - } - - current_requests.pop().unwrap() - }; - - let handlers = request_handlers.read(); - let request_handler = &handlers[request.handler_index]; - request_handler.handle_request(&request); - } - }); - } - fn load_assets_in_folder_recursive( &self, path: &Path, @@ -456,3 +365,43 @@ impl AssetServer { Ok(handle_ids) } } + +#[cfg(feature = "filesystem_watcher")] +pub fn filesystem_watcher_system(asset_server: Res) { + let mut changed = HashSet::default(); + + loop { + let result = { + let rwlock_guard = asset_server.filesystem_watcher.read(); + if let Some(filesystem_watcher) = rwlock_guard.as_ref() { + filesystem_watcher.receiver.try_recv() + } else { + break; + } + }; + let event = match result { + Ok(result) => result.unwrap(), + Err(TryRecvError::Empty) => break, + Err(TryRecvError::Disconnected) => panic!("FilesystemWatcher disconnected"), + }; + if let notify::event::Event { + kind: notify::event::EventKind::Modify(_), + paths, + .. + } = event + { + for path in paths.iter() { + if !changed.contains(path) { + let root_path = asset_server.get_root_path().unwrap(); + let relative_path = path.strip_prefix(root_path).unwrap(); + match asset_server.load_untyped(relative_path) { + Ok(_) => {} + Err(AssetServerError::AssetLoadError(error)) => panic!("{:?}", error), + Err(_) => {} + } + } + } + changed.extend(paths); + } + } +} diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index b2751a37fee1e..01186472482f2 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -8,6 +8,7 @@ mod loader; pub use asset_server::*; pub use assets::*; +use bevy_tasks::IoTaskPool; pub use handle::*; pub use load_request::*; pub use loader::*; @@ -33,15 +34,21 @@ pub struct AssetPlugin; impl Plugin for AssetPlugin { fn build(&self, app: &mut AppBuilder) { + let task_pool = app + .resources() + .get::() + .expect("IoTaskPool resource not found") + .0 + .clone(); app.add_stage_before(bevy_app::stage::PRE_UPDATE, stage::LOAD_ASSETS) .add_stage_after(bevy_app::stage::POST_UPDATE, stage::ASSET_EVENTS) - .init_resource::() + .add_resource(AssetServer::new(task_pool)) .register_property::(); #[cfg(feature = "filesystem_watcher")] app.add_system_to_stage( stage::LOAD_ASSETS, - AssetServer::filesystem_watcher_system.system(), + asset_server::filesystem_watcher_system.system(), ); } } diff --git a/crates/bevy_core/Cargo.toml b/crates/bevy_core/Cargo.toml index 061e8050797b3..79f9b0a332586 100644 --- a/crates/bevy_core/Cargo.toml +++ b/crates/bevy_core/Cargo.toml @@ -26,6 +26,9 @@ bevy_property = { path = "../bevy_property", version = "0.2.1" } bevy_type_registry = { path = "../bevy_type_registry", version = "0.2.1" } bevy_math = { path = "../bevy_math", version = "0.2.1" } bevy_utils = { path = "../bevy_utils", version = "0.2.1" } +bevy_tasks = { path = "../bevy_tasks", version = "0.2.1" } + +log = { version = "0.4", features = ["release_max_level_info"] } [target.'cfg(target_arch = "wasm32")'.dependencies] instant = "0.1.6" diff --git a/crates/bevy_core/src/lib.rs b/crates/bevy_core/src/lib.rs index c1e8f7169f708..be901f68140a0 100644 --- a/crates/bevy_core/src/lib.rs +++ b/crates/bevy_core/src/lib.rs @@ -1,15 +1,17 @@ mod bytes; mod float_ord; mod label; +mod task_pool_options; mod time; pub use bytes::*; pub use float_ord::*; pub use label::*; +pub use task_pool_options::DefaultTaskPoolOptions; pub use time::*; pub mod prelude { - pub use crate::{EntityLabels, Labels, Time, Timer}; + pub use crate::{DefaultTaskPoolOptions, EntityLabels, Labels, Time, Timer}; } use bevy_app::prelude::*; @@ -23,6 +25,12 @@ pub struct CorePlugin; impl Plugin for CorePlugin { fn build(&self, app: &mut AppBuilder) { + // Setup the default bevy task pools + app.resources_mut() + .get_cloned::() + .unwrap_or_else(DefaultTaskPoolOptions::default) + .create_default_pools(app.resources_mut()); + app.init_resource::