bevy_ecs/system/commands/
parallel_scope.rs

1use bevy_utils::Parallel;
2
3use crate::{
4    entity::{Entities, EntityAllocator},
5    prelude::World,
6    system::{Deferred, SystemBuffer, SystemMeta, SystemParam},
7};
8
9use super::{CommandQueue, Commands};
10
11#[derive(Default)]
12struct ParallelCommandQueue {
13    thread_queues: Parallel<CommandQueue>,
14}
15
16/// An alternative to [`Commands`] that can be used in parallel contexts, such as those
17/// in [`Query::par_iter`](crate::system::Query::par_iter).
18///
19/// For cases where multiple non-computation-heavy (lightweight) bundles of the same
20/// [`Bundle`](crate::prelude::Bundle) type need to be spawned, consider using
21/// [`Commands::spawn_batch`] for better performance.
22///
23/// # Note
24///
25/// Because command application order will depend on how many threads are ran,
26/// non-commutative commands may result in non-deterministic results.
27///
28/// # Example
29///
30/// ```
31/// # use bevy_ecs::prelude::*;
32/// # use bevy_tasks::ComputeTaskPool;
33/// #
34/// # #[derive(Component)]
35/// # struct Velocity;
36/// # impl Velocity { fn magnitude(&self) -> f32 { 42.0 } }
37/// fn parallel_command_system(
38///     mut query: Query<(Entity, &Velocity)>,
39///     par_commands: ParallelCommands
40/// ) {
41///     query.par_iter().for_each(|(entity, velocity)| {
42///         if velocity.magnitude() > 10.0 {
43///             par_commands.command_scope(|mut commands| {
44///                 commands.entity(entity).despawn();
45///             });
46///         }
47///     });
48/// }
49/// # bevy_ecs::system::assert_is_system(parallel_command_system);
50/// ```
51#[derive(SystemParam)]
52pub struct ParallelCommands<'w, 's> {
53    state: Deferred<'s, ParallelCommandQueue>,
54    allocator: &'w EntityAllocator,
55    entities: &'w Entities,
56}
57
58impl SystemBuffer for ParallelCommandQueue {
59    #[inline]
60    fn apply(&mut self, _system_meta: &SystemMeta, world: &mut World) {
61        #[cfg(feature = "trace")]
62        let _system_span = _system_meta.commands_span.enter();
63        for cq in self.thread_queues.iter_mut() {
64            cq.apply(world);
65        }
66    }
67}
68
69impl<'w, 's> ParallelCommands<'w, 's> {
70    /// Temporarily provides access to the [`Commands`] for the current thread.
71    ///
72    /// For an example, see the type-level documentation for [`ParallelCommands`].
73    pub fn command_scope<R>(&self, f: impl FnOnce(Commands) -> R) -> R {
74        self.state.thread_queues.scope(|queue| {
75            let commands = Commands::new_from_entities(queue, self.allocator, self.entities);
76            f(commands)
77        })
78    }
79}