azalea_client/plugins/
task_pool.rs1use std::marker::PhantomData;
4
5use bevy_app::{App, Last, Plugin};
6use bevy_ecs::prelude::*;
7use bevy_tasks::{
8 AsyncComputeTaskPool, ComputeTaskPool, IoTaskPool, TaskPoolBuilder,
9 tick_global_task_pools_on_main_thread,
10};
11
12#[derive(Default)]
15pub struct TaskPoolPlugin {
16 pub task_pool_options: TaskPoolOptions,
19}
20
21impl Plugin for TaskPoolPlugin {
22 fn build(&self, app: &mut App) {
23 self.task_pool_options.create_default_pools();
25
26 #[cfg(not(target_arch = "wasm32"))]
27 app.add_systems(Last, tick_global_task_pools);
28 }
29}
30
31pub struct NonSendMarker(PhantomData<*mut ()>);
32#[cfg(not(target_arch = "wasm32"))]
33fn tick_global_task_pools(_main_thread_marker: Option<NonSend<NonSendMarker>>) {
34 tick_global_task_pools_on_main_thread();
35}
36
37#[derive(Clone, Resource)]
41pub struct TaskPoolOptions {
42 pub min_total_threads: usize,
45 pub max_total_threads: usize,
48
49 pub io: TaskPoolThreadAssignmentPolicy,
51 pub async_compute: TaskPoolThreadAssignmentPolicy,
53 pub compute: TaskPoolThreadAssignmentPolicy,
55}
56
57impl Default for TaskPoolOptions {
58 fn default() -> Self {
59 TaskPoolOptions {
60 min_total_threads: 1,
62 max_total_threads: usize::MAX,
63
64 io: TaskPoolThreadAssignmentPolicy {
66 min_threads: 1,
67 max_threads: 4,
68 percent: 0.25,
69 },
70
71 async_compute: TaskPoolThreadAssignmentPolicy {
73 min_threads: 1,
74 max_threads: 4,
75 percent: 0.25,
76 },
77
78 compute: TaskPoolThreadAssignmentPolicy {
80 min_threads: 1,
81 max_threads: usize::MAX,
82 percent: 1.0, },
84 }
85 }
86}
87
88impl TaskPoolOptions {
89 pub fn create_default_pools(&self) {
92 let total_threads = bevy_tasks::available_parallelism()
93 .clamp(self.min_total_threads, self.max_total_threads);
94
95 let mut remaining_threads = total_threads;
96
97 {
98 let io_threads = self
100 .io
101 .get_number_of_threads(remaining_threads, total_threads);
102
103 remaining_threads = remaining_threads.saturating_sub(io_threads);
104
105 IoTaskPool::get_or_init(|| {
106 TaskPoolBuilder::default()
107 .num_threads(io_threads)
108 .thread_name("IO Task Pool".to_string())
109 .build()
110 });
111 }
112
113 {
114 let async_compute_threads = self
116 .async_compute
117 .get_number_of_threads(remaining_threads, total_threads);
118
119 remaining_threads = remaining_threads.saturating_sub(async_compute_threads);
120
121 AsyncComputeTaskPool::get_or_init(|| {
122 TaskPoolBuilder::default()
123 .num_threads(async_compute_threads)
124 .thread_name("Async Compute Task Pool".to_string())
125 .build()
126 });
127 }
128
129 {
130 let compute_threads = self
133 .compute
134 .get_number_of_threads(remaining_threads, total_threads);
135
136 ComputeTaskPool::get_or_init(|| {
137 TaskPoolBuilder::default()
138 .num_threads(compute_threads)
139 .thread_name("Compute Task Pool".to_string())
140 .build()
141 });
142 }
143 }
144}
145
146#[derive(Clone)]
149pub struct TaskPoolThreadAssignmentPolicy {
150 pub min_threads: usize,
152 pub max_threads: usize,
154 pub percent: f32,
160}
161
162impl TaskPoolThreadAssignmentPolicy {
163 fn get_number_of_threads(&self, remaining_threads: usize, total_threads: usize) -> usize {
165 assert!(self.percent >= 0.0);
166 let mut desired = (total_threads as f32 * self.percent).round() as usize;
167
168 desired = desired.min(remaining_threads);
170
171 desired.clamp(self.min_threads, self.max_threads)
175 }
176}