1use bevy_ecs::archetype::ArchetypeEntity;
4use bevy_ecs::component::{ComponentId, StorageType};
5use bevy_ecs::ptr::{Ptr, PtrMut};
6use bevy_ecs::query::{IterQueryData, QueryFilter, QueryItem};
7use bevy_ecs::storage::TableId;
8use bevy_ecs::system::Query;
9use bevy_ecs::world::unsafe_world_cell::UnsafeWorldCell;
10
11#[macro_export]
30macro_rules! adaptive_for_each_mut {
31 ($query:expr, |$item:pat_param| $body:expr $(,)?) => {
32 $crate::ecs::AdaptiveQueryIterMut::new(&mut $query, 1).for_each(|$item| $body)
33 };
34 ($query:expr, $serial_threshold:expr, |$item:pat_param| $body:expr $(,)?) => {
35 $crate::ecs::AdaptiveQueryIterMut::new(&mut $query, $serial_threshold)
36 .for_each(|$item| $body)
37 };
38 ($query:expr $(,)?) => {
40 $crate::ecs::AdaptiveQueryIterMut::new(&mut $query, 1)
41 };
42}
43
44#[doc(hidden)]
49pub struct AdaptiveQueryIterMut<'query, 'world, 'state, D, F>
50where
51 D: IterQueryData,
52 F: QueryFilter,
53{
54 query: &'query mut Query<'world, 'state, D, F>,
55 serial_threshold: usize,
56}
57
58impl<'query, 'world, 'state, D, F> AdaptiveQueryIterMut<'query, 'world, 'state, D, F>
59where
60 D: IterQueryData,
61 F: QueryFilter,
62{
63 #[doc(hidden)]
64 pub fn new(query: &'query mut Query<'world, 'state, D, F>, serial_threshold: usize) -> Self {
65 Self {
66 query,
67 serial_threshold,
68 }
69 }
70
71 pub fn for_each<Func>(self, func: Func)
73 where
74 Func: for<'item> Fn(QueryItem<'item, 'state, D>) + Send + Sync + Clone,
75 {
76 if self.query.iter().nth(self.serial_threshold).is_some() {
77 self.query.par_iter_mut().for_each(func);
78 } else {
79 self.query.iter_mut().for_each(func);
80 }
81 }
82}
83
84pub unsafe fn get_component_unchecked_mut<'w>(
90 unsafe_world_cell: UnsafeWorldCell<'w>,
91 entity: &'w ArchetypeEntity,
92 table_id: TableId,
93 storage: StorageType,
94 component_id: ComponentId,
95) -> PtrMut<'w> {
96 let storages = unsafe { unsafe_world_cell.storages() };
97 match storage {
98 StorageType::Table => unsafe {
100 let table = storages.tables.get(table_id).unwrap_unchecked();
101 table
102 .get_component(component_id, entity.table_row())
103 .unwrap_unchecked()
104 .assert_unique()
105 },
106 StorageType::SparseSet => unsafe {
107 let sparse_set = storages.sparse_sets.get(component_id).unwrap_unchecked();
108 sparse_set
109 .get(entity.id())
110 .unwrap_unchecked()
111 .assert_unique()
112 },
113 }
114}
115
116pub unsafe fn get_component_unchecked<'w>(
122 unsafe_world_cell: UnsafeWorldCell<'w>,
123 entity: &'w ArchetypeEntity,
124 table_id: TableId,
125 storage: StorageType,
126 component_id: ComponentId,
127) -> Ptr<'w> {
128 let storages = unsafe { unsafe_world_cell.storages() };
129 match storage {
130 StorageType::Table => unsafe {
132 let table = storages.tables.get(table_id).unwrap_unchecked();
133 table
134 .get_component(component_id, entity.table_row())
135 .unwrap_unchecked()
136 },
137 StorageType::SparseSet => unsafe {
138 let sparse_set = storages.sparse_sets.get(component_id).unwrap_unchecked();
139 sparse_set.get(entity.id()).unwrap_unchecked()
140 },
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use bevy_app::{App, TaskPoolPlugin};
147 use bevy_ecs::prelude::*;
148
149 #[derive(Component)]
150 struct Value(u32);
151
152 #[test]
153 fn adaptive_iteration_supports_default_and_custom_thresholds() {
154 let mut app = App::new();
155 app.add_plugins(TaskPoolPlugin::default());
156 let world = app.world_mut();
157 let first = world.spawn(Value(0)).id();
158 let mut query_state = world.query::<&mut Value>();
159
160 {
161 let mut query = query_state.query_mut(world);
162 crate::adaptive_for_each_mut!(query, |mut value| value.0 += 1);
163 }
164
165 let second = world.spawn(Value(0)).id();
166 {
167 let mut query = query_state.query_mut(world);
168 crate::adaptive_for_each_mut!(query, 2, |mut value| value.0 += 1);
169 }
170 {
171 let mut query = query_state.query_mut(world);
172 crate::adaptive_for_each_mut!(query, |mut value| value.0 += 1);
173 }
174
175 assert_eq!(world.get::<Value>(first).unwrap().0, 3);
176 assert_eq!(world.get::<Value>(second).unwrap().0, 2);
177 }
178}