diff --git a/crates/bevy_ecs/src/query/access.rs b/crates/bevy_ecs/src/query/access.rs index d7253f073ed7f..848309be77ecc 100644 --- a/crates/bevy_ecs/src/query/access.rs +++ b/crates/bevy_ecs/src/query/access.rs @@ -59,6 +59,8 @@ pub struct Access { /// Is `true` if this has mutable access to all elements in the collection. /// If this is true, then `reads_all` must also be true. writes_all: bool, + // Elements that are not accessed, but whose presence in an archetype affect query results. + archetypal: FixedBitSet, marker: PhantomData, } @@ -90,6 +92,7 @@ impl Access { writes_all: false, reads_and_writes: FixedBitSet::new(), writes: FixedBitSet::new(), + archetypal: FixedBitSet::new(), marker: PhantomData, } } @@ -116,6 +119,19 @@ impl Access { self.writes.insert(index.sparse_set_index()); } + /// Adds an archetypal (indirect) access to the element given by `index`. + /// + /// This is for elements whose values are not accessed (and thus will never cause conflicts), + /// but whose presence in an archetype may affect query results. + /// + /// Currently, this is only used for [`Has`]. + /// + /// [`Has`]: crate::query::Has + pub fn add_archetypal(&mut self, index: T) { + self.archetypal.grow(index.sparse_set_index() + 1); + self.archetypal.insert(index.sparse_set_index()); + } + /// Returns `true` if this can access the element given by `index`. pub fn has_read(&self, index: T) -> bool { self.reads_all || self.reads_and_writes.contains(index.sparse_set_index()) @@ -136,6 +152,18 @@ impl Access { self.writes_all || !self.writes.is_clear() } + /// Returns true if this has an archetypal (indirect) access to the element given by `index`. + /// + /// This is an element whose value is not accessed (and thus will never cause conflicts), + /// but whose presence in an archetype may affect query results. + /// + /// Currently, this is only used for [`Has`]. + /// + /// [`Has`]: crate::query::Has + pub fn has_archetypal(&self, index: T) -> bool { + self.archetypal.contains(index.sparse_set_index()) + } + /// Sets this as having access to all indexed elements (i.e. `&World`). pub fn read_all(&mut self) { self.reads_all = true; @@ -272,6 +300,18 @@ impl Access { pub fn writes(&self) -> impl Iterator + '_ { self.writes.ones().map(T::get_sparse_set_index) } + + /// Returns the indices of the elements that this has an archetypal access to. + /// + /// These are elements whose values are not accessed (and thus will never cause conflicts), + /// but whose presence in an archetype may affect query results. + /// + /// Currently, this is only used for [`Has`]. + /// + /// [`Has`]: crate::query::Has + pub fn archetypal(&self) -> impl Iterator + '_ { + self.archetypal.ones().map(T::get_sparse_set_index) + } } /// An [`Access`] that has been filtered to include and exclude certain combinations of elements. @@ -469,6 +509,20 @@ impl FilteredAccess { pub fn is_subset(&self, other: &FilteredAccess) -> bool { self.required.is_subset(&other.required) && self.access().is_subset(other.access()) } + + /// Returns the indices of the elements that this access filters for. + pub fn with_filters(&self) -> impl Iterator + '_ { + self.filter_sets + .iter() + .flat_map(|f| f.with.ones().map(T::get_sparse_set_index)) + } + + /// Returns the indices of the elements that this access filters out. + pub fn without_filters(&self) -> impl Iterator + '_ { + self.filter_sets + .iter() + .flat_map(|f| f.without.ones().map(T::get_sparse_set_index)) + } } #[derive(Clone, Eq, PartialEq)] diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 5e83637d33019..27f329c7b7103 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -1403,8 +1403,11 @@ unsafe impl WorldQuery for Has { *fetch } - fn update_component_access(_state: &Self::State, _access: &mut FilteredAccess) { - // Do nothing as presence of `Has` never affects whether two queries are disjoint + fn update_component_access( + &component_id: &Self::State, + access: &mut FilteredAccess, + ) { + access.access_mut().add_archetypal(component_id); } fn init_state(world: &mut World) -> ComponentId { diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 666549d52a6f3..70794527c8449 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -95,6 +95,26 @@ impl QueryState { ) -> &QueryState { &*(self as *const QueryState as *const QueryState) } + + /// Returns the archetype components accessed by this query. + pub fn archetype_component_access(&self) -> &Access { + &self.archetype_component_access + } + + /// Returns the components accessed by this query. + pub fn component_access(&self) -> &FilteredAccess { + &self.component_access + } + + /// Returns the tables matched by this query. + pub fn matched_tables(&self) -> &[TableId] { + &self.matched_table_ids + } + + /// Returns the archetypes matched by this query. + pub fn matched_archetypes(&self) -> &[ArchetypeId] { + &self.matched_archetype_ids + } } impl QueryState {