From 5c89681e10ba16dbf431100c870c55ad456072f3 Mon Sep 17 00:00:00 2001 From: BimDav <67792882+BimDav@users.noreply.github.com> Date: Mon, 17 Aug 2020 23:07:59 +0200 Subject: [PATCH 1/6] Add Either query for a to do a logical or on a pair of Mutated or Changed queries (using should_skip method). --- crates/bevy_ecs/hecs/src/query.rs | 61 +++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/crates/bevy_ecs/hecs/src/query.rs b/crates/bevy_ecs/hecs/src/query.rs index 992166caa7129..fb94d97682ead 100644 --- a/crates/bevy_ecs/hecs/src/query.rs +++ b/crates/bevy_ecs/hecs/src/query.rs @@ -240,6 +240,48 @@ impl<'a, T: Component> Fetch<'a> for FetchMut { } } +pub struct Either(PhantomData<(Q1, Q2)>); + +impl Query for Either { + type Fetch = FetchEither; +} + +#[doc(hidden)] +pub struct FetchEither(F1, F2); + +impl<'a, F1: Fetch<'a>, F2: Fetch<'a>> Fetch<'a> for FetchEither { + type Item = (F1::Item, F2::Item); + + fn access(archetype: &Archetype) -> Option { + F1::access(archetype).and(F2::access(archetype)) + } + + fn borrow(archetype: &Archetype) { + F1::borrow(archetype); + F2::borrow(archetype); + } + + unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { + Some(Self( + F1::get(archetype, offset)?, + F2::get(archetype, offset)?, + )) + } + + fn release(archetype: &Archetype) { + F1::release(archetype); + F2::release(archetype); + } + + unsafe fn next(&mut self) -> Self::Item { + (self.0.next(), self.1.next()) + } + + unsafe fn should_skip(&self) -> bool { + self.0.should_skip() && self.1.should_skip() + } +} + /// Query transformer that skips entities that have a `T` component that has /// not been mutated since the last pass of the system. This does not include /// components that were added in since the last pass. @@ -1065,6 +1107,25 @@ mod tests { assert_eq!(a_b_changed, vec![e2]); } + #[test] + fn either_mutated_query() { + let mut world = World::default(); + world.spawn((A(0), B(0))); + let e2 = world.spawn((A(0), B(0))); + world.spawn((A(0), B(0))); + + for mut b in world.query::>().iter().skip(1).take(1) { + b.0 += 1; + } + + let a_b_changed = world + .query::<(Either, Mutated>, Entity)>() + .iter() + .map(|((_a, _b), e)| e) + .collect::>(); + assert_eq!(a_b_changed, vec![e2]); + } + #[test] fn changed_query() { let mut world = World::default(); From 2d257c9813ada0efce59146b85669112e179f32b Mon Sep 17 00:00:00 2001 From: BimDav <67792882+BimDav@users.noreply.github.com> Date: Tue, 18 Aug 2020 02:06:18 +0200 Subject: [PATCH 2/6] Added documentation --- crates/bevy_ecs/hecs/src/lib.rs | 3 ++- crates/bevy_ecs/hecs/src/query.rs | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/crates/bevy_ecs/hecs/src/lib.rs b/crates/bevy_ecs/hecs/src/lib.rs index 1a9690f725edb..61e82a6ae1372 100644 --- a/crates/bevy_ecs/hecs/src/lib.rs +++ b/crates/bevy_ecs/hecs/src/lib.rs @@ -81,7 +81,8 @@ pub use bundle::{Bundle, DynamicBundle, MissingComponent}; pub use entities::{Entity, Location, NoSuchEntity}; pub use entity_builder::{BuiltEntity, EntityBuilder}; pub use query::{ - Access, Added, BatchedIter, Changed, Mut, Mutated, Query, QueryBorrow, QueryIter, With, Without, + Access, Added, BatchedIter, Changed, Either, Mut, Mutated, Query, QueryBorrow, QueryIter, With, + Without, }; pub use query_one::QueryOne; pub use world::{ArchetypesGeneration, Component, ComponentError, Iter, SpawnBatchIter, World}; diff --git a/crates/bevy_ecs/hecs/src/query.rs b/crates/bevy_ecs/hecs/src/query.rs index fb94d97682ead..0667eb48aecab 100644 --- a/crates/bevy_ecs/hecs/src/query.rs +++ b/crates/bevy_ecs/hecs/src/query.rs @@ -240,6 +240,26 @@ impl<'a, T: Component> Fetch<'a> for FetchMut { } } +/// 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)); +/// world.spawn((456, false)); +/// for mut b in world.query::>().iter().skip(1).take(1) { +/// *b += 1; +/// } +/// let components = world +/// .query::, Mutated>>() +/// .iter() +/// .map(|(b, i)| (*b, *i)) +/// .collect::>(); +/// assert_eq!(components, &[(false, 457)]); +/// ``` pub struct Either(PhantomData<(Q1, Q2)>); impl Query for Either { From 32cdc5ae4ba8085e5738a028ac7a0eb657fd9f86 Mon Sep 17 00:00:00 2001 From: BimDav <67792882+BimDav@users.noreply.github.com> Date: Wed, 26 Aug 2020 11:01:06 +0200 Subject: [PATCH 3/6] Refactor: Rename 'Either' to 'Or' --- crates/bevy_ecs/hecs/src/lib.rs | 2 +- crates/bevy_ecs/hecs/src/query.rs | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/bevy_ecs/hecs/src/lib.rs b/crates/bevy_ecs/hecs/src/lib.rs index 61e82a6ae1372..2960d7013f711 100644 --- a/crates/bevy_ecs/hecs/src/lib.rs +++ b/crates/bevy_ecs/hecs/src/lib.rs @@ -81,7 +81,7 @@ pub use bundle::{Bundle, DynamicBundle, MissingComponent}; pub use entities::{Entity, Location, NoSuchEntity}; pub use entity_builder::{BuiltEntity, EntityBuilder}; pub use query::{ - Access, Added, BatchedIter, Changed, Either, Mut, Mutated, Query, QueryBorrow, QueryIter, With, + Access, Added, BatchedIter, Changed, Mut, Mutated, Or, Query, QueryBorrow, QueryIter, With, Without, }; pub use query_one::QueryOne; diff --git a/crates/bevy_ecs/hecs/src/query.rs b/crates/bevy_ecs/hecs/src/query.rs index 0667eb48aecab..397f3b4e9bf13 100644 --- a/crates/bevy_ecs/hecs/src/query.rs +++ b/crates/bevy_ecs/hecs/src/query.rs @@ -254,22 +254,22 @@ impl<'a, T: Component> Fetch<'a> for FetchMut { /// *b += 1; /// } /// let components = world -/// .query::, Mutated>>() +/// .query::, Mutated>>() /// .iter() /// .map(|(b, i)| (*b, *i)) /// .collect::>(); /// assert_eq!(components, &[(false, 457)]); /// ``` -pub struct Either(PhantomData<(Q1, Q2)>); +pub struct Or(PhantomData<(Q1, Q2)>); -impl Query for Either { - type Fetch = FetchEither; +impl Query for Or { + type Fetch = FetchOr; } #[doc(hidden)] -pub struct FetchEither(F1, F2); +pub struct FetchOr(F1, F2); -impl<'a, F1: Fetch<'a>, F2: Fetch<'a>> Fetch<'a> for FetchEither { +impl<'a, F1: Fetch<'a>, F2: Fetch<'a>> Fetch<'a> for FetchOr { type Item = (F1::Item, F2::Item); fn access(archetype: &Archetype) -> Option { @@ -1128,7 +1128,7 @@ mod tests { } #[test] - fn either_mutated_query() { + fn or_mutated_query() { let mut world = World::default(); world.spawn((A(0), B(0))); let e2 = world.spawn((A(0), B(0))); @@ -1139,7 +1139,7 @@ mod tests { } let a_b_changed = world - .query::<(Either, Mutated>, Entity)>() + .query::<(Or, Mutated>, Entity)>() .iter() .map(|((_a, _b), e)| e) .collect::>(); From 49719d497f90d8ca1f9daadbd9ca154a1f7c9084 Mon Sep 17 00:00:00 2001 From: BimDav <67792882+BimDav@users.noreply.github.com> Date: Wed, 26 Aug 2020 11:17:28 +0200 Subject: [PATCH 4/6] Test covers (left), (right) and (left and right) change in Or<&left, &right> --- crates/bevy_ecs/hecs/src/query.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ecs/hecs/src/query.rs b/crates/bevy_ecs/hecs/src/query.rs index 397f3b4e9bf13..dbc4539aaa356 100644 --- a/crates/bevy_ecs/hecs/src/query.rs +++ b/crates/bevy_ecs/hecs/src/query.rs @@ -1130,11 +1130,17 @@ mod tests { #[test] fn or_mutated_query() { let mut world = World::default(); - world.spawn((A(0), B(0))); + let e1 = world.spawn((A(0), B(0))); let e2 = world.spawn((A(0), B(0))); - world.spawn((A(0), B(0))); + let e3 = world.spawn((A(0), B(0))); + let _e4 = world.spawn((A(0), B(0))); - for mut b in world.query::>().iter().skip(1).take(1) { + // Mutate A in entities e1 and e2 + for mut a in world.query::>().iter().take(2) { + a.0 += 1; + } + // Mutate B in entities e2 and e3 + for mut b in world.query::>().iter().skip(1).take(2) { b.0 += 1; } @@ -1143,7 +1149,8 @@ mod tests { .iter() .map(|((_a, _b), e)| e) .collect::>(); - assert_eq!(a_b_changed, vec![e2]); + // e1 has mutated A, e3 has mutated B, e2 has mutated A and B, _e4 has no mutated component + assert_eq!(a_b_changed, vec![e1, e2, e3]); } #[test] From 523057645b7b019e197f0ed7b29a1ea916377036 Mon Sep 17 00:00:00 2001 From: BimDav <67792882+BimDav@users.noreply.github.com> Date: Wed, 26 Aug 2020 21:20:25 +0200 Subject: [PATCH 5/6] Macro to implement Or<()> for many tuple lengths --- crates/bevy_ecs/hecs/src/query.rs | 109 +++++++++++++++++------------- 1 file changed, 63 insertions(+), 46 deletions(-) diff --git a/crates/bevy_ecs/hecs/src/query.rs b/crates/bevy_ecs/hecs/src/query.rs index dbc4539aaa356..3a9ab0422cb16 100644 --- a/crates/bevy_ecs/hecs/src/query.rs +++ b/crates/bevy_ecs/hecs/src/query.rs @@ -240,67 +240,84 @@ impl<'a, T: Component> Fetch<'a> for FetchMut { } } +macro_rules! impl_or_query { + ( $( $T:ident ),+; $( $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 ),+); + + fn access(archetype: &Archetype) -> Option { + if false $(|| $T::access(archetype).is_none() )+ { + return None + } + Some(Access::Read) + } + + fn borrow(archetype: &Archetype) { + $( + $T::borrow(archetype); + )+ + } + + unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { + Some(Self(( $( $T::get(archetype, offset)?),+ ))) + } + + fn release(archetype: &Archetype) { + $( + $T::release(archetype); + )+ + } + + unsafe fn next(&mut self) -> Self::Item { + let ($( $t ),+) = &mut self.0; + ($( $t.next() ),+) + } + + unsafe fn should_skip(&self) -> bool { + let ($( $t ),+) = &self.0; + true $( && $t.should_skip() )+ + } + } + }; +} + +impl_or_query!(Q1, Q2; q1, q2); +impl_or_query!(Q1, Q2, Q3; q1, q2, q3); +impl_or_query!(Q1, Q2, Q3, Q4; q1, q2, q3, q4); +impl_or_query!(Q1, Q2, Q3, Q4, Q5; q1, q2, q3, q4, q5); +impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6; q1, q2, q3, q4, q5, q6); +impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6, Q7; q1, q2, q3, q4, q5, q6, q7); +impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8; q1, q2, q3, q4, q5, q6, q7, q8); +impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9; q1, q2, q3, q4, q5, q6, q7, q8, q9); +impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, Q10; 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)); -/// world.spawn((456, false)); +/// world.spawn((123, true, 1., Some(1))); +/// world.spawn((456, false, 2., Some(0))); /// for mut b in world.query::>().iter().skip(1).take(1) { /// *b += 1; /// } /// let components = world -/// .query::, Mutated>>() +/// .query::, Mutated, Mutated, Mutated>)>>() /// .iter() -/// .map(|(b, i)| (*b, *i)) +/// .map(|(b, i, f, o)| (*b, *i)) /// .collect::>(); /// assert_eq!(components, &[(false, 457)]); /// ``` -pub struct Or(PhantomData<(Q1, Q2)>); - -impl Query for Or { - type Fetch = FetchOr; -} +pub struct Or(PhantomData); +//pub struct Or(PhantomData<(Q1, Q2, Q3)>); #[doc(hidden)] -pub struct FetchOr(F1, F2); - -impl<'a, F1: Fetch<'a>, F2: Fetch<'a>> Fetch<'a> for FetchOr { - type Item = (F1::Item, F2::Item); - - fn access(archetype: &Archetype) -> Option { - F1::access(archetype).and(F2::access(archetype)) - } - - fn borrow(archetype: &Archetype) { - F1::borrow(archetype); - F2::borrow(archetype); - } - - unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { - Some(Self( - F1::get(archetype, offset)?, - F2::get(archetype, offset)?, - )) - } - - fn release(archetype: &Archetype) { - F1::release(archetype); - F2::release(archetype); - } - - unsafe fn next(&mut self) -> Self::Item { - (self.0.next(), self.1.next()) - } - - unsafe fn should_skip(&self) -> bool { - self.0.should_skip() && self.1.should_skip() - } -} +pub struct FetchOr(T); /// Query transformer that skips entities that have a `T` component that has /// not been mutated since the last pass of the system. This does not include @@ -1145,7 +1162,7 @@ mod tests { } let a_b_changed = world - .query::<(Or, Mutated>, Entity)>() + .query::<(Or<(Mutated, Mutated)>, Entity)>() .iter() .map(|((_a, _b), e)| e) .collect::>(); From 90055a28393e112dbd0bcf9e3f567aaa25fb2a23 Mon Sep 17 00:00:00 2001 From: BimDav <67792882+BimDav@users.noreply.github.com> Date: Sun, 30 Aug 2020 22:04:19 +0200 Subject: [PATCH 6/6] Fix Or<()>::access() method --- crates/bevy_ecs/hecs/src/query.rs | 39 +++++++++++++++++-------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/crates/bevy_ecs/hecs/src/query.rs b/crates/bevy_ecs/hecs/src/query.rs index 3a9ab0422cb16..58fcaf460d34a 100644 --- a/crates/bevy_ecs/hecs/src/query.rs +++ b/crates/bevy_ecs/hecs/src/query.rs @@ -241,7 +241,7 @@ impl<'a, T: Component> Fetch<'a> for FetchMut { } macro_rules! impl_or_query { - ( $( $T:ident ),+; $( $t:ident ),+ ) => { + ( $( $T:ident ),+ ) => { impl<$( $T: Query ),+> Query for Or<($( $T ),+)> { type Fetch = FetchOr<($( $T::Fetch ),+)>; } @@ -250,10 +250,11 @@ macro_rules! impl_or_query { type Item = ($( $T::Item ),+); fn access(archetype: &Archetype) -> Option { - if false $(|| $T::access(archetype).is_none() )+ { - return None - } - Some(Access::Read) + let mut max_access = None; + $( + max_access = max_access.max($T::access(archetype)); + )+ + max_access } fn borrow(archetype: &Archetype) { @@ -272,28 +273,30 @@ macro_rules! impl_or_query { )+ } + #[allow(non_snake_case)] unsafe fn next(&mut self) -> Self::Item { - let ($( $t ),+) = &mut self.0; - ($( $t.next() ),+) + let ($( $T ),+) = &mut self.0; + ($( $T.next() ),+) } + #[allow(non_snake_case)] unsafe fn should_skip(&self) -> bool { - let ($( $t ),+) = &self.0; - true $( && $t.should_skip() )+ + let ($( $T ),+) = &self.0; + true $( && $T.should_skip() )+ } } }; } -impl_or_query!(Q1, Q2; q1, q2); -impl_or_query!(Q1, Q2, Q3; q1, q2, q3); -impl_or_query!(Q1, Q2, Q3, Q4; q1, q2, q3, q4); -impl_or_query!(Q1, Q2, Q3, Q4, Q5; q1, q2, q3, q4, q5); -impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6; q1, q2, q3, q4, q5, q6); -impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6, Q7; q1, q2, q3, q4, q5, q6, q7); -impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8; q1, q2, q3, q4, q5, q6, q7, q8); -impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9; q1, q2, q3, q4, q5, q6, q7, q8, q9); -impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, Q10; q1, q2, q3, q4, q5, q6, q7, q8, q9, q10); +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.