Skip to content

Commit 94d1d23

Browse files
committed
Add methods for querying lists of entities. (#4879)
# Objective Improve querying ergonomics around collections and iterators of entities. Example how queries over Children might be done currently. ```rust fn system(foo_query: Query<(&Foo, &Children)>, bar_query: Query<(&Bar, &Children)>) { for (foo, children) in &foo_query { for child in children.iter() { if let Ok((bar, children)) = bar_query.get(*child) { for child in children.iter() { if let Ok((foo, children)) = foo_query.get(*child) { // D: } } } } } } ``` Answers #4868 Partially addresses #4864 Fixes #1470 ## Solution Based on the great work by @deontologician in #2563 Added `iter_many` and `many_for_each_mut` to `Query`. These take a list of entities (Anything that implements `IntoIterator<Item: Borrow<Entity>>`). `iter_many` returns a `QueryManyIter` iterator over immutable results of a query (mutable data will be cast to an immutable form). `many_for_each_mut` calls a closure for every result of the query, ensuring not aliased mutability. This iterator goes over the list of entities in order and returns the result from the query for it. Skipping over any entities that don't match the query. Also added `unsafe fn iter_many_unsafe`. ### Examples ```rust #[derive(Component)] struct Counter { value: i32 } #[derive(Component)] struct Friends { list: Vec<Entity>, } fn system( friends_query: Query<&Friends>, mut counter_query: Query<&mut Counter>, ) { for friends in &friends_query { for counter in counter_query.iter_many(&friends.list) { println!("Friend's counter: {:?}", counter.value); } counter_query.many_for_each_mut(&friends.list, |mut counter| { counter.value += 1; println!("Friend's counter: {:?}", counter.value); }); } } ``` Here's how example in the Objective section can be written with this PR. ```rust fn system(foo_query: Query<(&Foo, &Children)>, bar_query: Query<(&Bar, &Children)>) { for (foo, children) in &foo_query { for (bar, children) in bar_query.iter_many(children) { for (foo, children) in foo_query.iter_many(children) { // :D } } } } ``` ## Additional changes Implemented `IntoIterator` for `&Children` because why not. ## Todo - Bikeshed! Co-authored-by: deontologician <[email protected]> Co-authored-by: devil-ira <[email protected]>
1 parent 292cd03 commit 94d1d23

File tree

7 files changed

+449
-8
lines changed

7 files changed

+449
-8
lines changed

crates/bevy_ecs/src/query/iter.rs

Lines changed: 110 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use crate::{
22
archetype::{ArchetypeId, Archetypes},
3+
entity::{Entities, Entity},
4+
prelude::World,
35
query::{Fetch, QueryState, WorldQuery},
46
storage::{TableId, Tables},
5-
world::World,
67
};
7-
use std::{marker::PhantomData, mem::MaybeUninit};
8+
use std::{borrow::Borrow, marker::PhantomData, mem::MaybeUninit};
89

910
use super::{QueryFetch, QueryItem, ReadOnlyFetch};
1011

@@ -71,6 +72,113 @@ where
7172
}
7273
}
7374

75+
/// An [`Iterator`] over query results of a [`Query`](crate::system::Query).
76+
///
77+
/// This struct is created by the [`Query::iter_many`](crate::system::Query::iter_many) method.
78+
pub struct QueryManyIter<
79+
'w,
80+
's,
81+
Q: WorldQuery,
82+
QF: Fetch<'w, State = Q::State>,
83+
F: WorldQuery,
84+
I: Iterator,
85+
> where
86+
I::Item: Borrow<Entity>,
87+
{
88+
entity_iter: I,
89+
entities: &'w Entities,
90+
tables: &'w Tables,
91+
archetypes: &'w Archetypes,
92+
fetch: QF,
93+
filter: QueryFetch<'w, F>,
94+
query_state: &'s QueryState<Q, F>,
95+
}
96+
97+
impl<'w, 's, Q: WorldQuery, QF: Fetch<'w, State = Q::State>, F: WorldQuery, I: Iterator>
98+
QueryManyIter<'w, 's, Q, QF, F, I>
99+
where
100+
I::Item: Borrow<Entity>,
101+
{
102+
/// # Safety
103+
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
104+
/// have unique access to the components they query.
105+
/// This does not validate that `world.id()` matches `query_state.world_id`. Calling this on a `world`
106+
/// with a mismatched [`WorldId`](crate::world::WorldId) is unsound.
107+
pub(crate) unsafe fn new<EntityList: IntoIterator<IntoIter = I>>(
108+
world: &'w World,
109+
query_state: &'s QueryState<Q, F>,
110+
entity_list: EntityList,
111+
last_change_tick: u32,
112+
change_tick: u32,
113+
) -> QueryManyIter<'w, 's, Q, QF, F, I> {
114+
let fetch = QF::init(
115+
world,
116+
&query_state.fetch_state,
117+
last_change_tick,
118+
change_tick,
119+
);
120+
let filter = QueryFetch::<F>::init(
121+
world,
122+
&query_state.filter_state,
123+
last_change_tick,
124+
change_tick,
125+
);
126+
QueryManyIter {
127+
query_state,
128+
entities: &world.entities,
129+
archetypes: &world.archetypes,
130+
tables: &world.storages.tables,
131+
fetch,
132+
filter,
133+
entity_iter: entity_list.into_iter(),
134+
}
135+
}
136+
}
137+
138+
impl<'w, 's, Q: WorldQuery, QF: Fetch<'w, State = Q::State>, F: WorldQuery, I: Iterator> Iterator
139+
for QueryManyIter<'w, 'w, Q, QF, F, I>
140+
where
141+
I::Item: Borrow<Entity>,
142+
{
143+
type Item = QF::Item;
144+
145+
#[inline(always)]
146+
fn next(&mut self) -> Option<Self::Item> {
147+
unsafe {
148+
for entity in self.entity_iter.by_ref() {
149+
let location = match self.entities.get(*entity.borrow()) {
150+
Some(location) => location,
151+
None => continue,
152+
};
153+
154+
if !self
155+
.query_state
156+
.matched_archetypes
157+
.contains(location.archetype_id.index())
158+
{
159+
continue;
160+
}
161+
162+
let archetype = &self.archetypes[location.archetype_id];
163+
164+
self.fetch
165+
.set_archetype(&self.query_state.fetch_state, archetype, self.tables);
166+
self.filter
167+
.set_archetype(&self.query_state.filter_state, archetype, self.tables);
168+
if self.filter.archetype_filter_fetch(location.index) {
169+
return Some(self.fetch.archetype_fetch(location.index));
170+
}
171+
}
172+
None
173+
}
174+
}
175+
176+
fn size_hint(&self) -> (usize, Option<usize>) {
177+
let (_, max_size) = self.entity_iter.size_hint();
178+
(0, max_size)
179+
}
180+
}
181+
74182
pub struct QueryCombinationIter<'w, 's, Q: WorldQuery, F: WorldQuery, const K: usize> {
75183
tables: &'w Tables,
76184
archetypes: &'w Archetypes,

crates/bevy_ecs/src/query/mod.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ unsafe fn debug_checked_unreachable() -> ! {
2121
mod tests {
2222
use super::WorldQuery;
2323
use crate::prelude::{AnyOf, Entity, Or, With, Without};
24+
use crate::system::{IntoSystem, Query, System};
2425
use crate::{self as bevy_ecs, component::Component, world::World};
2526
use std::collections::HashSet;
2627

@@ -516,4 +517,44 @@ mod tests {
516517
assert_eq!(custom_param_entities, normal_entities);
517518
}
518519
}
520+
521+
#[test]
522+
fn many_entities() {
523+
let mut world = World::new();
524+
world.spawn().insert_bundle((A(0), B(0)));
525+
world.spawn().insert_bundle((A(0), B(0)));
526+
world.spawn().insert(A(0));
527+
world.spawn().insert(B(0));
528+
{
529+
fn system(has_a: Query<Entity, With<A>>, has_a_and_b: Query<(&A, &B)>) {
530+
assert_eq!(has_a_and_b.iter_many(&has_a).count(), 2);
531+
}
532+
let mut system = IntoSystem::into_system(system);
533+
system.initialize(&mut world);
534+
system.run((), &mut world);
535+
}
536+
{
537+
fn system(has_a: Query<Entity, With<A>>, mut b_query: Query<&mut B>) {
538+
b_query.many_for_each_mut(&has_a, |mut b| {
539+
b.0 = 1;
540+
});
541+
}
542+
let mut system = IntoSystem::into_system(system);
543+
system.initialize(&mut world);
544+
system.run((), &mut world);
545+
}
546+
{
547+
fn system(query: Query<(Option<&A>, &B)>) {
548+
for (maybe_a, b) in &query {
549+
match maybe_a {
550+
Some(_) => assert_eq!(b.0, 1),
551+
None => assert_eq!(b.0, 0),
552+
}
553+
}
554+
}
555+
let mut system = IntoSystem::into_system(system);
556+
system.initialize(&mut world);
557+
system.run((), &mut world);
558+
}
559+
}
519560
}

crates/bevy_ecs/src/query/state.rs

Lines changed: 138 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ 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

19-
use super::{QueryFetch, QueryItem, ROQueryFetch, ROQueryItem};
19+
use super::{QueryFetch, QueryItem, QueryManyIter, ROQueryFetch, ROQueryItem};
2020

2121
/// Provides scoped access to a [`World`] state according to a given [`WorldQuery`] and query filter.
2222
pub struct QueryState<Q: WorldQuery, F: WorldQuery = ()> {
@@ -556,6 +556,32 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
556556
}
557557
}
558558

559+
/// Returns an [`Iterator`] over the query results of a list of [`Entity`]'s.
560+
///
561+
/// This can only return immutable data (mutable data will be cast to an immutable form).
562+
/// See [`Self::many_for_each_mut`] for queries that contain at least one mutable component.
563+
///
564+
#[inline]
565+
pub fn iter_many<'w, 's, EntityList: IntoIterator>(
566+
&'s mut self,
567+
world: &'w World,
568+
entities: EntityList,
569+
) -> QueryManyIter<'w, 's, Q, ROQueryFetch<'w, Q>, F, EntityList::IntoIter>
570+
where
571+
EntityList::Item: Borrow<Entity>,
572+
{
573+
// SAFETY: query is read only
574+
unsafe {
575+
self.update_archetypes(world);
576+
self.iter_many_unchecked_manual(
577+
entities,
578+
world,
579+
world.last_change_tick(),
580+
world.read_change_tick(),
581+
)
582+
}
583+
}
584+
559585
/// Returns an [`Iterator`] over the query results for the given [`World`].
560586
///
561587
/// # Safety
@@ -611,6 +637,35 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
611637
QueryIter::new(world, self, last_change_tick, change_tick)
612638
}
613639

640+
/// Returns an [`Iterator`] for the given [`World`] and list of [`Entity`]'s, where the last change and
641+
/// the current change tick are given.
642+
///
643+
/// # Safety
644+
///
645+
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
646+
/// have unique access to the components they query.
647+
/// this does not check for entity uniqueness
648+
/// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world`
649+
/// with a mismatched [`WorldId`] is unsound.
650+
#[inline]
651+
pub(crate) unsafe fn iter_many_unchecked_manual<
652+
'w,
653+
's,
654+
QF: Fetch<'w, State = Q::State>,
655+
EntityList: IntoIterator,
656+
>(
657+
&'s self,
658+
entities: EntityList,
659+
world: &'w World,
660+
last_change_tick: u32,
661+
change_tick: u32,
662+
) -> QueryManyIter<'w, 's, Q, QF, F, EntityList::IntoIter>
663+
where
664+
EntityList::Item: Borrow<Entity>,
665+
{
666+
QueryManyIter::new(world, self, entities, last_change_tick, change_tick)
667+
}
668+
614669
/// Returns an [`Iterator`] over all possible combinations of `K` query results for the
615670
/// given [`World`] without repetition.
616671
/// This can only be called for read-only queries.
@@ -775,6 +830,29 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
775830
);
776831
}
777832

833+
/// Runs `func` on each query result where the entities match.
834+
#[inline]
835+
pub fn many_for_each_mut<EntityList: IntoIterator>(
836+
&mut self,
837+
world: &mut World,
838+
entities: EntityList,
839+
func: impl FnMut(QueryItem<'_, Q>),
840+
) where
841+
EntityList::Item: Borrow<Entity>,
842+
{
843+
// SAFETY: query has unique world access
844+
unsafe {
845+
self.update_archetypes(world);
846+
self.many_for_each_unchecked_manual(
847+
world,
848+
entities,
849+
func,
850+
world.last_change_tick(),
851+
world.read_change_tick(),
852+
);
853+
};
854+
}
855+
778856
/// Runs `func` on each query result for the given [`World`], where the last change and
779857
/// the current change tick are given. This is faster than the equivalent
780858
/// iter() method, but cannot be chained like a normal [`Iterator`].
@@ -797,7 +875,7 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
797875
change_tick: u32,
798876
) {
799877
// 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
878+
// QueryIter, QueryIterationCursor, QueryState::for_each_unchecked_manual, QueryState::many_for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual
801879
let mut fetch = QF::init(world, &self.fetch_state, last_change_tick, change_tick);
802880
let mut filter = <QueryFetch<F> as Fetch>::init(
803881
world,
@@ -866,7 +944,7 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
866944
change_tick: u32,
867945
) {
868946
// 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
947+
// QueryIter, QueryIterationCursor, QueryState::for_each_unchecked_manual, QueryState::many_for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual
870948
self.task_pool
871949
.as_ref()
872950
.expect("Cannot iterate query in parallel. No ComputeTaskPool initialized.")
@@ -969,6 +1047,62 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
9691047
});
9701048
}
9711049

1050+
/// Runs `func` on each query result for the given [`World`] and list of [`Entity`]'s, where the last change and
1051+
/// the current change tick are given. This is faster than the equivalent
1052+
/// iter() method, but cannot be chained like a normal [`Iterator`].
1053+
///
1054+
/// # Safety
1055+
///
1056+
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
1057+
/// have unique access to the components they query.
1058+
/// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world`
1059+
/// with a mismatched [`WorldId`] is unsound.
1060+
pub(crate) unsafe fn many_for_each_unchecked_manual<EntityList: IntoIterator>(
1061+
&self,
1062+
world: &World,
1063+
entity_list: EntityList,
1064+
mut func: impl FnMut(QueryItem<'_, Q>),
1065+
last_change_tick: u32,
1066+
change_tick: u32,
1067+
) where
1068+
EntityList::Item: Borrow<Entity>,
1069+
{
1070+
// NOTE: If you are changing query iteration code, remember to update the following places, where relevant:
1071+
// QueryIter, QueryIterationCursor, QueryState::for_each_unchecked_manual, QueryState::many_for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual
1072+
let mut fetch =
1073+
<QueryFetch<Q> as Fetch>::init(world, &self.fetch_state, last_change_tick, change_tick);
1074+
let mut filter = <QueryFetch<F> as Fetch>::init(
1075+
world,
1076+
&self.filter_state,
1077+
last_change_tick,
1078+
change_tick,
1079+
);
1080+
1081+
let tables = &world.storages.tables;
1082+
1083+
for entity in entity_list.into_iter() {
1084+
let location = match world.entities.get(*entity.borrow()) {
1085+
Some(location) => location,
1086+
None => continue,
1087+
};
1088+
1089+
if !self
1090+
.matched_archetypes
1091+
.contains(location.archetype_id.index())
1092+
{
1093+
continue;
1094+
}
1095+
1096+
let archetype = &world.archetypes[location.archetype_id];
1097+
1098+
fetch.set_archetype(&self.fetch_state, archetype, tables);
1099+
filter.set_archetype(&self.filter_state, archetype, tables);
1100+
if filter.archetype_filter_fetch(location.index) {
1101+
func(fetch.archetype_fetch(location.index));
1102+
}
1103+
}
1104+
}
1105+
9721106
/// Returns a single immutable query result when there is exactly one entity matching
9731107
/// the query.
9741108
///

0 commit comments

Comments
 (0)