Path: blob/main/crates/bevy_ecs/src/query/world_query.rs
9368 views
use crate::{1archetype::Archetype,2change_detection::Tick,3component::{ComponentId, Components},4query::FilteredAccess,5storage::Table,6world::{unsafe_world_cell::UnsafeWorldCell, World},7};8use variadics_please::all_tuples;910/// Types that can be used as parameters in a [`Query`].11/// Types that implement this should also implement either [`QueryData`] or [`QueryFilter`]12///13/// # Safety14///15/// Implementor must ensure that16/// [`update_component_access`], [`QueryData::provide_extra_access`], [`matches_component_set`], [`QueryData::fetch`], [`QueryFilter::filter_fetch`] and [`init_fetch`]17/// obey the following:18///19/// - For each component mutably accessed by [`QueryData::fetch`], [`update_component_access`] or [`QueryData::provide_extra_access`] should add write access unless read or write access has already been added, in which case it should panic.20/// - For each component readonly accessed by [`QueryData::fetch`] or [`QueryFilter::filter_fetch`], [`update_component_access`] or [`QueryData::provide_extra_access`] should add read access unless write access has already been added, in which case it should panic.21/// - If `fetch` mutably accesses the same component twice, [`update_component_access`] should panic.22/// - [`update_component_access`] may not add a `Without` filter for a component unless [`matches_component_set`] always returns `false` when the component set contains that component.23/// - [`update_component_access`] may not add a `With` filter for a component unless [`matches_component_set`] always returns `false` when the component set doesn't contain that component.24/// - In cases where the query represents a disjunction (such as an `Or` filter) where each element is a valid [`WorldQuery`], the following rules must be obeyed:25/// - [`matches_component_set`] must be a disjunction of the element's implementations26/// - [`update_component_access`] must replace the filters with a disjunction of filters27/// - Each filter in that disjunction must be a conjunction of the corresponding element's filter with the previous `access`28/// - For each resource readonly accessed by [`init_fetch`], [`update_component_access`] should add read access.29/// - Mutable resource access is not allowed.30/// - Any access added during [`QueryData::provide_extra_access`] must be a subset of `available_access`, and must not conflict with any access in `access`.31///32/// When implementing [`update_component_access`], note that `add_read` and `add_write` both also add a `With` filter, whereas `extend_access` does not change the filters.33///34/// [`QueryData::provide_extra_access`]: crate::query::QueryData::provide_extra_access35/// [`QueryData::fetch`]: crate::query::QueryData::fetch36/// [`QueryFilter::filter_fetch`]: crate::query::QueryFilter::filter_fetch37/// [`init_fetch`]: Self::init_fetch38/// [`matches_component_set`]: Self::matches_component_set39/// [`Query`]: crate::system::Query40/// [`update_component_access`]: Self::update_component_access41/// [`QueryData`]: crate::query::QueryData42/// [`QueryFilter`]: crate::query::QueryFilter43pub unsafe trait WorldQuery {44/// Per archetype/table state retrieved by this [`WorldQuery`] to compute [`Self::Item`](crate::query::QueryData::Item) for each entity.45type Fetch<'w>: Clone;4647/// State used to construct a [`Self::Fetch`](WorldQuery::Fetch). This will be cached inside [`QueryState`](crate::query::QueryState),48/// so it is best to move as much data / computation here as possible to reduce the cost of49/// constructing [`Self::Fetch`](WorldQuery::Fetch).50type State: Send + Sync + Sized;5152/// This function manually implements subtyping for the query fetches.53fn shrink_fetch<'wlong: 'wshort, 'wshort>(fetch: Self::Fetch<'wlong>) -> Self::Fetch<'wshort>;5455/// Creates a new instance of [`Self::Fetch`](WorldQuery::Fetch),56/// by combining data from the [`World`] with the cached [`Self::State`](WorldQuery::State).57/// Readonly accesses resources registered in [`WorldQuery::update_component_access`].58///59/// # Safety60///61/// - `state` must have been initialized (via [`WorldQuery::init_state`]) using the same `world` passed62/// in to this function.63/// - `world` must have the **right** to access any access registered in `update_component_access`.64/// - There must not be simultaneous resource access conflicting with readonly resource access registered in [`WorldQuery::update_component_access`].65unsafe fn init_fetch<'w, 's>(66world: UnsafeWorldCell<'w>,67state: &'s Self::State,68last_run: Tick,69this_run: Tick,70) -> Self::Fetch<'w>;7172/// Returns true if (and only if) every table of every archetype matched by this fetch contains73/// all of the matched components.74///75/// This is used to select a more efficient "table iterator"76/// for "dense" queries. If this returns true, [`WorldQuery::set_table`] must be used before77/// [`QueryData::fetch`](crate::query::QueryData::fetch) can be called for iterators. If this returns false,78/// [`WorldQuery::set_archetype`] must be used before [`QueryData::fetch`](crate::query::QueryData::fetch) can be called for79/// iterators.80const IS_DENSE: bool;8182/// Adjusts internal state to account for the next [`Archetype`]. This will always be called on83/// archetypes that match this [`WorldQuery`].84///85/// # Safety86///87/// - `archetype` and `tables` must be from the same [`World`] that [`WorldQuery::init_state`] was called on.88/// - `table` must correspond to `archetype`.89/// - `state` must be the [`State`](Self::State) that `fetch` was initialized with.90unsafe fn set_archetype<'w, 's>(91fetch: &mut Self::Fetch<'w>,92state: &'s Self::State,93archetype: &'w Archetype,94table: &'w Table,95);9697/// Adjusts internal state to account for the next [`Table`]. This will always be called on tables98/// that match this [`WorldQuery`].99///100/// # Safety101///102/// - `table` must be from the same [`World`] that [`WorldQuery::init_state`] was called on.103/// - `state` must be the [`State`](Self::State) that `fetch` was initialized with.104unsafe fn set_table<'w, 's>(105fetch: &mut Self::Fetch<'w>,106state: &'s Self::State,107table: &'w Table,108);109110/// Adds any component accesses used by this [`WorldQuery`] to `access`.111///112/// Used to check which queries are disjoint and can run in parallel113// This does not have a default body of `{}` because 99% of cases need to add accesses114// and forgetting to do so would be unsound.115fn update_component_access(state: &Self::State, access: &mut FilteredAccess);116117/// Creates and initializes a [`State`](WorldQuery::State) for this [`WorldQuery`] type.118fn init_state(world: &mut World) -> Self::State;119120/// Attempts to initialize a [`State`](WorldQuery::State) for this [`WorldQuery`] type using read-only121/// access to [`Components`].122fn get_state(components: &Components) -> Option<Self::State>;123124/// Returns `true` if this query matches a set of components. Otherwise, returns `false`.125///126/// Used to check which [`Archetype`]s can be skipped by the query127/// (if none of the [`Component`](crate::component::Component)s match).128/// This is how archetypal query filters like `With` work.129fn matches_component_set(130state: &Self::State,131set_contains_id: &impl Fn(ComponentId) -> bool,132) -> bool;133}134135macro_rules! impl_tuple_world_query {136($(#[$meta:meta])* $(($name: ident, $state: ident)),*) => {137138#[expect(139clippy::allow_attributes,140reason = "This is a tuple-related macro; as such the lints below may not always apply."141)]142#[allow(143non_snake_case,144reason = "The names of some variables are provided by the macro's caller, not by us."145)]146#[allow(147unused_variables,148reason = "Zero-length tuples won't use any of the parameters."149)]150#[allow(151clippy::unused_unit,152reason = "Zero-length tuples will generate some function bodies equivalent to `()`; however, this macro is meant for all applicable tuples, and as such it makes no sense to rewrite it just for that case."153)]154$(#[$meta])*155// SAFETY:156// `fetch` accesses are the conjunction of the subqueries' accesses157// This is sound because `update_component_access` adds accesses according to the implementations of all the subqueries.158// `update_component_access` adds all `With` and `Without` filters from the subqueries.159// This is sound because `matches_component_set` always returns `false` if any the subqueries' implementations return `false`.160unsafe impl<$($name: WorldQuery),*> WorldQuery for ($($name,)*) {161type Fetch<'w> = ($($name::Fetch<'w>,)*);162type State = ($($name::State,)*);163164165fn shrink_fetch<'wlong: 'wshort, 'wshort>(fetch: Self::Fetch<'wlong>) -> Self::Fetch<'wshort> {166let ($($name,)*) = fetch;167($(168$name::shrink_fetch($name),169)*)170}171172#[inline]173unsafe fn init_fetch<'w, 's>(world: UnsafeWorldCell<'w>, state: &'s Self::State, last_run: Tick, this_run: Tick) -> Self::Fetch<'w> {174let ($($name,)*) = state;175// SAFETY: The invariants are upheld by the caller.176($(unsafe { $name::init_fetch(world, $name, last_run, this_run) },)*)177}178179const IS_DENSE: bool = true $(&& $name::IS_DENSE)*;180181#[inline]182unsafe fn set_archetype<'w, 's>(183fetch: &mut Self::Fetch<'w>,184state: &'s Self::State,185archetype: &'w Archetype,186table: &'w Table187) {188let ($($name,)*) = fetch;189let ($($state,)*) = state;190// SAFETY: The invariants are upheld by the caller.191$(unsafe { $name::set_archetype($name, $state, archetype, table); })*192}193194#[inline]195unsafe fn set_table<'w, 's>(fetch: &mut Self::Fetch<'w>, state: &'s Self::State, table: &'w Table) {196let ($($name,)*) = fetch;197let ($($state,)*) = state;198// SAFETY: The invariants are upheld by the caller.199$(unsafe { $name::set_table($name, $state, table); })*200}201202203fn update_component_access(state: &Self::State, access: &mut FilteredAccess) {204let ($($name,)*) = state;205$($name::update_component_access($name, access);)*206}207fn init_state(world: &mut World) -> Self::State {208($($name::init_state(world),)*)209}210fn get_state(components: &Components) -> Option<Self::State> {211Some(($($name::get_state(components)?,)*))212}213214fn matches_component_set(state: &Self::State, set_contains_id: &impl Fn(ComponentId) -> bool) -> bool {215let ($($name,)*) = state;216true $(&& $name::matches_component_set($name, set_contains_id))*217}218}219};220}221222all_tuples!(223#[doc(fake_variadic)]224impl_tuple_world_query,2250,22615,227F,228S229);230231232