From 2553464286e92ef024e7ecd1ef7dbf0c1b7002e0 Mon Sep 17 00:00:00 2001 From: Mike Hsu Date: Wed, 28 Sep 2022 14:14:30 -0700 Subject: [PATCH 1/8] tick local executor --- crates/bevy_tasks/src/task_pool.rs | 139 ++++++++++++++++------------- 1 file changed, 75 insertions(+), 64 deletions(-) diff --git a/crates/bevy_tasks/src/task_pool.rs b/crates/bevy_tasks/src/task_pool.rs index e0ac0101d56ac..de8329c2799e4 100644 --- a/crates/bevy_tasks/src/task_pool.rs +++ b/crates/bevy_tasks/src/task_pool.rs @@ -8,7 +8,7 @@ use std::{ }; use concurrent_queue::ConcurrentQueue; -use futures_lite::{future, pin}; +use futures_lite::{future, pin, FutureExt}; use crate::Task; @@ -117,9 +117,16 @@ impl TaskPool { thread_builder .spawn(move || { - let shutdown_future = ex.run(shutdown_rx.recv()); - // Use unwrap_err because we expect a Closed error - future::block_on(shutdown_future).unwrap_err(); + TaskPool::LOCAL_EXECUTOR.with(|local_executor| { + let tick_forever = async move { + loop { + local_executor.tick().await; + } + }; + let shutdown_future = ex.run(tick_forever.or(shutdown_rx.recv())); + // Use unwrap_err because we expect a Closed error + future::block_on(shutdown_future).unwrap_err(); + }); }) .expect("Failed to spawn thread.") }) @@ -216,71 +223,75 @@ impl TaskPool { F: for<'scope> FnOnce(&'scope Scope<'scope, 'env, T>), T: Send + 'static, { - // SAFETY: This safety comment applies to all references transmuted to 'env. - // Any futures spawned with these references need to return before this function completes. - // This is guaranteed because we drive all the futures spawned onto the Scope - // to completion in this function. However, rust has no way of knowing this so we - // transmute the lifetimes to 'env here to appease the compiler as it is unable to validate safety. - let executor: &async_executor::Executor = &*self.executor; - let executor: &'env async_executor::Executor = unsafe { mem::transmute(executor) }; - let task_scope_executor = &async_executor::Executor::default(); - let task_scope_executor: &'env async_executor::Executor = - unsafe { mem::transmute(task_scope_executor) }; - let spawned: ConcurrentQueue> = ConcurrentQueue::unbounded(); - let spawned_ref: &'env ConcurrentQueue> = - unsafe { mem::transmute(&spawned) }; - - let scope = Scope { - executor, - task_scope_executor, - spawned: spawned_ref, - scope: PhantomData, - env: PhantomData, - }; - - let scope_ref: &'env Scope<'_, 'env, T> = unsafe { mem::transmute(&scope) }; - - f(scope_ref); - - if spawned.is_empty() { - Vec::new() - } else { - let get_results = async move { - let mut results = Vec::with_capacity(spawned.len()); - while let Ok(task) = spawned.pop() { - results.push(task.await); - } - - results + TaskPool::LOCAL_EXECUTOR.with(|local_executor| { + // SAFETY: This safety comment applies to all references transmuted to 'env. + // Any futures spawned with these references need to return before this function completes. + // This is guaranteed because we drive all the futures spawned onto the Scope + // to completion in this function. However, rust has no way of knowing this so we + // transmute the lifetimes to 'env here to appease the compiler as it is unable to validate safety. + let executor: &async_executor::Executor = &*self.executor; + let executor: &'env async_executor::Executor = unsafe { mem::transmute(executor) }; + let task_scope_executor = &async_executor::Executor::default(); + let task_scope_executor: &'env async_executor::Executor = + unsafe { mem::transmute(task_scope_executor) }; + let spawned: ConcurrentQueue> = ConcurrentQueue::unbounded(); + let spawned_ref: &'env ConcurrentQueue> = + unsafe { mem::transmute(&spawned) }; + + let scope = Scope { + executor, + task_scope_executor, + spawned: spawned_ref, + scope: PhantomData, + env: PhantomData, }; - // Pin the futures on the stack. - pin!(get_results); - - // 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 get_results: Pin<&mut (dyn Future> + 'static + Send)> = get_results; - let get_results: Pin<&'static mut (dyn Future> + 'static + Send)> = - unsafe { mem::transmute(get_results) }; - - // The thread that calls scope() will participate in driving tasks in the pool - // forward until the tasks that are spawned by this scope() call - // complete. (If the caller of scope() happens to be a thread in - // this thread pool, and we only have one thread in the pool, then - // simply calling future::block_on(spawned) would deadlock.) - let mut spawned = task_scope_executor.spawn(get_results); - - loop { - if let Some(result) = future::block_on(future::poll_once(&mut spawned)) { - break result; + let scope_ref: &'env Scope<'_, 'env, T> = unsafe { mem::transmute(&scope) }; + + f(scope_ref); + + if spawned.is_empty() { + Vec::new() + } else { + let get_results = async move { + let mut results = Vec::with_capacity(spawned.len()); + while let Ok(task) = spawned.pop() { + results.push(task.await); + } + + results }; - self.executor.try_tick(); - task_scope_executor.try_tick(); + // Pin the futures on the stack. + pin!(get_results); + + // 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 get_results: Pin<&mut (dyn Future> + 'static + Send)> = + get_results; + let get_results: Pin<&'static mut (dyn Future> + 'static + Send)> = + unsafe { mem::transmute(get_results) }; + + // The thread that calls scope() will participate in driving tasks in the pool + // forward until the tasks that are spawned by this scope() call + // complete. (If the caller of scope() happens to be a thread in + // this thread pool, and we only have one thread in the pool, then + // simply calling future::block_on(spawned) would deadlock.) + let mut spawned = task_scope_executor.spawn(get_results); + + loop { + if let Some(result) = future::block_on(future::poll_once(&mut spawned)) { + break result; + }; + + self.executor.try_tick(); + task_scope_executor.try_tick(); + local_executor.try_tick(); + } } - } + }) } /// Spawns a static future onto the thread pool. The returned Task is a future. It can also be From bf883a17112d3907450bfb3bafaecedbc66054cb Mon Sep 17 00:00:00 2001 From: Mike Hsu Date: Sun, 2 Oct 2022 22:47:18 -0700 Subject: [PATCH 2/8] add function to tick local executors --- crates/bevy_tasks/src/task_pool.rs | 18 ++++++++++++++++++ crates/bevy_tasks/src/usages.rs | 24 ++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/crates/bevy_tasks/src/task_pool.rs b/crates/bevy_tasks/src/task_pool.rs index de8329c2799e4..113ce3f80ec06 100644 --- a/crates/bevy_tasks/src/task_pool.rs +++ b/crates/bevy_tasks/src/task_pool.rs @@ -317,6 +317,24 @@ impl TaskPool { { Task::new(TaskPool::LOCAL_EXECUTOR.with(|executor| executor.spawn(future))) } + + /// Runs a function with the local executor. Typically used to tick + /// the local executor on the main thread as it needs to share time with + /// other things. + /// + /// ```rust + /// use bevy_tasks::TaskPool; + /// + /// TaskPool::new().with_local_executor(|local_executor| { + /// local_executor.try_tick(); + /// }); + /// ``` + pub fn with_local_executor(&self, f: F) -> R + where + F: FnOnce(&async_executor::LocalExecutor) -> R, + { + Self::LOCAL_EXECUTOR.with(f) + } } impl Default for TaskPool { diff --git a/crates/bevy_tasks/src/usages.rs b/crates/bevy_tasks/src/usages.rs index 419d842f47168..6247d31b5916b 100644 --- a/crates/bevy_tasks/src/usages.rs +++ b/crates/bevy_tasks/src/usages.rs @@ -109,3 +109,27 @@ impl Deref for IoTaskPool { &self.0 } } + +/// Used by `bevy_app` to tick the global tasks pools on the main thread. +pub fn tick_global_task_pools_on_main_thread() { + COMPUTE_TASK_POOL + .get() + .unwrap() + .with_local_executor(|compute_local_executor| { + ASYNC_COMPUTE_TASK_POOL + .get() + .unwrap() + .with_local_executor(|async_local_executor| { + IO_TASK_POOL + .get() + .unwrap() + .with_local_executor(|io_local_executor| { + for _ in 0..20 { + compute_local_executor.try_tick(); + async_local_executor.try_tick(); + io_local_executor.try_tick(); + } + }); + }); + }); +} From f95c4176c69b19650927761929105fa38c582ff0 Mon Sep 17 00:00:00 2001 From: Mike Hsu Date: Tue, 4 Oct 2022 11:12:05 -0700 Subject: [PATCH 3/8] add system to tick local executors on main thread --- crates/bevy_core/src/lib.rs | 10 ++++++++++ crates/bevy_tasks/src/lib.rs | 5 ++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/crates/bevy_core/src/lib.rs b/crates/bevy_core/src/lib.rs index 8fc580848fce9..9c9a48b2dc09b 100644 --- a/crates/bevy_core/src/lib.rs +++ b/crates/bevy_core/src/lib.rs @@ -4,7 +4,9 @@ mod name; mod task_pool_options; +use bevy_ecs::schedule::IntoSystemDescriptor; use bevy_ecs::system::Resource; +use bevy_ecs::world::World; pub use bytemuck::{bytes_of, cast_slice, Pod, Zeroable}; pub use name::*; pub use task_pool_options::*; @@ -17,6 +19,7 @@ pub mod prelude { use bevy_app::prelude::*; use bevy_ecs::entity::Entity; +use bevy_tasks::prelude::tick_global_task_pools_on_main_thread; use bevy_utils::{Duration, HashSet, Instant}; use std::borrow::Cow; use std::ops::Range; @@ -34,6 +37,13 @@ impl Plugin for CorePlugin { .unwrap_or_default() .create_default_pools(); + // run function in an exclusive system to make sure it runs on main thread + fn tick_local_executors(_world: &mut World) { + tick_global_task_pools_on_main_thread(); + } + + app.add_system_to_stage(bevy_app::CoreStage::Last, tick.at_end()); + app.register_type::().register_type::(); register_rust_types(app); diff --git a/crates/bevy_tasks/src/lib.rs b/crates/bevy_tasks/src/lib.rs index 62c42a7717868..437bc3a9b8062 100644 --- a/crates/bevy_tasks/src/lib.rs +++ b/crates/bevy_tasks/src/lib.rs @@ -29,7 +29,10 @@ pub mod prelude { pub use crate::{ iter::ParallelIterator, slice::{ParallelSlice, ParallelSliceMut}, - usages::{AsyncComputeTaskPool, ComputeTaskPool, IoTaskPool}, + usages::{ + tick_global_task_pools_on_main_thread, AsyncComputeTaskPool, ComputeTaskPool, + IoTaskPool, + }, }; } From 7bddf73c2246b8eb5126565ea89ca75fca99e44b Mon Sep 17 00:00:00 2001 From: Mike Hsu Date: Tue, 4 Oct 2022 12:21:48 -0700 Subject: [PATCH 4/8] remove unnecessary wrapping system --- crates/bevy_core/src/lib.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/crates/bevy_core/src/lib.rs b/crates/bevy_core/src/lib.rs index 9c9a48b2dc09b..9d980aaf65a0f 100644 --- a/crates/bevy_core/src/lib.rs +++ b/crates/bevy_core/src/lib.rs @@ -6,7 +6,6 @@ mod task_pool_options; use bevy_ecs::schedule::IntoSystemDescriptor; use bevy_ecs::system::Resource; -use bevy_ecs::world::World; pub use bytemuck::{bytes_of, cast_slice, Pod, Zeroable}; pub use name::*; pub use task_pool_options::*; @@ -37,12 +36,10 @@ impl Plugin for CorePlugin { .unwrap_or_default() .create_default_pools(); - // run function in an exclusive system to make sure it runs on main thread - fn tick_local_executors(_world: &mut World) { - tick_global_task_pools_on_main_thread(); - } - - app.add_system_to_stage(bevy_app::CoreStage::Last, tick.at_end()); + app.add_system_to_stage( + bevy_app::CoreStage::Last, + tick_global_task_pools_on_main_thread.at_end(), + ); app.register_type::().register_type::(); From 45e27ca599d039d378130c4c1e6471499308b7df Mon Sep 17 00:00:00 2001 From: Mike Hsu Date: Tue, 4 Oct 2022 13:34:33 -0700 Subject: [PATCH 5/8] add test --- crates/bevy_core/Cargo.toml | 3 +++ crates/bevy_core/src/lib.rs | 39 +++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/crates/bevy_core/Cargo.toml b/crates/bevy_core/Cargo.toml index 08bd7987c36f7..5c26103cb7a82 100644 --- a/crates/bevy_core/Cargo.toml +++ b/crates/bevy_core/Cargo.toml @@ -20,3 +20,6 @@ bevy_utils = { path = "../bevy_utils", version = "0.9.0-dev" } # other bytemuck = "1.5" + +[dev-dependencies] +crossbeam-channel = "0.5.0" diff --git a/crates/bevy_core/src/lib.rs b/crates/bevy_core/src/lib.rs index 9d980aaf65a0f..9a351ab31a094 100644 --- a/crates/bevy_core/src/lib.rs +++ b/crates/bevy_core/src/lib.rs @@ -99,3 +99,42 @@ fn register_math_types(app: &mut App) { /// Wraps to 0 when it reaches the maximum u32 value #[derive(Default, Resource, Clone, Copy)] pub struct FrameCount(pub u32); + +#[cfg(test)] +mod tests { + use super::*; + use bevy_tasks::prelude::{AsyncComputeTaskPool, ComputeTaskPool, IoTaskPool}; + + #[test] + fn runs_spawn_local_tasks() { + let mut app = App::new(); + app.add_plugin(CorePlugin); + + let (async_tx, async_rx) = crossbeam_channel::unbounded(); + AsyncComputeTaskPool::get() + .spawn_local(async move { + async_tx.send(()).unwrap(); + }) + .detach(); + + let (compute_tx, compute_rx) = crossbeam_channel::unbounded(); + ComputeTaskPool::get() + .spawn_local(async move { + compute_tx.send(()).unwrap(); + }) + .detach(); + + let (io_tx, io_rx) = crossbeam_channel::unbounded(); + IoTaskPool::get() + .spawn_local(async move { + io_tx.send(()).unwrap(); + }) + .detach(); + + app.run(); + + async_rx.try_recv().unwrap(); + compute_rx.try_recv().unwrap(); + io_rx.try_recv().unwrap(); + } +} From c6699c0f8a6b6d52ce361a7604b833728d099737 Mon Sep 17 00:00:00 2001 From: Mike Hsu Date: Tue, 4 Oct 2022 13:34:54 -0700 Subject: [PATCH 6/8] bump ticker up to 100 loops --- crates/bevy_tasks/src/usages.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/bevy_tasks/src/usages.rs b/crates/bevy_tasks/src/usages.rs index 6247d31b5916b..e552be92284a0 100644 --- a/crates/bevy_tasks/src/usages.rs +++ b/crates/bevy_tasks/src/usages.rs @@ -110,7 +110,8 @@ impl Deref for IoTaskPool { } } -/// Used by `bevy_app` to tick the global tasks pools on the main thread. +/// Used by `bevy_core` to tick the global tasks pools on the main thread. +/// This will run a maximum of 100 local tasks per executor per call to this function. pub fn tick_global_task_pools_on_main_thread() { COMPUTE_TASK_POOL .get() @@ -124,7 +125,7 @@ pub fn tick_global_task_pools_on_main_thread() { .get() .unwrap() .with_local_executor(|io_local_executor| { - for _ in 0..20 { + for _ in 0..100 { compute_local_executor.try_tick(); async_local_executor.try_tick(); io_local_executor.try_tick(); From 109296ee84b7d4eca9fd76ded7ca251de530e879 Mon Sep 17 00:00:00 2001 From: Mike Hsu Date: Tue, 4 Oct 2022 13:51:38 -0700 Subject: [PATCH 7/8] remove ticking local executor from scope --- crates/bevy_tasks/src/task_pool.rs | 124 ++++++++++++++--------------- 1 file changed, 60 insertions(+), 64 deletions(-) diff --git a/crates/bevy_tasks/src/task_pool.rs b/crates/bevy_tasks/src/task_pool.rs index 113ce3f80ec06..fefd24798163a 100644 --- a/crates/bevy_tasks/src/task_pool.rs +++ b/crates/bevy_tasks/src/task_pool.rs @@ -223,75 +223,71 @@ impl TaskPool { F: for<'scope> FnOnce(&'scope Scope<'scope, 'env, T>), T: Send + 'static, { - TaskPool::LOCAL_EXECUTOR.with(|local_executor| { - // SAFETY: This safety comment applies to all references transmuted to 'env. - // Any futures spawned with these references need to return before this function completes. - // This is guaranteed because we drive all the futures spawned onto the Scope - // to completion in this function. However, rust has no way of knowing this so we - // transmute the lifetimes to 'env here to appease the compiler as it is unable to validate safety. - let executor: &async_executor::Executor = &*self.executor; - let executor: &'env async_executor::Executor = unsafe { mem::transmute(executor) }; - let task_scope_executor = &async_executor::Executor::default(); - let task_scope_executor: &'env async_executor::Executor = - unsafe { mem::transmute(task_scope_executor) }; - let spawned: ConcurrentQueue> = ConcurrentQueue::unbounded(); - let spawned_ref: &'env ConcurrentQueue> = - unsafe { mem::transmute(&spawned) }; - - let scope = Scope { - executor, - task_scope_executor, - spawned: spawned_ref, - scope: PhantomData, - env: PhantomData, - }; - - let scope_ref: &'env Scope<'_, 'env, T> = unsafe { mem::transmute(&scope) }; - - f(scope_ref); + // SAFETY: This safety comment applies to all references transmuted to 'env. + // Any futures spawned with these references need to return before this function completes. + // This is guaranteed because we drive all the futures spawned onto the Scope + // to completion in this function. However, rust has no way of knowing this so we + // transmute the lifetimes to 'env here to appease the compiler as it is unable to validate safety. + let executor: &async_executor::Executor = &*self.executor; + let executor: &'env async_executor::Executor = unsafe { mem::transmute(executor) }; + let task_scope_executor = &async_executor::Executor::default(); + let task_scope_executor: &'env async_executor::Executor = + unsafe { mem::transmute(task_scope_executor) }; + let spawned: ConcurrentQueue> = ConcurrentQueue::unbounded(); + let spawned_ref: &'env ConcurrentQueue> = + unsafe { mem::transmute(&spawned) }; + + let scope = Scope { + executor, + task_scope_executor, + spawned: spawned_ref, + scope: PhantomData, + env: PhantomData, + }; + + let scope_ref: &'env Scope<'_, 'env, T> = unsafe { mem::transmute(&scope) }; + + f(scope_ref); + + if spawned.is_empty() { + Vec::new() + } else { + let get_results = async move { + let mut results = Vec::with_capacity(spawned.len()); + while let Ok(task) = spawned.pop() { + results.push(task.await); + } - if spawned.is_empty() { - Vec::new() - } else { - let get_results = async move { - let mut results = Vec::with_capacity(spawned.len()); - while let Ok(task) = spawned.pop() { - results.push(task.await); - } + results + }; - results + // Pin the futures on the stack. + pin!(get_results); + + // 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 get_results: Pin<&mut (dyn Future> + 'static + Send)> = get_results; + let get_results: Pin<&'static mut (dyn Future> + 'static + Send)> = + unsafe { mem::transmute(get_results) }; + + // The thread that calls scope() will participate in driving tasks in the pool + // forward until the tasks that are spawned by this scope() call + // complete. (If the caller of scope() happens to be a thread in + // this thread pool, and we only have one thread in the pool, then + // simply calling future::block_on(spawned) would deadlock.) + let mut spawned = task_scope_executor.spawn(get_results); + + loop { + if let Some(result) = future::block_on(future::poll_once(&mut spawned)) { + break result; }; - // Pin the futures on the stack. - pin!(get_results); - - // 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 get_results: Pin<&mut (dyn Future> + 'static + Send)> = - get_results; - let get_results: Pin<&'static mut (dyn Future> + 'static + Send)> = - unsafe { mem::transmute(get_results) }; - - // The thread that calls scope() will participate in driving tasks in the pool - // forward until the tasks that are spawned by this scope() call - // complete. (If the caller of scope() happens to be a thread in - // this thread pool, and we only have one thread in the pool, then - // simply calling future::block_on(spawned) would deadlock.) - let mut spawned = task_scope_executor.spawn(get_results); - - loop { - if let Some(result) = future::block_on(future::poll_once(&mut spawned)) { - break result; - }; - - self.executor.try_tick(); - task_scope_executor.try_tick(); - local_executor.try_tick(); - } + self.executor.try_tick(); + task_scope_executor.try_tick(); } - }) + } } /// Spawns a static future onto the thread pool. The returned Task is a future. It can also be From b391507fb5e749ef213d0fbe806625457663c314 Mon Sep 17 00:00:00 2001 From: Mike Hsu Date: Tue, 4 Oct 2022 14:08:27 -0700 Subject: [PATCH 8/8] fix building for wasm32 --- crates/bevy_core/src/lib.rs | 8 ++++++-- crates/bevy_tasks/src/lib.rs | 7 +++---- crates/bevy_tasks/src/usages.rs | 1 + 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/crates/bevy_core/src/lib.rs b/crates/bevy_core/src/lib.rs index 9a351ab31a094..e2aab006653ac 100644 --- a/crates/bevy_core/src/lib.rs +++ b/crates/bevy_core/src/lib.rs @@ -4,7 +4,6 @@ mod name; mod task_pool_options; -use bevy_ecs::schedule::IntoSystemDescriptor; use bevy_ecs::system::Resource; pub use bytemuck::{bytes_of, cast_slice, Pod, Zeroable}; pub use name::*; @@ -18,11 +17,15 @@ pub mod prelude { use bevy_app::prelude::*; use bevy_ecs::entity::Entity; -use bevy_tasks::prelude::tick_global_task_pools_on_main_thread; use bevy_utils::{Duration, HashSet, Instant}; use std::borrow::Cow; use std::ops::Range; +#[cfg(not(target_arch = "wasm32"))] +use bevy_ecs::schedule::IntoSystemDescriptor; +#[cfg(not(target_arch = "wasm32"))] +use bevy_tasks::tick_global_task_pools_on_main_thread; + /// Adds core functionality to Apps. #[derive(Default)] pub struct CorePlugin; @@ -36,6 +39,7 @@ impl Plugin for CorePlugin { .unwrap_or_default() .create_default_pools(); + #[cfg(not(target_arch = "wasm32"))] app.add_system_to_stage( bevy_app::CoreStage::Last, tick_global_task_pools_on_main_thread.at_end(), diff --git a/crates/bevy_tasks/src/lib.rs b/crates/bevy_tasks/src/lib.rs index 437bc3a9b8062..802f6c267b7cf 100644 --- a/crates/bevy_tasks/src/lib.rs +++ b/crates/bevy_tasks/src/lib.rs @@ -18,6 +18,8 @@ mod single_threaded_task_pool; pub use single_threaded_task_pool::{Scope, TaskPool, TaskPoolBuilder}; mod usages; +#[cfg(not(target_arch = "wasm32"))] +pub use usages::tick_global_task_pools_on_main_thread; pub use usages::{AsyncComputeTaskPool, ComputeTaskPool, IoTaskPool}; mod iter; @@ -29,10 +31,7 @@ pub mod prelude { pub use crate::{ iter::ParallelIterator, slice::{ParallelSlice, ParallelSliceMut}, - usages::{ - tick_global_task_pools_on_main_thread, AsyncComputeTaskPool, ComputeTaskPool, - IoTaskPool, - }, + usages::{AsyncComputeTaskPool, ComputeTaskPool, IoTaskPool}, }; } diff --git a/crates/bevy_tasks/src/usages.rs b/crates/bevy_tasks/src/usages.rs index e552be92284a0..1d0c83b271c2f 100644 --- a/crates/bevy_tasks/src/usages.rs +++ b/crates/bevy_tasks/src/usages.rs @@ -112,6 +112,7 @@ impl Deref for IoTaskPool { /// Used by `bevy_core` to tick the global tasks pools on the main thread. /// This will run a maximum of 100 local tasks per executor per call to this function. +#[cfg(not(target_arch = "wasm32"))] pub fn tick_global_task_pools_on_main_thread() { COMPUTE_TASK_POOL .get()