diff --git a/crates/bevy_ecs/hecs/macros/src/lib.rs b/crates/bevy_ecs/hecs/macros/src/lib.rs index b5e054b70d..58df575d26 100644 --- a/crates/bevy_ecs/hecs/macros/src/lib.rs +++ b/crates/bevy_ecs/hecs/macros/src/lib.rs @@ -281,22 +281,24 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream { let mut tokens = TokenStream::new(); let max_queries = 4; let queries = get_idents(|i| format!("Q{}", i), max_queries); + let filters = get_idents(|i| format!("F{}", i), max_queries); let lifetimes = get_lifetimes(|i| format!("'q{}", i), max_queries); let mut query_fns = Vec::new(); let mut query_fn_muts = Vec::new(); for i in 0..max_queries { let query = &queries[i]; let lifetime = &lifetimes[i]; + let filter = &filters[i]; let fn_name = Ident::new(&format!("q{}", i), Span::call_site()); let fn_name_mut = Ident::new(&format!("q{}_mut", i), Span::call_site()); let index = Index::from(i); query_fns.push(quote! { - pub fn #fn_name(&self) -> &Query<#lifetime, #query> { + pub fn #fn_name(&self) -> &Query<#lifetime, #query, #filter> { &self.value.#index } }); query_fn_muts.push(quote! { - pub fn #fn_name_mut(&mut self) -> &mut Query<#lifetime, #query> { + pub fn #fn_name_mut(&mut self) -> &mut Query<#lifetime, #query, #filter> { &mut self.value.#index } }); @@ -304,15 +306,16 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream { for query_count in 1..=max_queries { let query = &queries[0..query_count]; + let filter = &filters[0..query_count]; let lifetime = &lifetimes[0..query_count]; let query_fn = &query_fns[0..query_count]; let query_fn_mut = &query_fn_muts[0..query_count]; tokens.extend(TokenStream::from(quote! { - impl<#(#lifetime,)* #(#query: HecsQuery,)*> QueryTuple for (#(Query<#lifetime, #query>,)*) { + impl<#(#lifetime,)* #(#query: HecsQuery,)* #(#filter: QueryFilter,)*> QueryTuple for (#(Query<#lifetime, #query, #filter>,)*) { unsafe fn new(world: &World, component_access: &TypeAccess) -> Self { ( #( - Query::<#query>::new( + Query::<#query, #filter>::new( std::mem::transmute(world), std::mem::transmute(component_access), ), @@ -322,12 +325,12 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream { fn get_accesses() -> Vec { vec![ - #(<#query::Fetch as Fetch>::access(),)* + #(QueryAccess::union(vec![<#query::Fetch as Fetch>::access(), #filter::access()]),)* ] } } - impl<#(#lifetime,)* #(#query: HecsQuery,)*> QuerySet<(#(Query<#lifetime, #query>,)*)> { + impl<#(#lifetime,)* #(#query: HecsQuery,)* #(#filter: QueryFilter,)*> QuerySet<(#(Query<#lifetime, #query, #filter>,)*)> { #(#query_fn)* #(#query_fn_mut)* } diff --git a/crates/bevy_ecs/hecs/src/access.rs b/crates/bevy_ecs/hecs/src/access.rs index 7d3dd1e538..258e27570d 100644 --- a/crates/bevy_ecs/hecs/src/access.rs +++ b/crates/bevy_ecs/hecs/src/access.rs @@ -282,10 +282,11 @@ impl TypeAccess { #[cfg(test)] mod tests { - use crate::{ArchetypeComponent, Entity, Fetch, Query, TypeAccess, With, World}; + use crate::{ArchetypeComponent, Entity, Fetch, Query, QueryAccess, TypeAccess, World}; use std::vec; struct A; + #[derive(Clone, Eq, PartialEq, Debug)] struct B; struct C; @@ -344,7 +345,7 @@ mod tests { ); let mut a_with_b_type_access = TypeAccess::default(); - as Query>::Fetch::access() + QueryAccess::with::(<&A as Query>::Fetch::access()) .get_world_archetype_access(&world, Some(&mut a_with_b_type_access)); assert_eq!( @@ -353,7 +354,7 @@ mod tests { ); let mut a_with_b_option_c_type_access = TypeAccess::default(); - )> as Query>::Fetch::access() + QueryAccess::with::(<(&A, Option<&mut C>) as Query>::Fetch::access()) .get_world_archetype_access(&world, Some(&mut a_with_b_option_c_type_access)); assert_eq!( diff --git a/crates/bevy_ecs/hecs/src/filter.rs b/crates/bevy_ecs/hecs/src/filter.rs new file mode 100644 index 0000000000..a237f1607a --- /dev/null +++ b/crates/bevy_ecs/hecs/src/filter.rs @@ -0,0 +1,245 @@ +use crate::{archetype::Archetype, Component, QueryAccess}; +use core::{any::TypeId, marker::PhantomData, ptr::NonNull}; +use std::{boxed::Box, vec}; + +pub trait QueryFilter: Sized { + type EntityFilter: EntityFilter; + fn access() -> QueryAccess; + fn get_entity_filter(archetype: &Archetype) -> Option; +} + +pub trait EntityFilter: Sized { + const DANGLING: Self; + + /// # Safety + /// This might access archetype data in an unsafe manner. In general filters should be read-only and they should only access + /// the data they have claimed in `access()`. + unsafe fn matches_entity(&self, _offset: usize) -> bool; +} + +pub struct AnyEntityFilter; + +impl EntityFilter for AnyEntityFilter { + const DANGLING: Self = AnyEntityFilter; + + #[inline] + unsafe fn matches_entity(&self, _offset: usize) -> bool { + true + } +} + +pub struct Or(pub T); + +/// Query transformer that retrieves components of type `T` that have been mutated since the start of the frame. +/// Added components do not count as mutated. +pub struct Mutated(NonNull, PhantomData); + +/// Query transformer that retrieves components of type `T` that have been added since the start of the frame. +pub struct Added(NonNull, PhantomData); + +/// Query transformer that retrieves components of type `T` that have either been mutated or added since the start of the frame. +pub struct Changed(NonNull, NonNull, PhantomData); + +impl QueryFilter for () { + type EntityFilter = AnyEntityFilter; + + fn access() -> QueryAccess { + QueryAccess::None + } + + #[inline] + fn get_entity_filter(_archetype: &Archetype) -> Option { + Some(AnyEntityFilter) + } +} + +impl QueryFilter for Added { + type EntityFilter = Self; + + fn access() -> QueryAccess { + QueryAccess::read::() + } + + #[inline] + fn get_entity_filter(archetype: &Archetype) -> Option { + archetype + .get_type_state(TypeId::of::()) + .map(|state| Added(state.added(), Default::default())) + } +} + +impl EntityFilter for Added { + const DANGLING: Self = Added(NonNull::dangling(), PhantomData::); + + #[inline] + unsafe fn matches_entity(&self, offset: usize) -> bool { + *self.0.as_ptr().add(offset) + } +} + +impl QueryFilter for Mutated { + type EntityFilter = Self; + + fn access() -> QueryAccess { + QueryAccess::read::() + } + + #[inline] + fn get_entity_filter(archetype: &Archetype) -> Option { + archetype + .get_type_state(TypeId::of::()) + .map(|state| Mutated(state.mutated(), Default::default())) + } +} + +impl EntityFilter for Mutated { + const DANGLING: Self = Mutated(NonNull::dangling(), PhantomData::); + + unsafe fn matches_entity(&self, offset: usize) -> bool { + *self.0.as_ptr().add(offset) + } +} + +impl QueryFilter for Changed { + type EntityFilter = Self; + + fn access() -> QueryAccess { + QueryAccess::read::() + } + + #[inline] + fn get_entity_filter(archetype: &Archetype) -> Option { + archetype + .get_type_state(TypeId::of::()) + .map(|state| Changed(state.added(), state.mutated(), Default::default())) + } +} + +impl EntityFilter for Changed { + const DANGLING: Self = Changed(NonNull::dangling(), NonNull::dangling(), PhantomData::); + + #[inline] + unsafe fn matches_entity(&self, offset: usize) -> bool { + *self.0.as_ptr().add(offset) || *self.1.as_ptr().add(offset) + } +} + +pub struct Without(PhantomData); + +impl QueryFilter for Without { + type EntityFilter = AnyEntityFilter; + + fn access() -> QueryAccess { + QueryAccess::without::(QueryAccess::None) + } + + #[inline] + fn get_entity_filter(archetype: &Archetype) -> Option { + if archetype.has_type(TypeId::of::()) { + None + } else { + Some(AnyEntityFilter) + } + } +} + +pub struct With(PhantomData); + +impl QueryFilter for With { + type EntityFilter = AnyEntityFilter; + + fn access() -> QueryAccess { + QueryAccess::with::(QueryAccess::None) + } + + #[inline] + fn get_entity_filter(archetype: &Archetype) -> Option { + if archetype.has_type(TypeId::of::()) { + Some(AnyEntityFilter) + } else { + None + } + } +} + +macro_rules! impl_query_filter_tuple { + ($($filter: ident),*) => { + #[allow(unused_variables)] + #[allow(non_snake_case)] + impl<$($filter: QueryFilter),*> QueryFilter for ($($filter,)*) { + type EntityFilter = ($($filter::EntityFilter,)*); + + fn access() -> QueryAccess { + QueryAccess::union(vec![ + $($filter::access(),)+ + ]) + } + + fn get_entity_filter(archetype: &Archetype) -> Option { + Some(($($filter::get_entity_filter(archetype)?,)*)) + } + + } + + #[allow(unused_variables)] + #[allow(non_snake_case)] + impl<$($filter: EntityFilter),*> EntityFilter for ($($filter,)*) { + const DANGLING: Self = ($($filter::DANGLING,)*); + unsafe fn matches_entity(&self, offset: usize) -> bool { + let ($($filter,)*) = self; + true $(&& $filter.matches_entity(offset))* + } + } + + #[allow(unused_variables)] + #[allow(non_snake_case)] + impl<$($filter: QueryFilter),*> QueryFilter for Or<($($filter,)*)> { + type EntityFilter = Or<($(Option<<$filter as QueryFilter>::EntityFilter>,)*)>; + fn access() -> QueryAccess { + QueryAccess::union(vec![ + $(QueryAccess::Optional(Box::new($filter::access())),)+ + ]) + } + + fn get_entity_filter(archetype: &Archetype) -> Option { + let mut matches_something = false; + $( + let $filter = $filter::get_entity_filter(archetype); + matches_something = matches_something || $filter.is_some(); + )* + if matches_something { + Some(Or(($($filter,)*))) + } else { + None + } + } + + } + #[allow(unused_variables)] + #[allow(non_snake_case)] + impl<$($filter: EntityFilter),*> EntityFilter for Or<($(Option<$filter>,)*)> { + const DANGLING: Self = Or(($(Some($filter::DANGLING),)*)); + unsafe fn matches_entity(&self, offset: usize) -> bool { + let Or(($($filter,)*)) = self; + false $(|| $filter.as_ref().map_or(false, |filter|filter.matches_entity(offset)))* + } + } + }; +} + +impl_query_filter_tuple!(A); +impl_query_filter_tuple!(A, B); +impl_query_filter_tuple!(A, B, C); +impl_query_filter_tuple!(A, B, C, D); +impl_query_filter_tuple!(A, B, C, D, E); +impl_query_filter_tuple!(A, B, C, D, E, F); +impl_query_filter_tuple!(A, B, C, D, E, F, G); +impl_query_filter_tuple!(A, B, C, D, E, F, G, H); +impl_query_filter_tuple!(A, B, C, D, E, F, G, H, I); +impl_query_filter_tuple!(A, B, C, D, E, F, G, H, I, J); +impl_query_filter_tuple!(A, B, C, D, E, F, G, H, I, J, K); +impl_query_filter_tuple!(A, B, C, D, E, F, G, H, I, J, K, L); +impl_query_filter_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M); +impl_query_filter_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N); +impl_query_filter_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O); +impl_query_filter_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P); diff --git a/crates/bevy_ecs/hecs/src/lib.rs b/crates/bevy_ecs/hecs/src/lib.rs index d72b2506c8..a754857f44 100644 --- a/crates/bevy_ecs/hecs/src/lib.rs +++ b/crates/bevy_ecs/hecs/src/lib.rs @@ -69,6 +69,7 @@ mod borrow; mod bundle; mod entities; mod entity_builder; +mod filter; mod query; #[cfg(feature = "serde")] mod serde; @@ -80,10 +81,8 @@ pub use borrow::{AtomicBorrow, Ref, RefMut}; pub use bundle::{Bundle, DynamicBundle, MissingComponent}; pub use entities::{Entity, EntityReserver, Location, NoSuchEntity}; pub use entity_builder::{BuiltEntity, EntityBuilder}; -pub use query::{ - Added, Batch, BatchedIter, Changed, Mut, Mutated, Or, Query, QueryIter, ReadOnlyFetch, With, - Without, -}; +pub use filter::{Added, Changed, EntityFilter, Mutated, Or, QueryFilter, With, Without}; +pub use query::{Batch, BatchedIter, Mut, Query, QueryIter, ReadOnlyFetch}; pub use world::{ArchetypesGeneration, Component, ComponentError, SpawnBatchIter, World}; // Unstable implementation details needed by the macros diff --git a/crates/bevy_ecs/hecs/src/query.rs b/crates/bevy_ecs/hecs/src/query.rs index 93445ee86f..d4c494a998 100644 --- a/crates/bevy_ecs/hecs/src/query.rs +++ b/crates/bevy_ecs/hecs/src/query.rs @@ -14,16 +14,17 @@ // modified by Bevy contributors +use crate::{ + access::QueryAccess, archetype::Archetype, Component, Entity, EntityFilter, MissingComponent, + QueryFilter, +}; use core::{ marker::PhantomData, ops::{Deref, DerefMut}, ptr::NonNull, }; - use std::vec; -use crate::{access::QueryAccess, archetype::Archetype, Component, Entity, MissingComponent}; - /// A collection of component types to fetch from a `World` pub trait Query { #[doc(hidden)] @@ -33,9 +34,6 @@ pub trait Query { /// A fetch that is read only. This should only be implemented for read-only fetches. pub unsafe trait ReadOnlyFetch {} -/// A fetch that will always match every entity in an archetype (aka Fetch::should_skip always returns false) -pub trait UnfilteredFetch {} - /// Streaming iterators over contiguous homogeneous ranges of components pub trait Fetch<'a>: Sized { /// Type of value to be fetched @@ -54,14 +52,6 @@ pub trait Fetch<'a>: Sized { /// `offset` must be in bounds of `archetype` unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option; - /// if this returns true, the nth item should be skipped during iteration - /// - /// # Safety - /// shouldn't be called if there is no current item - unsafe fn should_skip(&self, _n: usize) -> bool { - false - } - /// Access the `n`th item in this archetype without bounds checking /// /// # Safety @@ -75,7 +65,6 @@ pub trait Fetch<'a>: Sized { #[derive(Copy, Clone, Debug)] pub struct EntityFetch(NonNull); unsafe impl ReadOnlyFetch for EntityFetch {} -impl UnfilteredFetch for EntityFetch {} impl Query for Entity { type Fetch = EntityFetch; @@ -112,7 +101,6 @@ impl<'a, T: Component> Query for &'a T { pub struct FetchRead(NonNull); unsafe impl ReadOnlyFetch for FetchRead {} -impl UnfilteredFetch for FetchRead {} impl<'a, T: Component> Fetch<'a> for FetchRead { type Item = &'a T; @@ -197,7 +185,6 @@ impl<'a, T: Component> Query for Mut<'a, T> { } #[doc(hidden)] pub struct FetchMut(NonNull, NonNull); -impl UnfilteredFetch for FetchMut {} impl<'a, T: Component> Fetch<'a> for FetchMut { type Item = Mut<'a, T>; @@ -229,257 +216,9 @@ impl<'a, T: Component> Fetch<'a> for FetchMut { } } -macro_rules! impl_or_query { - ( $( $T:ident ),+ ) => { - impl<$( $T: Query ),+> Query for Or<($( $T ),+)> { - type Fetch = FetchOr<($( $T::Fetch ),+)>; - } - - impl<'a, $( $T: Fetch<'a> ),+> Fetch<'a> for FetchOr<($( $T ),+)> { - type Item = ($( $T::Item ),+); - - const DANGLING: Self = Self(($( $T::DANGLING ),+)); - - fn access() -> QueryAccess { - QueryAccess::union(vec![ - $($T::access(),)+ - ]) - } - - - unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { - Some(Self(( $( $T::get(archetype, offset)?),+ ))) - } - - #[allow(non_snake_case)] - unsafe fn fetch(&self, n: usize) -> Self::Item { - let ($( $T ),+) = &self.0; - ($( $T.fetch(n) ),+) - } - - #[allow(non_snake_case)] - unsafe fn should_skip(&self, n: usize) -> bool { - let ($( $T ),+) = &self.0; - true $( && $T.should_skip(n) )+ - } - } - - unsafe impl<$( $T: ReadOnlyFetch ),+> ReadOnlyFetch for Or<($( $T ),+)> {} - unsafe impl<$( $T: ReadOnlyFetch ),+> ReadOnlyFetch for FetchOr<($( $T ),+)> {} - }; -} - -impl_or_query!(Q1, Q2); -impl_or_query!(Q1, Q2, Q3); -impl_or_query!(Q1, Q2, Q3, Q4); -impl_or_query!(Q1, Q2, Q3, Q4, Q5); -impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6); -impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6, Q7); -impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8); -impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9); -impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, Q10); - -/// Query transformer performing a logical or on a pair of queries -/// Intended to be used on Mutated or Changed queries. -/// # Example -/// ``` -/// # use bevy_hecs::*; -/// let mut world = World::new(); -/// world.spawn((123, true, 1., Some(1))); -/// world.spawn((456, false, 2., Some(0))); -/// for mut b in world.query_mut::>().skip(1).take(1) { -/// *b += 1; -/// } -/// let components = world -/// .query_mut::, Mutated, Mutated, Mutated>)>>() -/// .map(|(b, i, f, o)| (*b, *i)) -/// .collect::>(); -/// assert_eq!(components, &[(false, 457)]); -/// ``` -pub struct Or(pub T); -//pub struct Or(PhantomData<(Q1, Q2, Q3)>); - -#[doc(hidden)] -pub struct FetchOr(T); - -/// Query transformer that retrieves components of type `T` that have been mutated since the start of the frame. -/// Added components do not count as mutated. -pub struct Mutated<'a, T> { - value: &'a T, -} - -impl<'a, T: Component> Deref for Mutated<'a, T> { - type Target = T; - - #[inline] - fn deref(&self) -> &T { - self.value - } -} - -impl<'a, T: Component> Query for Mutated<'a, T> { - type Fetch = FetchMutated; -} - -#[doc(hidden)] -pub struct FetchMutated(NonNull, NonNull); -unsafe impl ReadOnlyFetch for FetchMutated {} - -impl<'a, T: Component> Fetch<'a> for FetchMutated { - type Item = Mutated<'a, T>; - - const DANGLING: Self = Self(NonNull::dangling(), NonNull::dangling()); - - #[inline] - fn access() -> QueryAccess { - QueryAccess::read::() - } - - unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { - archetype - .get_with_type_state::() - .map(|(components, type_state)| { - Self( - NonNull::new_unchecked(components.as_ptr().add(offset)), - NonNull::new_unchecked(type_state.mutated().as_ptr().add(offset)), - ) - }) - } - - unsafe fn should_skip(&self, n: usize) -> bool { - // skip if the current item wasn't mutated - !*self.1.as_ptr().add(n) - } - - #[inline] - unsafe fn fetch(&self, n: usize) -> Self::Item { - Mutated { - value: &*self.0.as_ptr().add(n), - } - } -} - -/// Query transformer that retrieves components of type `T` that have been added since the start of the frame. -pub struct Added<'a, T> { - value: &'a T, -} - -impl<'a, T: Component> Deref for Added<'a, T> { - type Target = T; - - #[inline] - fn deref(&self) -> &T { - self.value - } -} - -impl<'a, T: Component> Query for Added<'a, T> { - type Fetch = FetchAdded; -} - -#[doc(hidden)] -pub struct FetchAdded(NonNull, NonNull); -unsafe impl ReadOnlyFetch for FetchAdded {} - -impl<'a, T: Component> Fetch<'a> for FetchAdded { - type Item = Added<'a, T>; - - const DANGLING: Self = Self(NonNull::dangling(), NonNull::dangling()); - - #[inline] - fn access() -> QueryAccess { - QueryAccess::read::() - } - - unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { - archetype - .get_with_type_state::() - .map(|(components, type_state)| { - Self( - NonNull::new_unchecked(components.as_ptr().add(offset)), - NonNull::new_unchecked(type_state.added().as_ptr().add(offset)), - ) - }) - } - - unsafe fn should_skip(&self, n: usize) -> bool { - // skip if the current item wasn't added - !*self.1.as_ptr().add(n) - } - - #[inline] - unsafe fn fetch(&self, n: usize) -> Self::Item { - Added { - value: &*self.0.as_ptr().add(n), - } - } -} - -/// Query transformer that retrieves components of type `T` that have either been mutated or added since the start of the frame. -pub struct Changed<'a, T> { - value: &'a T, -} - -impl<'a, T: Component> Deref for Changed<'a, T> { - type Target = T; - - #[inline] - fn deref(&self) -> &T { - self.value - } -} - -impl<'a, T: Component> Query for Changed<'a, T> { - type Fetch = FetchChanged; -} - -#[doc(hidden)] -pub struct FetchChanged(NonNull, NonNull, NonNull); -unsafe impl ReadOnlyFetch for FetchChanged {} - -impl<'a, T: Component> Fetch<'a> for FetchChanged { - type Item = Changed<'a, T>; - - const DANGLING: Self = Self( - NonNull::dangling(), - NonNull::dangling(), - NonNull::dangling(), - ); - - #[inline] - fn access() -> QueryAccess { - QueryAccess::read::() - } - - unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { - archetype - .get_with_type_state::() - .map(|(components, type_state)| { - Self( - NonNull::new_unchecked(components.as_ptr().add(offset)), - NonNull::new_unchecked(type_state.added().as_ptr().add(offset)), - NonNull::new_unchecked(type_state.mutated().as_ptr().add(offset)), - ) - }) - } - - unsafe fn should_skip(&self, n: usize) -> bool { - // skip if the current item wasn't added or mutated - !*self.1.as_ptr().add(n) && !*self.2.as_ptr().add(n) - } - - #[inline] - unsafe fn fetch(&self, n: usize) -> Self::Item { - Changed { - value: &*self.0.as_ptr().add(n), - } - } -} - #[doc(hidden)] pub struct TryFetch(Option); unsafe impl ReadOnlyFetch for TryFetch where T: ReadOnlyFetch {} -impl UnfilteredFetch for TryFetch where T: UnfilteredFetch {} impl<'a, T: Fetch<'a>> Fetch<'a> for TryFetch { type Item = Option; @@ -498,142 +237,27 @@ impl<'a, T: Fetch<'a>> Fetch<'a> for TryFetch { unsafe fn fetch(&self, n: usize) -> Option { Some(self.0.as_ref()?.fetch(n)) } - - unsafe fn should_skip(&self, n: usize) -> bool { - self.0.as_ref().map_or(false, |fetch| fetch.should_skip(n)) - } } -/// Query transformer skipping entities that have a `T` component -/// -/// See also `QueryBorrow::without`. -/// -/// # Example -/// ``` -/// # use bevy_hecs::*; -/// let mut world = World::new(); -/// let a = world.spawn((123, true, "abc")); -/// let b = world.spawn((456, false)); -/// let c = world.spawn((42, "def")); -/// let entities = world.query::>() -/// .map(|(e, &i)| (e, i)) -/// .collect::>(); -/// assert_eq!(entities, &[(c, 42)]); -/// ``` -pub struct Without(PhantomData<(Q, fn(T))>); - -impl Query for Without { - type Fetch = FetchWithout; -} - -#[doc(hidden)] -pub struct FetchWithout(F, PhantomData); -unsafe impl<'a, T: Component, F: Fetch<'a>> ReadOnlyFetch for FetchWithout where - F: ReadOnlyFetch -{ -} -impl UnfilteredFetch for FetchWithout where F: UnfilteredFetch {} - -impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWithout { - type Item = F::Item; - - const DANGLING: Self = Self(F::DANGLING, PhantomData); - - #[inline] - fn access() -> QueryAccess { - QueryAccess::without::(F::access()) - } - - unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { - if archetype.has::() { - return None; - } - Some(Self(F::get(archetype, offset)?, PhantomData)) - } - - unsafe fn fetch(&self, n: usize) -> F::Item { - self.0.fetch(n) - } - - unsafe fn should_skip(&self, n: usize) -> bool { - self.0.should_skip(n) - } -} - -/// Query transformer skipping entities that do not have a `T` component -/// -/// See also `QueryBorrow::with`. -/// -/// # Example -/// ``` -/// # use bevy_hecs::*; -/// let mut world = World::new(); -/// let a = world.spawn((123, true, "abc")); -/// let b = world.spawn((456, false)); -/// let c = world.spawn((42, "def")); -/// let entities = world.query::>() -/// .map(|(e, &i)| (e, i)) -/// .collect::>(); -/// assert_eq!(entities.len(), 2); -/// assert!(entities.contains(&(a, 123))); -/// assert!(entities.contains(&(b, 456))); -/// ``` -pub struct With(PhantomData<(Q, fn(T))>); - -impl Query for With { - type Fetch = FetchWith; -} - -#[doc(hidden)] -pub struct FetchWith(F, PhantomData); -unsafe impl<'a, T: Component, F: Fetch<'a>> ReadOnlyFetch for FetchWith where F: ReadOnlyFetch {} -impl UnfilteredFetch for FetchWith where F: UnfilteredFetch {} - -impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWith { - type Item = F::Item; - - const DANGLING: Self = Self(F::DANGLING, PhantomData); - - #[inline] - fn access() -> QueryAccess { - QueryAccess::with::(F::access()) - } - - unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { - if !archetype.has::() { - return None; - } - Some(Self(F::get(archetype, offset)?, PhantomData)) - } - - unsafe fn fetch(&self, n: usize) -> F::Item { - self.0.fetch(n) - } - - unsafe fn should_skip(&self, n: usize) -> bool { - self.0.should_skip(n) - } -} - -struct ChunkInfo { +struct ChunkInfo { fetch: Q::Fetch, + filter: F::EntityFilter, len: usize, } /// Iterator over the set of entities with the components in `Q` -pub struct QueryIter<'w, Q: Query> { +pub struct QueryIter<'w, Q: Query, F: QueryFilter> { archetypes: &'w [Archetype], archetype_index: usize, - chunk_info: ChunkInfo, + chunk_info: ChunkInfo, chunk_position: usize, } -impl<'w, Q: Query> QueryIter<'w, Q> { - // #[allow(clippy::declare_interior_mutable_const)] // no trait bounds on const fns - // const EMPTY: Q::Fetch = Q::Fetch::DANGLING; - const EMPTY: ChunkInfo = ChunkInfo { +impl<'w, Q: Query, F: QueryFilter> QueryIter<'w, Q, F> { + const EMPTY: ChunkInfo = ChunkInfo { fetch: Q::Fetch::DANGLING, len: 0, + filter: F::EntityFilter::DANGLING, }; /// Creates a new QueryIter @@ -648,7 +272,7 @@ impl<'w, Q: Query> QueryIter<'w, Q> { } } -impl<'w, Q: Query> Iterator for QueryIter<'w, Q> { +impl<'w, Q: Query, F: QueryFilter> Iterator for QueryIter<'w, Q, F> { type Item = >::Item; #[inline] @@ -660,18 +284,21 @@ impl<'w, Q: Query> Iterator for QueryIter<'w, Q> { self.archetype_index += 1; self.chunk_position = 0; self.chunk_info = Q::Fetch::get(archetype, 0) - .map(|fetch| ChunkInfo { - fetch, - len: archetype.len(), + .and_then(|fetch| { + Some(ChunkInfo { + fetch, + len: archetype.len(), + filter: F::get_entity_filter(archetype)?, + }) }) .unwrap_or(Self::EMPTY); continue; } - if self + if !self .chunk_info - .fetch - .should_skip(self.chunk_position as usize) + .filter + .matches_entity(self.chunk_position as usize) { self.chunk_position += 1; continue; @@ -687,10 +314,7 @@ impl<'w, Q: Query> Iterator for QueryIter<'w, Q> { // if the Fetch is an UnfilteredFetch, then we can cheaply compute the length of the query by getting // the length of each matching archetype -impl<'w, Q: Query> ExactSizeIterator for QueryIter<'w, Q> -where - Q::Fetch: UnfilteredFetch, -{ +impl<'w, Q: Query> ExactSizeIterator for QueryIter<'w, Q, ()> { fn len(&self) -> usize { self.archetypes .iter() @@ -700,20 +324,21 @@ where } } -struct ChunkIter { +struct ChunkIter { fetch: Q::Fetch, + filter: F::EntityFilter, position: usize, len: usize, } -impl ChunkIter { +impl ChunkIter { unsafe fn next<'a>(&mut self) -> Option<>::Item> { loop { if self.position == self.len { return None; } - if self.fetch.should_skip(self.position as usize) { + if !self.filter.matches_entity(self.position as usize) { self.position += 1; continue; } @@ -726,15 +351,15 @@ impl ChunkIter { } /// Batched version of `QueryIter` -pub struct BatchedIter<'w, Q: Query> { +pub struct BatchedIter<'w, Q: Query, F: QueryFilter> { archetypes: &'w [Archetype], archetype_index: usize, batch_size: usize, batch: usize, - _marker: PhantomData, + _marker: PhantomData<(Q, F)>, } -impl<'w, Q: Query> BatchedIter<'w, Q> { +impl<'w, Q: Query, F: QueryFilter> BatchedIter<'w, Q, F> { pub(crate) fn new(archetypes: &'w [Archetype], batch_size: usize) -> Self { Self { archetypes, @@ -746,11 +371,11 @@ impl<'w, Q: Query> BatchedIter<'w, Q> { } } -unsafe impl<'w, Q: Query> Send for BatchedIter<'w, Q> {} -unsafe impl<'w, Q: Query> Sync for BatchedIter<'w, Q> {} +unsafe impl<'w, Q: Query, F: QueryFilter> Send for BatchedIter<'w, Q, F> {} +unsafe impl<'w, Q: Query, F: QueryFilter> Sync for BatchedIter<'w, Q, F> {} -impl<'w, Q: Query> Iterator for BatchedIter<'w, Q> { - type Item = Batch<'w, Q>; +impl<'w, Q: Query, F: QueryFilter> Iterator for BatchedIter<'w, Q, F> { + type Item = Batch<'w, Q, F>; fn next(&mut self) -> Option { loop { @@ -761,7 +386,10 @@ impl<'w, Q: Query> Iterator for BatchedIter<'w, Q> { self.batch = 0; continue; } - if let Some(fetch) = unsafe { Q::Fetch::get(archetype, offset) } { + if let (Some(fetch), Some(filter)) = ( + unsafe { Q::Fetch::get(archetype, offset) }, + F::get_entity_filter(archetype), + ) { self.batch += 1; return Some(Batch { _marker: PhantomData, @@ -769,6 +397,7 @@ impl<'w, Q: Query> Iterator for BatchedIter<'w, Q> { fetch, position: 0, len: self.batch_size.min(archetype.len() - offset), + filter, }, }); } else { @@ -784,12 +413,12 @@ impl<'w, Q: Query> Iterator for BatchedIter<'w, Q> { } /// A sequence of entities yielded by `BatchedIter` -pub struct Batch<'q, Q: Query> { +pub struct Batch<'q, Q: Query, F: QueryFilter> { _marker: PhantomData<&'q ()>, - state: ChunkIter, + state: ChunkIter, } -impl<'q, 'w, Q: Query> Iterator for Batch<'q, Q> { +impl<'q, 'w, Q: Query, F: QueryFilter> Iterator for Batch<'q, Q, F> { type Item = >::Item; fn next(&mut self) -> Option { @@ -798,8 +427,8 @@ impl<'q, 'w, Q: Query> Iterator for Batch<'q, Q> { } } -unsafe impl<'q, Q: Query> Send for Batch<'q, Q> {} -unsafe impl<'q, Q: Query> Sync for Batch<'q, Q> {} +unsafe impl<'q, Q: Query, F: QueryFilter> Send for Batch<'q, Q, F> {} +unsafe impl<'q, Q: Query, F: QueryFilter> Sync for Batch<'q, Q, F> {} macro_rules! tuple_impl { ($($name: ident),*) => { @@ -825,13 +454,6 @@ macro_rules! tuple_impl { let ($($name,)*) = self; ($($name.fetch(n),)*) } - - #[allow(unused_variables)] - unsafe fn should_skip(&self, n: usize) -> bool { - #[allow(non_snake_case)] - let ($($name,)*) = self; - $($name.should_skip(n)||)* false - } } impl<$($name: Query),*> Query for ($($name,)*) { @@ -839,7 +461,6 @@ macro_rules! tuple_impl { } unsafe impl<$($name: ReadOnlyFetch),*> ReadOnlyFetch for ($($name,)*) {} - impl<$($name: UnfilteredFetch),*> UnfilteredFetch for ($($name,)*) {} }; } @@ -847,7 +468,7 @@ smaller_tuples_too!(tuple_impl, O, N, M, L, K, J, I, H, G, F, E, D, C, B, A); #[cfg(test)] mod tests { - use crate::{Entity, Mut, Mutated, World}; + use crate::{Added, Changed, Entity, Mut, Mutated, Or, World}; use std::{vec, vec::Vec}; use super::*; @@ -863,8 +484,7 @@ mod tests { fn get_added(world: &World) -> Vec { world - .query::<(Added, Entity)>() - .map(|(_added, e)| e) + .query_filtered::>() .collect::>() }; @@ -880,8 +500,7 @@ mod tests { assert_eq!(get_added::(&world), vec![e2]); let added = world - .query::<(Entity, Added, Added)>() - .map(|a| a.0) + .query_filtered::, Added)>() .collect::>(); assert_eq!(added, vec![e2]); } @@ -900,24 +519,21 @@ mod tests { } } - fn get_mutated_a(world: &mut World) -> Vec { - world - .query_mut::<(Mutated, Entity)>() - .map(|(_a, e)| e) - .collect::>() + fn get_filtered(world: &mut World) -> Vec { + world.query_filtered::().collect::>() }; - assert_eq!(get_mutated_a(&mut world), vec![e1, e3]); + assert_eq!(get_filtered::>(&mut world), vec![e1, e3]); // ensure changing an entity's archetypes also moves its mutated state world.insert(e1, (C,)).unwrap(); - assert_eq!(get_mutated_a(&mut world), vec![e3, e1], "changed entities list should not change (although the order will due to archetype moves)"); + assert_eq!(get_filtered::>(&mut world), vec![e3, e1], "changed entities list should not change (although the order will due to archetype moves)"); // spawning a new A entity should not change existing mutated state world.insert(e1, (A(0), B)).unwrap(); assert_eq!( - get_mutated_a(&mut world), + get_filtered::>(&mut world), vec![e3, e1], "changed entities list should not change" ); @@ -925,7 +541,7 @@ mod tests { // removing an unchanged entity should not change mutated state world.despawn(e2).unwrap(); assert_eq!( - get_mutated_a(&mut world), + get_filtered::>(&mut world), vec![e3, e1], "changed entities list should not change" ); @@ -933,26 +549,23 @@ mod tests { // removing a changed entity should remove it from enumeration world.despawn(e1).unwrap(); assert_eq!( - get_mutated_a(&mut world), + get_filtered::>(&mut world), vec![e3], "e1 should no longer be returned" ); world.clear_trackers(); - assert!(world - .query_mut::<(Mutated, Entity)>() - .map(|(_a, e)| e) - .next() - .is_none()); + assert!(get_filtered::>(&mut world).is_empty()); let e4 = world.spawn(()); world.insert_one(e4, A(0)).unwrap(); - assert!(get_mutated_a(&mut world).is_empty()); + assert!(get_filtered::>(&mut world).is_empty()); + assert_eq!(get_filtered::>(&mut world), vec![e4]); world.insert_one(e4, A(1)).unwrap(); - assert_eq!(get_mutated_a(&mut world), vec![e4]); + assert_eq!(get_filtered::>(&mut world), vec![e4]); world.clear_trackers(); @@ -961,19 +574,10 @@ mod tests { // non existing components even when changing archetype. world.insert(e4, (A(0), B(0))).unwrap(); - let added_a = world.query::<(Added, Entity)>().map(|(_, e)| e).next(); - assert!(added_a.is_none()); - - assert_eq!(get_mutated_a(&mut world), vec![e4]); - - let added_b = world.query::<(Added, Entity)>().map(|(_, e)| e).next(); - assert!(added_b.is_some()); - - let mutated_b = world - .query_mut::<(Mutated, Entity)>() - .map(|(_, e)| e) - .next(); - assert!(mutated_b.is_none()); + assert!(get_filtered::>(&mut world).is_empty()); + assert_eq!(get_filtered::>(&mut world), vec![e4]); + assert_eq!(get_filtered::>(&mut world), vec![e4]); + assert!(get_filtered::>(&mut world).is_empty()); } #[test] @@ -992,8 +596,7 @@ mod tests { } let a_b_mutated = world - .query_mut::<(Mutated, Mutated, Entity)>() - .map(|(_a, _b, e)| e) + .query_filtered_mut::, Mutated)>() .collect::>(); assert_eq!(a_b_mutated, vec![e2]); } @@ -1016,8 +619,7 @@ mod tests { } let a_b_mutated = world - .query_mut::<(Or<(Mutated, Mutated)>, Entity)>() - .map(|((_a, _b), e)| e) + .query_filtered_mut::, Mutated)>>() .collect::>(); // e1 has mutated A, e3 has mutated B, e2 has mutated A and B, _e4 has no mutated component assert_eq!(a_b_mutated, vec![e1, e2, e3]); @@ -1030,8 +632,7 @@ mod tests { fn get_changed(world: &World) -> Vec { world - .query::<(Changed, Entity)>() - .map(|(_a, e)| e) + .query_filtered::>() .collect::>() }; assert_eq!(get_changed(&world), vec![e1]); diff --git a/crates/bevy_ecs/hecs/src/world.rs b/crates/bevy_ecs/hecs/src/world.rs index 8f3768f7ea..0f32f08e88 100644 --- a/crates/bevy_ecs/hecs/src/world.rs +++ b/crates/bevy_ecs/hecs/src/world.rs @@ -15,8 +15,8 @@ // modified by Bevy contributors use crate::{ - alloc::vec::Vec, borrow::EntityRef, query::ReadOnlyFetch, BatchedIter, EntityReserver, Fetch, - Mut, QueryIter, RefMut, + alloc::vec::Vec, borrow::EntityRef, filter::EntityFilter, query::ReadOnlyFetch, BatchedIter, + EntityReserver, Fetch, Mut, QueryFilter, QueryIter, RefMut, }; use bevy_utils::{HashMap, HashSet}; use core::{any::TypeId, fmt, mem, ptr}; @@ -250,7 +250,16 @@ impl World { /// assert!(entities.contains(&(b, 456, false))); /// ``` #[inline] - pub fn query(&self) -> QueryIter<'_, Q> + pub fn query(&self) -> QueryIter<'_, Q, ()> + where + Q::Fetch: ReadOnlyFetch, + { + // SAFE: read-only access to world and read only query prevents mutable access + unsafe { self.query_unchecked() } + } + + #[inline] + pub fn query_filtered(&self) -> QueryIter<'_, Q, F> where Q::Fetch: ReadOnlyFetch, { @@ -283,7 +292,13 @@ impl World { /// assert!(entities.contains(&(b, 456, false))); /// ``` #[inline] - pub fn query_mut(&mut self) -> QueryIter<'_, Q> { + pub fn query_mut(&mut self) -> QueryIter<'_, Q, ()> { + // SAFE: unique mutable access + unsafe { self.query_unchecked() } + } + + #[inline] + pub fn query_filtered_mut(&mut self) -> QueryIter<'_, Q, F> { // SAFE: unique mutable access unsafe { self.query_unchecked() } } @@ -291,7 +306,19 @@ impl World { /// Like `query`, but instead of returning a single iterator it returns a "batched iterator", /// where each batch is `batch_size`. This is generally used for parallel iteration. #[inline] - pub fn query_batched(&self, batch_size: usize) -> BatchedIter<'_, Q> + pub fn query_batched(&self, batch_size: usize) -> BatchedIter<'_, Q, ()> + where + Q::Fetch: ReadOnlyFetch, + { + // SAFE: read-only access to world and read only query prevents mutable access + unsafe { self.query_batched_unchecked(batch_size) } + } + + #[inline] + pub fn query_batched_filtered( + &self, + batch_size: usize, + ) -> BatchedIter<'_, Q, F> where Q::Fetch: ReadOnlyFetch, { @@ -302,7 +329,16 @@ impl World { /// Like `query`, but instead of returning a single iterator it returns a "batched iterator", /// where each batch is `batch_size`. This is generally used for parallel iteration. #[inline] - pub fn query_batched_mut(&mut self, batch_size: usize) -> BatchedIter<'_, Q> { + pub fn query_batched_mut(&mut self, batch_size: usize) -> BatchedIter<'_, Q, ()> { + // SAFE: unique mutable access + unsafe { self.query_batched_unchecked(batch_size) } + } + + #[inline] + pub fn query_batched_filtered_mut( + &mut self, + batch_size: usize, + ) -> BatchedIter<'_, Q, F> { // SAFE: unique mutable access unsafe { self.query_batched_unchecked(batch_size) } } @@ -321,7 +357,7 @@ impl World { /// This does not check for mutable query correctness. To be safe, make sure mutable queries /// have unique access to the components they query. #[inline] - pub unsafe fn query_unchecked(&self) -> QueryIter<'_, Q> { + pub unsafe fn query_unchecked(&self) -> QueryIter<'_, Q, F> { QueryIter::new(&self.archetypes) } @@ -332,10 +368,10 @@ impl World { /// This does not check for mutable query correctness. To be safe, make sure mutable queries /// have unique access to the components they query. #[inline] - pub unsafe fn query_batched_unchecked( + pub unsafe fn query_batched_unchecked( &self, batch_size: usize, - ) -> BatchedIter<'_, Q> { + ) -> BatchedIter<'_, Q, F> { BatchedIter::new(&self.archetypes, batch_size) } @@ -361,7 +397,19 @@ impl World { Q::Fetch: ReadOnlyFetch, { // SAFE: read-only access to world and read only query prevents mutable access - unsafe { self.query_one_unchecked::(entity) } + unsafe { self.query_one_unchecked::(entity) } + } + + #[inline] + pub fn query_one_filtered( + &self, + entity: Entity, + ) -> Result<::Item, NoSuchEntity> + where + Q::Fetch: ReadOnlyFetch, + { + // SAFE: read-only access to world and read only query prevents mutable access + unsafe { self.query_one_unchecked::(entity) } } /// Prepare a query against a single entity @@ -384,7 +432,16 @@ impl World { entity: Entity, ) -> Result<::Item, NoSuchEntity> { // SAFE: unique mutable access to world - unsafe { self.query_one_unchecked::(entity) } + unsafe { self.query_one_unchecked::(entity) } + } + + #[inline] + pub fn query_one_filtered_mut( + &mut self, + entity: Entity, + ) -> Result<::Item, NoSuchEntity> { + // SAFE: unique mutable access to world + unsafe { self.query_one_unchecked::(entity) } } /// Prepare a query against a single entity, without checking the safety of mutable queries @@ -395,15 +452,22 @@ impl World { /// This does not check for mutable query correctness. To be safe, make sure mutable queries /// have unique access to the components they query. #[inline] - pub unsafe fn query_one_unchecked( + pub unsafe fn query_one_unchecked( &self, entity: Entity, ) -> Result<::Item, NoSuchEntity> { let loc = self.entities.get(entity)?; - ::get(&self.archetypes[loc.archetype as usize], 0) - .filter(|fetch| !fetch.should_skip(loc.index)) - .map(|fetch| fetch.fetch(loc.index)) - .ok_or(NoSuchEntity) + let archetype = &self.archetypes[loc.archetype as usize]; + let matches_filter = F::get_entity_filter(archetype) + .map(|entity_filter| entity_filter.matches_entity(loc.index)) + .unwrap_or(false); + if matches_filter { + ::get(archetype, 0) + .map(|fetch| fetch.fetch(loc.index)) + .ok_or(NoSuchEntity) + } else { + Err(NoSuchEntity) + } } /// Borrow the `T` component of `entity` diff --git a/crates/bevy_ecs/hecs/tests/tests.rs b/crates/bevy_ecs/hecs/tests/tests.rs index 64cc821d99..e0f9920ee9 100644 --- a/crates/bevy_ecs/hecs/tests/tests.rs +++ b/crates/bevy_ecs/hecs/tests/tests.rs @@ -251,7 +251,6 @@ fn query_batched() { .flat_map(|x| x) .map(|e| e) .collect::>(); - dbg!(&entities); assert_eq!(entities.len(), 3); assert!(entities.contains(&a)); assert!(entities.contains(&b)); @@ -355,22 +354,22 @@ fn added_tracking() { let a = world.spawn((123,)); assert_eq!(world.query::<&i32>().count(), 1); - assert_eq!(world.query::>().count(), 1); + assert_eq!(world.query_filtered::<(), Added>().count(), 1); assert_eq!(world.query_mut::<&i32>().count(), 1); - assert_eq!(world.query_mut::>().count(), 1); + assert_eq!(world.query_filtered_mut::<(), Added>().count(), 1); assert!(world.query_one::<&i32>(a).is_ok()); - assert!(world.query_one::>(a).is_ok()); + assert!(world.query_one_filtered::<(), Added>(a).is_ok()); assert!(world.query_one_mut::<&i32>(a).is_ok()); - assert!(world.query_one_mut::>(a).is_ok()); + assert!(world.query_one_filtered_mut::<(), Added>(a).is_ok()); world.clear_trackers(); assert_eq!(world.query::<&i32>().count(), 1); - assert_eq!(world.query::>().count(), 0); + assert_eq!(world.query_filtered::<(), Added>().count(), 0); assert_eq!(world.query_mut::<&i32>().count(), 1); - assert_eq!(world.query_mut::>().count(), 0); + assert_eq!(world.query_filtered_mut::<(), Added>().count(), 0); assert!(world.query_one_mut::<&i32>(a).is_ok()); - assert!(world.query_one_mut::>(a).is_err()); + assert!(world.query_one_filtered_mut::<(), Added>(a).is_err()); } #[test] diff --git a/crates/bevy_ecs/src/system/into_system.rs b/crates/bevy_ecs/src/system/into_system.rs index 3df4be0665..548f25a7e7 100644 --- a/crates/bevy_ecs/src/system/into_system.rs +++ b/crates/bevy_ecs/src/system/into_system.rs @@ -220,7 +220,7 @@ mod tests { fn query_system_gets() { fn query_system( mut ran: ResMut, - entity_query: Query>, + entity_query: Query>, b_query: Query<&B>, a_c_query: Query<(&A, &C)>, d_query: Query<&D>, @@ -281,9 +281,9 @@ mod tests { use crate::{Added, Changed, Mutated, Or}; let query_system = move |mut ran: ResMut, set: QuerySet<( - Query, Changed)>>, - Query, Added)>>, - Query, Mutated)>>, + Query<(), Or<(Changed, Changed)>>, + Query<(), Or<(Added, Added)>>, + Query<(), Or<(Mutated, Mutated)>>, )>| { let changed = set.q0().iter().count(); let added = set.q1().iter().count(); diff --git a/crates/bevy_ecs/src/system/query/mod.rs b/crates/bevy_ecs/src/system/query/mod.rs index b61e621c27..da4745f200 100644 --- a/crates/bevy_ecs/src/system/query/mod.rs +++ b/crates/bevy_ecs/src/system/query/mod.rs @@ -4,17 +4,17 @@ pub use query_set::*; use bevy_hecs::{ ArchetypeComponent, Batch, BatchedIter, Component, ComponentError, Entity, Fetch, Mut, - Query as HecsQuery, QueryIter, ReadOnlyFetch, TypeAccess, World, + Query as HecsQuery, QueryFilter, QueryIter, ReadOnlyFetch, TypeAccess, World, }; use bevy_tasks::ParallelIterator; use std::marker::PhantomData; /// Provides scoped access to a World according to a given [HecsQuery] #[derive(Debug)] -pub struct Query<'a, Q: HecsQuery> { +pub struct Query<'a, Q: HecsQuery, F: QueryFilter = ()> { pub(crate) world: &'a World, pub(crate) component_access: &'a TypeAccess, - _marker: PhantomData, + _marker: PhantomData<(Q, F)>, } /// An error that occurs when using a [Query] @@ -26,7 +26,7 @@ pub enum QueryError { NoSuchEntity, } -impl<'a, Q: HecsQuery> Query<'a, Q> { +impl<'a, Q: HecsQuery, F: QueryFilter> Query<'a, Q, F> { /// # Safety /// This will create a Query that could violate memory safety rules. Make sure that this is only called in /// ways that ensure the Queries have unique mutable access. @@ -44,7 +44,7 @@ impl<'a, Q: HecsQuery> Query<'a, Q> { /// Iterates over the query results. This can only be called for read-only queries #[inline] - pub fn iter(&self) -> QueryIter<'_, Q> + pub fn iter(&self) -> QueryIter<'_, Q, F> where Q::Fetch: ReadOnlyFetch, { @@ -54,7 +54,7 @@ impl<'a, Q: HecsQuery> Query<'a, Q> { /// Iterates over the query results #[inline] - pub fn iter_mut(&mut self) -> QueryIter<'_, Q> { + pub fn iter_mut(&mut self) -> QueryIter<'_, Q, F> { // SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict unsafe { self.world.query_unchecked() } } @@ -63,13 +63,13 @@ impl<'a, Q: HecsQuery> Query<'a, Q> { /// # Safety /// This allows aliased mutability. You must make sure this call does not result in multiple mutable references to the same component #[inline] - pub unsafe fn iter_unsafe(&self) -> QueryIter<'_, Q> { + pub unsafe fn iter_unsafe(&self) -> QueryIter<'_, Q, F> { // SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict self.world.query_unchecked() } #[inline] - pub fn par_iter(&self, batch_size: usize) -> ParIter<'_, Q> + pub fn par_iter(&self, batch_size: usize) -> ParIter<'_, Q, F> where Q::Fetch: ReadOnlyFetch, { @@ -78,7 +78,7 @@ impl<'a, Q: HecsQuery> Query<'a, Q> { } #[inline] - pub fn par_iter_mut(&mut self, batch_size: usize) -> ParIter<'_, Q> { + pub fn par_iter_mut(&mut self, batch_size: usize) -> ParIter<'_, Q, F> { // SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict unsafe { ParIter::new(self.world.query_batched_unchecked(batch_size)) } } @@ -92,7 +92,7 @@ impl<'a, Q: HecsQuery> Query<'a, Q> { // SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict unsafe { self.world - .query_one_unchecked::(entity) + .query_one_unchecked::(entity) .map_err(|_err| QueryError::NoSuchEntity) } } @@ -103,7 +103,7 @@ impl<'a, Q: HecsQuery> Query<'a, Q> { // SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict unsafe { self.world - .query_one_unchecked::(entity) + .query_one_unchecked::(entity) .map_err(|_err| QueryError::NoSuchEntity) } } @@ -117,7 +117,7 @@ impl<'a, Q: HecsQuery> Query<'a, Q> { entity: Entity, ) -> Result<::Item, QueryError> { self.world - .query_one_unchecked::(entity) + .query_one_unchecked::(entity) .map_err(|_err| QueryError::NoSuchEntity) } @@ -196,23 +196,23 @@ impl<'a, Q: HecsQuery> Query<'a, Q> { } /// Parallel version of QueryIter -pub struct ParIter<'w, Q: HecsQuery> { - batched_iter: BatchedIter<'w, Q>, +pub struct ParIter<'w, Q: HecsQuery, F: QueryFilter> { + batched_iter: BatchedIter<'w, Q, F>, } -impl<'w, Q: HecsQuery> ParIter<'w, Q> { - pub fn new(batched_iter: BatchedIter<'w, Q>) -> Self { +impl<'w, Q: HecsQuery, F: QueryFilter> ParIter<'w, Q, F> { + pub fn new(batched_iter: BatchedIter<'w, Q, F>) -> Self { Self { batched_iter } } } -unsafe impl<'w, Q: HecsQuery> Send for ParIter<'w, Q> {} +unsafe impl<'w, Q: HecsQuery, F: QueryFilter> Send for ParIter<'w, Q, F> {} -impl<'w, Q: HecsQuery> ParallelIterator> for ParIter<'w, Q> { +impl<'w, Q: HecsQuery, F: QueryFilter> ParallelIterator> for ParIter<'w, Q, F> { type Item = >::Item; #[inline] - fn next_batch(&mut self) -> Option> { + fn next_batch(&mut self) -> Option> { self.batched_iter.next() } } diff --git a/crates/bevy_ecs/src/system/query/query_set.rs b/crates/bevy_ecs/src/system/query/query_set.rs index c3b6802e77..c9b570055b 100644 --- a/crates/bevy_ecs/src/system/query/query_set.rs +++ b/crates/bevy_ecs/src/system/query/query_set.rs @@ -1,6 +1,7 @@ use crate::Query; use bevy_hecs::{ - impl_query_set, ArchetypeComponent, Fetch, Query as HecsQuery, QueryAccess, TypeAccess, World, + impl_query_set, ArchetypeComponent, Fetch, Query as HecsQuery, QueryAccess, QueryFilter, + TypeAccess, World, }; pub struct QuerySet { diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 8cbdba8ea1..047778d56e 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -1,10 +1,11 @@ -pub use bevy_hecs::SystemParam; - use crate::{ - ChangedRes, Commands, FromResources, Local, Query, QuerySet, QueryTuple, Res, ResMut, Resource, - ResourceIndex, Resources, SystemState, + ChangedRes, Commands, FromResources, Local, Query, QueryAccess, QuerySet, QueryTuple, Res, + ResMut, Resource, ResourceIndex, Resources, SystemState, +}; +pub use bevy_hecs::SystemParam; +use bevy_hecs::{ + ArchetypeComponent, Fetch, Or, Query as HecsQuery, QueryFilter, TypeAccess, World, }; -use bevy_hecs::{ArchetypeComponent, Fetch, Or, Query as HecsQuery, TypeAccess, World}; use parking_lot::Mutex; use std::{any::TypeId, sync::Arc}; @@ -20,7 +21,7 @@ pub trait SystemParam: Sized { ) -> Option; } -impl<'a, Q: HecsQuery> SystemParam for Query<'a, Q> { +impl<'a, Q: HecsQuery, F: QueryFilter> SystemParam for Query<'a, Q, F> { #[inline] unsafe fn get_param( system_state: &mut SystemState, @@ -39,9 +40,8 @@ impl<'a, Q: HecsQuery> SystemParam for Query<'a, Q> { system_state .query_archetype_component_accesses .push(TypeAccess::default()); - system_state - .query_accesses - .push(vec![::access()]); + let access = QueryAccess::union(vec![Q::Fetch::access(), F::access()]); + system_state.query_accesses.push(vec![access]); system_state .query_type_names .push(std::any::type_name::()); diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index c58287a5de..becec6b88c 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -40,7 +40,7 @@ pub fn camera_system( windows: Res, mut queries: QuerySet<( Query<(Entity, &mut Camera, &mut T)>, - Query<(Entity, Added)>, + Query>, )>, ) { let mut changed_window_ids = Vec::new(); @@ -71,7 +71,7 @@ pub fn camera_system( } let mut added_cameras = vec![]; - for (entity, _camera) in &mut queries.q1().iter() { + for entity in &mut queries.q1().iter() { added_cameras.push(entity); } for (entity, mut camera, mut camera_projection) in queries.q0_mut().iter_mut() { diff --git a/crates/bevy_render/src/camera/visible_entities.rs b/crates/bevy_render/src/camera/visible_entities.rs index 6c2227c9da..775f0dc58e 100644 --- a/crates/bevy_render/src/camera/visible_entities.rs +++ b/crates/bevy_render/src/camera/visible_entities.rs @@ -26,7 +26,7 @@ impl VisibleEntities { pub fn visible_entities_system( mut camera_query: Query<(&Camera, &GlobalTransform, &mut VisibleEntities)>, draw_query: Query<(Entity, &Draw)>, - draw_transform_query: Query>, + draw_transform_query: Query<&GlobalTransform, With>, ) { for (camera, camera_global_transform, mut visible_entities) in camera_query.iter_mut() { visible_entities.value.clear(); diff --git a/crates/bevy_transform/src/hierarchy/hierarchy_maintenance_system.rs b/crates/bevy_transform/src/hierarchy/hierarchy_maintenance_system.rs index b7e4c069d7..d6d93c8209 100644 --- a/crates/bevy_transform/src/hierarchy/hierarchy_maintenance_system.rs +++ b/crates/bevy_transform/src/hierarchy/hierarchy_maintenance_system.rs @@ -5,7 +5,7 @@ use smallvec::SmallVec; pub fn parent_update_system( commands: &mut Commands, - removed_parent_query: Query>, + removed_parent_query: Query<(Entity, &PreviousParent), Without>, // TODO: ideally this only runs when the Parent component has changed mut changed_parent_query: Query<(Entity, &Parent, Option<&mut PreviousParent>)>, mut children_query: Query<&mut Children>, diff --git a/crates/bevy_transform/src/transform_propagate_system.rs b/crates/bevy_transform/src/transform_propagate_system.rs index 4727b24528..ab077e90fb 100644 --- a/crates/bevy_transform/src/transform_propagate_system.rs +++ b/crates/bevy_transform/src/transform_propagate_system.rs @@ -3,13 +3,11 @@ use bevy_ecs::prelude::*; pub fn transform_propagate_system( mut root_query: Query< - Without< - Parent, - With, &Transform, &mut GlobalTransform)>, - >, + (Option<&Children>, &Transform, &mut GlobalTransform), + (Without, With), >, - mut transform_query: Query>, - children_query: Query>>>, + mut transform_query: Query<(&Transform, &mut GlobalTransform), With>, + children_query: Query, (With, With)>, ) { for (children, transform, mut global_transform) in root_query.iter_mut() { *global_transform = GlobalTransform::from(*transform); @@ -29,8 +27,8 @@ pub fn transform_propagate_system( fn propagate_recursive( parent: &GlobalTransform, - transform_query: &mut Query>, - children_query: &Query>>>, + transform_query: &mut Query<(&Transform, &mut GlobalTransform), With>, + children_query: &Query, (With, With)>, entity: Entity, ) { log::trace!("Updating Transform for {:?}", entity); diff --git a/crates/bevy_ui/src/flex/mod.rs b/crates/bevy_ui/src/flex/mod.rs index de829f22b4..368cd294c8 100644 --- a/crates/bevy_ui/src/flex/mod.rs +++ b/crates/bevy_ui/src/flex/mod.rs @@ -160,10 +160,13 @@ unsafe impl Sync for FlexSurface {} pub fn flex_node_system( windows: Res, mut flex_surface: ResMut, - root_node_query: Query>>, - node_query: Query, Option<&CalculatedSize>)>>, - changed_size_query: Query)>>, - children_query: Query)>>, + root_node_query: Query, Without)>, + node_query: Query<(Entity, &Style, Option<&CalculatedSize>), (With, Changed