mirror of
https://github.com/bevyengine/bevy
synced 2024-11-25 22:20:20 +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" }
|
bevy_ecs_macros = { path = "macros", version = "0.8.0-dev" }
|
||||||
|
|
||||||
async-channel = "1.4"
|
async-channel = "1.4"
|
||||||
|
thread_local = "1.1.4"
|
||||||
fixedbitset = "0.4"
|
fixedbitset = "0.4"
|
||||||
fxhash = "0.2"
|
fxhash = "0.2"
|
||||||
downcast-rs = "1.2"
|
downcast-rs = "1.2"
|
||||||
|
|
|
@ -39,7 +39,7 @@ pub mod prelude {
|
||||||
},
|
},
|
||||||
system::{
|
system::{
|
||||||
Commands, In, IntoChainSystem, IntoExclusiveSystem, IntoSystem, Local, NonSend,
|
Commands, In, IntoChainSystem, IntoExclusiveSystem, IntoSystem, Local, NonSend,
|
||||||
NonSendMut, ParamSet, Query, RemovedComponents, Res, ResMut, System,
|
NonSendMut, ParallelCommands, ParamSet, Query, RemovedComponents, Res, ResMut, System,
|
||||||
SystemParamFunction,
|
SystemParamFunction,
|
||||||
},
|
},
|
||||||
world::{FromWorld, Mut, World},
|
world::{FromWorld, Mut, World},
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
mod command_queue;
|
mod command_queue;
|
||||||
|
mod parallel_scope;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bundle::Bundle,
|
bundle::Bundle,
|
||||||
|
@ -8,6 +9,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use bevy_utils::tracing::{error, warn};
|
use bevy_utils::tracing::{error, warn};
|
||||||
pub use command_queue::CommandQueue;
|
pub use command_queue::CommandQueue;
|
||||||
|
pub use parallel_scope::*;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use super::Resource;
|
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