diff --git a/crates/bevy_ecs/Cargo.toml b/crates/bevy_ecs/Cargo.toml index 98b9c33bb07c8..43b8d8b7ea66f 100644 --- a/crates/bevy_ecs/Cargo.toml +++ b/crates/bevy_ecs/Cargo.toml @@ -52,6 +52,10 @@ bevy_debug_stepping = [] ## This will often provide more detailed error messages. track_location = [] +## Makes queries become uncached by default. +## Mostly useful for unit tests. +query_uncached_default = [] + # Executor Backend ## Uses `async-executor` as a task execution backend. diff --git a/crates/bevy_ecs/src/entity_disabling.rs b/crates/bevy_ecs/src/entity_disabling.rs index c2fc5ee6c53b9..c251b479364d2 100644 --- a/crates/bevy_ecs/src/entity_disabling.rs +++ b/crates/bevy_ecs/src/entity_disabling.rs @@ -97,7 +97,7 @@ //! [`Query` performance]: crate::prelude::Query#performance use crate::{ - component::{ComponentId, Components, StorageType}, + component::ComponentId, query::FilteredAccess, world::{FromWorld, World}, }; @@ -248,14 +248,6 @@ impl DefaultQueryFilters { } } } - - pub(super) fn is_dense(&self, components: &Components) -> bool { - self.disabling_ids().all(|component_id| { - components - .get_info(component_id) - .is_some_and(|info| info.storage_type() == StorageType::Table) - }) - } } #[cfg(test)] diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index d2f59ac29f83d..d639f4ad2bd45 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -88,7 +88,10 @@ pub mod prelude { message::{Message, MessageMutator, MessageReader, MessageWriter, Messages}, name::{Name, NameOrEntity}, observer::{Observer, On, Trigger}, - query::{Added, Allow, AnyOf, Changed, Has, Or, QueryBuilder, QueryState, With, Without}, + query::{ + Added, Allow, AnyOf, Changed, Has, Or, QueryBuilder, QueryState, UncachedQueryState, + With, Without, + }, related, relationship::RelationshipTarget, resource::Resource, @@ -101,7 +104,7 @@ pub mod prelude { Command, Commands, Deferred, EntityCommand, EntityCommands, If, In, InMut, InRef, IntoSystem, Local, NonSend, NonSendMut, ParamSet, Populated, Query, ReadOnlySystem, Res, ResMut, Single, System, SystemIn, SystemInput, SystemParamBuilder, - SystemParamFunction, + SystemParamFunction, UncachedQuery, }, world::{ EntityMut, EntityRef, EntityWorldMut, FilteredResources, FilteredResourcesMut, @@ -475,8 +478,12 @@ mod tests { assert_eq!(ents, &[(e, A(123))]); let f = world.spawn((TableStored("def"), A(456), B(1))).id(); - let ents = query.iter(&world).map(|(e, &i)| (e, i)).collect::>(); - assert_eq!(ents, &[(e, A(123)), (f, A(456))]); + let ents = query + .iter(&world) + .map(|(e, &i)| (e, i)) + .collect::>(); + assert!(ents.contains(&(e, A(123)))); + assert!(ents.contains(&(f, A(456)))); } #[test] diff --git a/crates/bevy_ecs/src/query/cache.rs b/crates/bevy_ecs/src/query/cache.rs new file mode 100644 index 0000000000000..d3d21ec56beab --- /dev/null +++ b/crates/bevy_ecs/src/query/cache.rs @@ -0,0 +1,475 @@ +use crate::query::QueryState; +use alloc::borrow::Cow; +use alloc::vec::Vec; +use bevy_ecs::archetype::{Archetype, ArchetypeGeneration, ArchetypeId, Archetypes}; +use bevy_ecs::component::ComponentId; +use bevy_ecs::prelude::World; +use bevy_ecs::query::state::StorageId; +use bevy_ecs::query::{FilteredAccess, QueryBuilder, QueryData, QueryFilter}; +use bevy_ecs::storage::{SparseSetIndex, TableId}; +use bevy_ecs::world::unsafe_world_cell::UnsafeWorldCell; +use bevy_ecs::world::WorldId; +use core::fmt::{Debug, Formatter}; +use fixedbitset::FixedBitSet; +use log::warn; + +/// Borrow-only view over the non-cache fields of a QueryState. +#[doc(hidden)] +pub struct UncachedQueryBorrow<'a, D: QueryData, F: QueryFilter> { + pub(crate) world_id: WorldId, + pub(crate) component_access: &'a FilteredAccess, + pub(crate) fetch_state: &'a D::State, + pub(crate) filter_state: &'a F::State, +} + +impl<'a, D: QueryData, F: QueryFilter> UncachedQueryBorrow<'a, D, F> { + /// # Panics + /// + /// If `world_id` does not match the [`World`] used to call `QueryState::new` for this instance. + /// + /// Many unsafe query methods require the world to match for soundness. This function is the easiest + /// way of ensuring that it matches. + #[inline] + #[track_caller] + pub fn validate_world(&self, world_id: WorldId) { + #[inline(never)] + #[track_caller] + #[cold] + fn panic_mismatched(this: WorldId, other: WorldId) -> ! { + panic!("Encountered a mismatched World. This QueryState was created from {this:?}, but a method was called using {other:?}."); + } + + if self.world_id != world_id { + panic_mismatched(self.world_id, world_id); + } + } + + /// Returns `true` if this query matches a set of components. Otherwise, returns `false`. + /// + /// # Safety + /// `archetype` must be from the `World` this [`UncachedQueryBorrow`] was initialized from. + pub unsafe fn matches_archetype(&self, archetype: &Archetype) -> bool { + D::matches_component_set(self.fetch_state, &|id| archetype.contains(id)) + && F::matches_component_set(self.filter_state, &|id| archetype.contains(id)) + && self.component_access.filter_sets.iter().any(|set| { + set.with + .ones() + .all(|index| archetype.contains(ComponentId::get_sparse_set_index(index))) + && set + .without + .ones() + .all(|index| !archetype.contains(ComponentId::get_sparse_set_index(index))) + }) + } + + /// Iterate through all new archetypes more recent than the provided [`ArchetypeGeneration`], + /// and call `f` on each of them. + pub fn iter_archetypes( + &self, + archetype_generation: ArchetypeGeneration, + archetypes: &Archetypes, + mut f: impl FnMut(&Archetype), + ) { + if self.component_access.required.is_empty() { + archetypes[archetype_generation..] + .iter() + .for_each(|archetype| { + // SAFETY: The validate_world call ensures that the world is the same the QueryState + // was initialized from. + if unsafe { self.matches_archetype(archetype) } { + f(archetype); + } + }); + } else { + // if there are required components, we can optimize by only iterating through archetypes + // that contain at least one of the required components + let potential_archetype_ids = self + .component_access + .required + .ones() + .filter_map(|idx| { + let component_id = ComponentId::get_sparse_set_index(idx); + archetypes + .component_index() + .get(&component_id) + .map(|index| index.keys()) + }) + // select the component with the fewest archetypes + .min_by_key(ExactSizeIterator::len); + if let Some(archetype_ids) = potential_archetype_ids { + for archetype_id in archetype_ids { + // exclude archetypes that have already been processed + if archetype_id < &archetype_generation.0 { + continue; + } + // SAFETY: get_potential_archetypes only returns archetype ids that are valid for the world + let archetype = &archetypes[*archetype_id]; + // SAFETY: The validate_world call ensures that the world is the same the QueryState + // was initialized from. + if unsafe { self.matches_archetype(archetype) } { + f(archetype); + } + } + } + } + } +} + +impl QueryState { + /// Updates the state's internal view of the [`World`]'s archetypes. If this is not called before querying data, + /// the results may not accurately reflect what is in the `world`. + /// + /// This is only required if a `manual` method (such as [`Self::get_manual`]) is being called, and it only needs to + /// be called if the `world` has been structurally mutated (i.e. added/removed a component or resource). Users using + /// non-`manual` methods such as [`QueryState::get`] do not need to call this as it will be automatically called for them. + /// + /// If you have an [`UnsafeWorldCell`] instead of `&World`, consider using [`QueryState::update_archetypes_unsafe_world_cell`]. + /// + /// # Panics + /// + /// If `world` does not match the one used to call `QueryState::new` for this instance. + pub fn update_archetypes(&mut self, world: &World) { + self.update_archetypes_unsafe_world_cell(world.as_unsafe_world_cell_readonly()); + } + + /// Updates the state's internal view of the `world`'s archetypes. If this is not called before querying data, + /// the results may not accurately reflect what is in the `world`. + /// + /// This is only required if a `manual` method (such as [`Self::get_manual`]) is being called, and it only needs to + /// be called if the `world` has been structurally mutated (i.e. added/removed a component or resource). Users using + /// non-`manual` methods such as [`QueryState::get`] do not need to call this as it will be automatically called for them. + /// + /// # Note + /// + /// This method only accesses world metadata. + /// + /// # Panics + /// + /// If `world` does not match the one used to call `QueryState::new` for this instance. + pub fn update_archetypes_unsafe_world_cell(&mut self, world: UnsafeWorldCell) { + self.split_cache(|prefix, cache| { + cache.update_archetypes(&prefix, world); + }); + } + + /// Returns `true` if this query matches this [`Archetype`]. Otherwise, returns `false`. + /// + /// # Safety + /// `archetype` must be from the `World` this state was initialized from. + pub(crate) unsafe fn matches_archetype(&self, archetype: &Archetype) -> bool { + // SAFETY: from parent function's safety constraint + unsafe { self.cache.contains(self, archetype) } + } +} + +impl QueryState { + /// Iterate through all archetypes that match the [`QueryState`] with an [`ArchetypeGeneration`] higher than the provided one, + /// and call `f` on each of them. + fn iter_archetypes( + &self, + archetype_generation: ArchetypeGeneration, + archetypes: &Archetypes, + f: impl FnMut(&Archetype), + ) { + self.as_uncached() + .iter_archetypes(archetype_generation, archetypes, f); + } +} + +/// Types that can cache archetypes matched by a `Query`. +pub trait QueryCache: Debug + Clone + Sync { + /// Returns the data needed to iterate through the archetypes that match the query. + /// Usually used to populate a `QueryIterationCursor` + fn iteration_data<'s, 'a: 's, D: QueryData, F: QueryFilter>( + &'a self, + query: &QueryState, + world: UnsafeWorldCell, + ) -> IterationData<'s>; + + /// Returns true if the cache contains information about the archetype being matches by the query + /// + /// # Safety + /// `archetype` must be from the `World` the state was initialized from. + unsafe fn contains( + &self, + query: &QueryState, + archetype: &Archetype, + ) -> bool; + + /// Creates a new [`QueryCache`] but does not populate it with the matched results from the World yet + fn from_world_uninitialized(world: &World) -> Self; + + /// Creates a new [`QueryCache`] but does not populate it with the matched results from the World yet + fn from_builder_uninitialized( + builder: &QueryBuilder, + ) -> Self; + + /// Update the [`QueryCache`] by storing in the cache every new archetypes that match the query. + fn update_archetypes( + &mut self, + uncached: &UncachedQueryBorrow<'_, D, F>, + world: UnsafeWorldCell, + ); + + /// Return a new cache that contains the archetypes matched by the intersection of itself and the + /// other cache. + fn join(&self, other: &Self) -> Self; +} + +/// Contains a list of matches tables or archetypes, that can be used to iterate through archetypes +/// that match a query +#[derive(Clone)] +#[doc(hidden)] +pub struct IterationData<'s> { + pub(super) is_dense: bool, + pub(super) storage_ids: Cow<'s, [StorageId]>, +} + +/// Default [`QueryCache`] to use if caching is enabled for a query. +/// Will store a pre-computed list of archetypes or tables that match a query. +#[derive(Clone)] +pub struct CacheState { + 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, + // NOTE: we maintain both a bitset and a vec because iterating the vec is faster + pub(super) matched_storage_ids: Vec, + // Represents whether this query iteration is dense or not. When this is true + // `matched_storage_ids` stores `TableId`s, otherwise it stores `ArchetypeId`s. + pub(super) is_dense: bool, +} + +impl QueryCache for CacheState { + fn iteration_data<'s, 'a: 's, D: QueryData, F: QueryFilter>( + &'a self, + _: &QueryState, + _: UnsafeWorldCell, + ) -> IterationData<'s> { + IterationData { + storage_ids: Cow::Borrowed(&self.matched_storage_ids), + is_dense: self.is_dense, + } + } + + unsafe fn contains( + &self, + _: &QueryState, + archetype: &Archetype, + ) -> bool { + self.matched_archetypes.contains(archetype.id().index()) + } + + fn from_world_uninitialized(_: &World) -> Self { + // For queries without dynamic filters the dense-ness of the query is equal to the dense-ness + // of its static type parameters. + let is_dense = D::IS_DENSE && F::IS_DENSE; + Self { + archetype_generation: ArchetypeGeneration::initial(), + matched_tables: Default::default(), + matched_archetypes: Default::default(), + matched_storage_ids: Vec::new(), + is_dense, + } + } + + fn from_builder_uninitialized( + builder: &QueryBuilder, + ) -> Self { + // For dynamic queries the dense-ness is given by the query builder. + let is_dense = builder.is_dense(); + Self { + archetype_generation: ArchetypeGeneration::initial(), + matched_tables: Default::default(), + matched_archetypes: Default::default(), + matched_storage_ids: Vec::new(), + is_dense, + } + } + fn update_archetypes( + &mut self, + uncached: &UncachedQueryBorrow<'_, D, F>, + world: UnsafeWorldCell, + ) { + uncached.validate_world(world.id()); + if self.archetype_generation == world.archetypes().generation() { + // skip if we are already up to date + return; + } + let old_generation = core::mem::replace( + &mut self.archetype_generation, + world.archetypes().generation(), + ); + uncached.iter_archetypes(old_generation, world.archetypes(), |archetype| { + self.cache_archetype(archetype); + }); + } + + fn join(&self, other: &Self) -> Self { + if self.archetype_generation != other.archetype_generation { + warn!("You have tried to join queries with different archetype_generations. This could lead to unpredictable results."); + } + // the join is dense of both the queries were dense. + let is_dense = self.is_dense && other.is_dense; + // take the intersection of the matched ids + let mut matched_tables = self.matched_tables.clone(); + let mut matched_archetypes = self.matched_archetypes.clone(); + matched_tables.intersect_with(&other.matched_tables); + matched_archetypes.intersect_with(&other.matched_archetypes); + let matched_storage_ids = if is_dense { + matched_tables + .ones() + .map(|id| StorageId { + table_id: TableId::from_usize(id), + }) + .collect() + } else { + matched_archetypes + .ones() + .map(|id| StorageId { + archetype_id: ArchetypeId::new(id), + }) + .collect() + }; + CacheState { + archetype_generation: self.archetype_generation, + matched_tables, + matched_archetypes, + matched_storage_ids, + is_dense, + } + } +} + +impl Debug for CacheState { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + f.debug_struct("CacheState") + .field("matched_table_count", &self.matched_tables.count_ones(..)) + .field( + "matched_archetype_count", + &self.matched_archetypes.count_ones(..), + ) + .finish() + } +} + +impl CacheState { + /// Returns the tables matched by this query. + pub fn matched_tables(&self) -> impl Iterator + '_ { + self.matched_tables.ones().map(TableId::from_usize) + } + + /// Returns the archetypes matched by this query. + pub fn matched_archetypes(&self) -> impl Iterator + '_ { + self.matched_archetypes.ones().map(ArchetypeId::new) + } + + /// Add a new archetype in the cache + fn cache_archetype(&mut self, archetype: &Archetype) { + let archetype_index = archetype.id().index(); + if !self.matched_archetypes.contains(archetype_index) { + self.matched_archetypes.grow_and_insert(archetype_index); + if !self.is_dense { + self.matched_storage_ids.push(StorageId { + archetype_id: archetype.id(), + }); + } + } + let table_index = archetype.table_id().as_usize(); + if !self.matched_tables.contains(table_index) { + self.matched_tables.grow_and_insert(table_index); + if self.is_dense { + self.matched_storage_ids.push(StorageId { + table_id: archetype.table_id(), + }); + } + } + } +} + +/// [`QueryCache`] used if caching is disabled for a query. +/// +/// We will not cache any matching archetypes for a query, so they will have to be recomputed +/// from scratch every time. +#[derive(Debug, Clone)] +pub struct Uncached { + pub(super) is_dense: bool, +} + +impl QueryCache for Uncached { + fn iteration_data<'s, 'a: 's, D: QueryData, F: QueryFilter>( + &'a self, + query: &QueryState, + world: UnsafeWorldCell, + ) -> IterationData<'s> { + let mut storage_ids = Vec::new(); + let mut matched_storages = FixedBitSet::new(); + query.iter_archetypes( + ArchetypeGeneration::initial(), + world.archetypes(), + |archetype| { + if let Some(storage_id) = if self.is_dense { + let table_index = archetype.table_id().as_usize(); + (!matched_storages.contains(table_index)).then(|| { + matched_storages.grow_and_insert(table_index); + StorageId { + table_id: archetype.table_id(), + } + }) + } else { + let archetype_index = archetype.id().index(); + (!matched_storages.contains(archetype_index)).then(|| { + matched_storages.grow_and_insert(archetype_index); + StorageId { + archetype_id: archetype.id(), + } + }) + } { + storage_ids.push(storage_id); + } + }, + ); + IterationData { + is_dense: self.is_dense, + storage_ids: Cow::Owned(storage_ids), + } + } + + unsafe fn contains( + &self, + query: &QueryState, + archetype: &Archetype, + ) -> bool { + // SAFETY: satisfied from QueryCache::contains's safety constraints + unsafe { query.as_uncached().matches_archetype(archetype) } + } + + fn from_world_uninitialized(_: &World) -> Self { + // For queries without dynamic filters the dense-ness of the query is equal to the dense-ness + // of its static type parameters. + Uncached { + is_dense: D::IS_DENSE && F::IS_DENSE, + } + } + + fn from_builder_uninitialized( + builder: &QueryBuilder, + ) -> Self { + Uncached { + is_dense: builder.is_dense(), + } + } + + fn update_archetypes( + &mut self, + uncached: &UncachedQueryBorrow, + world: UnsafeWorldCell, + ) { + uncached.validate_world(world.id()); + } + + fn join(&self, _: &Self) -> Self { + self.clone() + } +} diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index 13e69ce3cb969..727cc68ae851b 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -11,7 +11,9 @@ use crate::{ FilteredEntityMut, FilteredEntityRef, }, }; +use alloc::borrow::Cow; use alloc::vec::Vec; +use bevy_ecs::query::cache::QueryCache; use core::{ cmp::Ordering, fmt::{self, Debug, Formatter}, @@ -25,21 +27,21 @@ use nonmax::NonMaxU32; /// /// This struct is created by the [`Query::iter`](crate::system::Query::iter) and /// [`Query::iter_mut`](crate::system::Query::iter_mut) methods. -pub struct QueryIter<'w, 's, D: QueryData, F: QueryFilter> { +pub struct QueryIter<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache> { world: UnsafeWorldCell<'w>, tables: &'w Tables, archetypes: &'w Archetypes, - query_state: &'s QueryState, - cursor: QueryIterationCursor<'w, 's, D, F>, + query_state: &'s QueryState, + cursor: QueryIterationCursor<'w, 's, D, F, C>, } -impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache> QueryIter<'w, 's, D, F, C> { /// # Safety /// - `world` must have permission to access any of the components registered in `query_state`. /// - `world` must be the same one used to initialize `query_state`. pub(crate) unsafe fn new( world: UnsafeWorldCell<'w>, - query_state: &'s QueryState, + query_state: &'s QueryState, last_run: Tick, this_run: Tick, ) -> Self { @@ -79,7 +81,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { /// } /// } /// ``` - pub fn remaining(&self) -> QueryIter<'w, 's, D, F> + pub fn remaining(&self) -> QueryIter<'w, 's, D, F, C> where D: ReadOnlyQueryData, { @@ -116,7 +118,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { /// } /// } /// ``` - pub fn remaining_mut(&mut self) -> QueryIter<'_, 's, D, F> { + pub fn remaining_mut(&mut self) -> QueryIter<'_, 's, D, F, C> { QueryIter { world: self.world, tables: self.tables, @@ -515,6 +517,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { 's, D, F, + C, impl ExactSizeIterator + DoubleEndedIterator + FusedIterator + 'w, > where @@ -572,6 +575,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { 's, D, F, + C, impl ExactSizeIterator + DoubleEndedIterator + FusedIterator + 'w, > where @@ -637,6 +641,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { 's, D, F, + C, impl ExactSizeIterator + DoubleEndedIterator + FusedIterator + 'w, > { self.sort_impl::(move |keyed_query| { @@ -669,6 +674,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { 's, D, F, + C, impl ExactSizeIterator + DoubleEndedIterator + FusedIterator + 'w, > { self.sort_impl::(move |keyed_query| { @@ -761,6 +767,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { 's, D, F, + C, impl ExactSizeIterator + DoubleEndedIterator + FusedIterator + 'w, > where @@ -794,6 +801,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { 's, D, F, + C, impl ExactSizeIterator + DoubleEndedIterator + FusedIterator + 'w, > where @@ -829,6 +837,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { 's, D, F, + C, impl ExactSizeIterator + DoubleEndedIterator + FusedIterator + 'w, > where @@ -859,6 +868,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { 's, D, F, + C, impl ExactSizeIterator + DoubleEndedIterator + FusedIterator + 'w, > { // On the first successful iteration of `QueryIterationCursor`, `archetype_entities` or `table_entities` @@ -902,7 +912,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { } } -impl<'w, 's, D: QueryData, F: QueryFilter> Iterator for QueryIter<'w, 's, D, F> { +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache> Iterator for QueryIter<'w, 's, D, F, C> { type Item = D::Item<'w, 's>; #[inline(always)] @@ -935,7 +945,12 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Iterator for QueryIter<'w, 's, D, F> accum = func(accum, item); } - for id in self.cursor.storage_id_iter.clone().copied() { + let mut i = self.cursor.storage_index; + let len = self.cursor.storage_ids.as_ref().len(); + while i < len { + // Take a short-lived copy of the id to avoid holding a borrow of `self` across the call + let id = self.cursor.storage_ids.as_ref()[i]; + i += 1; // SAFETY: // - The range(None) is equivalent to [0, storage.entity_count) accum = unsafe { self.fold_over_storage_range(accum, &mut func, id, None) }; @@ -945,48 +960,62 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Iterator for QueryIter<'w, 's, D, F> } // This is correct as [`QueryIter`] always returns `None` once exhausted. -impl<'w, 's, D: QueryData, F: QueryFilter> FusedIterator for QueryIter<'w, 's, D, F> {} +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache> FusedIterator + for QueryIter<'w, 's, D, F, C> +{ +} // SAFETY: [`QueryIter`] is guaranteed to return every matching entity once and only once. -unsafe impl<'w, 's, F: QueryFilter> EntitySetIterator for QueryIter<'w, 's, Entity, F> {} +unsafe impl<'w, 's, F: QueryFilter, C: QueryCache> EntitySetIterator + for QueryIter<'w, 's, Entity, F, C> +{ +} // SAFETY: [`QueryIter`] is guaranteed to return every matching entity once and only once. -unsafe impl<'w, 's, F: QueryFilter> EntitySetIterator for QueryIter<'w, 's, EntityRef<'_>, F> {} +unsafe impl<'w, 's, F: QueryFilter, C: QueryCache> EntitySetIterator + for QueryIter<'w, 's, EntityRef<'_>, F, C> +{ +} // SAFETY: [`QueryIter`] is guaranteed to return every matching entity once and only once. -unsafe impl<'w, 's, F: QueryFilter> EntitySetIterator for QueryIter<'w, 's, EntityMut<'_>, F> {} +unsafe impl<'w, 's, F: QueryFilter, C: QueryCache> EntitySetIterator + for QueryIter<'w, 's, EntityMut<'_>, F, C> +{ +} // SAFETY: [`QueryIter`] is guaranteed to return every matching entity once and only once. -unsafe impl<'w, 's, F: QueryFilter> EntitySetIterator - for QueryIter<'w, 's, FilteredEntityRef<'_, '_>, F> +unsafe impl<'w, 's, F: QueryFilter, C: QueryCache> EntitySetIterator + for QueryIter<'w, 's, FilteredEntityRef<'_, '_>, F, C> { } // SAFETY: [`QueryIter`] is guaranteed to return every matching entity once and only once. -unsafe impl<'w, 's, F: QueryFilter> EntitySetIterator - for QueryIter<'w, 's, FilteredEntityMut<'_, '_>, F> +unsafe impl<'w, 's, F: QueryFilter, C: QueryCache> EntitySetIterator + for QueryIter<'w, 's, FilteredEntityMut<'_, '_>, F, C> { } // SAFETY: [`QueryIter`] is guaranteed to return every matching entity once and only once. -unsafe impl<'w, 's, F: QueryFilter, B: Bundle> EntitySetIterator - for QueryIter<'w, 's, EntityRefExcept<'_, '_, B>, F> +unsafe impl<'w, 's, F: QueryFilter, B: Bundle, C: QueryCache> EntitySetIterator + for QueryIter<'w, 's, EntityRefExcept<'_, '_, B>, F, C> { } // SAFETY: [`QueryIter`] is guaranteed to return every matching entity once and only once. -unsafe impl<'w, 's, F: QueryFilter, B: Bundle> EntitySetIterator - for QueryIter<'w, 's, EntityMutExcept<'_, '_, B>, F> +unsafe impl<'w, 's, F: QueryFilter, B: Bundle, C: QueryCache> EntitySetIterator + for QueryIter<'w, 's, EntityMutExcept<'_, '_, B>, F, C> { } -impl<'w, 's, D: QueryData, F: QueryFilter> Debug for QueryIter<'w, 's, D, F> { +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache> Debug for QueryIter<'w, 's, D, F, C> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("QueryIter").finish() } } -impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter> Clone for QueryIter<'w, 's, D, F> { +impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, C: QueryCache> Clone + for QueryIter<'w, 's, D, F, C> +{ fn clone(&self) -> Self { self.remaining() } @@ -997,7 +1026,7 @@ impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter> Clone for QueryIter<'w, 's, D /// This struct is created by the [`QueryIter::sort`], [`QueryIter::sort_unstable`], /// [`QueryIter::sort_by`], [`QueryIter::sort_unstable_by`], [`QueryIter::sort_by_key`], /// [`QueryIter::sort_unstable_by_key`], and [`QueryIter::sort_by_cached_key`] methods. -pub struct QuerySortedIter<'w, 's, D: QueryData, F: QueryFilter, I> +pub struct QuerySortedIter<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache, I> where I: Iterator, { @@ -1006,10 +1035,11 @@ where tables: &'w Tables, archetypes: &'w Archetypes, fetch: D::Fetch<'w>, - query_state: &'s QueryState, + query_state: &'s QueryState, } -impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> QuerySortedIter<'w, 's, D, F, I> +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache, I: Iterator> + QuerySortedIter<'w, 's, D, F, C, I> where I: Iterator, { @@ -1019,11 +1049,11 @@ where /// - `entity_list` must only contain unique entities or be empty. pub(crate) unsafe fn new>( world: UnsafeWorldCell<'w>, - query_state: &'s QueryState, + query_state: &'s QueryState, entity_list: EntityList, last_run: Tick, this_run: Tick, - ) -> QuerySortedIter<'w, 's, D, F, I> { + ) -> QuerySortedIter<'w, 's, D, F, C, I> { let fetch = D::init_fetch(world, &query_state.fetch_state, last_run, this_run); QuerySortedIter { query_state, @@ -1080,8 +1110,8 @@ where } } -impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> Iterator - for QuerySortedIter<'w, 's, D, F, I> +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache, I: Iterator> Iterator + for QuerySortedIter<'w, 's, D, F, C, I> where I: Iterator, { @@ -1099,8 +1129,8 @@ where } } -impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> DoubleEndedIterator - for QuerySortedIter<'w, 's, D, F, I> +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache, I: Iterator> DoubleEndedIterator + for QuerySortedIter<'w, 's, D, F, C, I> where I: DoubleEndedIterator, { @@ -1112,16 +1142,16 @@ where } } -impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> ExactSizeIterator - for QuerySortedIter<'w, 's, D, F, I> +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache, I: Iterator> ExactSizeIterator + for QuerySortedIter<'w, 's, D, F, C, I> where I: ExactSizeIterator, { } // This is correct as [`QuerySortedIter`] returns `None` once exhausted if `entity_iter` does. -impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> FusedIterator - for QuerySortedIter<'w, 's, D, F, I> +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache, I: Iterator> FusedIterator + for QuerySortedIter<'w, 's, D, F, C, I> where I: FusedIterator, { @@ -1130,13 +1160,13 @@ where // SAFETY: // `I` stems from a collected and sorted `EntitySetIterator` ([`QueryIter`]). // Fetching unique entities maintains uniqueness. -unsafe impl<'w, 's, F: QueryFilter, I: Iterator> EntitySetIterator - for QuerySortedIter<'w, 's, Entity, F, I> +unsafe impl<'w, 's, F: QueryFilter, C: QueryCache, I: Iterator> EntitySetIterator + for QuerySortedIter<'w, 's, Entity, F, C, I> { } -impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> Debug - for QuerySortedIter<'w, 's, D, F, I> +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache, I: Iterator> Debug + for QuerySortedIter<'w, 's, D, F, C, I> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("QuerySortedIter").finish() @@ -1149,8 +1179,14 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> Debug /// Entities that don't match the query are skipped. /// /// This struct is created by the [`Query::iter_many`](crate::system::Query::iter_many) and [`Query::iter_many_mut`](crate::system::Query::iter_many_mut) methods. -pub struct QueryManyIter<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> -{ +pub struct QueryManyIter< + 'w, + 's, + D: QueryData, + F: QueryFilter, + C: QueryCache, + I: Iterator, +> { world: UnsafeWorldCell<'w>, entity_iter: I, entities: &'w Entities, @@ -1158,22 +1194,22 @@ pub struct QueryManyIter<'w, 's, D: QueryData, F: QueryFilter, I: Iterator, filter: F::Fetch<'w>, - query_state: &'s QueryState, + query_state: &'s QueryState, } -impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> - QueryManyIter<'w, 's, D, F, I> +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache, I: Iterator> + QueryManyIter<'w, 's, D, F, C, I> { /// # Safety /// - `world` must have permission to access any of the components registered in `query_state`. /// - `world` must be the same one used to initialize `query_state`. pub(crate) unsafe fn new>( world: UnsafeWorldCell<'w>, - query_state: &'s QueryState, + query_state: &'s QueryState, entity_list: EntityList, last_run: Tick, this_run: Tick, - ) -> QueryManyIter<'w, 's, D, F, I> { + ) -> QueryManyIter<'w, 's, D, F, C, I> { let fetch = D::init_fetch(world, &query_state.fetch_state, last_run, this_run); let filter = F::init_fetch(world, &query_state.filter_state, last_run, this_run); QueryManyIter { @@ -1206,7 +1242,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> archetypes: &'w Archetypes, fetch: &mut D::Fetch<'w>, filter: &mut F::Fetch<'w>, - query_state: &'s QueryState, + query_state: &'s QueryState, ) -> Option> { for entity_borrow in entity_iter { let entity = entity_borrow.entity(); @@ -1214,14 +1250,10 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> continue; }; - if !query_state - .matched_archetypes - .contains(location.archetype_id.index()) - { + let archetype = archetypes.get(location.archetype_id).debug_checked_unwrap(); + if !query_state.matches_archetype(archetype) { continue; } - - let archetype = archetypes.get(location.archetype_id).debug_checked_unwrap(); let table = tables.get(location.table_id).debug_checked_unwrap(); // SAFETY: `archetype` is from the world that `fetch/filter` were created for, @@ -1379,6 +1411,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> 's, D, F, + C, impl ExactSizeIterator + DoubleEndedIterator + FusedIterator + 'w, > where @@ -1437,6 +1470,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> 's, D, F, + C, impl ExactSizeIterator + DoubleEndedIterator + FusedIterator + 'w, > where @@ -1503,6 +1537,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> 's, D, F, + C, impl ExactSizeIterator + DoubleEndedIterator + FusedIterator + 'w, > { self.sort_impl::(move |keyed_query| { @@ -1534,6 +1569,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> 's, D, F, + C, impl ExactSizeIterator + DoubleEndedIterator + FusedIterator + 'w, > { self.sort_impl::(move |keyed_query| { @@ -1610,7 +1646,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> /// .sort_by_key::(|entity_ref| { /// ( /// entity_ref.contains::(), - // entity_ref.get::().copied() + /// entity_ref.get::().copied() /// ) /// }) /// .rev() @@ -1628,6 +1664,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> 's, D, F, + C, impl ExactSizeIterator + DoubleEndedIterator + FusedIterator + 'w, > where @@ -1660,6 +1697,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> 's, D, F, + C, impl ExactSizeIterator + DoubleEndedIterator + FusedIterator + 'w, > where @@ -1694,6 +1732,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> 's, D, F, + C, impl ExactSizeIterator + DoubleEndedIterator + FusedIterator + 'w, > where @@ -1723,6 +1762,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> 's, D, F, + C, impl ExactSizeIterator + DoubleEndedIterator + FusedIterator + 'w, > { let world = self.world; @@ -1762,8 +1802,14 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> } } -impl<'w, 's, D: QueryData, F: QueryFilter, I: DoubleEndedIterator> - QueryManyIter<'w, 's, D, F, I> +impl< + 'w, + 's, + D: QueryData, + F: QueryFilter, + C: QueryCache, + I: DoubleEndedIterator, + > QueryManyIter<'w, 's, D, F, C, I> { /// Get next result from the back of the query #[inline(always)] @@ -1788,8 +1834,14 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: DoubleEndedIterator> Iterator - for QueryManyIter<'w, 's, D, F, I> +impl< + 'w, + 's, + D: ReadOnlyQueryData, + F: QueryFilter, + C: QueryCache, + I: Iterator, + > Iterator for QueryManyIter<'w, 's, D, F, C, I> { type Item = D::Item<'w, 's>; @@ -1822,8 +1874,9 @@ impl< 's, D: ReadOnlyQueryData, F: QueryFilter, + C: QueryCache, I: DoubleEndedIterator, - > DoubleEndedIterator for QueryManyIter<'w, 's, D, F, I> + > DoubleEndedIterator for QueryManyIter<'w, 's, D, F, C, I> { #[inline(always)] fn next_back(&mut self) -> Option { @@ -1845,19 +1898,25 @@ impl< } // This is correct as [`QueryManyIter`] always returns `None` once exhausted. -impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, I: Iterator> - FusedIterator for QueryManyIter<'w, 's, D, F, I> +impl< + 'w, + 's, + D: ReadOnlyQueryData, + F: QueryFilter, + C: QueryCache, + I: Iterator, + > FusedIterator for QueryManyIter<'w, 's, D, F, C, I> { } // SAFETY: Fetching unique entities maintains uniqueness. -unsafe impl<'w, 's, F: QueryFilter, I: EntitySetIterator> EntitySetIterator - for QueryManyIter<'w, 's, Entity, F, I> +unsafe impl<'w, 's, F: QueryFilter, C: QueryCache, I: EntitySetIterator> EntitySetIterator + for QueryManyIter<'w, 's, Entity, F, C, I> { } -impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> Debug - for QueryManyIter<'w, 's, D, F, I> +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache, I: Iterator> Debug + for QueryManyIter<'w, 's, D, F, C, I> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("QueryManyIter").finish() @@ -1877,23 +1936,28 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> /// [`iter_many_unique`]: crate::system::Query::iter_many /// [`iter_many_unique_mut`]: crate::system::Query::iter_many_mut /// [`Query`]: crate::system::Query -pub struct QueryManyUniqueIter<'w, 's, D: QueryData, F: QueryFilter, I: EntitySetIterator>( - QueryManyIter<'w, 's, D, F, I>, -); - -impl<'w, 's, D: QueryData, F: QueryFilter, I: EntitySetIterator> - QueryManyUniqueIter<'w, 's, D, F, I> +pub struct QueryManyUniqueIter< + 'w, + 's, + D: QueryData, + F: QueryFilter, + C: QueryCache, + I: EntitySetIterator, +>(QueryManyIter<'w, 's, D, F, C, I>); + +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache, I: EntitySetIterator> + QueryManyUniqueIter<'w, 's, D, F, C, I> { /// # Safety /// - `world` must have permission to access any of the components registered in `query_state`. /// - `world` must be the same one used to initialize `query_state`. pub(crate) unsafe fn new>( world: UnsafeWorldCell<'w>, - query_state: &'s QueryState, + query_state: &'s QueryState, entity_list: EntityList, last_run: Tick, this_run: Tick, - ) -> QueryManyUniqueIter<'w, 's, D, F, I> { + ) -> QueryManyUniqueIter<'w, 's, D, F, C, I> { QueryManyUniqueIter(QueryManyIter::new( world, query_state, @@ -1904,8 +1968,8 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: EntitySetIterator> } } -impl<'w, 's, D: QueryData, F: QueryFilter, I: EntitySetIterator> Iterator - for QueryManyUniqueIter<'w, 's, D, F, I> +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache, I: EntitySetIterator> Iterator + for QueryManyUniqueIter<'w, 's, D, F, C, I> { type Item = D::Item<'w, 's>; @@ -1913,7 +1977,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: EntitySetIterator> Iterator fn next(&mut self) -> Option { // SAFETY: Entities are guaranteed to be unique, thus do not alias. unsafe { - QueryManyIter::<'w, 's, D, F, I>::fetch_next_aliased_unchecked( + QueryManyIter::<'w, 's, D, F, C, I>::fetch_next_aliased_unchecked( &mut self.0.entity_iter, self.0.entities, self.0.tables, @@ -1932,19 +1996,19 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: EntitySetIterator> Iterator } // This is correct as [`QueryManyIter`] always returns `None` once exhausted. -impl<'w, 's, D: QueryData, F: QueryFilter, I: EntitySetIterator> FusedIterator - for QueryManyUniqueIter<'w, 's, D, F, I> +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache, I: EntitySetIterator> FusedIterator + for QueryManyUniqueIter<'w, 's, D, F, C, I> { } // SAFETY: Fetching unique entities maintains uniqueness. -unsafe impl<'w, 's, F: QueryFilter, I: EntitySetIterator> EntitySetIterator - for QueryManyUniqueIter<'w, 's, Entity, F, I> +unsafe impl<'w, 's, F: QueryFilter, C: QueryCache, I: EntitySetIterator> EntitySetIterator + for QueryManyUniqueIter<'w, 's, Entity, F, C, I> { } -impl<'w, 's, D: QueryData, F: QueryFilter, I: EntitySetIterator> Debug - for QueryManyUniqueIter<'w, 's, D, F, I> +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache, I: EntitySetIterator> Debug + for QueryManyUniqueIter<'w, 's, D, F, C, I> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("QueryManyUniqueIter").finish() @@ -1956,17 +2020,24 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: EntitySetIterator> Debug /// This struct is created by the [`sort`](QueryManyIter), [`sort_unstable`](QueryManyIter), /// [`sort_by`](QueryManyIter), [`sort_unstable_by`](QueryManyIter), [`sort_by_key`](QueryManyIter), /// [`sort_unstable_by_key`](QueryManyIter), and [`sort_by_cached_key`](QueryManyIter) methods of [`QueryManyIter`]. -pub struct QuerySortedManyIter<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> { +pub struct QuerySortedManyIter< + 'w, + 's, + D: QueryData, + F: QueryFilter, + C: QueryCache, + I: Iterator, +> { entity_iter: I, entities: &'w Entities, tables: &'w Tables, archetypes: &'w Archetypes, fetch: D::Fetch<'w>, - query_state: &'s QueryState, + query_state: &'s QueryState, } -impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> - QuerySortedManyIter<'w, 's, D, F, I> +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache, I: Iterator> + QuerySortedManyIter<'w, 's, D, F, C, I> { /// # Safety /// - `world` must have permission to access any of the components registered in `query_state`. @@ -1974,11 +2045,11 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> /// - `entity_list` must only contain unique entities or be empty. pub(crate) unsafe fn new>( world: UnsafeWorldCell<'w>, - query_state: &'s QueryState, + query_state: &'s QueryState, entity_list: EntityList, last_run: Tick, this_run: Tick, - ) -> QuerySortedManyIter<'w, 's, D, F, I> { + ) -> QuerySortedManyIter<'w, 's, D, F, C, I> { let fetch = D::init_fetch(world, &query_state.fetch_state, last_run, this_run); QuerySortedManyIter { query_state, @@ -2055,8 +2126,14 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> } } -impl<'w, 's, D: QueryData, F: QueryFilter, I: DoubleEndedIterator> - QuerySortedManyIter<'w, 's, D, F, I> +impl< + 'w, + 's, + D: QueryData, + F: QueryFilter, + C: QueryCache, + I: DoubleEndedIterator, + > QuerySortedManyIter<'w, 's, D, F, C, I> { /// Get next result from the query #[inline(always)] @@ -2074,8 +2151,8 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: DoubleEndedIterator } } -impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, I: Iterator> Iterator - for QuerySortedManyIter<'w, 's, D, F, I> +impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, C: QueryCache, I: Iterator> + Iterator for QuerySortedManyIter<'w, 's, D, F, C, I> { type Item = D::Item<'w, 's>; @@ -2093,8 +2170,14 @@ impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, I: Iterator> I } } -impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, I: DoubleEndedIterator> - DoubleEndedIterator for QuerySortedManyIter<'w, 's, D, F, I> +impl< + 'w, + 's, + D: ReadOnlyQueryData, + F: QueryFilter, + C: QueryCache, + I: DoubleEndedIterator, + > DoubleEndedIterator for QuerySortedManyIter<'w, 's, D, F, C, I> { #[inline(always)] fn next_back(&mut self) -> Option { @@ -2106,13 +2189,19 @@ impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, I: DoubleEndedIterator> - ExactSizeIterator for QuerySortedManyIter<'w, 's, D, F, I> +impl< + 'w, + 's, + D: ReadOnlyQueryData, + F: QueryFilter, + C: QueryCache, + I: ExactSizeIterator, + > ExactSizeIterator for QuerySortedManyIter<'w, 's, D, F, C, I> { } -impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> Debug - for QuerySortedManyIter<'w, 's, D, F, I> +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache, I: Iterator> Debug + for QuerySortedManyIter<'w, 's, D, F, C, I> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("QuerySortedManyIter").finish() @@ -2182,20 +2271,23 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> Debug /// [`Query`]: crate::system::Query /// [`Query::iter_combinations`]: crate::system::Query::iter_combinations /// [`Query::iter_combinations_mut`]: crate::system::Query::iter_combinations_mut -pub struct QueryCombinationIter<'w, 's, D: QueryData, F: QueryFilter, const K: usize> { +pub struct QueryCombinationIter<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache, const K: usize> +{ tables: &'w Tables, archetypes: &'w Archetypes, - query_state: &'s QueryState, - cursors: [QueryIterationCursor<'w, 's, D, F>; K], + query_state: &'s QueryState, + cursors: [QueryIterationCursor<'w, 's, D, F, C>; K], } -impl<'w, 's, D: QueryData, F: QueryFilter, const K: usize> QueryCombinationIter<'w, 's, D, F, K> { +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache, const K: usize> + QueryCombinationIter<'w, 's, D, F, C, K> +{ /// # Safety /// - `world` must have permission to access any of the components registered in `query_state`. /// - `world` must be the same one used to initialize `query_state`. pub(crate) unsafe fn new( world: UnsafeWorldCell<'w>, - query_state: &'s QueryState, + query_state: &'s QueryState, last_run: Tick, this_run: Tick, ) -> Self { @@ -2203,10 +2295,11 @@ impl<'w, 's, D: QueryData, F: QueryFilter, const K: usize> QueryCombinationIter< // Initialize array with cursors. // There is no FromIterator on arrays, so instead initialize it manually with MaybeUninit - let mut array: MaybeUninit<[QueryIterationCursor<'w, 's, D, F>; K]> = MaybeUninit::uninit(); + let mut array: MaybeUninit<[QueryIterationCursor<'w, 's, D, F, C>; K]> = + MaybeUninit::uninit(); let ptr = array .as_mut_ptr() - .cast::>(); + .cast::>(); ptr.write(QueryIterationCursor::init( world, query_state, @@ -2291,8 +2384,8 @@ impl<'w, 's, D: QueryData, F: QueryFilter, const K: usize> QueryCombinationIter< // Iterator type is intentionally implemented only for read-only access. // Doing so for mutable references would be unsound, because calling `next` // multiple times would allow multiple owned references to the same data to exist. -impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, const K: usize> Iterator - for QueryCombinationIter<'w, 's, D, F, K> +impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, C: QueryCache, const K: usize> Iterator + for QueryCombinationIter<'w, 's, D, F, C, K> { type Item = [D::Item<'w, 's>; K]; @@ -2334,7 +2427,8 @@ impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, const K: usize> Iterator } } -impl<'w, 's, D: QueryData, F: QueryFilter> ExactSizeIterator for QueryIter<'w, 's, D, F> +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache> ExactSizeIterator + for QueryIter<'w, 's, D, F, C> where F: ArchetypeFilter, { @@ -2344,23 +2438,26 @@ where } // This is correct as [`QueryCombinationIter`] always returns `None` once exhausted. -impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, const K: usize> FusedIterator - for QueryCombinationIter<'w, 's, D, F, K> +impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, C: QueryCache, const K: usize> FusedIterator + for QueryCombinationIter<'w, 's, D, F, C, K> { } -impl<'w, 's, D: QueryData, F: QueryFilter, const K: usize> Debug - for QueryCombinationIter<'w, 's, D, F, K> +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache, const K: usize> Debug + for QueryCombinationIter<'w, 's, D, F, C, K> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("QueryCombinationIter").finish() } } -struct QueryIterationCursor<'w, 's, D: QueryData, F: QueryFilter> { - // whether the query iteration is dense or not. Mirrors QueryState's `is_dense` field. +struct QueryIterationCursor<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache> { + // whether the query iteration is dense or not. is_dense: bool, - storage_id_iter: core::slice::Iter<'s, StorageId>, + // Storage ids to iterate over; owned for uncached, borrowed for cached + storage_ids: Cow<'s, [StorageId]>, + // Current index into storage_ids + storage_index: usize, table_entities: &'w [Entity], archetype_entities: &'w [ArchetypeEntity], fetch: D::Fetch<'w>, @@ -2369,35 +2466,41 @@ struct QueryIterationCursor<'w, 's, D: QueryData, F: QueryFilter> { current_len: u32, // either table row or archetype index, depending on whether both `D`'s and `F`'s fetches are dense current_row: u32, + _cache: core::marker::PhantomData, } -impl Clone for QueryIterationCursor<'_, '_, D, F> { +impl Clone for QueryIterationCursor<'_, '_, D, F, C> { fn clone(&self) -> Self { Self { is_dense: self.is_dense, - storage_id_iter: self.storage_id_iter.clone(), + storage_ids: self.storage_ids.clone(), + storage_index: self.storage_index, table_entities: self.table_entities, archetype_entities: self.archetype_entities, fetch: self.fetch.clone(), filter: self.filter.clone(), current_len: self.current_len, current_row: self.current_row, + _cache: core::marker::PhantomData, } } } -impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> { +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache + 's> + QueryIterationCursor<'w, 's, D, F, C> +{ /// # Safety /// - `world` must have permission to access any of the components registered in `query_state`. /// - `world` must be the same one used to initialize `query_state`. unsafe fn init_empty( world: UnsafeWorldCell<'w>, - query_state: &'s QueryState, + query_state: &'s QueryState, last_run: Tick, this_run: Tick, ) -> Self { QueryIterationCursor { - storage_id_iter: [].iter(), + storage_ids: Cow::Borrowed(&[]), + storage_index: 0, ..Self::init(world, query_state, last_run, this_run) } } @@ -2407,34 +2510,40 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> { /// - `world` must be the same one used to initialize `query_state`. unsafe fn init( world: UnsafeWorldCell<'w>, - query_state: &'s QueryState, + query_state: &'s QueryState, last_run: Tick, this_run: Tick, ) -> Self { let fetch = D::init_fetch(world, &query_state.fetch_state, last_run, this_run); let filter = F::init_fetch(world, &query_state.filter_state, last_run, this_run); + let iteration_data = query_state.cache.iteration_data(query_state, world); + let storage_ids = iteration_data.storage_ids; QueryIterationCursor { fetch, filter, table_entities: &[], archetype_entities: &[], - storage_id_iter: query_state.matched_storage_ids.iter(), - is_dense: query_state.is_dense, + storage_ids, + storage_index: 0, + is_dense: iteration_data.is_dense, current_len: 0, current_row: 0, + _cache: core::marker::PhantomData, } } - fn reborrow(&mut self) -> QueryIterationCursor<'_, 's, D, F> { + fn reborrow(&mut self) -> QueryIterationCursor<'_, 's, D, F, C> { QueryIterationCursor { is_dense: self.is_dense, fetch: D::shrink_fetch(self.fetch.clone()), filter: F::shrink_fetch(self.filter.clone()), table_entities: self.table_entities, archetype_entities: self.archetype_entities, - storage_id_iter: self.storage_id_iter.clone(), + storage_ids: self.storage_ids.clone(), + storage_index: self.storage_index, current_len: self.current_len, current_row: self.current_row, + _cache: core::marker::PhantomData, } } @@ -2444,7 +2553,10 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> { /// The result of `next` and any previous calls to `peek_last` with this row must have been /// dropped to prevent aliasing mutable references. #[inline] - unsafe fn peek_last(&mut self, query_state: &'s QueryState) -> Option> { + unsafe fn peek_last( + &mut self, + query_state: &'s QueryState, + ) -> Option> { if self.current_row > 0 { let index = self.current_row - 1; if self.is_dense { @@ -2488,7 +2600,9 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> { /// 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) -> u32 { - let ids = self.storage_id_iter.clone(); + let ids = self.storage_ids.as_ref()[self.storage_index..] + .iter() + .copied(); let remaining_matched: u32 = if self.is_dense { // SAFETY: The if check ensures that storage_id_iter stores TableIds unsafe { ids.map(|id| tables[id.table_id].entity_count()).sum() } @@ -2512,13 +2626,17 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> { &mut self, tables: &'w Tables, archetypes: &'w Archetypes, - query_state: &'s QueryState, + query_state: &'s QueryState, ) -> Option> { if self.is_dense { loop { // we are on the beginning of the query, or finished processing a table, so skip to the next if self.current_row == self.current_len { - let table_id = self.storage_id_iter.next()?.table_id; + let table_id = { + let id = *self.storage_ids.as_ref().get(self.storage_index)?; + self.storage_index += 1; + id.table_id + }; let table = tables.get(table_id).debug_checked_unwrap(); if table.is_empty() { continue; @@ -2559,7 +2677,11 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> { } else { loop { if self.current_row == self.current_len { - let archetype_id = self.storage_id_iter.next()?.archetype_id; + let archetype_id = { + let id = *self.storage_ids.as_ref().get(self.storage_index)?; + self.storage_index += 1; + id.archetype_id + }; let archetype = archetypes.get(archetype_id).debug_checked_unwrap(); if archetype.is_empty() { continue; diff --git a/crates/bevy_ecs/src/query/mod.rs b/crates/bevy_ecs/src/query/mod.rs index fb8899fd5de87..22426bf388836 100644 --- a/crates/bevy_ecs/src/query/mod.rs +++ b/crates/bevy_ecs/src/query/mod.rs @@ -2,6 +2,7 @@ mod access; mod builder; +mod cache; mod error; mod fetch; mod filter; @@ -13,6 +14,7 @@ mod world_query; pub use access::*; pub use bevy_ecs_macros::{QueryData, QueryFilter}; pub use builder::*; +pub use cache::*; pub use error::*; pub use fetch::*; pub use filter::*; @@ -120,6 +122,7 @@ mod tests { world::{unsafe_world_cell::UnsafeWorldCell, World}, }; use alloc::{vec, vec::Vec}; + use bevy_ecs::query::CacheState; use bevy_ecs_macros::QueryFilter; use core::{any::type_name, fmt::Debug, hash::Hash}; use std::{collections::HashSet, println}; @@ -169,7 +172,7 @@ mod tests { F: ArchetypeFilter, { let mut query = world.query_filtered::(); - let query_type = type_name::>(); + let query_type = type_name::>(); let iter = query.iter_combinations::(world); assert_all_sizes_iterator_equal(iter, expected_size, 0, query_type); let iter = query.iter_combinations::(world); diff --git a/crates/bevy_ecs/src/query/par_iter.rs b/crates/bevy_ecs/src/query/par_iter.rs index 6242f8e39b8be..c6fa3b1a482ea 100644 --- a/crates/bevy_ecs/src/query/par_iter.rs +++ b/crates/bevy_ecs/src/query/par_iter.rs @@ -8,20 +8,25 @@ use crate::{ use super::{QueryData, QueryFilter, QueryItem, QueryState, ReadOnlyQueryData}; use alloc::vec::Vec; +#[cfg(all(not(target_arch = "wasm32"), feature = "multi_threaded"))] +use bevy_ecs::query::IterationData; +use bevy_ecs::query::QueryCache; /// A parallel iterator over query results of a [`Query`](crate::system::Query). /// /// This struct is created by the [`Query::par_iter`](crate::system::Query::par_iter) and /// [`Query::par_iter_mut`](crate::system::Query::par_iter_mut) methods. -pub struct QueryParIter<'w, 's, D: QueryData, F: QueryFilter> { +pub struct QueryParIter<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache> { pub(crate) world: UnsafeWorldCell<'w>, - pub(crate) state: &'s QueryState, + pub(crate) state: &'s QueryState, + #[cfg(all(not(target_arch = "wasm32"), feature = "multi_threaded"))] + pub(crate) iteration_data: IterationData<'s>, pub(crate) last_run: Tick, pub(crate) this_run: Tick, pub(crate) batching_strategy: BatchingStrategy, } -impl<'w, 's, D: QueryData, F: QueryFilter> QueryParIter<'w, 's, D, F> { +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache> QueryParIter<'w, 's, D, F, C> { /// Changes the batching strategy used when iterating. /// /// For more information on how this affects the resultant iteration, see @@ -132,8 +137,8 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryParIter<'w, 's, D, F> { #[cfg(all(not(target_arch = "wasm32"), feature = "multi_threaded"))] fn get_batch_size(&self, thread_count: usize) -> u32 { let max_items = || { - let id_iter = self.state.matched_storage_ids.iter(); - if self.state.is_dense { + let id_iter = self.iteration_data.storage_ids.iter(); + if self.iteration_data.is_dense { // SAFETY: We only access table metadata. let tables = unsafe { &self.world.world_metadata().storages().tables }; id_iter @@ -161,17 +166,24 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryParIter<'w, 's, D, F> { /// /// [`Entity`]: crate::entity::Entity /// [`Query::par_iter_many`]: crate::system::Query::par_iter_many -pub struct QueryParManyIter<'w, 's, D: QueryData, F: QueryFilter, E: EntityEquivalent> { +pub struct QueryParManyIter< + 'w, + 's, + D: QueryData, + F: QueryFilter, + C: QueryCache, + E: EntityEquivalent, +> { pub(crate) world: UnsafeWorldCell<'w>, - pub(crate) state: &'s QueryState, + pub(crate) state: &'s QueryState, pub(crate) entity_list: Vec, pub(crate) last_run: Tick, pub(crate) this_run: Tick, pub(crate) batching_strategy: BatchingStrategy, } -impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, E: EntityEquivalent + Sync> - QueryParManyIter<'w, 's, D, F, E> +impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, C: QueryCache, E: EntityEquivalent + Sync> + QueryParManyIter<'w, 's, D, F, C, E> { /// Changes the batching strategy used when iterating. /// @@ -315,18 +327,24 @@ impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, E: EntityEquivalent + Sync> /// [`EntitySet`]: crate::entity::EntitySet /// [`Query::par_iter_many_unique`]: crate::system::Query::par_iter_many_unique /// [`Query::par_iter_many_unique_mut`]: crate::system::Query::par_iter_many_unique_mut -pub struct QueryParManyUniqueIter<'w, 's, D: QueryData, F: QueryFilter, E: EntityEquivalent + Sync> -{ +pub struct QueryParManyUniqueIter< + 'w, + 's, + D: QueryData, + F: QueryFilter, + C: QueryCache, + E: EntityEquivalent + Sync, +> { pub(crate) world: UnsafeWorldCell<'w>, - pub(crate) state: &'s QueryState, + pub(crate) state: &'s QueryState, pub(crate) entity_list: UniqueEntityEquivalentVec, pub(crate) last_run: Tick, pub(crate) this_run: Tick, pub(crate) batching_strategy: BatchingStrategy, } -impl<'w, 's, D: QueryData, F: QueryFilter, E: EntityEquivalent + Sync> - QueryParManyUniqueIter<'w, 's, D, F, E> +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache, E: EntityEquivalent + Sync> + QueryParManyUniqueIter<'w, 's, D, F, C, E> { /// Changes the batching strategy used when iterating. /// diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 91a979fa2e5e7..c34cec987706d 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -1,12 +1,11 @@ use crate::{ - archetype::{Archetype, ArchetypeGeneration, ArchetypeId}, + archetype::ArchetypeId, change_detection::Tick, - component::ComponentId, entity::{Entity, EntityEquivalent, EntitySet, UniqueEntityArray}, entity_disabling::DefaultQueryFilters, prelude::FromWorld, query::{FilteredAccess, QueryCombinationIter, QueryIter, QueryParIter, WorldQuery}, - storage::{SparseSetIndex, TableId}, + storage::TableId, system::Query, world::{unsafe_world_cell::UnsafeWorldCell, World, WorldId}, }; @@ -14,25 +13,22 @@ use crate::{ #[cfg(all(not(target_arch = "wasm32"), feature = "multi_threaded"))] use crate::entity::UniqueEntityEquivalentSlice; -use alloc::vec::Vec; -use bevy_utils::prelude::DebugName; -use core::{fmt, ptr}; -use fixedbitset::FixedBitSet; -use log::warn; -#[cfg(feature = "trace")] -use tracing::Span; - use super::{ NopWorldQuery, QueryBuilder, QueryData, QueryEntityError, QueryFilter, QueryManyIter, QueryManyUniqueIter, QuerySingleError, ROQueryItem, ReadOnlyQueryData, }; +use bevy_ecs::query::cache::{QueryCache, Uncached, UncachedQueryBorrow}; +use bevy_utils::prelude::DebugName; +use core::{fmt, ptr}; +#[cfg(feature = "trace")] +use tracing::Span; /// An ID for either a table or an archetype. Used for Query iteration. /// /// Query iteration is exclusively dense (over tables) or archetypal (over archetypes) based on whether -/// the query filters are dense or not. This is represented by the [`QueryState::is_dense`] field. +/// the query filters are dense or not. /// -/// Note that `D::IS_DENSE` and `F::IS_DENSE` have no relationship with `QueryState::is_dense` and +/// Note that `D::IS_DENSE` and `F::IS_DENSE` have no relationship with the dense-ness of the query and /// any combination of their values can happen. /// /// This is a union instead of an enum as the usage is determined at compile time, as all [`StorageId`]s for @@ -49,6 +45,16 @@ pub(super) union StorageId { pub(super) archetype_id: ArchetypeId, } +/// Makes queries uncached by default if they don't explicitly specify the caching behavior. +/// +/// This is mostly useful for unit tests. +#[cfg(feature = "query_uncached_default")] +pub type DefaultCache = Uncached; + +/// Makes queries cached by default if they don't explicitly specify the caching behavior. +#[cfg(not(feature = "query_uncached_default"))] +pub type DefaultCache = bevy_ecs::query::cache::CacheState; + /// 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: @@ -61,43 +67,35 @@ pub(super) union StorageId { /// [`State`]: crate::query::world_query::WorldQuery::State /// [`Fetch`]: crate::query::world_query::WorldQuery::Fetch /// [`Table`]: crate::storage::Table +/// [`Archetype`]: crate::archetype::Archetype #[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 { +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, + /// Cache that can store archetypes and tables that match the query so that we can iterate through them faster. + pub(crate) cache: C, /// [`FilteredAccess`] computed by combining the `D` and `F` access. Used to check which other queries /// this query can run in parallel with. /// Note that because we do a zero-cost reference conversion in `Query::as_readonly`, /// the access for a read-only query may include accesses for the original mutable version, /// but the `Query` does not have exclusive access to those components. pub(crate) component_access: FilteredAccess, - // NOTE: we maintain both a bitset and a vec because iterating the vec is faster - pub(super) matched_storage_ids: Vec, - // Represents whether this query iteration is dense or not. When this is true - // `matched_storage_ids` stores `TableId`s, otherwise it stores `ArchetypeId`s. - pub(super) is_dense: bool, pub(crate) fetch_state: D::State, pub(crate) filter_state: F::State, #[cfg(feature = "trace")] par_iter_span: Span, } -impl fmt::Debug for QueryState { +/// Convenience alias for an uncached query state using the on-the-fly matching strategy. +pub type UncachedQueryState = QueryState; + +impl fmt::Debug for QueryState { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("QueryState") .field("world_id", &self.world_id) - .field("matched_table_count", &self.matched_tables.count_ones(..)) - .field( - "matched_archetype_count", - &self.matched_archetypes.count_ones(..), - ) + .field("cache", &self.cache) .finish_non_exhaustive() } } @@ -108,9 +106,44 @@ impl FromWorld for QueryState { } } -impl QueryState { +impl QueryState { + /// Returns a borrow-only view of the [`QueryState`] that doesn't use a cache + #[inline] + pub(crate) fn as_uncached(&self) -> UncachedQueryBorrow<'_, D, F> { + UncachedQueryBorrow { + world_id: self.world_id, + component_access: &self.component_access, + fetch_state: &self.fetch_state, + filter_state: &self.filter_state, + } + } + + /// Safely provides access to a borrow-only uncached view and a mutable reference to the cache. + #[inline] + pub fn split_cache( + &mut self, + f: impl for<'a> FnOnce(UncachedQueryBorrow<'a, D, F>, &mut C) -> R, + ) -> R { + // Create raw pointers to immutably borrowed fields to avoid overlapping borrows with &mut cache. + let world_id = self.world_id; + let component_access = ptr::from_ref(&self.component_access); + let fetch_state = ptr::from_ref(&self.fetch_state); + let filter_state = ptr::from_ref(&self.filter_state); + // SAFETY: We only create shared references to fields distinct from `cache` and do not move `self`. + let prefix = unsafe { + UncachedQueryBorrow { + world_id, + component_access: &*component_access, + fetch_state: &*fetch_state, + filter_state: &*filter_state, + } + }; + let cache = &mut self.cache; + f(prefix, cache) + } + /// Converts this `QueryState` reference to a `QueryState` that does not access anything mutably. - pub fn as_readonly(&self) -> &QueryState { + pub fn as_readonly(&self) -> &QueryState { // SAFETY: invariant on `WorldQuery` trait upholds that `D::ReadOnly` and `F::ReadOnly` // have a subset of the access, and match the exact same archetypes/tables as `D`/`F` respectively. unsafe { self.as_transmuted_state::() } @@ -121,7 +154,7 @@ impl QueryState { /// /// This doesn't use `NopWorldQuery` as it loses filter functionality, for example /// `NopWorldQuery>` is functionally equivalent to `With`. - pub(crate) fn as_nop(&self) -> &QueryState, F> { + pub(crate) fn as_nop(&self) -> &QueryState, F, C> { // SAFETY: `NopWorldQuery` doesn't have any accesses and defers to // `D` for table/archetype matching unsafe { self.as_transmuted_state::, F>() } @@ -141,8 +174,8 @@ impl QueryState { NewF: QueryFilter, >( &self, - ) -> &QueryState { - &*ptr::from_ref(self).cast::>() + ) -> &QueryState { + &*ptr::from_ref(self).cast::>() } /// Returns the components accessed by this query. @@ -150,16 +183,6 @@ impl QueryState { &self.component_access } - /// Returns the tables matched by this query. - pub fn matched_tables(&self) -> impl Iterator + '_ { - self.matched_tables.ones().map(TableId::from_usize) - } - - /// Returns the archetypes matched by this query. - pub fn matched_archetypes(&self) -> impl Iterator + '_ { - self.matched_archetypes.ones().map(ArchetypeId::new) - } - /// Creates a new [`QueryState`] from a given [`World`] and inherits the result of `world.id()`. pub fn new(world: &mut World) -> Self { let mut state = Self::new_uninitialized(world); @@ -223,25 +246,18 @@ impl QueryState { // properly considered in a global "cross-query" context (both within systems and across systems). component_access.extend(&filter_component_access); - // For queries without dynamic filters the dense-ness of the query is equal to the dense-ness - // of its static type parameters. - let mut is_dense = D::IS_DENSE && F::IS_DENSE; - if let Some(default_filters) = world.get_resource::() { default_filters.modify_access(&mut component_access); - is_dense &= default_filters.is_dense(world.components()); } + let cache = C::from_world_uninitialized::(world); + Self { world_id: world.id(), - archetype_generation: ArchetypeGeneration::initial(), - matched_storage_ids: Vec::new(), - is_dense, fetch_state, filter_state, component_access, - matched_tables: Default::default(), - matched_archetypes: Default::default(), + cache, #[cfg(feature = "trace")] par_iter_span: tracing::info_span!( "par_for_each", @@ -266,24 +282,18 @@ impl QueryState { let mut component_access = builder.access().clone(); - // For dynamic queries the dense-ness is given by the query builder. - let mut is_dense = builder.is_dense(); - if let Some(default_filters) = builder.world().get_resource::() { default_filters.modify_access(&mut component_access); - is_dense &= default_filters.is_dense(builder.world().components()); } + let cache = C::from_builder_uninitialized(builder); + let mut state = Self { world_id: builder.world().id(), - archetype_generation: ArchetypeGeneration::initial(), - matched_storage_ids: Vec::new(), - is_dense, fetch_state, filter_state, component_access, - matched_tables: Default::default(), - matched_archetypes: Default::default(), + cache, #[cfg(feature = "trace")] par_iter_span: tracing::info_span!( "par_for_each", @@ -298,7 +308,7 @@ impl QueryState { /// Creates a [`Query`] from the given [`QueryState`] and [`World`]. /// /// This will create read-only queries, see [`Self::query_mut`] for mutable queries. - pub fn query<'w, 's>(&'s mut self, world: &'w World) -> Query<'w, 's, D::ReadOnly, F> { + pub fn query<'w, 's>(&'s mut self, world: &'w World) -> Query<'w, 's, D::ReadOnly, F, C> { self.update_archetypes(world); self.query_manual(world) } @@ -314,7 +324,7 @@ impl QueryState { /// access to `self`. /// /// This will create read-only queries, see [`Self::query_mut`] for mutable queries. - pub fn query_manual<'w, 's>(&'s self, world: &'w World) -> Query<'w, 's, D::ReadOnly, F> { + pub fn query_manual<'w, 's>(&'s self, world: &'w World) -> Query<'w, 's, D::ReadOnly, F, C> { self.validate_world(world.id()); // SAFETY: // - We have read access to the entire world, and we call `as_readonly()` so the query only performs read access. @@ -326,7 +336,7 @@ impl QueryState { } /// Creates a [`Query`] from the given [`QueryState`] and [`World`]. - pub fn query_mut<'w, 's>(&'s mut self, world: &'w mut World) -> Query<'w, 's, D, F> { + pub fn query_mut<'w, 's>(&'s mut self, world: &'w mut World) -> Query<'w, 's, D, F, C> { let last_run = world.last_change_tick(); let this_run = world.change_tick(); // SAFETY: We have exclusive access to the entire world. @@ -342,7 +352,7 @@ impl QueryState { pub unsafe fn query_unchecked<'w, 's>( &'s mut self, world: UnsafeWorldCell<'w>, - ) -> Query<'w, 's, D, F> { + ) -> Query<'w, 's, D, F, C> { self.update_archetypes_unsafe_world_cell(world); // SAFETY: Caller ensures we have the required access unsafe { self.query_unchecked_manual(world) } @@ -367,7 +377,7 @@ impl QueryState { pub unsafe fn query_unchecked_manual<'w, 's>( &'s self, world: UnsafeWorldCell<'w>, - ) -> Query<'w, 's, D, F> { + ) -> Query<'w, 's, D, F, C> { let last_run = world.last_change_tick(); let this_run = world.change_tick(); // SAFETY: @@ -387,7 +397,7 @@ impl QueryState { world: UnsafeWorldCell<'w>, last_run: Tick, this_run: Tick, - ) -> Query<'w, 's, D, F> { + ) -> Query<'w, 's, D, F, C> { self.update_archetypes_unsafe_world_cell(world); // SAFETY: // - The caller ensured we have the correct access to the world. @@ -416,7 +426,7 @@ impl QueryState { world: UnsafeWorldCell<'w>, last_run: Tick, this_run: Tick, - ) -> Query<'w, 's, D, F> { + ) -> Query<'w, 's, D, F, C> { // SAFETY: // - The caller ensured we have the correct access to the world. // - The caller ensured that the world matches. @@ -472,91 +482,6 @@ impl QueryState { .contains(entity) } - /// Updates the state's internal view of the [`World`]'s archetypes. If this is not called before querying data, - /// the results may not accurately reflect what is in the `world`. - /// - /// This is only required if a `manual` method (such as [`Self::get_manual`]) is being called, and it only needs to - /// be called if the `world` has been structurally mutated (i.e. added/removed a component or resource). Users using - /// non-`manual` methods such as [`QueryState::get`] do not need to call this as it will be automatically called for them. - /// - /// If you have an [`UnsafeWorldCell`] instead of `&World`, consider using [`QueryState::update_archetypes_unsafe_world_cell`]. - /// - /// # Panics - /// - /// If `world` does not match the one used to call `QueryState::new` for this instance. - #[inline] - pub fn update_archetypes(&mut self, world: &World) { - self.update_archetypes_unsafe_world_cell(world.as_unsafe_world_cell_readonly()); - } - - /// Updates the state's internal view of the `world`'s archetypes. If this is not called before querying data, - /// the results may not accurately reflect what is in the `world`. - /// - /// This is only required if a `manual` method (such as [`Self::get_manual`]) is being called, and it only needs to - /// be called if the `world` has been structurally mutated (i.e. added/removed a component or resource). Users using - /// non-`manual` methods such as [`QueryState::get`] do not need to call this as it will be automatically called for them. - /// - /// # Note - /// - /// This method only accesses world metadata. - /// - /// # Panics - /// - /// If `world` does not match the one used to call `QueryState::new` for this instance. - pub fn update_archetypes_unsafe_world_cell(&mut self, world: UnsafeWorldCell) { - self.validate_world(world.id()); - if self.component_access.required.is_empty() { - let archetypes = world.archetypes(); - let old_generation = - core::mem::replace(&mut self.archetype_generation, archetypes.generation()); - - for archetype in &archetypes[old_generation..] { - // SAFETY: The validate_world call ensures that the world is the same the QueryState - // was initialized from. - unsafe { - self.new_archetype(archetype); - } - } - } else { - // skip if we are already up to date - if self.archetype_generation == world.archetypes().generation() { - return; - } - // if there are required components, we can optimize by only iterating through archetypes - // that contain at least one of the required components - let potential_archetypes = self - .component_access - .required - .ones() - .filter_map(|idx| { - let component_id = ComponentId::get_sparse_set_index(idx); - world - .archetypes() - .component_index() - .get(&component_id) - .map(|index| index.keys()) - }) - // select the component with the fewest archetypes - .min_by_key(ExactSizeIterator::len); - if let Some(archetypes) = potential_archetypes { - for archetype_id in archetypes { - // exclude archetypes that have already been processed - if archetype_id < &self.archetype_generation.0 { - continue; - } - // SAFETY: get_potential_archetypes only returns archetype ids that are valid for the world - let archetype = &world.archetypes()[*archetype_id]; - // SAFETY: The validate_world call ensures that the world is the same the QueryState - // was initialized from. - unsafe { - self.new_archetype(archetype); - } - } - } - self.archetype_generation = world.archetypes().generation(); - } - } - /// # Panics /// /// If `world_id` does not match the [`World`] used to call `QueryState::new` for this instance. @@ -566,60 +491,7 @@ impl QueryState { #[inline] #[track_caller] pub fn validate_world(&self, world_id: WorldId) { - #[inline(never)] - #[track_caller] - #[cold] - fn panic_mismatched(this: WorldId, other: WorldId) -> ! { - panic!("Encountered a mismatched World. This QueryState was created from {this:?}, but a method was called using {other:?}."); - } - - if self.world_id != world_id { - panic_mismatched(self.world_id, world_id); - } - } - - /// Update the current [`QueryState`] with information from the provided [`Archetype`] - /// (if applicable, i.e. if the archetype has any intersecting [`ComponentId`] with the current [`QueryState`]). - /// - /// # Safety - /// `archetype` must be from the `World` this state was initialized from. - pub unsafe fn new_archetype(&mut self, archetype: &Archetype) { - if D::matches_component_set(&self.fetch_state, &|id| archetype.contains(id)) - && F::matches_component_set(&self.filter_state, &|id| archetype.contains(id)) - && self.matches_component_set(&|id| archetype.contains(id)) - { - let archetype_index = archetype.id().index(); - if !self.matched_archetypes.contains(archetype_index) { - self.matched_archetypes.grow_and_insert(archetype_index); - if !self.is_dense { - self.matched_storage_ids.push(StorageId { - archetype_id: archetype.id(), - }); - } - } - let table_index = archetype.table_id().as_usize(); - if !self.matched_tables.contains(table_index) { - self.matched_tables.grow_and_insert(table_index); - if self.is_dense { - self.matched_storage_ids.push(StorageId { - table_id: archetype.table_id(), - }); - } - } - } - } - - /// Returns `true` if this query matches a set of components. Otherwise, returns `false`. - pub fn matches_component_set(&self, set_contains_id: &impl Fn(ComponentId) -> bool) -> bool { - self.component_access.filter_sets.iter().any(|set| { - set.with - .ones() - .all(|index| set_contains_id(ComponentId::get_sparse_set_index(index))) - && set - .without - .ones() - .all(|index| !set_contains_id(ComponentId::get_sparse_set_index(index))) - }) + self.as_uncached().validate_world(world_id); } /// Use this to transform a [`QueryState`] into a more generic [`QueryState`]. @@ -633,7 +505,7 @@ impl QueryState { pub fn transmute<'a, NewD: QueryData>( &self, world: impl Into>, - ) -> QueryState { + ) -> QueryState { self.transmute_filtered::(world.into()) } @@ -644,7 +516,7 @@ impl QueryState { pub fn transmute_filtered<'a, NewD: QueryData, NewF: QueryFilter>( &self, world: impl Into>, - ) -> QueryState { + ) -> QueryState { let world = world.into(); self.validate_world(world.id()); @@ -679,14 +551,10 @@ impl QueryState { QueryState { world_id: self.world_id, - archetype_generation: self.archetype_generation, - matched_storage_ids: self.matched_storage_ids.clone(), - is_dense: self.is_dense, + cache: self.cache.clone(), fetch_state, filter_state, component_access: self_access, - matched_tables: self.matched_tables.clone(), - matched_archetypes: self.matched_archetypes.clone(), #[cfg(feature = "trace")] par_iter_span: tracing::info_span!( "par_for_each", @@ -718,8 +586,8 @@ impl QueryState { pub fn join<'a, OtherD: QueryData, NewD: QueryData>( &self, world: impl Into>, - other: &QueryState, - ) -> QueryState { + other: &QueryState, + ) -> QueryState { self.join_filtered::<_, (), NewD, ()>(world, other) } @@ -738,8 +606,8 @@ impl QueryState { >( &self, world: impl Into>, - other: &QueryState, - ) -> QueryState { + other: &QueryState, + ) -> QueryState { if self.world_id != other.world_id { panic!("Joining queries initialized on different worlds is not allowed."); } @@ -796,44 +664,12 @@ impl QueryState { DebugName::type_name::<(NewD, NewF)>(), DebugName::type_name::<(D, F)>(), DebugName::type_name::<(OtherD, OtherF)>() ); - if self.archetype_generation != other.archetype_generation { - warn!("You have tried to join queries with different archetype_generations. This could lead to unpredictable results."); - } - - // the join is dense of both the queries were dense. - let is_dense = self.is_dense && other.is_dense; - - // take the intersection of the matched ids - let mut matched_tables = self.matched_tables.clone(); - let mut matched_archetypes = self.matched_archetypes.clone(); - matched_tables.intersect_with(&other.matched_tables); - matched_archetypes.intersect_with(&other.matched_archetypes); - let matched_storage_ids = if is_dense { - matched_tables - .ones() - .map(|id| StorageId { - table_id: TableId::from_usize(id), - }) - .collect() - } else { - matched_archetypes - .ones() - .map(|id| StorageId { - archetype_id: ArchetypeId::new(id), - }) - .collect() - }; - QueryState { world_id: self.world_id, - archetype_generation: self.archetype_generation, - matched_storage_ids, - is_dense, fetch_state: new_fetch_state, filter_state: new_filter_state, component_access: joined_component_access, - matched_tables, - matched_archetypes, + cache: self.cache.join(&other.cache), #[cfg(feature = "trace")] par_iter_span: tracing::info_span!( "par_for_each", @@ -1090,7 +926,7 @@ impl QueryState { /// If you need to iterate multiple times at once but get borrowing errors, /// consider using [`Self::update_archetypes`] followed by multiple [`Self::iter_manual`] calls. #[inline] - pub fn iter<'w, 's>(&'s mut self, world: &'w World) -> QueryIter<'w, 's, D::ReadOnly, F> { + pub fn iter<'w, 's>(&'s mut self, world: &'w World) -> QueryIter<'w, 's, D::ReadOnly, F, C> { self.query(world).into_iter() } @@ -1099,7 +935,7 @@ impl QueryState { /// This iterator is always guaranteed to return results from each matching entity once and only once. /// Iteration order is not guaranteed. #[inline] - pub fn iter_mut<'w, 's>(&'s mut self, world: &'w mut World) -> QueryIter<'w, 's, D, F> { + pub fn iter_mut<'w, 's>(&'s mut self, world: &'w mut World) -> QueryIter<'w, 's, D, F, C> { self.query_mut(world).into_iter() } @@ -1111,7 +947,7 @@ impl QueryState { /// /// This can only be called for read-only queries. #[inline] - pub fn iter_manual<'w, 's>(&'s self, world: &'w World) -> QueryIter<'w, 's, D::ReadOnly, F> { + pub fn iter_manual<'w, 's>(&'s self, world: &'w World) -> QueryIter<'w, 's, D::ReadOnly, F, C> { self.query_manual(world).into_iter() } @@ -1134,7 +970,7 @@ impl QueryState { /// /// The `iter_combinations` method does not guarantee order of iteration. /// - /// This iterator is always guaranteed to return results from each unique pair of matching entities. + /// This iterator is always guaranteed to return results from each unique pai/* */r of matching entities. /// Iteration order is not guaranteed. /// /// This can only be called for read-only queries, see [`Self::iter_combinations_mut`] for @@ -1143,7 +979,7 @@ impl QueryState { pub fn iter_combinations<'w, 's, const K: usize>( &'s mut self, world: &'w World, - ) -> QueryCombinationIter<'w, 's, D::ReadOnly, F, K> { + ) -> QueryCombinationIter<'w, 's, D::ReadOnly, F, C, K> { self.query(world).iter_combinations_inner() } @@ -1168,7 +1004,7 @@ impl QueryState { pub fn iter_combinations_mut<'w, 's, const K: usize>( &'s mut self, world: &'w mut World, - ) -> QueryCombinationIter<'w, 's, D, F, K> { + ) -> QueryCombinationIter<'w, 's, D, F, C, K> { self.query_mut(world).iter_combinations_inner() } @@ -1188,7 +1024,7 @@ impl QueryState { &'s mut self, world: &'w World, entities: EntityList, - ) -> QueryManyIter<'w, 's, D::ReadOnly, F, EntityList::IntoIter> { + ) -> QueryManyIter<'w, 's, D::ReadOnly, F, C, EntityList::IntoIter> { self.query(world).iter_many_inner(entities) } @@ -1211,7 +1047,7 @@ impl QueryState { &'s self, world: &'w World, entities: EntityList, - ) -> QueryManyIter<'w, 's, D::ReadOnly, F, EntityList::IntoIter> { + ) -> QueryManyIter<'w, 's, D::ReadOnly, F, C, EntityList::IntoIter> { self.query_manual(world).iter_many_inner(entities) } @@ -1224,7 +1060,7 @@ impl QueryState { &'s mut self, world: &'w mut World, entities: EntityList, - ) -> QueryManyIter<'w, 's, D, F, EntityList::IntoIter> { + ) -> QueryManyIter<'w, 's, D, F, C, EntityList::IntoIter> { self.query_mut(world).iter_many_inner(entities) } @@ -1241,7 +1077,7 @@ impl QueryState { &'s mut self, world: &'w World, entities: EntityList, - ) -> QueryManyUniqueIter<'w, 's, D::ReadOnly, F, EntityList::IntoIter> { + ) -> QueryManyUniqueIter<'w, 's, D::ReadOnly, F, C, EntityList::IntoIter> { self.query(world).iter_many_unique_inner(entities) } @@ -1265,7 +1101,7 @@ impl QueryState { &'s self, world: &'w World, entities: EntityList, - ) -> QueryManyUniqueIter<'w, 's, D::ReadOnly, F, EntityList::IntoIter> { + ) -> QueryManyUniqueIter<'w, 's, D::ReadOnly, F, C, EntityList::IntoIter> { self.query_manual(world).iter_many_unique_inner(entities) } @@ -1278,7 +1114,7 @@ impl QueryState { &'s mut self, world: &'w mut World, entities: EntityList, - ) -> QueryManyUniqueIter<'w, 's, D, F, EntityList::IntoIter> { + ) -> QueryManyUniqueIter<'w, 's, D, F, C, EntityList::IntoIter> { self.query_mut(world).iter_many_unique_inner(entities) } /// Returns an [`Iterator`] over the query results for the given [`World`]. @@ -1294,7 +1130,7 @@ impl QueryState { pub unsafe fn iter_unchecked<'w, 's>( &'s mut self, world: UnsafeWorldCell<'w>, - ) -> QueryIter<'w, 's, D, F> { + ) -> QueryIter<'w, 's, D, F, C> { self.query_unchecked(world).into_iter() } @@ -1313,7 +1149,7 @@ impl QueryState { pub unsafe fn iter_combinations_unchecked<'w, 's, const K: usize>( &'s mut self, world: UnsafeWorldCell<'w>, - ) -> QueryCombinationIter<'w, 's, D, F, K> { + ) -> QueryCombinationIter<'w, 's, D, F, C, K> { self.query_unchecked(world).iter_combinations_inner() } @@ -1329,7 +1165,7 @@ impl QueryState { pub fn par_iter<'w, 's>( &'s mut self, world: &'w World, - ) -> QueryParIter<'w, 's, D::ReadOnly, F> { + ) -> QueryParIter<'w, 's, D::ReadOnly, F, C> { self.query(world).par_iter_inner() } @@ -1378,7 +1214,10 @@ impl QueryState { /// [`par_iter`]: Self::par_iter /// [`ComputeTaskPool`]: bevy_tasks::ComputeTaskPool #[inline] - pub fn par_iter_mut<'w, 's>(&'s mut self, world: &'w mut World) -> QueryParIter<'w, 's, D, F> { + pub fn par_iter_mut<'w, 's>( + &'s mut self, + world: &'w mut World, + ) -> QueryParIter<'w, 's, D, F, C> { self.query_mut(world).par_iter_inner() } @@ -1415,6 +1254,7 @@ impl QueryState { // QueryIter, QueryIterationCursor, QueryManyIter, QueryCombinationIter,QueryState::par_fold_init_unchecked_manual, // QueryState::par_many_fold_init_unchecked_manual, QueryState::par_many_unique_fold_init_unchecked_manual use arrayvec::ArrayVec; + let iteration_data = self.cache.iteration_data(self, world); bevy_tasks::ComputeTaskPool::get().scope(|scope| { // SAFETY: We only access table data that has been registered in `self.component_access`. @@ -1463,14 +1303,14 @@ impl QueryState { }; let storage_entity_count = |storage_id: StorageId| -> u32 { - if self.is_dense { + if iteration_data.is_dense { tables[storage_id.table_id].entity_count() } else { archetypes[storage_id.archetype_id].len() } }; - for storage_id in &self.matched_storage_ids { + for storage_id in iteration_data.storage_ids.iter() { let count = storage_entity_count(*storage_id); // skip empty storage @@ -1558,7 +1398,7 @@ impl QueryState { } } -impl QueryState { +impl QueryState { /// Runs `func` on each read-only query result in parallel for the given [`Entity`] list, /// where the last change and the current change tick are given. This is faster than the equivalent /// `iter_many()` method, but cannot be chained like a normal [`Iterator`]. @@ -1621,7 +1461,7 @@ impl QueryState { } } -impl QueryState { +impl QueryState { /// Returns a single immutable query result when there is exactly one entity matching /// the query. /// @@ -1766,7 +1606,9 @@ impl QueryState { } } -impl From> for QueryState { +impl From> + for QueryState +{ fn from(mut value: QueryBuilder) -> Self { QueryState::from_builder(&mut value) } @@ -2212,29 +2054,21 @@ mod tests { let mut query = QueryState::<()>::new(&mut world); // There are no sparse components involved thus the query is dense - assert!(query.is_dense); + assert!(query.cache.is_dense); assert_eq!(3, query.query(&world).count()); - world.register_disabling_component::(); - - let mut query = QueryState::<()>::new(&mut world); - // The query doesn't ask for sparse components, but the default filters adds - // a sparse components thus it is NOT dense - assert!(!query.is_dense); - assert_eq!(1, query.query(&world).count()); - let mut df = DefaultQueryFilters::from_world(&mut world); df.register_disabling_component(world.register_component::()); world.insert_resource(df); let mut query = QueryState::<()>::new(&mut world); // If the filter is instead a table components, the query can still be dense - assert!(query.is_dense); + assert!(query.cache.is_dense); assert_eq!(1, query.query(&world).count()); let mut query = QueryState::<&Sparse>::new(&mut world); // But only if the original query was dense - assert!(!query.is_dense); + assert!(!query.cache.is_dense); assert_eq!(1, query.query(&world).count()); } } diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index 2b9fe4319b973..e6abfd63e5b6e 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -1391,9 +1391,9 @@ mod tests { world.spawn((A(2), B(2))); { let query = system_state.get(&world); - assert_eq!( - query.iter().collect::>(), - vec![&A(1), &A(2)], + let result = query.iter().collect::>(); + assert!( + result.contains(&&A(1)) && result.contains(&&A(2)), "components from both archetypes returned" ); } diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index 36a1ca3dc7d69..5ae71677d6ae1 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -11,6 +11,7 @@ use crate::{ }, world::unsafe_world_cell::UnsafeWorldCell, }; +use bevy_ecs::query::{DefaultCache, QueryCache, Uncached}; use core::{ marker::PhantomData, mem::MaybeUninit, @@ -482,23 +483,30 @@ use core::{ /// ``` /// /// [autovectorization]: https://en.wikipedia.org/wiki/Automatic_vectorization -pub struct Query<'world, 'state, D: QueryData, F: QueryFilter = ()> { +pub struct Query<'world, 'state, D: QueryData, F: QueryFilter = (), C: QueryCache = DefaultCache> { // SAFETY: Must have access to the components registered in `state`. world: UnsafeWorldCell<'world>, - state: &'state QueryState, + state: &'state QueryState, last_run: Tick, this_run: Tick, } -impl Clone for Query<'_, '_, D, F> { +/// Similar to [`Query`] but does not perform any caching of the archetypes that match the query. +/// +/// This can be useful for one-off queries that don't need to pay the extra memory cost of storing the list +/// of matched archetypes. However the query iteration time will be slower on repeated usages since it won't +/// make use of the cache. +pub type UncachedQuery<'world, 'state, D, F = ()> = Query<'world, 'state, D, F, Uncached>; + +impl Clone for Query<'_, '_, D, F, C> { fn clone(&self) -> Self { *self } } -impl Copy for Query<'_, '_, D, F> {} +impl Copy for Query<'_, '_, D, F, C> {} -impl core::fmt::Debug for Query<'_, '_, D, F> { +impl core::fmt::Debug for Query<'_, '_, D, F, C> { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { f.debug_struct("Query") .field("matched_entities", &self.iter().count()) @@ -510,7 +518,7 @@ impl core::fmt::Debug for Query<'_, '_, D, F> { } } -impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache> Query<'w, 's, D, F, C> { /// Creates a new query. /// /// # Safety @@ -521,7 +529,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { #[inline] pub(crate) unsafe fn new( world: UnsafeWorldCell<'w>, - state: &'s QueryState, + state: &'s QueryState, last_run: Tick, this_run: Tick, ) -> Self { @@ -542,14 +550,14 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// # See also /// /// [`into_readonly`](Self::into_readonly) for a version that consumes the `Query` to return one with the full `'world` lifetime. - pub fn as_readonly(&self) -> Query<'_, 's, D::ReadOnly, F> { + pub fn as_readonly(&self) -> Query<'_, 's, D::ReadOnly, F, C> { // SAFETY: The reborrowed query is converted to read-only, so it cannot perform mutable access, // and the original query is held with a shared borrow, so it cannot perform mutable access either. unsafe { self.reborrow_unsafe() }.into_readonly() } /// Returns another `Query` from this does not return any data, which can be faster. - fn as_nop(&self) -> Query<'_, 's, NopWorldQuery, F> { + fn as_nop(&self) -> Query<'_, 's, NopWorldQuery, F, C> { let new_state = self.state.as_nop(); // SAFETY: // - The reborrowed query is converted to read-only, so it cannot perform mutable access, @@ -569,7 +577,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// # See also /// /// [`as_readonly`](Self::as_readonly) for a version that borrows the `Query` instead of consuming it. - pub fn into_readonly(self) -> Query<'w, 's, D::ReadOnly, F> { + pub fn into_readonly(self) -> Query<'w, 's, D::ReadOnly, F, C> { let new_state = self.state.as_readonly(); // SAFETY: // - This is memory safe because it turns the query immutable. @@ -601,7 +609,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// } /// } /// ``` - pub fn reborrow(&mut self) -> Query<'_, 's, D, F> { + pub fn reborrow(&mut self) -> Query<'_, 's, D, F, C> { // SAFETY: this query is exclusively borrowed while the new one exists, so // no overlapping access can occur. unsafe { self.reborrow_unsafe() } @@ -618,7 +626,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// # See also /// /// - [`reborrow`](Self::reborrow) for the safe versions. - pub unsafe fn reborrow_unsafe(&self) -> Query<'_, 's, D, F> { + pub unsafe fn reborrow_unsafe(&self) -> Query<'_, 's, D, F, C> { // SAFETY: // - This is memory safe because the caller ensures that there are no conflicting references. // - The world matches because it was the same one used to construct self. @@ -636,7 +644,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// # See also /// /// - [`reborrow_unsafe`](Self::reborrow_unsafe) for a safer version that constrains the returned `'w` lifetime to the length of the borrow. - unsafe fn copy_unsafe(&self) -> Query<'w, 's, D, F> { + unsafe fn copy_unsafe(&self) -> Query<'w, 's, D, F, C> { // SAFETY: // - This is memory safe because the caller ensures that there are no conflicting references. // - The world matches because it was the same one used to construct self. @@ -670,7 +678,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// /// [`iter_mut`](Self::iter_mut) for mutable query items. #[inline] - pub fn iter(&self) -> QueryIter<'_, 's, D::ReadOnly, F> { + pub fn iter(&self) -> QueryIter<'_, 's, D::ReadOnly, F, C> { self.as_readonly().into_iter() } @@ -701,7 +709,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// /// [`iter`](Self::iter) for read-only query items. #[inline] - pub fn iter_mut(&mut self) -> QueryIter<'_, 's, D, F> { + pub fn iter_mut(&mut self) -> QueryIter<'_, 's, D, F, C> { self.reborrow().into_iter() } @@ -731,7 +739,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { #[inline] pub fn iter_combinations( &self, - ) -> QueryCombinationIter<'_, 's, D::ReadOnly, F, K> { + ) -> QueryCombinationIter<'_, 's, D::ReadOnly, F, C, K> { self.as_readonly().iter_combinations_inner() } @@ -761,7 +769,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { #[inline] pub fn iter_combinations_mut( &mut self, - ) -> QueryCombinationIter<'_, 's, D, F, K> { + ) -> QueryCombinationIter<'_, 's, D, F, C, K> { self.reborrow().iter_combinations_inner() } @@ -790,7 +798,9 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// - [`iter_combinations`](Self::iter_combinations) for read-only query item combinations. /// - [`iter_combinations_mut`](Self::iter_combinations_mut) for mutable query item combinations. #[inline] - pub fn iter_combinations_inner(self) -> QueryCombinationIter<'w, 's, D, F, K> { + pub fn iter_combinations_inner( + self, + ) -> QueryCombinationIter<'w, 's, D, F, C, K> { // SAFETY: `self.world` has permission to access the required components. unsafe { QueryCombinationIter::new(self.world, self.state, self.last_run, self.this_run) } } @@ -836,7 +846,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { pub fn iter_many>( &self, entities: EntityList, - ) -> QueryManyIter<'_, 's, D::ReadOnly, F, EntityList::IntoIter> { + ) -> QueryManyIter<'_, 's, D::ReadOnly, F, C, EntityList::IntoIter> { self.as_readonly().iter_many_inner(entities) } @@ -881,7 +891,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { pub fn iter_many_mut>( &mut self, entities: EntityList, - ) -> QueryManyIter<'_, 's, D, F, EntityList::IntoIter> { + ) -> QueryManyIter<'_, 's, D, F, C, EntityList::IntoIter> { self.reborrow().iter_many_inner(entities) } @@ -899,7 +909,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { pub fn iter_many_inner>( self, entities: EntityList, - ) -> QueryManyIter<'w, 's, D, F, EntityList::IntoIter> { + ) -> QueryManyIter<'w, 's, D, F, C, EntityList::IntoIter> { // SAFETY: `self.world` has permission to access the required components. unsafe { QueryManyIter::new( @@ -964,7 +974,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { pub fn iter_many_unique( &self, entities: EntityList, - ) -> QueryManyUniqueIter<'_, 's, D::ReadOnly, F, EntityList::IntoIter> { + ) -> QueryManyUniqueIter<'_, 's, D::ReadOnly, F, C, EntityList::IntoIter> { self.as_readonly().iter_many_unique_inner(entities) } @@ -1019,7 +1029,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { pub fn iter_many_unique_mut( &mut self, entities: EntityList, - ) -> QueryManyUniqueIter<'_, 's, D, F, EntityList::IntoIter> { + ) -> QueryManyUniqueIter<'_, 's, D, F, C, EntityList::IntoIter> { self.reborrow().iter_many_unique_inner(entities) } @@ -1074,7 +1084,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { pub fn iter_many_unique_inner( self, entities: EntityList, - ) -> QueryManyUniqueIter<'w, 's, D, F, EntityList::IntoIter> { + ) -> QueryManyUniqueIter<'w, 's, D, F, C, EntityList::IntoIter> { // SAFETY: `self.world` has permission to access the required components. unsafe { QueryManyUniqueIter::new( @@ -1101,7 +1111,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// /// - [`iter`](Self::iter) and [`iter_mut`](Self::iter_mut) for the safe versions. #[inline] - pub unsafe fn iter_unsafe(&self) -> QueryIter<'_, 's, D, F> { + pub unsafe fn iter_unsafe(&self) -> QueryIter<'_, 's, D, F, C> { // SAFETY: The caller promises that this will not result in multiple mutable references. unsafe { self.reborrow_unsafe() }.into_iter() } @@ -1122,7 +1132,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { #[inline] pub unsafe fn iter_combinations_unsafe( &self, - ) -> QueryCombinationIter<'_, 's, D, F, K> { + ) -> QueryCombinationIter<'_, 's, D, F, C, K> { // SAFETY: The caller promises that this will not result in multiple mutable references. unsafe { self.reborrow_unsafe() }.iter_combinations_inner() } @@ -1144,7 +1154,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { pub unsafe fn iter_many_unsafe>( &self, entities: EntityList, - ) -> QueryManyIter<'_, 's, D, F, EntityList::IntoIter> { + ) -> QueryManyIter<'_, 's, D, F, C, EntityList::IntoIter> { // SAFETY: The caller promises that this will not result in multiple mutable references. unsafe { self.reborrow_unsafe() }.iter_many_inner(entities) } @@ -1166,7 +1176,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { pub unsafe fn iter_many_unique_unsafe( &self, entities: EntityList, - ) -> QueryManyUniqueIter<'_, 's, D, F, EntityList::IntoIter> { + ) -> QueryManyUniqueIter<'_, 's, D, F, C, EntityList::IntoIter> { // SAFETY: The caller promises that this will not result in multiple mutable references. unsafe { self.reborrow_unsafe() }.iter_many_unique_inner(entities) } @@ -1187,7 +1197,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// [`par_iter_mut`]: Self::par_iter_mut /// [`World`]: crate::world::World #[inline] - pub fn par_iter(&self) -> QueryParIter<'_, 's, D::ReadOnly, F> { + pub fn par_iter(&self) -> QueryParIter<'_, 's, D::ReadOnly, F, C> { self.as_readonly().par_iter_inner() } @@ -1222,7 +1232,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// [`par_iter`]: Self::par_iter /// [`World`]: crate::world::World #[inline] - pub fn par_iter_mut(&mut self) -> QueryParIter<'_, 's, D, F> { + pub fn par_iter_mut(&mut self) -> QueryParIter<'_, 's, D, F, C> { self.reborrow().par_iter_inner() } @@ -1253,10 +1263,12 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// # bevy_ecs::system::assert_is_system(gravity_system); /// ``` #[inline] - pub fn par_iter_inner(self) -> QueryParIter<'w, 's, D, F> { + pub fn par_iter_inner(self) -> QueryParIter<'w, 's, D, F, C> { QueryParIter { world: self.world, state: self.state, + #[cfg(all(not(target_arch = "wasm32"), feature = "multi_threaded"))] + iteration_data: self.state.cache.iteration_data(self.state, self.world), last_run: self.last_run, this_run: self.this_run, batching_strategy: BatchingStrategy::new(), @@ -1282,7 +1294,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { pub fn par_iter_many>( &self, entities: EntityList, - ) -> QueryParManyIter<'_, 's, D::ReadOnly, F, EntityList::Item> { + ) -> QueryParManyIter<'_, 's, D::ReadOnly, F, C, EntityList::Item> { QueryParManyIter { world: self.world, state: self.state.as_readonly(), @@ -1311,7 +1323,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { pub fn par_iter_many_unique>( &self, entities: EntityList, - ) -> QueryParManyUniqueIter<'_, 's, D::ReadOnly, F, EntityList::Item> { + ) -> QueryParManyUniqueIter<'_, 's, D::ReadOnly, F, C, EntityList::Item> { QueryParManyUniqueIter { world: self.world, state: self.state.as_readonly(), @@ -1340,7 +1352,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { pub fn par_iter_many_unique_mut>( &mut self, entities: EntityList, - ) -> QueryParManyUniqueIter<'_, 's, D, F, EntityList::Item> { + ) -> QueryParManyUniqueIter<'_, 's, D, F, C, EntityList::Item> { QueryParManyUniqueIter { world: self.world, state: self.state, @@ -1545,21 +1557,18 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { .entities() .get(entity) .ok_or(EntityDoesNotExistError::new(entity, self.world.entities()))?; - if !self - .state - .matched_archetypes - .contains(location.archetype_id.index()) - { - return Err(QueryEntityError::QueryDoesNotMatch( - entity, - location.archetype_id, - )); - } + let archetype = self .world .archetypes() .get(location.archetype_id) .debug_checked_unwrap(); + if !self.state.matches_archetype(archetype) { + return Err(QueryEntityError::QueryDoesNotMatch( + entity, + location.archetype_id, + )); + } let mut fetch = D::init_fetch( self.world, &self.state.fetch_state, @@ -2240,7 +2249,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// assert_valid_transmute_filtered::, (), Entity, Or<(Changed, With)>>(); /// ``` #[track_caller] - pub fn transmute_lens(&mut self) -> QueryLens<'_, NewD> { + pub fn transmute_lens(&mut self) -> QueryLens<'_, NewD, (), C> { self.transmute_lens_filtered::() } @@ -2296,7 +2305,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// /// - [`transmute_lens`](Self::transmute_lens) to convert to a lens using a mutable borrow of the [`Query`]. #[track_caller] - pub fn transmute_lens_inner(self) -> QueryLens<'w, NewD> { + pub fn transmute_lens_inner(self) -> QueryLens<'w, NewD, (), C> { self.transmute_lens_filtered_inner::() } @@ -2312,7 +2321,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { #[track_caller] pub fn transmute_lens_filtered( &mut self, - ) -> QueryLens<'_, NewD, NewF> { + ) -> QueryLens<'_, NewD, NewF, C> { self.reborrow().transmute_lens_filtered_inner() } @@ -2333,7 +2342,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { #[track_caller] pub fn transmute_lens_filtered_inner( self, - ) -> QueryLens<'w, NewD, NewF> { + ) -> QueryLens<'w, NewD, NewF, C> { let state = self.state.transmute_filtered::(self.world); QueryLens { world: self.world, @@ -2344,7 +2353,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { } /// Gets a [`QueryLens`] with the same accesses as the existing query - pub fn as_query_lens(&mut self) -> QueryLens<'_, D> { + pub fn as_query_lens(&mut self) -> QueryLens<'_, D, (), C> { self.transmute_lens() } @@ -2353,7 +2362,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// # See also /// /// - [`as_query_lens`](Self::as_query_lens) to convert to a lens using a mutable borrow of the [`Query`]. - pub fn into_query_lens(self) -> QueryLens<'w, D> { + pub fn into_query_lens(self) -> QueryLens<'w, D, (), C> { self.transmute_lens_inner() } @@ -2413,8 +2422,8 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// See [`Self::transmute_lens`] for more details. pub fn join<'a, OtherD: QueryData, NewD: QueryData>( &'a mut self, - other: &'a mut Query, - ) -> QueryLens<'a, NewD> { + other: &'a mut Query, + ) -> QueryLens<'a, NewD, (), C> { self.join_filtered(other) } @@ -2440,8 +2449,8 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// - [`join`](Self::join) to join using a mutable borrow of the [`Query`]. pub fn join_inner( self, - other: Query<'w, '_, OtherD>, - ) -> QueryLens<'w, NewD> { + other: Query<'w, '_, OtherD, (), C>, + ) -> QueryLens<'w, NewD, (), C> { self.join_filtered_inner(other) } @@ -2460,8 +2469,8 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { NewF: QueryFilter, >( &'a mut self, - other: &'a mut Query, - ) -> QueryLens<'a, NewD, NewF> { + other: &'a mut Query, + ) -> QueryLens<'a, NewD, NewF, C> { self.reborrow().join_filtered_inner(other.reborrow()) } @@ -2484,8 +2493,8 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { NewF: QueryFilter, >( self, - other: Query<'w, '_, OtherD, OtherF>, - ) -> QueryLens<'w, NewD, NewF> { + other: Query<'w, '_, OtherD, OtherF, C>, + ) -> QueryLens<'w, NewD, NewF, C> { let state = self .state .join_filtered::(self.world, other.state); @@ -2498,9 +2507,9 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { } } -impl<'w, 's, D: QueryData, F: QueryFilter> IntoIterator for Query<'w, 's, D, F> { +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache> IntoIterator for Query<'w, 's, D, F, C> { type Item = D::Item<'w, 's>; - type IntoIter = QueryIter<'w, 's, D, F>; + type IntoIter = QueryIter<'w, 's, D, F, C>; fn into_iter(self) -> Self::IntoIter { // SAFETY: @@ -2511,25 +2520,29 @@ impl<'w, 's, D: QueryData, F: QueryFilter> IntoIterator for Query<'w, 's, D, F> } } -impl<'w, 's, D: QueryData, F: QueryFilter> IntoIterator for &'w Query<'_, 's, D, F> { +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache> IntoIterator + for &'w Query<'_, 's, D, F, C> +{ type Item = ROQueryItem<'w, 's, D>; - type IntoIter = QueryIter<'w, 's, D::ReadOnly, F>; + type IntoIter = QueryIter<'w, 's, D::ReadOnly, F, C>; fn into_iter(self) -> Self::IntoIter { self.iter() } } -impl<'w, 's, D: QueryData, F: QueryFilter> IntoIterator for &'w mut Query<'_, 's, D, F> { +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache> IntoIterator + for &'w mut Query<'_, 's, D, F, C> +{ type Item = D::Item<'w, 's>; - type IntoIter = QueryIter<'w, 's, D, F>; + type IntoIter = QueryIter<'w, 's, D, F, C>; fn into_iter(self) -> Self::IntoIter { self.iter_mut() } } -impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter> Query<'w, 's, D, F> { +impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, C: QueryCache> Query<'w, 's, D, F, C> { /// Returns an [`Iterator`] over the query items, with the actual "inner" world lifetime. /// /// This can only return immutable data (mutable data will be cast to an immutable form). @@ -2554,7 +2567,7 @@ impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter> Query<'w, 's, D, F> { /// # bevy_ecs::system::assert_is_system(report_names_system); /// ``` #[inline] - pub fn iter_inner(&self) -> QueryIter<'w, 's, D::ReadOnly, F> { + pub fn iter_inner(&self) -> QueryIter<'w, 's, D::ReadOnly, F, C> { (*self).into_iter() } } @@ -2562,16 +2575,16 @@ impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter> Query<'w, 's, D, F> { /// Type returned from [`Query::transmute_lens`] containing the new [`QueryState`]. /// /// Call [`query`](QueryLens::query) or [`into`](Into::into) to construct the resulting [`Query`] -pub struct QueryLens<'w, Q: QueryData, F: QueryFilter = ()> { +pub struct QueryLens<'w, Q: QueryData, F: QueryFilter = (), C: QueryCache = DefaultCache> { world: UnsafeWorldCell<'w>, - state: QueryState, + state: QueryState, last_run: Tick, this_run: Tick, } -impl<'w, Q: QueryData, F: QueryFilter> QueryLens<'w, Q, F> { +impl<'w, Q: QueryData, F: QueryFilter, C: QueryCache> QueryLens<'w, Q, F, C> { /// Create a [`Query`] from the underlying [`QueryState`]. - pub fn query(&mut self) -> Query<'_, '_, Q, F> { + pub fn query(&mut self) -> Query<'_, '_, Q, F, C> { Query { world: self.world, state: &self.state, @@ -2581,11 +2594,11 @@ impl<'w, Q: QueryData, F: QueryFilter> QueryLens<'w, Q, F> { } } -impl<'w, Q: ReadOnlyQueryData, F: QueryFilter> QueryLens<'w, Q, F> { +impl<'w, Q: ReadOnlyQueryData, F: QueryFilter, C: QueryCache> QueryLens<'w, Q, F, C> { /// Create a [`Query`] from the underlying [`QueryState`]. /// This returns results with the actual "inner" world lifetime, /// so it may only be used with read-only queries to prevent mutable aliasing. - pub fn query_inner(&self) -> Query<'w, '_, Q, F> { + pub fn query_inner(&self) -> Query<'w, '_, Q, F, C> { Query { world: self.world, state: &self.state, @@ -2595,18 +2608,18 @@ impl<'w, Q: ReadOnlyQueryData, F: QueryFilter> QueryLens<'w, Q, F> { } } -impl<'w, 's, Q: QueryData, F: QueryFilter> From<&'s mut QueryLens<'w, Q, F>> - for Query<'s, 's, Q, F> +impl<'w, 's, Q: QueryData, F: QueryFilter, C: QueryCache> From<&'s mut QueryLens<'w, Q, F, C>> + for Query<'s, 's, Q, F, C> { - fn from(value: &'s mut QueryLens<'w, Q, F>) -> Query<'s, 's, Q, F> { + fn from(value: &'s mut QueryLens<'w, Q, F, C>) -> Query<'s, 's, Q, F, C> { value.query() } } -impl<'w, 'q, Q: QueryData, F: QueryFilter> From<&'q mut Query<'w, '_, Q, F>> - for QueryLens<'q, Q, F> +impl<'w, 'q, Q: QueryData, F: QueryFilter, C: QueryCache> From<&'q mut Query<'w, '_, Q, F, C>> + for QueryLens<'q, Q, F, C> { - fn from(value: &'q mut Query<'w, '_, Q, F>) -> QueryLens<'q, Q, F> { + fn from(value: &'q mut Query<'w, '_, Q, F, C>) -> QueryLens<'q, Q, F, C> { value.transmute_lens_filtered() } } @@ -2616,7 +2629,7 @@ impl<'w, 'q, Q: QueryData, F: QueryFilter> From<&'q mut Query<'w, '_, Q, F>> /// This [`SystemParam`](crate::system::SystemParam) fails validation if zero or more than one matching entity exists. /// This will cause the system to be skipped, according to the rules laid out in [`SystemParamValidationError`](crate::system::SystemParamValidationError). /// -/// Use [`Option>`] instead if zero or one matching entities can exist. +/// Use [`Option>`] instead if zero or one matching entities can exist. /// /// See [`Query`] for more details. /// @@ -2675,53 +2688,61 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Single<'w, 's, D, F> { /// See [`Query`] for more details. /// /// [System parameter]: crate::system::SystemParam -pub struct Populated<'w, 's, D: QueryData, F: QueryFilter = ()>(pub(crate) Query<'w, 's, D, F>); +pub struct Populated<'w, 's, D: QueryData, F: QueryFilter = (), C: QueryCache = DefaultCache>( + pub(crate) Query<'w, 's, D, F, C>, +); -impl<'w, 's, D: QueryData, F: QueryFilter> Deref for Populated<'w, 's, D, F> { - type Target = Query<'w, 's, D, F>; +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache> Deref for Populated<'w, 's, D, F, C> { + type Target = Query<'w, 's, D, F, C>; fn deref(&self) -> &Self::Target { &self.0 } } -impl DerefMut for Populated<'_, '_, D, F> { +impl DerefMut for Populated<'_, '_, D, F, C> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } -impl<'w, 's, D: QueryData, F: QueryFilter> Populated<'w, 's, D, F> { +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache> Populated<'w, 's, D, F, C> { /// Returns the inner item with ownership. - pub fn into_inner(self) -> Query<'w, 's, D, F> { + pub fn into_inner(self) -> Query<'w, 's, D, F, C> { self.0 } } -impl<'w, 's, D: QueryData, F: QueryFilter> IntoIterator for Populated<'w, 's, D, F> { - type Item = as IntoIterator>::Item; +impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache> IntoIterator + for Populated<'w, 's, D, F, C> +{ + type Item = as IntoIterator>::Item; - type IntoIter = as IntoIterator>::IntoIter; + type IntoIter = as IntoIterator>::IntoIter; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } } -impl<'a, 'w, 's, D: QueryData, F: QueryFilter> IntoIterator for &'a Populated<'w, 's, D, F> { - type Item = <&'a Query<'w, 's, D, F> as IntoIterator>::Item; +impl<'a, 'w, 's, D: QueryData, F: QueryFilter, C: QueryCache> IntoIterator + for &'a Populated<'w, 's, D, F, C> +{ + type Item = <&'a Query<'w, 's, D, F, C> as IntoIterator>::Item; - type IntoIter = <&'a Query<'w, 's, D, F> as IntoIterator>::IntoIter; + type IntoIter = <&'a Query<'w, 's, D, F, C> as IntoIterator>::IntoIter; fn into_iter(self) -> Self::IntoIter { self.deref().into_iter() } } -impl<'a, 'w, 's, D: QueryData, F: QueryFilter> IntoIterator for &'a mut Populated<'w, 's, D, F> { - type Item = <&'a mut Query<'w, 's, D, F> as IntoIterator>::Item; +impl<'a, 'w, 's, D: QueryData, F: QueryFilter, C: QueryCache> IntoIterator + for &'a mut Populated<'w, 's, D, F, C> +{ + type Item = <&'a mut Query<'w, 's, D, F, C> as IntoIterator>::Item; - type IntoIter = <&'a mut Query<'w, 's, D, F> as IntoIterator>::IntoIter; + type IntoIter = <&'a mut Query<'w, 's, D, F, C> as IntoIterator>::IntoIter; fn into_iter(self) -> Self::IntoIter { self.deref_mut().into_iter() diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 8f013b52ec7db..0d7f13ca7ebac 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -64,6 +64,7 @@ use crate::{ }, }; use alloc::{boxed::Box, vec::Vec}; +use bevy_ecs::query::UncachedQueryState; use bevy_platform::sync::atomic::{AtomicU32, Ordering}; use bevy_ptr::{move_as_ptr, MovingPtr, OwningPtr, Ptr}; use bevy_utils::prelude::DebugName; @@ -1552,6 +1553,44 @@ impl World { self.query_filtered::() } + /// Returns [`UncachedQueryState`] for the given [`QueryData`], which is used to + /// run queries on the [`World`]. In contrast to [`QueryState`], this won't cache any of the + /// archetypes that match the query. + /// ``` + /// use bevy_ecs::{component::Component, entity::Entity, world::World}; + /// + /// #[derive(Component, Debug, PartialEq)] + /// struct Position { + /// x: f32, + /// y: f32, + /// } + /// + /// #[derive(Component)] + /// struct Velocity { + /// x: f32, + /// y: f32, + /// } + /// + /// let mut world = World::new(); + /// let entities = world.spawn_batch(vec![ + /// (Position { x: 0.0, y: 0.0}, Velocity { x: 1.0, y: 0.0 }), + /// (Position { x: 0.0, y: 0.0}, Velocity { x: 0.0, y: 1.0 }), + /// ]).collect::>(); + /// + /// let mut query = world.query_uncached::<(&mut Position, &Velocity)>(); + /// for (mut position, velocity) in query.iter_mut(&mut world) { + /// position.x += velocity.x; + /// position.y += velocity.y; + /// } + /// + /// assert_eq!(world.get::(entities[0]).unwrap(), &Position { x: 1.0, y: 0.0 }); + /// assert_eq!(world.get::(entities[1]).unwrap(), &Position { x: 0.0, y: 1.0 }); + /// ``` + #[inline] + pub fn query_uncached(&mut self) -> UncachedQueryState { + self.query_filtered_uncached::() + } + /// Returns [`QueryState`] for the given filtered [`QueryData`], which is used to efficiently /// run queries on the [`World`] by storing and reusing the [`QueryState`]. /// ``` @@ -1576,6 +1615,33 @@ impl World { QueryState::new(self) } + /// Returns [`UncachedQueryState`] for the given [`QueryData`], which is used to + /// run queries on the [`World`]. In contrast to [`QueryState`], this won't cache any of the + /// archetypes that match the query. + /// ``` + /// use bevy_ecs::{component::Component, entity::Entity, world::World, query::With}; + /// + /// #[derive(Component)] + /// struct A; + /// #[derive(Component)] + /// struct B; + /// + /// let mut world = World::new(); + /// let e1 = world.spawn(A).id(); + /// let e2 = world.spawn((A, B)).id(); + /// + /// let mut query = world.query_filtered_uncached::>(); + /// let matching_entities = query.iter(&world).collect::>(); + /// + /// assert_eq!(matching_entities, vec![e2]); + /// ``` + #[inline] + pub fn query_filtered_uncached( + &mut self, + ) -> UncachedQueryState { + QueryState::new(self) + } + /// Returns [`QueryState`] for the given [`QueryData`], which is used to efficiently /// run queries on the [`World`] by storing and reusing the [`QueryState`]. /// ``` @@ -1627,6 +1693,33 @@ impl World { self.try_query_filtered::() } + /// Returns [`UncachedQueryState`] for the given [`QueryData`], which is used to + /// run queries on the [`World`]. In contrast to [`QueryState`], this won't cache any of the + /// archetypes that match the query. + /// + /// Requires only an immutable world reference, but may fail if, for example, + /// the components that make up this query have not been registered into the world. + /// ``` + /// use bevy_ecs::{component::Component, entity::Entity, world::World}; + /// + /// #[derive(Component)] + /// struct A; + /// + /// let mut world = World::new(); + /// + /// let none_query = world.try_query_uncached::<&A>(); + /// assert!(none_query.is_none()); + /// + /// world.register_component::(); + /// + /// let some_query = world.try_query_uncached::<&A>(); + /// assert!(some_query.is_some()); + /// ``` + #[inline] + pub fn try_query_uncached(&mut self) -> Option> { + self.try_query_filtered_uncached::() + } + /// Returns [`QueryState`] for the given filtered [`QueryData`], which is used to efficiently /// run queries on the [`World`] by storing and reusing the [`QueryState`]. /// ``` @@ -1654,6 +1747,36 @@ impl World { QueryState::try_new(self) } + /// Returns [`UncachedQueryState`] for the given [`QueryData`], which is used to + /// run queries on the [`World`]. In contrast to [`QueryState`], this won't cache any of the + /// archetypes that match the query. + /// ``` + /// use bevy_ecs::{component::Component, entity::Entity, world::World, query::With}; + /// + /// #[derive(Component)] + /// struct A; + /// #[derive(Component)] + /// struct B; + /// + /// let mut world = World::new(); + /// let e1 = world.spawn(A).id(); + /// let e2 = world.spawn((A, B)).id(); + /// + /// let mut query = world.try_query_filtered_uncached::>().unwrap(); + /// let matching_entities = query.iter(&world).collect::>(); + /// + /// assert_eq!(matching_entities, vec![e2]); + /// ``` + /// + /// Requires only an immutable world reference, but may fail if, for example, + /// the components that make up this query have not been registered into the world. + #[inline] + pub fn try_query_filtered_uncached( + &self, + ) -> Option> { + QueryState::try_new(self) + } + /// Returns an iterator of entities that had components of type `T` removed /// since the last call to [`World::clear_trackers`]. pub fn removed(&self) -> impl Iterator + '_ { diff --git a/release-content/release-notes/uncached_queries.md b/release-content/release-notes/uncached_queries.md new file mode 100644 index 0000000000000..2da4f8fd9bcff --- /dev/null +++ b/release-content/release-notes/uncached_queries.md @@ -0,0 +1,26 @@ +--- +title: "UncachedQueries" +authors: ["@cbournhonesque"] +pull_requests: [21607] +--- + +`Query` and `QueryState` now have a third generic parameter `C: QueryCache` +that specify how the query caches the archetypes/tables that match it. + +There are two types that implement the `QueryCache` trait: + +- `CacheState`: this matches the current behavior that `Queries` have where +each matched archetype is cached +- `Uncached`: this won't perform any caching, so any query will just query +the world from scratch. + +This can be useful for one-off queries where there is no need to catch the +list of matched archetypes since it won't be re-used. A type alias +`UncachedQuery = Query` is provided for convenience. + +The following new methods on `World` are introduced: + +- `query_uncached` +- `query_filtered_uncached` +- `try_query_uncached` +- `try_query_filtered_uncached` diff --git a/tools/ci/src/commands/test.rs b/tools/ci/src/commands/test.rs index ae449f6a8fec2..dec3127a3d58e 100644 --- a/tools/ci/src/commands/test.rs +++ b/tools/ci/src/commands/test.rs @@ -24,6 +24,13 @@ impl Prepare for TestCommand { ), "Please fix failing tests in output above.", ), + PreparedCommand::new::( + cmd!( + sh, + "cargo test bevy_ecs --lib --bins --tests --features bevy_ecs/track_location,bevy_ecs/query_uncached_default {no_fail_fast...} {jobs_ref...} -- {test_threads_ref...}" + ), + "Please fix failing tests in output above.", + ), PreparedCommand::new::( cmd!( sh,