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