aboutsummaryrefslogtreecommitdiff
path: root/azalea-client/src/plugins/task_pool.rs
diff options
context:
space:
mode:
authormat <27899617+mat-1@users.noreply.github.com>2025-02-22 21:45:26 -0600
committerGitHub <noreply@github.com>2025-02-22 21:45:26 -0600
commite21e1b97bf9337e9f4747cd1b545b1b3a03e2ce7 (patch)
treeadd6f8bfce40d0c07845d8aa4c9945a0b918444c /azalea-client/src/plugins/task_pool.rs
parentf8130c3c92946d2293634ba4e252d6bc93026c3c (diff)
downloadazalea-drasl-e21e1b97bf9337e9f4747cd1b545b1b3a03e2ce7.tar.xz
Refactor azalea-client (#205)
* start organizing packet_handling more by moving packet handlers into their own functions * finish writing all the handler functions for packets * use macro for generating match statement for packet handler functions * fix set_entity_data * update config state to also use handler functions * organize az-client file structure by moving things into plugins directory * fix merge issues
Diffstat (limited to 'azalea-client/src/plugins/task_pool.rs')
-rw-r--r--azalea-client/src/plugins/task_pool.rs182
1 files changed, 182 insertions, 0 deletions
diff --git a/azalea-client/src/plugins/task_pool.rs b/azalea-client/src/plugins/task_pool.rs
new file mode 100644
index 00000000..ab56bf69
--- /dev/null
+++ b/azalea-client/src/plugins/task_pool.rs
@@ -0,0 +1,182 @@
+//! Borrowed from `bevy_core`.
+
+use std::marker::PhantomData;
+
+use bevy_app::{App, Last, Plugin};
+use bevy_ecs::system::{NonSend, Resource};
+use bevy_tasks::{
+ AsyncComputeTaskPool, ComputeTaskPool, IoTaskPool, TaskPoolBuilder,
+ tick_global_task_pools_on_main_thread,
+};
+
+/// Setup of default task pools: `AsyncComputeTaskPool`, `ComputeTaskPool`,
+/// `IoTaskPool`.
+#[derive(Default)]
+pub struct TaskPoolPlugin {
+ /// Options for the [`TaskPool`](bevy_tasks::TaskPool) created at
+ /// application start.
+ pub task_pool_options: TaskPoolOptions,
+}
+
+impl Plugin for TaskPoolPlugin {
+ fn build(&self, app: &mut App) {
+ // Setup the default bevy task pools
+ self.task_pool_options.create_default_pools();
+
+ #[cfg(not(target_arch = "wasm32"))]
+ app.add_systems(Last, tick_global_task_pools);
+ }
+}
+
+pub struct NonSendMarker(PhantomData<*mut ()>);
+#[cfg(not(target_arch = "wasm32"))]
+fn tick_global_task_pools(_main_thread_marker: Option<NonSend<NonSendMarker>>) {
+ tick_global_task_pools_on_main_thread();
+}
+
+/// Helper for configuring and creating the default task pools. For end-users
+/// who want full control, set up [`TaskPoolPlugin`]
+#[derive(Clone, Resource)]
+pub struct TaskPoolOptions {
+ /// If the number of physical cores is less than min_total_threads, force
+ /// using min_total_threads
+ pub min_total_threads: usize,
+ /// If the number of physical cores is greater than max_total_threads, force
+ /// using max_total_threads
+ pub max_total_threads: usize,
+
+ /// Used to determine number of IO threads to allocate
+ pub io: TaskPoolThreadAssignmentPolicy,
+ /// Used to determine number of async compute threads to allocate
+ pub async_compute: TaskPoolThreadAssignmentPolicy,
+ /// Used to determine number of compute threads to allocate
+ pub compute: TaskPoolThreadAssignmentPolicy,
+}
+
+impl Default for TaskPoolOptions {
+ fn default() -> Self {
+ TaskPoolOptions {
+ // By default, use however many cores are available on the system
+ min_total_threads: 1,
+ max_total_threads: usize::MAX,
+
+ // Use 25% of cores for IO, at least 1, no more than 4
+ io: TaskPoolThreadAssignmentPolicy {
+ min_threads: 1,
+ max_threads: 4,
+ percent: 0.25,
+ },
+
+ // Use 25% of cores for async compute, at least 1, no more than 4
+ async_compute: TaskPoolThreadAssignmentPolicy {
+ min_threads: 1,
+ max_threads: 4,
+ percent: 0.25,
+ },
+
+ // Use all remaining cores for compute (at least 1)
+ compute: TaskPoolThreadAssignmentPolicy {
+ min_threads: 1,
+ max_threads: usize::MAX,
+ percent: 1.0, // This 1.0 here means "whatever is left over"
+ },
+ }
+ }
+}
+
+impl TaskPoolOptions {
+ // /// Create a configuration that forces using the given number of threads.
+ // pub fn with_num_threads(thread_count: usize) -> Self {
+ // TaskPoolOptions {
+ // min_total_threads: thread_count,
+ // max_total_threads: thread_count,
+ // ..Default::default()
+ // }
+ // }
+
+ /// Inserts the default thread pools into the given resource map based on
+ /// the configured values
+ pub fn create_default_pools(&self) {
+ let total_threads = bevy_tasks::available_parallelism()
+ .clamp(self.min_total_threads, self.max_total_threads);
+
+ let mut remaining_threads = total_threads;
+
+ {
+ // Determine the number of IO threads we will use
+ let io_threads = self
+ .io
+ .get_number_of_threads(remaining_threads, total_threads);
+
+ remaining_threads = remaining_threads.saturating_sub(io_threads);
+
+ IoTaskPool::get_or_init(|| {
+ TaskPoolBuilder::default()
+ .num_threads(io_threads)
+ .thread_name("IO Task Pool".to_string())
+ .build()
+ });
+ }
+
+ {
+ // Determine the number of async compute threads we will use
+ let async_compute_threads = self
+ .async_compute
+ .get_number_of_threads(remaining_threads, total_threads);
+
+ remaining_threads = remaining_threads.saturating_sub(async_compute_threads);
+
+ AsyncComputeTaskPool::get_or_init(|| {
+ TaskPoolBuilder::default()
+ .num_threads(async_compute_threads)
+ .thread_name("Async Compute Task Pool".to_string())
+ .build()
+ });
+ }
+
+ {
+ // Determine the number of compute threads we will use
+ // This is intentionally last so that an end user can specify 1.0 as the percent
+ let compute_threads = self
+ .compute
+ .get_number_of_threads(remaining_threads, total_threads);
+
+ ComputeTaskPool::get_or_init(|| {
+ TaskPoolBuilder::default()
+ .num_threads(compute_threads)
+ .thread_name("Compute Task Pool".to_string())
+ .build()
+ });
+ }
+ }
+}
+
+/// Defines a simple way to determine how many threads to use given the number
+/// of remaining cores and number of total cores
+#[derive(Clone)]
+pub struct TaskPoolThreadAssignmentPolicy {
+ /// Force using at least this many threads
+ pub min_threads: usize,
+ /// Under no circumstance use more than this many threads for this pool
+ pub max_threads: usize,
+ /// Target using this percentage of total cores, clamped by min_threads and
+ /// max_threads. It is permitted to use 1.0 to try to use all remaining
+ /// threads
+ pub percent: f32,
+}
+
+impl TaskPoolThreadAssignmentPolicy {
+ /// Determine the number of threads to use for this task pool
+ fn get_number_of_threads(&self, remaining_threads: usize, total_threads: usize) -> usize {
+ assert!(self.percent >= 0.0);
+ let mut desired = (total_threads as f32 * self.percent).round() as usize;
+
+ // Limit ourselves to the number of cores available
+ desired = desired.min(remaining_threads);
+
+ // Clamp by min_threads, max_threads. (This may result in us using more threads
+ // than are available, this is intended. An example case where this
+ // might happen is a device with <= 2 threads.
+ desired.clamp(self.min_threads, self.max_threads)
+ }
+}