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
12 changes: 9 additions & 3 deletions crates/bevy_ecs/src/query/filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ pub trait QueryFilter: WorldQuery {
/// many elements are being iterated (such as `Iterator::collect()`).
const IS_ARCHETYPAL: bool;

/// Returns true if the provided [`Entity`] and [`TableRow`] should be included in the query results.
/// If false, the entity will be skipped.
///
/// Note that this is called after already restricting the matched [`Table`]s and [`Archetype`]s to the
/// ones that are compatible with the Filter's access.
///
/// # Safety
///
/// Must always be called _after_ [`WorldQuery::set_table`] or [`WorldQuery::set_archetype`]. `entity` and
Expand Down Expand Up @@ -357,7 +363,7 @@ impl<T: WorldQuery> Clone for OrFetch<'_, T> {
}
}

macro_rules! impl_query_filter_tuple {
macro_rules! impl_or_query_filter {
($(($filter: ident, $state: ident)),*) => {
#[allow(unused_variables)]
#[allow(non_snake_case)]
Expand Down Expand Up @@ -506,7 +512,7 @@ macro_rules! impl_tuple_query_filter {
}

all_tuples!(impl_tuple_query_filter, 0, 15, F);
all_tuples!(impl_query_filter_tuple, 0, 15, F, S);
all_tuples!(impl_or_query_filter, 0, 15, F, S);

/// A filter on a component that only retains results added after the system last ran.
///
Expand All @@ -524,7 +530,7 @@ all_tuples!(impl_query_filter_tuple, 0, 15, F, S);
/// # Time complexity
///
/// `Added` is not [`ArchetypeFilter`], which practically means that
/// if query (with `T` component filter) matches million entities,
/// if the query (with `T` component filter) matches a million entities,
/// `Added<T>` filter will iterate over all of them even if none of them were just added.
///
/// For example, these two systems are roughly equivalent in terms of performance:
Expand Down
10 changes: 5 additions & 5 deletions crates/bevy_ecs/src/query/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
}

/// Executes the equivalent of [`Iterator::for_each`] over a contiguous segment
/// from an table.
/// from a table.
///
/// # Safety
/// - all `rows` must be in `[0, table.entity_count)`.
Expand Down Expand Up @@ -656,7 +656,7 @@ struct QueryIterationCursor<'w, 's, D: QueryData, F: QueryFilter> {
archetype_entities: &'w [ArchetypeEntity],
fetch: D::Fetch<'w>,
filter: F::Fetch<'w>,
// length of the table table or length of the archetype, depending on whether both `D`'s and `F`'s fetches are dense
// length of the table or length of the archetype, depending on whether both `D`'s and `F`'s fetches are dense
current_len: usize,
// either table row or archetype index, depending on whether both `D`'s and `F`'s fetches are dense
current_row: usize,
Expand Down Expand Up @@ -743,7 +743,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {

/// How many values will this cursor return at most?
///
/// Note that if `D::IS_ARCHETYPAL && F::IS_ARCHETYPAL`, the return value
/// Note that if `F::IS_ARCHETYPAL`, the return value
/// will be **the exact count of remaining values**.
fn max_remaining(&self, tables: &'w Tables, archetypes: &'w Archetypes) -> usize {
let remaining_matched: usize = if Self::IS_DENSE {
Expand Down Expand Up @@ -788,7 +788,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
}

// SAFETY: set_table was called prior.
// `current_row` is a table row in range of the current table, because if it was not, then the if above would have been executed.
// `current_row` is a table row in range of the current table, because if it was not, then the above would have been executed.
let entity = unsafe { self.table_entities.get_unchecked(self.current_row) };
let row = TableRow::from_usize(self.current_row);
if !F::filter_fetch(&mut self.filter, *entity, row) {
Expand All @@ -799,7 +799,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
// SAFETY:
// - set_table was called prior.
// - `current_row` must be a table row in range of the current table,
// because if it was not, then the if above would have been executed.
// because if it was not, then the above would have been executed.
// - fetch is only called once for each `entity`.
let item = unsafe { D::fetch(&mut self.fetch, *entity, row) };

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 @@ -22,15 +22,30 @@ use super::{
};

/// Provides scoped access to a [`World`] state according to a given [`QueryData`] and [`QueryFilter`].
///
/// This data is cached between system runs, and is used to:
/// - store metadata about which [`Table`] or [`Archetype`] are matched by the query. "Matched" means
/// that the query will iterate over the data in the matched table/archetype.
/// - cache the [`State`] needed to compute the [`Fetch`] struct used to retrieve data
/// from a specific [`Table`] or [`Archetype`]
/// - build iterators that can iterate over the query results
///
/// [`State`]: crate::query::world_query::WorldQuery::State
/// [`Fetch`]: crate::query::world_query::WorldQuery::Fetch
/// [`Table`]: crate::storage::Table
#[repr(C)]
// SAFETY NOTE:
// Do not add any new fields that use the `D` or `F` generic parameters as this may
// make `QueryState::as_transmuted_state` unsound if not done with care.
pub struct QueryState<D: QueryData, F: QueryFilter = ()> {
world_id: WorldId,
pub(crate) archetype_generation: ArchetypeGeneration,
/// Metadata about the [`Table`](crate::storage::Table)s matched by this query.
pub(crate) matched_tables: FixedBitSet,
/// Metadata about the [`Archetype`]s matched by this query.
pub(crate) matched_archetypes: FixedBitSet,
/// [`FilteredAccess`] computed by combining the `D` and `F` access. Used to check which other queries
/// this query can run in parallel with.
pub(crate) component_access: FilteredAccess<ComponentId>,
// NOTE: we maintain both a TableId bitset and a vec because iterating the vec is faster
pub(crate) matched_table_ids: Vec<TableId>,
Expand Down Expand Up @@ -330,6 +345,11 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
}
}

/// Process the given [`Archetype`] to update internal metadata about the [`Table`](crate::storage::Table)s
/// and [`Archetype`]s that are matched by this query.
///
/// Returns `true` if the given `archetype` matches the query. Otherwise, returns `false`.
/// If there is no match, then there is no need to update the query's [`FilteredAccess`].
fn new_archetype_internal(&mut self, archetype: &Archetype) -> bool {
if D::matches_component_set(&self.fetch_state, &|id| archetype.contains(id))
&& F::matches_component_set(&self.filter_state, &|id| archetype.contains(id))
Expand Down
5 changes: 5 additions & 0 deletions crates/bevy_ecs/src/query/world_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ pub unsafe trait WorldQuery {
) -> Self::Item<'w>;

/// Adds any component accesses used by this [`WorldQuery`] to `access`.
///
/// Used to check which queries are disjoint and can run in parallel
// This does not have a default body of `{}` because 99% of cases need to add accesses
// and forgetting to do so would be unsound.
fn update_component_access(state: &Self::State, access: &mut FilteredAccess<ComponentId>);
Expand All @@ -132,6 +134,9 @@ pub unsafe trait WorldQuery {
fn get_state(world: &World) -> Option<Self::State>;

/// Returns `true` if this query matches a set of components. Otherwise, returns `false`.
///
/// Used to check which [`Archetype`]s can be skipped by the query
/// (if none of the [`Component`](crate::component::Component)s match)
fn matches_component_set(
state: &Self::State,
set_contains_id: &impl Fn(ComponentId) -> bool,
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/system/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
/// Returns an [`Iterator`] over the read-only query items generated from an [`Entity`] list.
///
/// Items are returned in the order of the list of entities, and may not be unique if the input
/// doesnn't guarantee uniqueness. Entities that don't match the query are skipped.
/// doesn't guarantee uniqueness. Entities that don't match the query are skipped.
///
/// # Example
///
Expand Down