diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index cb581b58b8..d5a1c3839e 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -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 @@ -357,7 +363,7 @@ impl 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)] @@ -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. /// @@ -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` 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: diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index cc911c1b95..35346be6fa 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -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)`. @@ -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, @@ -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 { @@ -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) { @@ -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) }; diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 75da60c8d5..57bad73e7b 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -22,6 +22,17 @@ 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 @@ -29,8 +40,12 @@ use super::{ pub struct QueryState { 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, // NOTE: we maintain both a TableId bitset and a vec because iterating the vec is faster pub(crate) matched_table_ids: Vec, @@ -330,6 +345,11 @@ impl QueryState { } } + /// 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)) diff --git a/crates/bevy_ecs/src/query/world_query.rs b/crates/bevy_ecs/src/query/world_query.rs index 9d0d0d829e..19c6a3254b 100644 --- a/crates/bevy_ecs/src/query/world_query.rs +++ b/crates/bevy_ecs/src/query/world_query.rs @@ -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); @@ -132,6 +134,9 @@ pub unsafe trait WorldQuery { fn get_state(world: &World) -> Option; /// 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, diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index fb87bb2e01..961c30c94d 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -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 ///