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
					
				@ -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 New Issue
	
	Block a user