mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 15:14:50 +00:00
Add ParallelCommands system parameter (#4749)
(follow-up to #4423) # Objective Currently, it isn't possible to easily fire commands from within par_for_each blocks. This PR allows for issuing commands from within parallel scopes.
This commit is contained in:
parent
2f5a1c6e16
commit
85cd0eb445
4 changed files with 102 additions and 1 deletions
|
@ -21,6 +21,7 @@ bevy_utils = { path = "../bevy_utils", version = "0.8.0-dev" }
|
|||
bevy_ecs_macros = { path = "macros", version = "0.8.0-dev" }
|
||||
|
||||
async-channel = "1.4"
|
||||
thread_local = "1.1.4"
|
||||
fixedbitset = "0.4"
|
||||
fxhash = "0.2"
|
||||
downcast-rs = "1.2"
|
||||
|
|
|
@ -39,7 +39,7 @@ pub mod prelude {
|
|||
},
|
||||
system::{
|
||||
Commands, In, IntoChainSystem, IntoExclusiveSystem, IntoSystem, Local, NonSend,
|
||||
NonSendMut, ParamSet, Query, RemovedComponents, Res, ResMut, System,
|
||||
NonSendMut, ParallelCommands, ParamSet, Query, RemovedComponents, Res, ResMut, System,
|
||||
SystemParamFunction,
|
||||
},
|
||||
world::{FromWorld, Mut, World},
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
mod command_queue;
|
||||
mod parallel_scope;
|
||||
|
||||
use crate::{
|
||||
bundle::Bundle,
|
||||
|
@ -8,6 +9,7 @@ use crate::{
|
|||
};
|
||||
use bevy_utils::tracing::{error, warn};
|
||||
pub use command_queue::CommandQueue;
|
||||
pub use parallel_scope::*;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use super::Resource;
|
||||
|
|
98
crates/bevy_ecs/src/system/commands/parallel_scope.rs
Normal file
98
crates/bevy_ecs/src/system/commands/parallel_scope.rs
Normal file
|
@ -0,0 +1,98 @@
|
|||
use std::cell::Cell;
|
||||
|
||||
use thread_local::ThreadLocal;
|
||||
|
||||
use crate::{
|
||||
entity::Entities,
|
||||
prelude::World,
|
||||
system::{SystemParam, SystemParamFetch, SystemParamState},
|
||||
};
|
||||
|
||||
use super::{CommandQueue, Commands};
|
||||
|
||||
#[doc(hidden)]
|
||||
#[derive(Default)]
|
||||
/// The internal [`SystemParamState`] of the [`ParallelCommands`] type
|
||||
pub struct ParallelCommandsState {
|
||||
thread_local_storage: ThreadLocal<Cell<CommandQueue>>,
|
||||
}
|
||||
|
||||
/// An alternative to [`Commands`] that can be used in parallel contexts, such as those in [`Query::par_for_each`](crate::system::Query::par_for_each)
|
||||
///
|
||||
/// Note: Because command application order will depend on how many threads are ran, non-commutative commands may result in non-deterministic results.
|
||||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// # use bevy_tasks::ComputeTaskPool;
|
||||
/// #
|
||||
/// # #[derive(Component)]
|
||||
/// # struct Velocity;
|
||||
/// # impl Velocity { fn magnitude(&self) -> f32 { 42.0 } }
|
||||
/// fn parallel_command_system(
|
||||
/// mut query: Query<(Entity, &Velocity)>,
|
||||
/// par_commands: ParallelCommands
|
||||
/// ) {
|
||||
/// query.par_for_each(32, |(entity, velocity)| {
|
||||
/// if velocity.magnitude() > 10.0 {
|
||||
/// par_commands.command_scope(|mut commands| {
|
||||
/// commands.entity(entity).despawn();
|
||||
/// });
|
||||
/// }
|
||||
/// });
|
||||
/// }
|
||||
/// # bevy_ecs::system::assert_is_system(parallel_command_system);
|
||||
///```
|
||||
pub struct ParallelCommands<'w, 's> {
|
||||
state: &'s mut ParallelCommandsState,
|
||||
entities: &'w Entities,
|
||||
}
|
||||
|
||||
impl SystemParam for ParallelCommands<'_, '_> {
|
||||
type Fetch = ParallelCommandsState;
|
||||
}
|
||||
|
||||
impl<'w, 's> SystemParamFetch<'w, 's> for ParallelCommandsState {
|
||||
type Item = ParallelCommands<'w, 's>;
|
||||
|
||||
unsafe fn get_param(
|
||||
state: &'s mut Self,
|
||||
_: &crate::system::SystemMeta,
|
||||
world: &'w World,
|
||||
_: u32,
|
||||
) -> Self::Item {
|
||||
ParallelCommands {
|
||||
state,
|
||||
entities: world.entities(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SAFE: no component or resource access to report
|
||||
unsafe impl SystemParamState for ParallelCommandsState {
|
||||
fn init(_: &mut World, _: &mut crate::system::SystemMeta) -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn apply(&mut self, world: &mut World) {
|
||||
for cq in self.thread_local_storage.iter_mut() {
|
||||
cq.get_mut().apply(world);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'w, 's> ParallelCommands<'w, 's> {
|
||||
pub fn command_scope<R>(&self, f: impl FnOnce(Commands) -> R) -> R {
|
||||
let store = &self.state.thread_local_storage;
|
||||
let command_queue_cell = store.get_or_default();
|
||||
let mut command_queue = command_queue_cell.take();
|
||||
|
||||
let r = f(Commands::new_from_entities(
|
||||
&mut command_queue,
|
||||
self.entities,
|
||||
));
|
||||
|
||||
command_queue_cell.set(command_queue);
|
||||
r
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue