Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions crates/bevy_ecs/src/query/access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ pub struct Access<T: SparseSetIndex> {
/// 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<T>,
}

Expand Down Expand Up @@ -90,6 +92,7 @@ impl<T: SparseSetIndex> Access<T> {
writes_all: false,
reads_and_writes: FixedBitSet::new(),
writes: FixedBitSet::new(),
archetypal: FixedBitSet::new(),
marker: PhantomData,
}
}
Expand All @@ -116,6 +119,19 @@ impl<T: SparseSetIndex> Access<T> {
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<T>`].
///
/// [`Has<T>`]: 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())
Expand All @@ -136,6 +152,18 @@ impl<T: SparseSetIndex> Access<T> {
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<T>`].
///
/// [`Has<T>`]: 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;
Expand Down Expand Up @@ -272,6 +300,18 @@ impl<T: SparseSetIndex> Access<T> {
pub fn writes(&self) -> impl Iterator<Item = T> + '_ {
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<T>`].
///
/// [`Has<T>`]: crate::query::Has
pub fn archetypal(&self) -> impl Iterator<Item = T> + '_ {
self.archetypal.ones().map(T::get_sparse_set_index)
}
}

/// An [`Access`] that has been filtered to include and exclude certain combinations of elements.
Expand Down Expand Up @@ -469,6 +509,20 @@ impl<T: SparseSetIndex> FilteredAccess<T> {
pub fn is_subset(&self, other: &FilteredAccess<T>) -> 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<Item = T> + '_ {
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<Item = T> + '_ {
self.filter_sets
.iter()
.flat_map(|f| f.without.ones().map(T::get_sparse_set_index))
}
}

#[derive(Clone, Eq, PartialEq)]
Expand Down
7 changes: 5 additions & 2 deletions crates/bevy_ecs/src/query/fetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1403,8 +1403,11 @@ unsafe impl<T: Component> WorldQuery for Has<T> {
*fetch
}

fn update_component_access(_state: &Self::State, _access: &mut FilteredAccess<ComponentId>) {
// Do nothing as presence of `Has<T>` never affects whether two queries are disjoint
fn update_component_access(
&component_id: &Self::State,
access: &mut FilteredAccess<ComponentId>,
) {
access.access_mut().add_archetypal(component_id);
}

fn init_state(world: &mut World) -> ComponentId {
Expand Down
20 changes: 20 additions & 0 deletions crates/bevy_ecs/src/query/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,26 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
) -> &QueryState<NewD, NewF> {
&*(self as *const QueryState<D, F> as *const QueryState<NewD, NewF>)
}

/// Returns the archetype components accessed by this query.
pub fn archetype_component_access(&self) -> &Access<ArchetypeComponentId> {
&self.archetype_component_access
}

/// Returns the components accessed by this query.
pub fn component_access(&self) -> &FilteredAccess<ComponentId> {
&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<D: QueryData, F: QueryFilter> QueryState<D, F> {
Expand Down