From 1cc838979af8b76d1175dd89c2736ffc963ee1ed Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Thu, 23 Jan 2025 09:21:22 +1100 Subject: [PATCH 1/5] Initial Commit --- crates/bevy_app/Cargo.toml | 17 +- crates/bevy_app/src/app.rs | 5 +- crates/bevy_app/src/panic_handler.rs | 13 +- crates/bevy_app/src/schedule_runner.rs | 103 +++++----- crates/bevy_app/src/task_pool_plugin.rs | 63 +++--- crates/bevy_asset/Cargo.toml | 7 + crates/bevy_audio/Cargo.toml | 4 + crates/bevy_internal/Cargo.toml | 12 ++ crates/bevy_log/Cargo.toml | 4 + crates/bevy_platform_support/Cargo.toml | 12 +- crates/bevy_platform_support/src/sync.rs | 25 ++- crates/bevy_platform_support/src/time.rs | 194 ------------------ .../src/time/fallback.rs | 176 ++++++++++++++++ crates/bevy_platform_support/src/time/mod.rs | 15 ++ crates/bevy_render/Cargo.toml | 10 + crates/bevy_tasks/Cargo.toml | 45 +++- crates/bevy_tasks/src/executor.rs | 22 +- crates/bevy_tasks/src/lib.rs | 130 +++++++----- .../src/single_threaded_task_pool.rs | 41 ++-- crates/bevy_tasks/src/usages.rs | 2 +- crates/bevy_winit/Cargo.toml | 10 + 21 files changed, 516 insertions(+), 394 deletions(-) delete mode 100644 crates/bevy_platform_support/src/time.rs create mode 100644 crates/bevy_platform_support/src/time/fallback.rs create mode 100644 crates/bevy_platform_support/src/time/mod.rs diff --git a/crates/bevy_app/Cargo.toml b/crates/bevy_app/Cargo.toml index e4fb9748c7ebc..424889dd64a32 100644 --- a/crates/bevy_app/Cargo.toml +++ b/crates/bevy_app/Cargo.toml @@ -69,6 +69,16 @@ portable-atomic = [ "bevy_reflect?/portable-atomic", ] +## Enables use of browser APIs. +## Note this is currently only applicable on `wasm32` architectures. +browser = [ + "bevy_platform_support/browser", + "bevy_tasks?/browser", + "dep:wasm-bindgen", + "dep:web-sys", + "dep:console_error_panic_hook", +] + [dependencies] # bevy bevy_derive = { path = "../bevy_derive", version = "0.16.0-dev" } @@ -86,14 +96,15 @@ thiserror = { version = "2", default-features = false } variadics_please = "1.1" tracing = { version = "0.1", default-features = false, optional = true } log = { version = "0.4", default-features = false } +cfg-if = "1.0.0" [target.'cfg(any(unix, windows))'.dependencies] ctrlc = { version = "3.4.4", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] -wasm-bindgen = { version = "0.2" } -web-sys = { version = "0.3", features = ["Window"] } -console_error_panic_hook = "0.1.6" +wasm-bindgen = { version = "0.2", optional = true } +web-sys = { version = "0.3", features = ["Window"], optional = true } +console_error_panic_hook = { version = "0.1.6", optional = true } [dev-dependencies] crossbeam-channel = "0.5.0" diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 799c936ec00b7..6e35bdd988671 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -1330,7 +1330,10 @@ type RunnerFn = Box AppExit>; fn run_once(mut app: App) -> AppExit { while app.plugins_state() == PluginsState::Adding { - #[cfg(all(not(target_arch = "wasm32"), feature = "bevy_tasks"))] + #[cfg(all( + not(all(target_arch = "wasm32", feature = "browser")), + feature = "bevy_tasks" + ))] bevy_tasks::tick_global_task_pools_on_main_thread(); } app.finish(); diff --git a/crates/bevy_app/src/panic_handler.rs b/crates/bevy_app/src/panic_handler.rs index 56d66da7281b2..010f311c219b6 100644 --- a/crates/bevy_app/src/panic_handler.rs +++ b/crates/bevy_app/src/panic_handler.rs @@ -39,13 +39,12 @@ pub struct PanicHandlerPlugin; impl Plugin for PanicHandlerPlugin { fn build(&self, _app: &mut App) { - #[cfg(target_arch = "wasm32")] - { - console_error_panic_hook::set_once(); - } - #[cfg(not(target_arch = "wasm32"))] - { - // Use the default target panic hook - Do nothing. + cfg_if::cfg_if! { + if #[cfg(all(target_arch = "wasm32", feature = "browser"))] { + console_error_panic_hook::set_once(); + } else { + // Use the default target panic hook - Do nothing. + } } } } diff --git a/crates/bevy_app/src/schedule_runner.rs b/crates/bevy_app/src/schedule_runner.rs index 2a0e1f966a49e..ba69779645187 100644 --- a/crates/bevy_app/src/schedule_runner.rs +++ b/crates/bevy_app/src/schedule_runner.rs @@ -6,7 +6,7 @@ use crate::{ use bevy_platform_support::time::Instant; use core::time::Duration; -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", feature = "browser"))] use { alloc::{boxed::Box, rc::Rc}, core::cell::RefCell, @@ -77,7 +77,10 @@ impl Plugin for ScheduleRunnerPlugin { let plugins_state = app.plugins_state(); if plugins_state != PluginsState::Cleaned { while app.plugins_state() == PluginsState::Adding { - #[cfg(all(not(target_arch = "wasm32"), feature = "bevy_tasks"))] + #[cfg(all( + not(all(target_arch = "wasm32", feature = "browser")), + feature = "bevy_tasks" + ))] bevy_tasks::tick_global_task_pools_on_main_thread(); } app.finish(); @@ -118,58 +121,56 @@ impl Plugin for ScheduleRunnerPlugin { Ok(None) }; - #[cfg(not(target_arch = "wasm32"))] - { - loop { - match tick(&mut app, wait) { - Ok(Some(_delay)) => { - #[cfg(feature = "std")] - std::thread::sleep(_delay); - } - Ok(None) => continue, - Err(exit) => return exit, + cfg_if::cfg_if! { + if #[cfg(all(target_arch = "wasm32", feature = "browser"))] { + fn set_timeout(callback: &Closure, dur: Duration) { + web_sys::window() + .unwrap() + .set_timeout_with_callback_and_timeout_and_arguments_0( + callback.as_ref().unchecked_ref(), + dur.as_millis() as i32, + ) + .expect("Should register `setTimeout`."); } - } - } - - #[cfg(target_arch = "wasm32")] - { - fn set_timeout(callback: &Closure, dur: Duration) { - web_sys::window() - .unwrap() - .set_timeout_with_callback_and_timeout_and_arguments_0( - callback.as_ref().unchecked_ref(), - dur.as_millis() as i32, - ) - .expect("Should register `setTimeout`."); - } - let asap = Duration::from_millis(1); - - let exit = Rc::new(RefCell::new(AppExit::Success)); - let closure_exit = exit.clone(); - - let mut app = Rc::new(app); - let moved_tick_closure = Rc::new(RefCell::new(None)); - let base_tick_closure = moved_tick_closure.clone(); - - let tick_app = move || { - let app = Rc::get_mut(&mut app).unwrap(); - let delay = tick(app, wait); - match delay { - Ok(delay) => set_timeout( - moved_tick_closure.borrow().as_ref().unwrap(), - delay.unwrap_or(asap), - ), - Err(code) => { - closure_exit.replace(code); + let asap = Duration::from_millis(1); + + let exit = Rc::new(RefCell::new(AppExit::Success)); + let closure_exit = exit.clone(); + + let mut app = Rc::new(app); + let moved_tick_closure = Rc::new(RefCell::new(None)); + let base_tick_closure = moved_tick_closure.clone(); + + let tick_app = move || { + let app = Rc::get_mut(&mut app).unwrap(); + let delay = tick(app, wait); + match delay { + Ok(delay) => set_timeout( + moved_tick_closure.borrow().as_ref().unwrap(), + delay.unwrap_or(asap), + ), + Err(code) => { + closure_exit.replace(code); + } + } + }; + *base_tick_closure.borrow_mut() = + Some(Closure::wrap(Box::new(tick_app) as Box)); + set_timeout(base_tick_closure.borrow().as_ref().unwrap(), asap); + + exit.take() + } else { + loop { + match tick(&mut app, wait) { + Ok(Some(_delay)) => { + #[cfg(feature = "std")] + std::thread::sleep(_delay); + } + Ok(None) => continue, + Err(exit) => return exit, } } - }; - *base_tick_closure.borrow_mut() = - Some(Closure::wrap(Box::new(tick_app) as Box)); - set_timeout(base_tick_closure.borrow().as_ref().unwrap(), asap); - - exit.take() + } } } } diff --git a/crates/bevy_app/src/task_pool_plugin.rs b/crates/bevy_app/src/task_pool_plugin.rs index d2146d9a65a46..12eb36e963f9d 100644 --- a/crates/bevy_app/src/task_pool_plugin.rs +++ b/crates/bevy_app/src/task_pool_plugin.rs @@ -14,11 +14,20 @@ use bevy_tasks::{AsyncComputeTaskPool, ComputeTaskPool, IoTaskPool, TaskPoolBuil use core::{fmt::Debug, marker::PhantomData}; use log::trace; -#[cfg(not(target_arch = "wasm32"))] -use {crate::Last, bevy_ecs::prelude::NonSend}; - -#[cfg(not(target_arch = "wasm32"))] -use bevy_tasks::tick_global_task_pools_on_main_thread; +cfg_if::cfg_if! { + if #[cfg(not(all(target_arch = "wasm32", feature = "browser")))] { + use {crate::Last, bevy_ecs::prelude::NonSend, bevy_tasks::tick_global_task_pools_on_main_thread}; + + /// A system used to check and advanced our task pools. + /// + /// Calls [`tick_global_task_pools_on_main_thread`], + /// and uses [`NonSendMarker`] to ensure that this system runs on the main thread + #[cfg(not(target_arch = "wasm32"))] + fn tick_global_task_pools(_main_thread_marker: Option>) { + tick_global_task_pools_on_main_thread(); + } + } +} /// Setup of default task pools: [`AsyncComputeTaskPool`], [`ComputeTaskPool`], [`IoTaskPool`]. #[derive(Default)] @@ -32,22 +41,13 @@ impl Plugin for TaskPoolPlugin { // Setup the default bevy task pools self.task_pool_options.create_default_pools(); - #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(all(target_arch = "wasm32", feature = "browser")))] _app.add_systems(Last, tick_global_task_pools); } } /// A dummy type that is [`!Send`](Send), to force systems to run on the main thread. pub struct NonSendMarker(PhantomData<*mut ()>); -/// A system used to check and advanced our task pools. -/// -/// Calls [`tick_global_task_pools_on_main_thread`], -/// and uses [`NonSendMarker`] to ensure that this system runs on the main thread -#[cfg(not(target_arch = "wasm32"))] -fn tick_global_task_pools(_main_thread_marker: Option>) { - tick_global_task_pools_on_main_thread(); -} - /// Defines a simple way to determine how many threads to use given the number of remaining cores /// and number of total cores #[derive(Clone)] @@ -184,20 +184,21 @@ impl TaskPoolOptions { remaining_threads = remaining_threads.saturating_sub(io_threads); IoTaskPool::get_or_init(|| { - #[cfg_attr(target_arch = "wasm32", expect(unused_mut))] - let mut builder = TaskPoolBuilder::default() + let builder = TaskPoolBuilder::default() .num_threads(io_threads) .thread_name("IO Task Pool".to_string()); - #[cfg(not(target_arch = "wasm32"))] - { + #[cfg(not(all(target_arch = "wasm32", feature = "browser")))] + let builder = { + let mut builder = builder; if let Some(f) = self.io.on_thread_spawn.clone() { builder = builder.on_thread_spawn(move || f()); } if let Some(f) = self.io.on_thread_destroy.clone() { builder = builder.on_thread_destroy(move || f()); } - } + builder + }; builder.build() }); @@ -213,20 +214,21 @@ impl TaskPoolOptions { remaining_threads = remaining_threads.saturating_sub(async_compute_threads); AsyncComputeTaskPool::get_or_init(|| { - #[cfg_attr(target_arch = "wasm32", expect(unused_mut))] - let mut builder = TaskPoolBuilder::default() + let builder = TaskPoolBuilder::default() .num_threads(async_compute_threads) .thread_name("Async Compute Task Pool".to_string()); - #[cfg(not(target_arch = "wasm32"))] - { + #[cfg(not(all(target_arch = "wasm32", feature = "browser")))] + let builder = { + let mut builder = builder; if let Some(f) = self.async_compute.on_thread_spawn.clone() { builder = builder.on_thread_spawn(move || f()); } if let Some(f) = self.async_compute.on_thread_destroy.clone() { builder = builder.on_thread_destroy(move || f()); } - } + builder + }; builder.build() }); @@ -242,20 +244,21 @@ impl TaskPoolOptions { trace!("Compute Threads: {}", compute_threads); ComputeTaskPool::get_or_init(|| { - #[cfg_attr(target_arch = "wasm32", expect(unused_mut))] - let mut builder = TaskPoolBuilder::default() + let builder = TaskPoolBuilder::default() .num_threads(compute_threads) .thread_name("Compute Task Pool".to_string()); - #[cfg(not(target_arch = "wasm32"))] - { + #[cfg(not(all(target_arch = "wasm32", feature = "browser")))] + let builder = { + let mut builder = builder; if let Some(f) = self.compute.on_thread_spawn.clone() { builder = builder.on_thread_spawn(move || f()); } if let Some(f) = self.compute.on_thread_destroy.clone() { builder = builder.on_thread_destroy(move || f()); } - } + builder + }; builder.build() }); diff --git a/crates/bevy_asset/Cargo.toml b/crates/bevy_asset/Cargo.toml index 5ae415c1f123d..bd38bcfef659f 100644 --- a/crates/bevy_asset/Cargo.toml +++ b/crates/bevy_asset/Cargo.toml @@ -61,6 +61,13 @@ web-sys = { version = "0.3", features = [ ] } wasm-bindgen-futures = "0.4" js-sys = "0.3" +# TODO: Assuming all wasm builds are for the browser. Require `no_std` support to break assumption. +bevy_app = { path = "../bevy_app", version = "0.16.0-dev", default-features = false, features = [ + "browser", +] } +bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev", default-features = false, features = [ + "browser", +] } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] notify-debouncer-full = { version = "0.4.0", optional = true } diff --git a/crates/bevy_audio/Cargo.toml b/crates/bevy_audio/Cargo.toml index 4d7967977e78f..4c582f1112d64 100644 --- a/crates/bevy_audio/Cargo.toml +++ b/crates/bevy_audio/Cargo.toml @@ -31,6 +31,10 @@ cpal = { version = "0.15", optional = true } rodio = { version = "0.20", default-features = false, features = [ "wasm-bindgen", ] } +# TODO: Assuming all wasm builds are for the browser. Require `no_std` support to break assumption. +bevy_app = { path = "../bevy_app", version = "0.16.0-dev", default-features = false, features = [ + "browser", +] } [features] mp3 = ["rodio/mp3"] diff --git a/crates/bevy_internal/Cargo.toml b/crates/bevy_internal/Cargo.toml index fe80dbffc5661..101b7b1922ade 100644 --- a/crates/bevy_internal/Cargo.toml +++ b/crates/bevy_internal/Cargo.toml @@ -309,6 +309,18 @@ bevy_text = { path = "../bevy_text", optional = true, version = "0.16.0-dev" } bevy_ui = { path = "../bevy_ui", optional = true, version = "0.16.0-dev" } bevy_winit = { path = "../bevy_winit", optional = true, version = "0.16.0-dev" } +[target.'cfg(target_arch = "wasm32")'.dependencies] +# TODO: Assuming all wasm builds are for the browser. Require `no_std` support to break assumption. +bevy_app = { path = "../bevy_app", version = "0.16.0-dev", default-features = false, features = [ + "browser", +] } +bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev", default-features = false, features = [ + "browser", +] } +bevy_platform_support = { path = "../bevy_platform_support", version = "0.16.0-dev", default-features = false, features = [ + "browser", +] } + [lints] workspace = true diff --git a/crates/bevy_log/Cargo.toml b/crates/bevy_log/Cargo.toml index 9a982b4209ced..ccd4f76811ad7 100644 --- a/crates/bevy_log/Cargo.toml +++ b/crates/bevy_log/Cargo.toml @@ -38,6 +38,10 @@ android_log-sys = "0.3.0" [target.'cfg(target_arch = "wasm32")'.dependencies] tracing-wasm = "0.2.1" +# TODO: Assuming all wasm builds are for the browser. Require `no_std` support to break assumption. +bevy_app = { path = "../bevy_app", version = "0.16.0-dev", default-features = false, features = [ + "browser", +] } [target.'cfg(target_os = "ios")'.dependencies] tracing-oslog = "0.2" diff --git a/crates/bevy_platform_support/Cargo.toml b/crates/bevy_platform_support/Cargo.toml index 91a7f4f6ca77f..4bbe4d05d6e70 100644 --- a/crates/bevy_platform_support/Cargo.toml +++ b/crates/bevy_platform_support/Cargo.toml @@ -23,6 +23,7 @@ std = [ "portable-atomic-util?/std", ] +## Allows access to the `alloc` crate. alloc = ["portable-atomic-util?/alloc"] ## `critical-section` provides the building blocks for synchronization primitives @@ -33,7 +34,12 @@ critical-section = ["dep:critical-section", "portable-atomic?/critical-section"] ## operations, even on targets without native support. portable-atomic = ["dep:portable-atomic", "dep:portable-atomic-util"] +## Enables use of browser APIs. +## Note this is currently only applicable on `wasm32` architectures. +browser = ["dep:web-time", "dep:getrandom"] + [dependencies] +cfg-if = "1.0.0" critical-section = { version = "1.2.0", default-features = false, optional = true } portable-atomic = { version = "1", default-features = false, features = [ "fallback", @@ -41,8 +47,10 @@ portable-atomic = { version = "1", default-features = false, features = [ portable-atomic-util = { version = "0.2.4", default-features = false, optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] -web-time = { version = "1.1", default-features = false } -getrandom = { version = "0.2.0", default-features = false, features = ["js"] } +web-time = { version = "1.1", default-features = false, optional = true } +getrandom = { version = "0.2.0", default-features = false, optional = true, features = [ + "js", +] } [lints] workspace = true diff --git a/crates/bevy_platform_support/src/sync.rs b/crates/bevy_platform_support/src/sync.rs index 8fd47989da4be..011ffe1fa22e9 100644 --- a/crates/bevy_platform_support/src/sync.rs +++ b/crates/bevy_platform_support/src/sync.rs @@ -16,15 +16,20 @@ pub mod atomic { AtomicU32, AtomicU64, AtomicU8, AtomicUsize, Ordering, }; - #[cfg(not(feature = "portable-atomic"))] - use core::sync::atomic; - - #[cfg(feature = "portable-atomic")] - use portable_atomic as atomic; + cfg_if::cfg_if! { + if #[cfg(feature = "portable-atomic")] { + use portable_atomic as atomic; + } else { + use core::sync::atomic; + } + } } -#[cfg(all(feature = "alloc", feature = "portable-atomic"))] -use portable_atomic_util as arc; - -#[cfg(all(feature = "alloc", not(feature = "portable-atomic")))] -use alloc::sync as arc; +#[cfg(feature = "alloc")] +cfg_if::cfg_if! { + if #[cfg(feature = "portable-atomic")] { + use portable_atomic_util as arc; + } else { + use alloc::sync as arc; + } +} diff --git a/crates/bevy_platform_support/src/time.rs b/crates/bevy_platform_support/src/time.rs deleted file mode 100644 index 5bf7678fd1e48..0000000000000 --- a/crates/bevy_platform_support/src/time.rs +++ /dev/null @@ -1,194 +0,0 @@ -//! Provides `Instant` for all platforms. - -pub use time::Instant; - -// TODO: Create a `web` feature to enable WASI compatibility. -// See https://github.com/bevyengine/bevy/issues/4906 -#[cfg(target_arch = "wasm32")] -use web_time as time; - -#[cfg(all(not(target_arch = "wasm32"), feature = "std"))] -use std::time; - -#[cfg(all(not(target_arch = "wasm32"), not(feature = "std")))] -use fallback as time; - -#[cfg(all(not(target_arch = "wasm32"), not(feature = "std")))] -mod fallback { - //! Provides a fallback implementation of `Instant` from the standard library. - - #![expect( - unsafe_code, - reason = "Instant fallback requires unsafe to allow users to update the internal value" - )] - - use crate::sync::atomic::{AtomicPtr, Ordering}; - - use core::{ - fmt, - ops::{Add, AddAssign, Sub, SubAssign}, - time::Duration, - }; - - static ELAPSED_GETTER: AtomicPtr Duration> = AtomicPtr::new(unset_getter as *mut _); - - /// Fallback implementation of `Instant` suitable for a `no_std` environment. - /// - /// If you are on any of the following target architectures, this is a drop-in replacement: - /// - /// - `x86` - /// - `x86_64` - /// - `aarch64` - /// - /// On any other architecture, you must call [`Instant::set_elapsed`], providing a method - /// which when called supplies a monotonically increasing count of elapsed nanoseconds relative - /// to some arbitrary point in time. - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct Instant(Duration); - - impl Instant { - /// Returns an instant corresponding to "now". - #[must_use] - pub fn now() -> Instant { - let getter = ELAPSED_GETTER.load(Ordering::Acquire); - - // SAFETY: Function pointer is always valid - let getter = unsafe { *getter }; - - Self((getter)()) - } - - /// Provides a function returning the amount of time that has elapsed since execution began. - /// The getter provided to this method will be used by [`now`](Instant::now). - /// - /// # Safety - /// - /// - The function provided must accurately represent the elapsed time. - /// - The function must preserve all invariants of the [`Instant`] type. - /// - The pointer to the function must be valid whenever [`Instant::now`] is called. - pub unsafe fn set_elapsed(getter: *mut fn() -> Duration) { - ELAPSED_GETTER.store(getter, Ordering::Release); - } - - /// Returns the amount of time elapsed from another instant to this one, - /// or zero duration if that instant is later than this one. - #[must_use] - pub fn duration_since(&self, earlier: Instant) -> Duration { - self.saturating_duration_since(earlier) - } - - /// Returns the amount of time elapsed from another instant to this one, - /// or None if that instant is later than this one. - /// - /// Due to monotonicity bugs, even under correct logical ordering of the passed `Instant`s, - /// this method can return `None`. - #[must_use] - pub fn checked_duration_since(&self, earlier: Instant) -> Option { - self.0.checked_sub(earlier.0) - } - - /// Returns the amount of time elapsed from another instant to this one, - /// or zero duration if that instant is later than this one. - #[must_use] - pub fn saturating_duration_since(&self, earlier: Instant) -> Duration { - self.0.saturating_sub(earlier.0) - } - - /// Returns the amount of time elapsed since this instant. - #[must_use] - pub fn elapsed(&self) -> Duration { - self.saturating_duration_since(Instant::now()) - } - - /// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as - /// `Instant` (which means it's inside the bounds of the underlying data structure), `None` - /// otherwise. - pub fn checked_add(&self, duration: Duration) -> Option { - self.0.checked_add(duration).map(Instant) - } - - /// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as - /// `Instant` (which means it's inside the bounds of the underlying data structure), `None` - /// otherwise. - pub fn checked_sub(&self, duration: Duration) -> Option { - self.0.checked_sub(duration).map(Instant) - } - } - - impl Add for Instant { - type Output = Instant; - - /// # Panics - /// - /// This function may panic if the resulting point in time cannot be represented by the - /// underlying data structure. See [`Instant::checked_add`] for a version without panic. - fn add(self, other: Duration) -> Instant { - self.checked_add(other) - .expect("overflow when adding duration to instant") - } - } - - impl AddAssign for Instant { - fn add_assign(&mut self, other: Duration) { - *self = *self + other; - } - } - - impl Sub for Instant { - type Output = Instant; - - fn sub(self, other: Duration) -> Instant { - self.checked_sub(other) - .expect("overflow when subtracting duration from instant") - } - } - - impl SubAssign for Instant { - fn sub_assign(&mut self, other: Duration) { - *self = *self - other; - } - } - - impl Sub for Instant { - type Output = Duration; - - /// Returns the amount of time elapsed from another instant to this one, - /// or zero duration if that instant is later than this one. - fn sub(self, other: Instant) -> Duration { - self.duration_since(other) - } - } - - impl fmt::Debug for Instant { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } - } - - fn unset_getter() -> Duration { - let _nanos: u64; - - #[cfg(target_arch = "x86")] - unsafe { - _nanos = core::arch::x86::_rdtsc(); - } - - #[cfg(target_arch = "x86_64")] - unsafe { - _nanos = core::arch::x86_64::_rdtsc(); - } - - #[cfg(target_arch = "aarch64")] - unsafe { - let mut ticks: u64; - core::arch::asm!("mrs {}, cntvct_el0", out(reg) ticks); - _nanos = ticks; - } - - #[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))] - panic!("An elapsed time getter has not been provided to `Instant`. Please use `Instant::set_elapsed(...)` before calling `Instant::now()`"); - - #[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))] - return Duration::from_nanos(_nanos); - } -} diff --git a/crates/bevy_platform_support/src/time/fallback.rs b/crates/bevy_platform_support/src/time/fallback.rs new file mode 100644 index 0000000000000..5d6dadbd6c049 --- /dev/null +++ b/crates/bevy_platform_support/src/time/fallback.rs @@ -0,0 +1,176 @@ +//! Provides a fallback implementation of `Instant` from the standard library. + +#![expect( + unsafe_code, + reason = "Instant fallback requires unsafe to allow users to update the internal value" +)] + +use crate::sync::atomic::{AtomicPtr, Ordering}; + +use core::{ + fmt, + ops::{Add, AddAssign, Sub, SubAssign}, + time::Duration, +}; + +static ELAPSED_GETTER: AtomicPtr Duration> = AtomicPtr::new(unset_getter as *mut _); + +/// Fallback implementation of `Instant` suitable for a `no_std` environment. +/// +/// If you are on any of the following target architectures, this is a drop-in replacement: +/// +/// - `x86` +/// - `x86_64` +/// - `aarch64` +/// +/// On any other architecture, you must call [`Instant::set_elapsed`], providing a method +/// which when called supplies a monotonically increasing count of elapsed nanoseconds relative +/// to some arbitrary point in time. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Instant(Duration); + +impl Instant { + /// Returns an instant corresponding to "now". + #[must_use] + pub fn now() -> Instant { + let getter = ELAPSED_GETTER.load(Ordering::Acquire); + + // SAFETY: Function pointer is always valid + let getter = unsafe { *getter }; + + Self((getter)()) + } + + /// Provides a function returning the amount of time that has elapsed since execution began. + /// The getter provided to this method will be used by [`now`](Instant::now). + /// + /// # Safety + /// + /// - The function provided must accurately represent the elapsed time. + /// - The function must preserve all invariants of the [`Instant`] type. + /// - The pointer to the function must be valid whenever [`Instant::now`] is called. + pub unsafe fn set_elapsed(getter: *mut fn() -> Duration) { + ELAPSED_GETTER.store(getter, Ordering::Release); + } + + /// Returns the amount of time elapsed from another instant to this one, + /// or zero duration if that instant is later than this one. + #[must_use] + pub fn duration_since(&self, earlier: Instant) -> Duration { + self.saturating_duration_since(earlier) + } + + /// Returns the amount of time elapsed from another instant to this one, + /// or None if that instant is later than this one. + /// + /// Due to monotonicity bugs, even under correct logical ordering of the passed `Instant`s, + /// this method can return `None`. + #[must_use] + pub fn checked_duration_since(&self, earlier: Instant) -> Option { + self.0.checked_sub(earlier.0) + } + + /// Returns the amount of time elapsed from another instant to this one, + /// or zero duration if that instant is later than this one. + #[must_use] + pub fn saturating_duration_since(&self, earlier: Instant) -> Duration { + self.0.saturating_sub(earlier.0) + } + + /// Returns the amount of time elapsed since this instant. + #[must_use] + pub fn elapsed(&self) -> Duration { + self.saturating_duration_since(Instant::now()) + } + + /// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as + /// `Instant` (which means it's inside the bounds of the underlying data structure), `None` + /// otherwise. + pub fn checked_add(&self, duration: Duration) -> Option { + self.0.checked_add(duration).map(Instant) + } + + /// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as + /// `Instant` (which means it's inside the bounds of the underlying data structure), `None` + /// otherwise. + pub fn checked_sub(&self, duration: Duration) -> Option { + self.0.checked_sub(duration).map(Instant) + } +} + +impl Add for Instant { + type Output = Instant; + + /// # Panics + /// + /// This function may panic if the resulting point in time cannot be represented by the + /// underlying data structure. See [`Instant::checked_add`] for a version without panic. + fn add(self, other: Duration) -> Instant { + self.checked_add(other) + .expect("overflow when adding duration to instant") + } +} + +impl AddAssign for Instant { + fn add_assign(&mut self, other: Duration) { + *self = *self + other; + } +} + +impl Sub for Instant { + type Output = Instant; + + fn sub(self, other: Duration) -> Instant { + self.checked_sub(other) + .expect("overflow when subtracting duration from instant") + } +} + +impl SubAssign for Instant { + fn sub_assign(&mut self, other: Duration) { + *self = *self - other; + } +} + +impl Sub for Instant { + type Output = Duration; + + /// Returns the amount of time elapsed from another instant to this one, + /// or zero duration if that instant is later than this one. + fn sub(self, other: Instant) -> Duration { + self.duration_since(other) + } +} + +impl fmt::Debug for Instant { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +fn unset_getter() -> Duration { + let _nanos: u64; + + #[cfg(target_arch = "x86")] + unsafe { + _nanos = core::arch::x86::_rdtsc(); + } + + #[cfg(target_arch = "x86_64")] + unsafe { + _nanos = core::arch::x86_64::_rdtsc(); + } + + #[cfg(target_arch = "aarch64")] + unsafe { + let mut ticks: u64; + core::arch::asm!("mrs {}, cntvct_el0", out(reg) ticks); + _nanos = ticks; + } + + #[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))] + panic!("An elapsed time getter has not been provided to `Instant`. Please use `Instant::set_elapsed(...)` before calling `Instant::now()`"); + + #[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))] + return Duration::from_nanos(_nanos); +} diff --git a/crates/bevy_platform_support/src/time/mod.rs b/crates/bevy_platform_support/src/time/mod.rs new file mode 100644 index 0000000000000..8090aa271a704 --- /dev/null +++ b/crates/bevy_platform_support/src/time/mod.rs @@ -0,0 +1,15 @@ +//! Provides `Instant` for all platforms. + +pub use time::Instant; + +cfg_if::cfg_if! { + if #[cfg(all(target_arch = "wasm32", feature = "browser"))] { + use web_time as time; + } else if #[cfg(feature = "std")] { + use std::time; + } else { + mod fallback; + + use fallback as time; + } +} diff --git a/crates/bevy_render/Cargo.toml b/crates/bevy_render/Cargo.toml index c2576c44e7c17..241ea4e71a5a9 100644 --- a/crates/bevy_render/Cargo.toml +++ b/crates/bevy_render/Cargo.toml @@ -115,6 +115,16 @@ web-sys = { version = "0.3.67", features = [ 'Window', ] } wasm-bindgen = "0.2" +# TODO: Assuming all wasm builds are for the browser. Require `no_std` support to break assumption. +bevy_app = { path = "../bevy_app", version = "0.16.0-dev", default-features = false, features = [ + "browser", +] } +bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev", default-features = false, features = [ + "browser", +] } +bevy_platform_support = { path = "../bevy_platform_support", version = "0.16.0-dev", default-features = false, features = [ + "browser", +] } [target.'cfg(all(target_arch = "wasm32", target_feature = "atomics"))'.dependencies] send_wrapper = "0.6.0" diff --git a/crates/bevy_tasks/Cargo.toml b/crates/bevy_tasks/Cargo.toml index 3e0c9720c9ba7..133731b18e1fb 100644 --- a/crates/bevy_tasks/Cargo.toml +++ b/crates/bevy_tasks/Cargo.toml @@ -10,6 +10,26 @@ keywords = ["bevy"] [features] default = ["std", "async_executor"] + +# Functionality + +## Enables multi-threading support. +## Without this feature, all tasks will be run on a single thread. +multi_threaded = ["std", "dep:async-channel", "dep:concurrent-queue"] + +## Uses `async-executor` as a task execution backend. +## This backend is incompatible with `no_std` targets. +async_executor = ["std", "dep:async-executor"] + +## Uses `edge-executor` as a task execution backend. +## Use this instead of `async-executor` if working on a `no_std` target. +edge_executor = ["dep:edge-executor"] + +# Platform Compatibility + +## Allows access to the `std` crate. Enabling this feature will prevent compilation +## on `no_std` targets, but provides access to certain additional features on +## supported platforms. std = [ "futures-lite/std", "async-task/std", @@ -17,13 +37,16 @@ std = [ "edge-executor?/std", "bevy_platform_support/std", ] -multi_threaded = ["std", "dep:async-channel", "dep:concurrent-queue"] -async_executor = ["std", "dep:async-executor"] -edge_executor = ["dep:edge-executor"] + +## `critical-section` provides the building blocks for synchronization primitives +## on all platforms, including `no_std`. critical-section = [ "bevy_platform_support/critical-section", "edge-executor?/critical-section", ] + +## `portable-atomic` provides additional platform support for atomic types and +## operations, even on targets without native support. portable-atomic = [ "bevy_platform_support/portable-atomic", "edge-executor?/portable-atomic", @@ -31,6 +54,15 @@ portable-atomic = [ "spin/portable_atomic", ] +## Enables use of browser APIs. +## Note this is currently only applicable on `wasm32` architectures. +browser = [ + "bevy_platform_support/browser", + "dep:wasm-bindgen-futures", + "dep:pin-project", + "dep:futures-channel", +] + [dependencies] bevy_platform_support = { path = "../bevy_platform_support", version = "0.16.0-dev", default-features = false, features = [ "alloc", @@ -49,6 +81,7 @@ derive_more = { version = "1", default-features = false, features = [ "deref", "deref_mut", ] } +cfg-if = "1.0.0" async-executor = { version = "1.11", optional = true } edge-executor = { version = "0.4.1", default-features = false, optional = true } @@ -57,9 +90,9 @@ async-io = { version = "2.0.0", optional = true } concurrent-queue = { version = "2.0.0", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] -wasm-bindgen-futures = "0.4" -pin-project = "1" -futures-channel = "0.3" +wasm-bindgen-futures = { version = "0.4", optional = true } +pin-project = { version = "1", optional = true } +futures-channel = { version = "0.3", optional = true } [dev-dependencies] web-time = { version = "1.1" } diff --git a/crates/bevy_tasks/src/executor.rs b/crates/bevy_tasks/src/executor.rs index 3c18ccd897fa1..7a698236430ab 100644 --- a/crates/bevy_tasks/src/executor.rs +++ b/crates/bevy_tasks/src/executor.rs @@ -14,21 +14,19 @@ use core::{ }; use derive_more::{Deref, DerefMut}; +cfg_if::cfg_if! { + if #[cfg(feature = "async_executor")] { + type ExecutorInner<'a> = async_executor::Executor<'a>; + type LocalExecutorInner<'a> = async_executor::LocalExecutor<'a>; + } else if #[cfg(feature = "edge_executor")] { + type ExecutorInner<'a> = edge_executor::Executor<'a, 64>; + type LocalExecutorInner<'a> = edge_executor::LocalExecutor<'a, 64>; + } +} + #[cfg(all(feature = "multi_threaded", not(target_arch = "wasm32")))] pub use async_task::FallibleTask; -#[cfg(feature = "async_executor")] -type ExecutorInner<'a> = async_executor::Executor<'a>; - -#[cfg(feature = "async_executor")] -type LocalExecutorInner<'a> = async_executor::LocalExecutor<'a>; - -#[cfg(all(not(feature = "async_executor"), feature = "edge_executor"))] -type ExecutorInner<'a> = edge_executor::Executor<'a, 64>; - -#[cfg(all(not(feature = "async_executor"), feature = "edge_executor"))] -type LocalExecutorInner<'a> = edge_executor::LocalExecutor<'a, 64>; - /// Wrapper around a multi-threading-aware async executor. /// Spawning will generally require tasks to be `Send` and `Sync` to allow multiple /// threads to send/receive/advance tasks. diff --git a/crates/bevy_tasks/src/lib.rs b/crates/bevy_tasks/src/lib.rs index 220f3dcae2631..e70c78058341f 100644 --- a/crates/bevy_tasks/src/lib.rs +++ b/crates/bevy_tasks/src/lib.rs @@ -11,22 +11,32 @@ extern crate std; extern crate alloc; -#[cfg(not(any(feature = "async_executor", feature = "edge_executor")))] -compile_error!("Either of the `async_executor` or the `edge_executor` features must be enabled."); - -#[cfg(not(target_arch = "wasm32"))] -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 {} +cfg_if::cfg_if! { + if #[cfg(feature = "async_executor")] { + // Using async executor backend. + mod executor; + } else if #[cfg(feature = "edge_executor")] { + // Using edge executor backend. + mod executor; + } else { + compile_error!("Either of the `async_executor` or the `edge_executor` features must be enabled."); + } } -#[cfg(target_arch = "wasm32")] -#[expect(missing_docs, reason = "Not all docs are written yet (#3492).")] mod conditional_send { - pub trait ConditionalSend {} - impl ConditionalSend for T {} + cfg_if::cfg_if! { + if #[cfg(target_arch = "wasm32")] { + /// Use [`ConditionalSend`] to mark an optional Send trait bound. Useful as on certain platforms (eg. Wasm), + /// futures aren't Send. + pub trait ConditionalSend {} + impl ConditionalSend for T {} + } else { + /// 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 {} + } + } } pub use conditional_send::*; @@ -43,44 +53,46 @@ pub type BoxedFuture<'a, T> = core::pin::Pin usize { - std::thread::available_parallelism() - .map(NonZero::::get) - .unwrap_or(1) -} - -/// Gets the logical CPU core count available to the current process. -/// -/// This will always return at least 1. -#[cfg(not(feature = "std"))] -pub fn available_parallelism() -> usize { - // Without access to std, assume a single thread is available - 1 +cfg_if::cfg_if! { + if #[cfg(feature = "async-io")] { + /// Gets the logical CPU core count available to the current process. + /// + /// This is identical to [`std::thread::available_parallelism`], except + /// it will return a default value of 1 if it internally errors out. + /// + /// This will always return at least 1. + #[cfg(feature = "std")] + pub fn available_parallelism() -> usize { + std::thread::available_parallelism() + .map(NonZero::::get) + .unwrap_or(1) + } + } else { + /// Gets the logical CPU core count available to the current process. + /// + /// This will always return at least 1. + #[cfg(not(feature = "std"))] + pub fn available_parallelism() -> usize { + // Without access to std, assume a single thread is available + 1 + } + } } diff --git a/crates/bevy_tasks/src/single_threaded_task_pool.rs b/crates/bevy_tasks/src/single_threaded_task_pool.rs index b928fa9b214b4..46cb5720efa77 100644 --- a/crates/bevy_tasks/src/single_threaded_task_pool.rs +++ b/crates/bevy_tasks/src/single_threaded_task_pool.rs @@ -234,26 +234,27 @@ impl TaskPool { where T: 'static + MaybeSend + MaybeSync, { - #[cfg(target_arch = "wasm32")] - return Task::wrap_future(future); - - #[cfg(all(not(target_arch = "wasm32"), feature = "std"))] - return LOCAL_EXECUTOR.with(|executor| { - let task = executor.spawn(future); - // Loop until all tasks are done - while executor.try_tick() {} - - Task::new(task) - }); - - #[cfg(all(not(target_arch = "wasm32"), not(feature = "std")))] - return { - let task = LOCAL_EXECUTOR.spawn(future); - // Loop until all tasks are done - while LOCAL_EXECUTOR.try_tick() {} - - Task::new(task) - }; + cfg_if::cfg_if! { + if #[cfg(all(target_arch = "wasm32", feature = "browser"))] { + return Task::wrap_future(future); + } else if #[cfg(feature = "std")] { + return LOCAL_EXECUTOR.with(|executor| { + let task = executor.spawn(future); + // Loop until all tasks are done + while executor.try_tick() {} + + Task::new(task) + }); + } else { + return { + let task = LOCAL_EXECUTOR.spawn(future); + // Loop until all tasks are done + while LOCAL_EXECUTOR.try_tick() {} + + Task::new(task) + }; + } + } } /// Spawns a static future on the JS event loop. This is exactly the same as [`TaskPool::spawn`]. diff --git a/crates/bevy_tasks/src/usages.rs b/crates/bevy_tasks/src/usages.rs index 385b30fdb402d..b83255f69b057 100644 --- a/crates/bevy_tasks/src/usages.rs +++ b/crates/bevy_tasks/src/usages.rs @@ -98,7 +98,7 @@ taskpool! { /// # Warning /// /// This function *must* be called on the main thread, or the task pools will not be updated appropriately. -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(all(target_arch = "wasm32", feature = "browser")))] pub fn tick_global_task_pools_on_main_thread() { COMPUTE_TASK_POOL .get() diff --git a/crates/bevy_winit/Cargo.toml b/crates/bevy_winit/Cargo.toml index 4caf374200487..92c5030035899 100644 --- a/crates/bevy_winit/Cargo.toml +++ b/crates/bevy_winit/Cargo.toml @@ -61,6 +61,16 @@ tracing = { version = "0.1", default-features = false, features = ["std"] } wasm-bindgen = { version = "0.2" } web-sys = "0.3" crossbeam-channel = "0.5" +# TODO: Assuming all wasm builds are for the browser. Require `no_std` support to break assumption. +bevy_app = { path = "../bevy_app", version = "0.16.0-dev", default-features = false, features = [ + "browser", +] } +bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev", default-features = false, features = [ + "browser", +] } +bevy_platform_support = { path = "../bevy_platform_support", version = "0.16.0-dev", default-features = false, features = [ + "browser", +] } [lints] workspace = true From 4a7039ae3faae48e6dcd5b59c8d6e6d250514213 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Thu, 23 Jan 2025 09:31:50 +1100 Subject: [PATCH 2/5] Fix feature gate bug --- crates/bevy_tasks/src/lib.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/crates/bevy_tasks/src/lib.rs b/crates/bevy_tasks/src/lib.rs index e70c78058341f..ed38e519bc202 100644 --- a/crates/bevy_tasks/src/lib.rs +++ b/crates/bevy_tasks/src/lib.rs @@ -115,18 +115,16 @@ pub mod prelude { pub use crate::block_on; } -#[cfg(feature = "std")] -use core::num::NonZero; - cfg_if::cfg_if! { - if #[cfg(feature = "async-io")] { + if #[cfg(feature = "std")] { + use core::num::NonZero; + /// Gets the logical CPU core count available to the current process. /// /// This is identical to [`std::thread::available_parallelism`], except /// it will return a default value of 1 if it internally errors out. /// /// This will always return at least 1. - #[cfg(feature = "std")] pub fn available_parallelism() -> usize { std::thread::available_parallelism() .map(NonZero::::get) @@ -136,7 +134,6 @@ cfg_if::cfg_if! { /// Gets the logical CPU core count available to the current process. /// /// This will always return at least 1. - #[cfg(not(feature = "std"))] pub fn available_parallelism() -> usize { // Without access to std, assume a single thread is available 1 From 57636027974b0071df2e826ffa5b54e04fcd5326 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Thu, 23 Jan 2025 09:37:50 +1100 Subject: [PATCH 3/5] Remove redundant feature gate --- crates/bevy_app/src/task_pool_plugin.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/bevy_app/src/task_pool_plugin.rs b/crates/bevy_app/src/task_pool_plugin.rs index 12eb36e963f9d..2e59def0268f6 100644 --- a/crates/bevy_app/src/task_pool_plugin.rs +++ b/crates/bevy_app/src/task_pool_plugin.rs @@ -22,7 +22,6 @@ cfg_if::cfg_if! { /// /// Calls [`tick_global_task_pools_on_main_thread`], /// and uses [`NonSendMarker`] to ensure that this system runs on the main thread - #[cfg(not(target_arch = "wasm32"))] fn tick_global_task_pools(_main_thread_marker: Option>) { tick_global_task_pools_on_main_thread(); } From ae2a0f4c0cd75cf77f1eeed015cfb88c138b1aaf Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Wed, 29 Jan 2025 10:58:01 +1100 Subject: [PATCH 4/5] Rename `browser` feature to `web` for consistency --- crates/bevy_app/Cargo.toml | 6 +++--- crates/bevy_app/src/app.rs | 2 +- crates/bevy_app/src/panic_handler.rs | 2 +- crates/bevy_app/src/schedule_runner.rs | 6 +++--- crates/bevy_app/src/task_pool_plugin.rs | 10 +++++----- crates/bevy_asset/Cargo.toml | 4 ++-- crates/bevy_audio/Cargo.toml | 2 +- crates/bevy_internal/Cargo.toml | 6 +++--- crates/bevy_log/Cargo.toml | 2 +- crates/bevy_platform_support/Cargo.toml | 2 +- crates/bevy_platform_support/src/time/mod.rs | 2 +- crates/bevy_render/Cargo.toml | 6 +++--- crates/bevy_tasks/Cargo.toml | 4 ++-- crates/bevy_tasks/src/lib.rs | 4 ++-- crates/bevy_tasks/src/single_threaded_task_pool.rs | 2 +- crates/bevy_tasks/src/usages.rs | 2 +- crates/bevy_winit/Cargo.toml | 6 +++--- 17 files changed, 34 insertions(+), 34 deletions(-) diff --git a/crates/bevy_app/Cargo.toml b/crates/bevy_app/Cargo.toml index 424889dd64a32..9fcc67dc41e3e 100644 --- a/crates/bevy_app/Cargo.toml +++ b/crates/bevy_app/Cargo.toml @@ -71,9 +71,9 @@ portable-atomic = [ ## Enables use of browser APIs. ## Note this is currently only applicable on `wasm32` architectures. -browser = [ - "bevy_platform_support/browser", - "bevy_tasks?/browser", +web = [ + "bevy_platform_support/web", + "bevy_tasks?/web", "dep:wasm-bindgen", "dep:web-sys", "dep:console_error_panic_hook", diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index b1145e3d3d5de..8c658cac00437 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -1331,7 +1331,7 @@ type RunnerFn = Box AppExit>; fn run_once(mut app: App) -> AppExit { while app.plugins_state() == PluginsState::Adding { #[cfg(all( - not(all(target_arch = "wasm32", feature = "browser")), + not(all(target_arch = "wasm32", feature = "web")), feature = "bevy_tasks" ))] bevy_tasks::tick_global_task_pools_on_main_thread(); diff --git a/crates/bevy_app/src/panic_handler.rs b/crates/bevy_app/src/panic_handler.rs index 010f311c219b6..71009b2ae1178 100644 --- a/crates/bevy_app/src/panic_handler.rs +++ b/crates/bevy_app/src/panic_handler.rs @@ -40,7 +40,7 @@ pub struct PanicHandlerPlugin; impl Plugin for PanicHandlerPlugin { fn build(&self, _app: &mut App) { cfg_if::cfg_if! { - if #[cfg(all(target_arch = "wasm32", feature = "browser"))] { + if #[cfg(all(target_arch = "wasm32", feature = "web"))] { console_error_panic_hook::set_once(); } else { // Use the default target panic hook - Do nothing. diff --git a/crates/bevy_app/src/schedule_runner.rs b/crates/bevy_app/src/schedule_runner.rs index ba69779645187..613a099ca769b 100644 --- a/crates/bevy_app/src/schedule_runner.rs +++ b/crates/bevy_app/src/schedule_runner.rs @@ -6,7 +6,7 @@ use crate::{ use bevy_platform_support::time::Instant; use core::time::Duration; -#[cfg(all(target_arch = "wasm32", feature = "browser"))] +#[cfg(all(target_arch = "wasm32", feature = "web"))] use { alloc::{boxed::Box, rc::Rc}, core::cell::RefCell, @@ -78,7 +78,7 @@ impl Plugin for ScheduleRunnerPlugin { if plugins_state != PluginsState::Cleaned { while app.plugins_state() == PluginsState::Adding { #[cfg(all( - not(all(target_arch = "wasm32", feature = "browser")), + not(all(target_arch = "wasm32", feature = "web")), feature = "bevy_tasks" ))] bevy_tasks::tick_global_task_pools_on_main_thread(); @@ -122,7 +122,7 @@ impl Plugin for ScheduleRunnerPlugin { }; cfg_if::cfg_if! { - if #[cfg(all(target_arch = "wasm32", feature = "browser"))] { + if #[cfg(all(target_arch = "wasm32", feature = "web"))] { fn set_timeout(callback: &Closure, dur: Duration) { web_sys::window() .unwrap() diff --git a/crates/bevy_app/src/task_pool_plugin.rs b/crates/bevy_app/src/task_pool_plugin.rs index 2e59def0268f6..98c10514a371a 100644 --- a/crates/bevy_app/src/task_pool_plugin.rs +++ b/crates/bevy_app/src/task_pool_plugin.rs @@ -15,7 +15,7 @@ use core::{fmt::Debug, marker::PhantomData}; use log::trace; cfg_if::cfg_if! { - if #[cfg(not(all(target_arch = "wasm32", feature = "browser")))] { + if #[cfg(not(all(target_arch = "wasm32", feature = "web")))] { use {crate::Last, bevy_ecs::prelude::NonSend, bevy_tasks::tick_global_task_pools_on_main_thread}; /// A system used to check and advanced our task pools. @@ -40,7 +40,7 @@ impl Plugin for TaskPoolPlugin { // Setup the default bevy task pools self.task_pool_options.create_default_pools(); - #[cfg(not(all(target_arch = "wasm32", feature = "browser")))] + #[cfg(not(all(target_arch = "wasm32", feature = "web")))] _app.add_systems(Last, tick_global_task_pools); } } @@ -187,7 +187,7 @@ impl TaskPoolOptions { .num_threads(io_threads) .thread_name("IO Task Pool".to_string()); - #[cfg(not(all(target_arch = "wasm32", feature = "browser")))] + #[cfg(not(all(target_arch = "wasm32", feature = "web")))] let builder = { let mut builder = builder; if let Some(f) = self.io.on_thread_spawn.clone() { @@ -217,7 +217,7 @@ impl TaskPoolOptions { .num_threads(async_compute_threads) .thread_name("Async Compute Task Pool".to_string()); - #[cfg(not(all(target_arch = "wasm32", feature = "browser")))] + #[cfg(not(all(target_arch = "wasm32", feature = "web")))] let builder = { let mut builder = builder; if let Some(f) = self.async_compute.on_thread_spawn.clone() { @@ -247,7 +247,7 @@ impl TaskPoolOptions { .num_threads(compute_threads) .thread_name("Compute Task Pool".to_string()); - #[cfg(not(all(target_arch = "wasm32", feature = "browser")))] + #[cfg(not(all(target_arch = "wasm32", feature = "web")))] let builder = { let mut builder = builder; if let Some(f) = self.compute.on_thread_spawn.clone() { diff --git a/crates/bevy_asset/Cargo.toml b/crates/bevy_asset/Cargo.toml index 148981087f643..c171380b7fb0f 100644 --- a/crates/bevy_asset/Cargo.toml +++ b/crates/bevy_asset/Cargo.toml @@ -66,10 +66,10 @@ wasm-bindgen-futures = "0.4" js-sys = "0.3" # TODO: Assuming all wasm builds are for the browser. Require `no_std` support to break assumption. bevy_app = { path = "../bevy_app", version = "0.16.0-dev", default-features = false, features = [ - "browser", + "web", ] } bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev", default-features = false, features = [ - "browser", + "web", ] } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/crates/bevy_audio/Cargo.toml b/crates/bevy_audio/Cargo.toml index 4c582f1112d64..57b4310e856e8 100644 --- a/crates/bevy_audio/Cargo.toml +++ b/crates/bevy_audio/Cargo.toml @@ -33,7 +33,7 @@ rodio = { version = "0.20", default-features = false, features = [ ] } # TODO: Assuming all wasm builds are for the browser. Require `no_std` support to break assumption. bevy_app = { path = "../bevy_app", version = "0.16.0-dev", default-features = false, features = [ - "browser", + "web", ] } [features] diff --git a/crates/bevy_internal/Cargo.toml b/crates/bevy_internal/Cargo.toml index 91b956f771b3b..55871b48bcad2 100644 --- a/crates/bevy_internal/Cargo.toml +++ b/crates/bevy_internal/Cargo.toml @@ -319,13 +319,13 @@ bevy_winit = { path = "../bevy_winit", optional = true, version = "0.16.0-dev" } [target.'cfg(target_arch = "wasm32")'.dependencies] # TODO: Assuming all wasm builds are for the browser. Require `no_std` support to break assumption. bevy_app = { path = "../bevy_app", version = "0.16.0-dev", default-features = false, features = [ - "browser", + "web", ] } bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev", default-features = false, features = [ - "browser", + "web", ] } bevy_platform_support = { path = "../bevy_platform_support", version = "0.16.0-dev", default-features = false, features = [ - "browser", + "web", ] } [lints] diff --git a/crates/bevy_log/Cargo.toml b/crates/bevy_log/Cargo.toml index ccd4f76811ad7..1e80026aae219 100644 --- a/crates/bevy_log/Cargo.toml +++ b/crates/bevy_log/Cargo.toml @@ -40,7 +40,7 @@ android_log-sys = "0.3.0" tracing-wasm = "0.2.1" # TODO: Assuming all wasm builds are for the browser. Require `no_std` support to break assumption. bevy_app = { path = "../bevy_app", version = "0.16.0-dev", default-features = false, features = [ - "browser", + "web", ] } [target.'cfg(target_os = "ios")'.dependencies] diff --git a/crates/bevy_platform_support/Cargo.toml b/crates/bevy_platform_support/Cargo.toml index 0ffd27bc3abdb..6fe814dba679a 100644 --- a/crates/bevy_platform_support/Cargo.toml +++ b/crates/bevy_platform_support/Cargo.toml @@ -47,7 +47,7 @@ portable-atomic = [ ## Enables use of browser APIs. ## Note this is currently only applicable on `wasm32` architectures. -browser = ["dep:web-time", "dep:getrandom"] +web = ["dep:web-time", "dep:getrandom"] [dependencies] cfg-if = "1.0.0" diff --git a/crates/bevy_platform_support/src/time/mod.rs b/crates/bevy_platform_support/src/time/mod.rs index 8090aa271a704..260d8e4aea124 100644 --- a/crates/bevy_platform_support/src/time/mod.rs +++ b/crates/bevy_platform_support/src/time/mod.rs @@ -3,7 +3,7 @@ pub use time::Instant; cfg_if::cfg_if! { - if #[cfg(all(target_arch = "wasm32", feature = "browser"))] { + if #[cfg(all(target_arch = "wasm32", feature = "web"))] { use web_time as time; } else if #[cfg(feature = "std")] { use std::time; diff --git a/crates/bevy_render/Cargo.toml b/crates/bevy_render/Cargo.toml index b5510178cbef6..91ded4a8442a6 100644 --- a/crates/bevy_render/Cargo.toml +++ b/crates/bevy_render/Cargo.toml @@ -118,13 +118,13 @@ web-sys = { version = "0.3.67", features = [ wasm-bindgen = "0.2" # TODO: Assuming all wasm builds are for the browser. Require `no_std` support to break assumption. bevy_app = { path = "../bevy_app", version = "0.16.0-dev", default-features = false, features = [ - "browser", + "web", ] } bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev", default-features = false, features = [ - "browser", + "web", ] } bevy_platform_support = { path = "../bevy_platform_support", version = "0.16.0-dev", default-features = false, features = [ - "browser", + "web", ] } [target.'cfg(all(target_arch = "wasm32", target_feature = "atomics"))'.dependencies] diff --git a/crates/bevy_tasks/Cargo.toml b/crates/bevy_tasks/Cargo.toml index 9d501ca2bdbbe..d7bb193738345 100644 --- a/crates/bevy_tasks/Cargo.toml +++ b/crates/bevy_tasks/Cargo.toml @@ -54,8 +54,8 @@ portable-atomic = [ ## Enables use of browser APIs. ## Note this is currently only applicable on `wasm32` architectures. -browser = [ - "bevy_platform_support/browser", +web = [ + "bevy_platform_support/web", "dep:wasm-bindgen-futures", "dep:pin-project", "dep:futures-channel", diff --git a/crates/bevy_tasks/src/lib.rs b/crates/bevy_tasks/src/lib.rs index ed38e519bc202..c275671376d9d 100644 --- a/crates/bevy_tasks/src/lib.rs +++ b/crates/bevy_tasks/src/lib.rs @@ -57,7 +57,7 @@ mod slice; pub use slice::{ParallelSlice, ParallelSliceMut}; #[cfg_attr( - all(target_arch = "wasm32", feature = "browser"), + all(target_arch = "wasm32", feature = "web"), path = "wasm_task.rs" )] mod task; @@ -82,7 +82,7 @@ mod usages; pub use futures_lite::future::poll_once; pub use usages::{AsyncComputeTaskPool, ComputeTaskPool, IoTaskPool}; -#[cfg(not(all(target_arch = "wasm32", feature = "browser")))] +#[cfg(not(all(target_arch = "wasm32", feature = "web")))] pub use usages::tick_global_task_pools_on_main_thread; #[cfg(feature = "std")] diff --git a/crates/bevy_tasks/src/single_threaded_task_pool.rs b/crates/bevy_tasks/src/single_threaded_task_pool.rs index 5c146487b0387..12be0dd40fe1d 100644 --- a/crates/bevy_tasks/src/single_threaded_task_pool.rs +++ b/crates/bevy_tasks/src/single_threaded_task_pool.rs @@ -238,7 +238,7 @@ impl TaskPool { T: 'static + MaybeSend + MaybeSync, { cfg_if::cfg_if! { - if #[cfg(all(target_arch = "wasm32", feature = "browser"))] { + if #[cfg(all(target_arch = "wasm32", feature = "web"))] { return Task::wrap_future(future); } else if #[cfg(feature = "std")] { return LOCAL_EXECUTOR.with(|executor| { diff --git a/crates/bevy_tasks/src/usages.rs b/crates/bevy_tasks/src/usages.rs index 3be9167c7dac8..9563007b0388e 100644 --- a/crates/bevy_tasks/src/usages.rs +++ b/crates/bevy_tasks/src/usages.rs @@ -81,7 +81,7 @@ taskpool! { /// # Warning /// /// This function *must* be called on the main thread, or the task pools will not be updated appropriately. -#[cfg(not(all(target_arch = "wasm32", feature = "browser")))] +#[cfg(not(all(target_arch = "wasm32", feature = "web")))] pub fn tick_global_task_pools_on_main_thread() { COMPUTE_TASK_POOL .get() diff --git a/crates/bevy_winit/Cargo.toml b/crates/bevy_winit/Cargo.toml index abbe35bbf1698..549a1e4ced184 100644 --- a/crates/bevy_winit/Cargo.toml +++ b/crates/bevy_winit/Cargo.toml @@ -68,13 +68,13 @@ web-sys = "0.3" crossbeam-channel = "0.5" # TODO: Assuming all wasm builds are for the browser. Require `no_std` support to break assumption. bevy_app = { path = "../bevy_app", version = "0.16.0-dev", default-features = false, features = [ - "browser", + "web", ] } bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev", default-features = false, features = [ - "browser", + "web", ] } bevy_platform_support = { path = "../bevy_platform_support", version = "0.16.0-dev", default-features = false, features = [ - "browser", + "web", ] } [lints] From 96ec974ac64ce952d7b9c5a09a81e3b1dbcda40a Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Wed, 29 Jan 2025 10:59:32 +1100 Subject: [PATCH 5/5] Formatting --- crates/bevy_tasks/src/lib.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/bevy_tasks/src/lib.rs b/crates/bevy_tasks/src/lib.rs index c275671376d9d..0e3b0475dc8a6 100644 --- a/crates/bevy_tasks/src/lib.rs +++ b/crates/bevy_tasks/src/lib.rs @@ -56,10 +56,7 @@ pub mod futures; mod slice; pub use slice::{ParallelSlice, ParallelSliceMut}; -#[cfg_attr( - all(target_arch = "wasm32", feature = "web"), - path = "wasm_task.rs" -)] +#[cfg_attr(all(target_arch = "wasm32", feature = "web"), path = "wasm_task.rs")] mod task; pub use task::Task;