bevy_ecs/system/commands/
parallel_scope.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
use bevy_utils::Parallel;

use crate::{
    self as bevy_ecs,
    entity::Entities,
    prelude::World,
    system::{Deferred, SystemBuffer, SystemMeta, SystemParam},
};

use super::{CommandQueue, Commands};

#[derive(Default)]
struct ParallelCommandQueue {
    thread_queues: Parallel<CommandQueue>,
}

/// An alternative to [`Commands`] that can be used in parallel contexts, such as those in [`Query::par_iter`](crate::system::Query::par_iter)
///
/// 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_iter().for_each(|(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);
///```
#[derive(SystemParam)]
pub struct ParallelCommands<'w, 's> {
    state: Deferred<'s, ParallelCommandQueue>,
    entities: &'w Entities,
}

impl SystemBuffer for ParallelCommandQueue {
    #[inline]
    fn apply(&mut self, _system_meta: &SystemMeta, world: &mut World) {
        #[cfg(feature = "trace")]
        let _system_span = _system_meta.commands_span.enter();
        for cq in self.thread_queues.iter_mut() {
            cq.apply(world);
        }
    }
}

impl<'w, 's> ParallelCommands<'w, 's> {
    /// Temporarily provides access to the [`Commands`] for the current thread.
    ///
    /// For an example, see the type-level documentation for [`ParallelCommands`].
    pub fn command_scope<R>(&self, f: impl FnOnce(Commands) -> R) -> R {
        self.state.thread_queues.scope(|queue| {
            let commands = Commands::new_from_entities(queue, self.entities);
            f(commands)
        })
    }
}