Skip to content

Commit f36f62f

Browse files
committed
Added QueryState::many_for_each_unchecked_manual and co.
1 parent f68ac41 commit f36f62f

File tree

2 files changed

+186
-26
lines changed

2 files changed

+186
-26
lines changed

crates/bevy_ecs/src/query/state.rs

Lines changed: 147 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use bevy_tasks::{ComputeTaskPool, TaskPool};
1414
#[cfg(feature = "trace")]
1515
use bevy_utils::tracing::Instrument;
1616
use fixedbitset::FixedBitSet;
17-
use std::{fmt, ops::Deref};
17+
use std::{borrow::Borrow, fmt, ops::Deref};
1818

1919
use super::{QueryFetch, QueryItem, ROQueryFetch, ROQueryItem};
2020

@@ -693,6 +693,93 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
693693
);
694694
}
695695

696+
/// Runs `func` on each query result for the given [`World`]. This is faster than the equivalent
697+
/// iter() method, but cannot be chained like a normal [`Iterator`].
698+
///
699+
/// This can only be called for read-only queries, see [`Self::for_each_mut`] for write-queries.
700+
#[inline]
701+
pub fn many_for_each<
702+
'w,
703+
E: Borrow<Entity>,
704+
EntityList: IntoIterator<Item = E>,
705+
FN: FnMut(ROQueryItem<'w, Q>),
706+
>(
707+
&mut self,
708+
world: &'w World,
709+
entities: EntityList,
710+
func: FN,
711+
) {
712+
// SAFETY: query is read only
713+
unsafe {
714+
self.update_archetypes(world);
715+
self.many_for_each_unchecked_manual::<ROQueryFetch<Q>, E, EntityList, FN>(
716+
world,
717+
entities,
718+
func,
719+
world.last_change_tick(),
720+
world.read_change_tick(),
721+
);
722+
}
723+
}
724+
725+
/// Runs `func` on each query result for the given [`World`]. This is faster than the equivalent
726+
/// `iter_mut()` method, but cannot be chained like a normal [`Iterator`].
727+
#[inline]
728+
pub fn many_for_each_mut<
729+
'w,
730+
E: Borrow<Entity>,
731+
EntityList: IntoIterator<Item = E>,
732+
FN: FnMut(QueryItem<'w, Q>),
733+
>(
734+
&mut self,
735+
world: &'w mut World,
736+
entities: EntityList,
737+
func: FN,
738+
) {
739+
// SAFETY: query has unique world access
740+
unsafe {
741+
self.update_archetypes(world);
742+
self.many_for_each_unchecked_manual::<QueryFetch<Q>, E, EntityList, FN>(
743+
world,
744+
entities,
745+
func,
746+
world.last_change_tick(),
747+
world.read_change_tick(),
748+
);
749+
}
750+
}
751+
752+
/// Runs `func` on each query result for the given [`World`]. This is faster than the equivalent
753+
/// iter() method, but cannot be chained like a normal [`Iterator`].
754+
///
755+
/// This can only be called for read-only queries.
756+
///
757+
/// # Safety
758+
///
759+
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
760+
/// have unique access to the components they query.
761+
#[inline]
762+
pub unsafe fn many_for_each_unchecked<
763+
'w,
764+
E: Borrow<Entity>,
765+
EntityList: IntoIterator<Item = E>,
766+
FN: FnMut(QueryItem<'w, Q>),
767+
>(
768+
&mut self,
769+
world: &'w World,
770+
entities: EntityList,
771+
func: FN,
772+
) {
773+
self.update_archetypes(world);
774+
self.many_for_each_unchecked_manual::<QueryFetch<Q>, E, EntityList, FN>(
775+
world,
776+
entities,
777+
func,
778+
world.last_change_tick(),
779+
world.read_change_tick(),
780+
);
781+
}
782+
696783
/// Runs `func` on each query result in parallel.
697784
///
698785
/// This can only be called for read-only queries, see [`Self::par_for_each_mut`] for
@@ -797,7 +884,7 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
797884
change_tick: u32,
798885
) {
799886
// NOTE: If you are changing query iteration code, remember to update the following places, where relevant:
800-
// QueryIter, QueryIterationCursor, QueryState::for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual
887+
// QueryIter, QueryIterationCursor, QueryState::for_each_unchecked_manual, QueryState::many_for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual
801888
let mut fetch = QF::init(world, &self.fetch_state, last_change_tick, change_tick);
802889
let mut filter = <QueryFetch<F> as Fetch>::init(
803890
world,
@@ -839,6 +926,63 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
839926
}
840927
}
841928

929+
/// Runs `func` on each query result for the given [`World`], where the last change and
930+
/// the current change tick are given. This is faster than the equivalent
931+
/// iter() method, but cannot be chained like a normal [`Iterator`].
932+
///
933+
/// # Safety
934+
///
935+
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
936+
/// have unique access to the components they query.
937+
/// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world`
938+
/// with a mismatched [`WorldId`] is unsound.
939+
pub(crate) unsafe fn many_for_each_unchecked_manual<
940+
'w,
941+
QF: Fetch<'w, State = Q::State>,
942+
E: Borrow<Entity>,
943+
EntityList: IntoIterator<Item = E>,
944+
FN: FnMut(QF::Item),
945+
>(
946+
&self,
947+
world: &'w World,
948+
entities: EntityList,
949+
mut func: FN,
950+
last_change_tick: u32,
951+
change_tick: u32,
952+
) {
953+
// NOTE: If you are changing query iteration code, remember to update the following places, where relevant:
954+
// QueryIter, QueryIterationCursor, QueryState::for_each_unchecked_manual, QueryState::many_for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual
955+
let mut fetch = QF::init(world, &self.fetch_state, last_change_tick, change_tick);
956+
let mut filter = <QueryFetch<F> as Fetch>::init(
957+
world,
958+
&self.filter_state,
959+
last_change_tick,
960+
change_tick,
961+
);
962+
let archetypes = &world.archetypes;
963+
let tables = &world.storages().tables;
964+
965+
for location in entities
966+
.into_iter()
967+
.filter_map(|e| world.entities.get(*e.borrow()))
968+
{
969+
if !self
970+
.matched_archetypes
971+
.contains(location.archetype_id.index())
972+
{
973+
continue;
974+
}
975+
976+
let archetype = &archetypes[location.archetype_id];
977+
978+
fetch.set_archetype(&self.fetch_state, archetype, tables);
979+
filter.set_archetype(&self.filter_state, archetype, tables);
980+
if filter.archetype_filter_fetch(location.index) {
981+
func(fetch.archetype_fetch(location.index))
982+
}
983+
}
984+
}
985+
842986
/// Runs `func` on each query result in parallel for the given [`World`], where the last change and
843987
/// the current change tick are given. This is faster than the equivalent
844988
/// iter() method, but cannot be chained like a normal [`Iterator`].
@@ -866,7 +1010,7 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
8661010
change_tick: u32,
8671011
) {
8681012
// NOTE: If you are changing query iteration code, remember to update the following places, where relevant:
869-
// QueryIter, QueryIterationCursor, QueryState::for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual
1013+
// QueryIter, QueryIterationCursor, QueryState::for_each_unchecked_manual, QueryState::many_for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual
8701014
self.task_pool
8711015
.as_ref()
8721016
.expect("Cannot iterate query in parallel. No ComputeTaskPool initialized.")

crates/bevy_ecs/src/system/query.rs

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,12 +1156,14 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
11561156
/// }
11571157
/// # bevy_ecs::system::assert_is_system(system);
11581158
/// ```
1159-
pub fn many_iter<I, EntityList, E>(&self, entities: EntityList) -> QueryManyIter<'_, '_, I, Q, F, E>
1160-
where
1159+
pub fn many_iter<
11611160
E: Borrow<Entity>,
11621161
I: Iterator<Item = E>,
11631162
EntityList: IntoIterator<IntoIter = I>,
1164-
{
1163+
>(
1164+
&self,
1165+
entities: EntityList,
1166+
) -> QueryManyIter<'_, '_, I, Q, F, E> {
11651167
QueryManyIter {
11661168
entities: entities.into_iter(),
11671169
query: self,
@@ -1195,16 +1197,23 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
11951197
/// }
11961198
/// # bevy_ecs::system::assert_is_system(system);
11971199
/// ```
1198-
pub fn many_for_each<EntityList, E>(&self, entities: EntityList, f: impl Fn(ROQueryItem<'_, Q>))
1199-
where
1200-
E: Borrow<Entity>,
1201-
EntityList: IntoIterator<Item = E>,
1202-
{
1203-
entities.into_iter().map(|e| *e.borrow()).for_each(|input| {
1204-
if let Ok(item) = self.get(input) {
1205-
f(item);
1206-
}
1207-
});
1200+
pub fn many_for_each<E: Borrow<Entity>, EntityList: IntoIterator<Item = E>>(
1201+
&self,
1202+
entities: EntityList,
1203+
f: impl Fn(ROQueryItem<'_, Q>),
1204+
) {
1205+
// SAFE: system runs without conflicts with other systems.
1206+
// same-system queries have runtime borrow checks when they conflict
1207+
unsafe {
1208+
self.state
1209+
.many_for_each_unchecked_manual::<ROQueryFetch<Q>, E, EntityList, _>(
1210+
self.world,
1211+
entities,
1212+
f,
1213+
self.last_change_tick,
1214+
self.change_tick,
1215+
);
1216+
};
12081217
}
12091218

12101219
/// Calls a closure on each result of [`Query`] where the entities match.
@@ -1235,16 +1244,23 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
12351244
/// }
12361245
/// # bevy_ecs::system::assert_is_system(system);
12371246
/// ```
1238-
pub fn many_for_each_mut<EntityList, E>(&mut self, entities: EntityList, mut f: impl FnMut(QueryItem<'_, Q>))
1239-
where
1240-
E: Borrow<Entity>,
1241-
EntityList: IntoIterator<Item = E>,
1242-
{
1243-
entities.into_iter().map(|e| *e.borrow()).for_each(|entity| {
1244-
if let Ok(item) = self.get_mut(entity) {
1245-
f(item);
1246-
}
1247-
});
1247+
pub fn many_for_each_mut<E: Borrow<Entity>, EntityList: IntoIterator<Item = E>>(
1248+
&mut self,
1249+
entities: EntityList,
1250+
f: impl FnMut(QueryItem<'_, Q>),
1251+
) {
1252+
// SAFE: system runs without conflicts with other systems.
1253+
// same-system queries have runtime borrow checks when they conflict
1254+
unsafe {
1255+
self.state
1256+
.many_for_each_unchecked_manual::<QueryFetch<Q>, E, EntityList, _>(
1257+
self.world,
1258+
entities,
1259+
f,
1260+
self.last_change_tick,
1261+
self.change_tick,
1262+
);
1263+
};
12481264
}
12491265
}
12501266

0 commit comments

Comments
 (0)