Skip to content

Commit d6cbbbb

Browse files
committed
Consolidate together Bevy's TaskPools
1 parent 9d420b4 commit d6cbbbb

File tree

19 files changed

+142
-266
lines changed

19 files changed

+142
-266
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5027,7 +5027,7 @@ current changes on git with [previous release tags][git_tag_comparison].
50275027
- [Fix confusing near and far fields in Camera][4457]
50285028
- [Allow minimising window if using a 2d camera][4527]
50295029
- [WGSL: use correct syntax for matrix access][5039]
5030-
- [Gltf: do not import `IoTaskPool` in wasm][5038]
5030+
- [Gltf: do not import `ComputeTaskPool` in wasm][5038]
50315031
- [Fix skinned mesh normal handling in mesh shader][5095]
50325032
- [Don't panic when `StandardMaterial` `normal_map` hasn't loaded yet][5307]
50335033
- [Fix incorrect rotation in `Transform::rotate_around`][5300]

crates/bevy_asset/src/processor/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::{
1818
};
1919
use bevy_ecs::prelude::*;
2020
use bevy_log::{debug, error, trace, warn};
21-
use bevy_tasks::IoTaskPool;
21+
use bevy_tasks::ComputeTaskPool;
2222
use bevy_utils::{BoxedFuture, HashMap, HashSet};
2323
use futures_io::ErrorKind;
2424
use futures_lite::{AsyncReadExt, AsyncWriteExt, StreamExt};
@@ -165,7 +165,7 @@ impl AssetProcessor {
165165
pub fn process_assets(&self) {
166166
let start_time = std::time::Instant::now();
167167
debug!("Processing Assets");
168-
IoTaskPool::get().scope(|scope| {
168+
ComputeTaskPool::get().scope(|scope| {
169169
scope.spawn(async move {
170170
self.initialize().await.unwrap();
171171
for source in self.sources().iter_processed() {
@@ -315,7 +315,7 @@ impl AssetProcessor {
315315
#[cfg(any(target_arch = "wasm32", not(feature = "multi-threaded")))]
316316
error!("AddFolder event cannot be handled in single threaded mode (or WASM) yet.");
317317
#[cfg(all(not(target_arch = "wasm32"), feature = "multi-threaded"))]
318-
IoTaskPool::get().scope(|scope| {
318+
ComputeTaskPool::get().scope(|scope| {
319319
scope.spawn(async move {
320320
self.process_assets_internal(scope, source, path)
321321
.await
@@ -457,7 +457,7 @@ impl AssetProcessor {
457457
loop {
458458
let mut check_reprocess_queue =
459459
std::mem::take(&mut self.data.asset_infos.write().await.check_reprocess_queue);
460-
IoTaskPool::get().scope(|scope| {
460+
ComputeTaskPool::get().scope(|scope| {
461461
for path in check_reprocess_queue.drain(..) {
462462
let processor = self.clone();
463463
let source = self.get_source(path.source()).unwrap();

crates/bevy_asset/src/server/loaders.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::{
44
};
55
use async_broadcast::RecvError;
66
use bevy_log::{error, warn};
7-
use bevy_tasks::IoTaskPool;
7+
use bevy_tasks::ComputeTaskPool;
88
use bevy_utils::{HashMap, TypeIdMap};
99
use std::{any::TypeId, sync::Arc};
1010
use thiserror::Error;
@@ -78,7 +78,7 @@ impl AssetLoaders {
7878
match maybe_loader {
7979
MaybeAssetLoader::Ready(_) => unreachable!(),
8080
MaybeAssetLoader::Pending { sender, .. } => {
81-
IoTaskPool::get()
81+
ComputeTaskPool::get()
8282
.spawn(async move {
8383
let _ = sender.broadcast(loader).await;
8484
})

crates/bevy_asset/src/server/mod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use crate::{
1919
};
2020
use bevy_ecs::prelude::*;
2121
use bevy_log::{error, info};
22-
use bevy_tasks::IoTaskPool;
22+
use bevy_tasks::ComputeTaskPool;
2323
use bevy_utils::{CowArc, HashSet};
2424
use crossbeam_channel::{Receiver, Sender};
2525
use futures_lite::StreamExt;
@@ -296,7 +296,7 @@ impl AssetServer {
296296
if should_load {
297297
let owned_handle = Some(handle.clone().untyped());
298298
let server = self.clone();
299-
IoTaskPool::get()
299+
ComputeTaskPool::get()
300300
.spawn(async move {
301301
if let Err(err) = server.load_internal(owned_handle, path, false, None).await {
302302
error!("{}", err);
@@ -366,7 +366,7 @@ impl AssetServer {
366366
let id = handle.id().untyped();
367367

368368
let server = self.clone();
369-
IoTaskPool::get()
369+
ComputeTaskPool::get()
370370
.spawn(async move {
371371
let path_clone = path.clone();
372372
match server.load_untyped_async(path).await {
@@ -551,7 +551,7 @@ impl AssetServer {
551551
pub fn reload<'a>(&self, path: impl Into<AssetPath<'a>>) {
552552
let server = self.clone();
553553
let path = path.into().into_owned();
554-
IoTaskPool::get()
554+
ComputeTaskPool::get()
555555
.spawn(async move {
556556
let mut reloaded = false;
557557

@@ -690,7 +690,7 @@ impl AssetServer {
690690

691691
let path = path.into_owned();
692692
let server = self.clone();
693-
IoTaskPool::get()
693+
ComputeTaskPool::get()
694694
.spawn(async move {
695695
let Ok(source) = server.get_source(path.source()) else {
696696
error!(

crates/bevy_core/src/lib.rs

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,7 @@ fn register_math_types(app: &mut App) {
112112
.register_type::<Vec<bevy_math::Vec3>>();
113113
}
114114

115-
/// Setup of default task pools: [`AsyncComputeTaskPool`](bevy_tasks::AsyncComputeTaskPool),
116-
/// [`ComputeTaskPool`](bevy_tasks::ComputeTaskPool), [`IoTaskPool`](bevy_tasks::IoTaskPool).
115+
/// Setup of default task pool: [`ComputeTaskPool`](bevy_tasks::ComputeTaskPool).
117116
#[derive(Default)]
118117
pub struct TaskPoolPlugin {
119118
/// Options for the [`TaskPool`](bevy_tasks::TaskPool) created at application start.
@@ -175,39 +174,23 @@ pub fn update_frame_count(mut frame_count: ResMut<FrameCount>) {
175174
#[cfg(test)]
176175
mod tests {
177176
use super::*;
178-
use bevy_tasks::prelude::{AsyncComputeTaskPool, ComputeTaskPool, IoTaskPool};
177+
use bevy_tasks::prelude::ComputeTaskPool;
179178

180179
#[test]
181180
fn runs_spawn_local_tasks() {
182181
let mut app = App::new();
183182
app.add_plugins((TaskPoolPlugin::default(), TypeRegistrationPlugin));
184183

185-
let (async_tx, async_rx) = crossbeam_channel::unbounded();
186-
AsyncComputeTaskPool::get()
187-
.spawn_local(async move {
188-
async_tx.send(()).unwrap();
189-
})
190-
.detach();
191-
192184
let (compute_tx, compute_rx) = crossbeam_channel::unbounded();
193185
ComputeTaskPool::get()
194186
.spawn_local(async move {
195187
compute_tx.send(()).unwrap();
196188
})
197189
.detach();
198190

199-
let (io_tx, io_rx) = crossbeam_channel::unbounded();
200-
IoTaskPool::get()
201-
.spawn_local(async move {
202-
io_tx.send(()).unwrap();
203-
})
204-
.detach();
205-
206191
app.run();
207192

208-
async_rx.try_recv().unwrap();
209193
compute_rx.try_recv().unwrap();
210-
io_rx.try_recv().unwrap();
211194
}
212195

213196
#[test]
Lines changed: 7 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,6 @@
1-
use bevy_tasks::{AsyncComputeTaskPool, ComputeTaskPool, IoTaskPool, TaskPoolBuilder};
1+
use bevy_tasks::{ComputeTaskPool, TaskPoolBuilder};
22
use bevy_utils::tracing::trace;
33

4-
/// Defines a simple way to determine how many threads to use given the number of remaining cores
5-
/// and number of total cores
6-
#[derive(Clone, Debug)]
7-
pub struct TaskPoolThreadAssignmentPolicy {
8-
/// Force using at least this many threads
9-
pub min_threads: usize,
10-
/// Under no circumstance use more than this many threads for this pool
11-
pub max_threads: usize,
12-
/// Target using this percentage of total cores, clamped by min_threads and max_threads. It is
13-
/// permitted to use 1.0 to try to use all remaining threads
14-
pub percent: f32,
15-
}
16-
17-
impl TaskPoolThreadAssignmentPolicy {
18-
/// Determine the number of threads to use for this task pool
19-
fn get_number_of_threads(&self, remaining_threads: usize, total_threads: usize) -> usize {
20-
assert!(self.percent >= 0.0);
21-
let mut desired = (total_threads as f32 * self.percent).round() as usize;
22-
23-
// Limit ourselves to the number of cores available
24-
desired = desired.min(remaining_threads);
25-
26-
// Clamp by min_threads, max_threads. (This may result in us using more threads than are
27-
// available, this is intended. An example case where this might happen is a device with
28-
// <= 2 threads.
29-
desired.clamp(self.min_threads, self.max_threads)
30-
}
31-
}
32-
334
/// Helper for configuring and creating the default task pools. For end-users who want full control,
345
/// set up [`TaskPoolPlugin`](super::TaskPoolPlugin)
356
#[derive(Clone, Debug)]
@@ -40,13 +11,6 @@ pub struct TaskPoolOptions {
4011
/// If the number of physical cores is greater than max_total_threads, force using
4112
/// max_total_threads
4213
pub max_total_threads: usize,
43-
44-
/// Used to determine number of IO threads to allocate
45-
pub io: TaskPoolThreadAssignmentPolicy,
46-
/// Used to determine number of async compute threads to allocate
47-
pub async_compute: TaskPoolThreadAssignmentPolicy,
48-
/// Used to determine number of compute threads to allocate
49-
pub compute: TaskPoolThreadAssignmentPolicy,
5014
}
5115

5216
impl Default for TaskPoolOptions {
@@ -55,27 +19,6 @@ impl Default for TaskPoolOptions {
5519
// By default, use however many cores are available on the system
5620
min_total_threads: 1,
5721
max_total_threads: usize::MAX,
58-
59-
// Use 25% of cores for IO, at least 1, no more than 4
60-
io: TaskPoolThreadAssignmentPolicy {
61-
min_threads: 1,
62-
max_threads: 4,
63-
percent: 0.25,
64-
},
65-
66-
// Use 25% of cores for async compute, at least 1, no more than 4
67-
async_compute: TaskPoolThreadAssignmentPolicy {
68-
min_threads: 1,
69-
max_threads: 4,
70-
percent: 0.25,
71-
},
72-
73-
// Use all remaining cores for compute (at least 1)
74-
compute: TaskPoolThreadAssignmentPolicy {
75-
min_threads: 1,
76-
max_threads: usize::MAX,
77-
percent: 1.0, // This 1.0 here means "whatever is left over"
78-
},
7922
}
8023
}
8124
}
@@ -96,57 +39,11 @@ impl TaskPoolOptions {
9639
.clamp(self.min_total_threads, self.max_total_threads);
9740
trace!("Assigning {} cores to default task pools", total_threads);
9841

99-
let mut remaining_threads = total_threads;
100-
101-
{
102-
// Determine the number of IO threads we will use
103-
let io_threads = self
104-
.io
105-
.get_number_of_threads(remaining_threads, total_threads);
106-
107-
trace!("IO Threads: {}", io_threads);
108-
remaining_threads = remaining_threads.saturating_sub(io_threads);
109-
110-
IoTaskPool::get_or_init(|| {
111-
TaskPoolBuilder::default()
112-
.num_threads(io_threads)
113-
.thread_name("IO Task Pool".to_string())
114-
.build()
115-
});
116-
}
117-
118-
{
119-
// Determine the number of async compute threads we will use
120-
let async_compute_threads = self
121-
.async_compute
122-
.get_number_of_threads(remaining_threads, total_threads);
123-
124-
trace!("Async Compute Threads: {}", async_compute_threads);
125-
remaining_threads = remaining_threads.saturating_sub(async_compute_threads);
126-
127-
AsyncComputeTaskPool::get_or_init(|| {
128-
TaskPoolBuilder::default()
129-
.num_threads(async_compute_threads)
130-
.thread_name("Async Compute Task Pool".to_string())
131-
.build()
132-
});
133-
}
134-
135-
{
136-
// Determine the number of compute threads we will use
137-
// This is intentionally last so that an end user can specify 1.0 as the percent
138-
let compute_threads = self
139-
.compute
140-
.get_number_of_threads(remaining_threads, total_threads);
141-
142-
trace!("Compute Threads: {}", compute_threads);
143-
144-
ComputeTaskPool::get_or_init(|| {
145-
TaskPoolBuilder::default()
146-
.num_threads(compute_threads)
147-
.thread_name("Compute Task Pool".to_string())
148-
.build()
149-
});
150-
}
42+
ComputeTaskPool::get_or_init(|| {
43+
TaskPoolBuilder::default()
44+
.num_threads(total_threads)
45+
.thread_name("Compute Task Pool".to_string())
46+
.build()
47+
});
15148
}
15249
}

crates/bevy_gltf/src/loader.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ use bevy_render::{
3434
};
3535
use bevy_scene::Scene;
3636
#[cfg(not(target_arch = "wasm32"))]
37-
use bevy_tasks::IoTaskPool;
37+
use bevy_tasks::ComputeTaskPool;
3838
use bevy_transform::components::Transform;
3939
use bevy_utils::{
4040
smallvec::{smallvec, SmallVec},
@@ -348,7 +348,7 @@ async fn load_gltf<'a, 'b, 'c>(
348348
}
349349
} else {
350350
#[cfg(not(target_arch = "wasm32"))]
351-
IoTaskPool::get()
351+
ComputeTaskPool::get()
352352
.scope(|scope| {
353353
gltf.textures().for_each(|gltf_texture| {
354354
let parent_path = load_context.path().parent().unwrap();

crates/bevy_pbr/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ bevy_derive = { path = "../bevy_derive", version = "0.14.0-dev" }
3131

3232
# other
3333
bitflags = "2.3"
34-
fixedbitset = "0.4"
3534
# direct dependency required for derive macro
3635
bytemuck = { version = "1", features = ["derive"] }
3736
radsort = "0.1"

crates/bevy_pbr/src/render/mesh.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use bevy_core_pipeline::{
1111
deferred::{AlphaMask3dDeferred, Opaque3dDeferred},
1212
};
1313
use bevy_derive::{Deref, DerefMut};
14-
use bevy_ecs::entity::EntityHashMap;
14+
use bevy_ecs::entity::{EntityHashMap, EntityHasher};
1515
use bevy_ecs::{
1616
prelude::*,
1717
query::ROQueryItem,
@@ -36,7 +36,10 @@ use bevy_render::{
3636
};
3737
use bevy_transform::components::GlobalTransform;
3838
use bevy_utils::{tracing::error, Entry, HashMap, Hashed};
39-
use std::cell::Cell;
39+
use std::{
40+
cell::Cell,
41+
hash::{Hash, Hasher},
42+
};
4043
use thread_local::ThreadLocal;
4144

4245
#[cfg(debug_assertions)]
@@ -268,7 +271,7 @@ pub struct RenderMeshInstances(EntityHashMap<RenderMeshInstance>);
268271

269272
pub fn extract_meshes(
270273
mut render_mesh_instances: ResMut<RenderMeshInstances>,
271-
mut thread_local_queues: Local<ThreadLocal<Cell<Vec<(Entity, RenderMeshInstance)>>>>,
274+
mut thread_local_queues: Local<ThreadLocal<Cell<Vec<(u64, Entity, RenderMeshInstance)>>>>,
272275
meshes_query: Extract<
273276
Query<(
274277
Entity,
@@ -316,9 +319,12 @@ pub fn extract_meshes(
316319
previous_transform: (&previous_transform).into(),
317320
flags: flags.bits(),
318321
};
322+
let mut hasher = EntityHasher::default();
323+
entity.hash(&mut hasher);
319324
let tls = thread_local_queues.get_or_default();
320325
let mut queue = tls.take();
321326
queue.push((
327+
hasher.finish(),
322328
entity,
323329
RenderMeshInstance {
324330
mesh_asset_id: handle.id(),
@@ -332,9 +338,17 @@ pub fn extract_meshes(
332338
},
333339
);
334340

341+
let render_mesh_instances = render_mesh_instances.bypass_change_detection();
335342
render_mesh_instances.clear();
336343
for queue in thread_local_queues.iter_mut() {
337-
render_mesh_instances.extend(queue.get_mut().drain(..));
344+
let queue = queue.get_mut();
345+
render_mesh_instances.reserve(queue.len());
346+
for (hash, entity, instance) in queue.drain(..) {
347+
render_mesh_instances
348+
.raw_entry_mut()
349+
.from_key_hashed_nocheck(hash, &entity)
350+
.insert(entity, instance);
351+
}
338352
}
339353
}
340354

crates/bevy_render/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ impl Plugin for RenderPlugin {
306306
};
307307
// In wasm, spawn a task and detach it for execution
308308
#[cfg(target_arch = "wasm32")]
309-
bevy_tasks::IoTaskPool::get()
309+
bevy_tasks::ComputeTaskPool::get()
310310
.spawn_local(async_renderer)
311311
.detach();
312312
// Otherwise, just block for it to complete

0 commit comments

Comments
 (0)