Skip to main content

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(#[automatically_derived]
impl ::core::default::Default for ParallelCommandQueue {
    #[inline]
    fn default() -> ParallelCommandQueue {
        ParallelCommandQueue {
            thread_queues: ::core::default::Default::default(),
        }
    }
}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(const _: () =
    {
        type __StructFieldsAlias<'w, 's> =
            (Deferred<'s, ParallelCommandQueue>, &'w EntityAllocator,
            &'w Entities);
        #[doc(hidden)]
        pub struct FetchState {
            state: <__StructFieldsAlias<'static, 'static> as
            bevy_ecs::system::SystemParam>::State,
        }
        unsafe impl bevy_ecs::system::SystemParam for ParallelCommands<'_, '_>
            {
            type State = FetchState<>;
            type Item<'w, 's> = ParallelCommands<'w, 's>;
            fn init_state(world: &mut bevy_ecs::world::World) -> Self::State {
                FetchState {
                    state: <__StructFieldsAlias<'_, '_> as
                            bevy_ecs::system::SystemParam>::init_state(world),
                }
            }
            fn init_access(state: &Self::State,
                system_meta: &mut bevy_ecs::system::SystemMeta,
                component_access_set: &mut bevy_ecs::query::FilteredAccessSet,
                world: &mut bevy_ecs::world::World) {
                <__StructFieldsAlias<'_, '_> as
                        bevy_ecs::system::SystemParam>::init_access(&state.state,
                    system_meta, component_access_set, world);
            }
            fn apply(state: &mut Self::State,
                system_meta: &bevy_ecs::system::SystemMeta,
                world: &mut bevy_ecs::world::World) {
                <__StructFieldsAlias<'_, '_> as
                        bevy_ecs::system::SystemParam>::apply(&mut state.state,
                    system_meta, world);
            }
            fn queue(state: &mut Self::State,
                system_meta: &bevy_ecs::system::SystemMeta,
                world: bevy_ecs::world::DeferredWorld) {
                <__StructFieldsAlias<'_, '_> as
                        bevy_ecs::system::SystemParam>::queue(&mut state.state,
                    system_meta, world);
            }
            #[inline]
            unsafe fn get_param<'w,
                's>(state: &'s mut Self::State,
                system_meta: &bevy_ecs::system::SystemMeta,
                world:
                    bevy_ecs::world::unsafe_world_cell::UnsafeWorldCell<'w>,
                change_tick: bevy_ecs::change_detection::Tick)
                ->
                    ::core::result::Result<Self::Item<'w, 's>,
                    bevy_ecs::system::SystemParamValidationError> {
                let (fieldstate, fieldallocator, fieldentities) =
                    &mut state.state;
                let fieldstate =
                    unsafe {
                                <Deferred<'s, ParallelCommandQueue> as
                                        bevy_ecs::system::SystemParam>::get_param(fieldstate,
                                    system_meta, world, change_tick)
                            }.map_err(|err|
                                bevy_ecs::system::SystemParamValidationError::new::<Self>(err.skipped,
                                    err.message, "::state"))?;
                let fieldallocator =
                    unsafe {
                                <&'w EntityAllocator as
                                        bevy_ecs::system::SystemParam>::get_param(fieldallocator,
                                    system_meta, world, change_tick)
                            }.map_err(|err|
                                bevy_ecs::system::SystemParamValidationError::new::<Self>(err.skipped,
                                    err.message, "::allocator"))?;
                let fieldentities =
                    unsafe {
                                <&'w Entities as
                                        bevy_ecs::system::SystemParam>::get_param(fieldentities,
                                    system_meta, world, change_tick)
                            }.map_err(|err|
                                bevy_ecs::system::SystemParamValidationError::new::<Self>(err.skipped,
                                    err.message, "::entities"))?;
                ::core::result::Result::Ok(ParallelCommands {
                        state: fieldstate,
                        allocator: fieldallocator,
                        entities: fieldentities,
                    })
            }
        }
        unsafe impl<'w, 's> bevy_ecs::system::ReadOnlySystemParam for
            ParallelCommands<'w, 's> where
            Deferred<'s,
            ParallelCommandQueue>: bevy_ecs::system::ReadOnlySystemParam,
            &'w EntityAllocator: bevy_ecs::system::ReadOnlySystemParam,
            &'w Entities: bevy_ecs::system::ReadOnlySystemParam {}
    };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}