Update ecs query docs (#12595)
# Objective I'm reading through the ecs query code for the first time, and updating the docs: - fixed some typos - added some docs about things I was confused about (in particular what the difference between `matches_component_set` and `update_component_access` was)
This commit is contained in:
parent
7673afb03e
commit
e33b93e312
@ -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<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)]
|
||||
@ -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<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:
|
||||
|
||||
@ -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) };
|
||||
|
||||
|
||||
@ -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<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>,
|
||||
@ -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))
|
||||
|
||||
@ -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>);
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
///
|
||||
|
||||
Loading…
Reference in New Issue
Block a user