From 43eeea1df75ae50a2074fee994d930b67bf886f1 Mon Sep 17 00:00:00 2001 From: Edvin Nillsson Date: Fri, 31 Mar 2023 16:41:36 +0200 Subject: [PATCH 1/3] Add Query system parameter to allow foreign entity access --- crates/ecs/src/filter.rs | 16 ++-- crates/ecs/src/systems/mod.rs | 163 ++++++++++++++++++++++++++++++++-- 2 files changed, 163 insertions(+), 16 deletions(-) diff --git a/crates/ecs/src/filter.rs b/crates/ecs/src/filter.rs index b2b43fd..782f5a6 100644 --- a/crates/ecs/src/filter.rs +++ b/crates/ecs/src/filter.rs @@ -45,8 +45,8 @@ impl SystemParameter for With< })) } - fn component_access() -> Option { - None + fn component_accesses() -> Vec { + vec![] } fn iterates_over_entities() -> bool { @@ -126,8 +126,8 @@ macro_rules! binary_filter_operation { })) } - fn component_access() -> Option { - None + fn component_accesses() -> Vec { + vec![] } fn iterates_over_entities() -> bool { @@ -189,8 +189,8 @@ impl SystemParameter for Not { })) } - fn component_access() -> Option { - None + fn component_accesses() -> Vec { + vec![] } fn iterates_over_entities() -> bool { @@ -239,8 +239,8 @@ mod tests { Some(Some(Self {})) } - fn component_access() -> Option { - None + fn component_accesses() -> Vec { + vec![] } fn iterates_over_entities() -> bool { diff --git a/crates/ecs/src/systems/mod.rs b/crates/ecs/src/systems/mod.rs index 1c0e7c3..a96aa23 100644 --- a/crates/ecs/src/systems/mod.rs +++ b/crates/ecs/src/systems/mod.rs @@ -4,7 +4,10 @@ pub mod iteration; use crate::systems::iteration::SequentiallyIterable; -use crate::{ArchetypeIndex, ReadComponentVec, World, WorldError, WriteComponentVec}; +use crate::{ + intersection_of_multiple_sets, ArchetypeIndex, ReadComponentVec, World, WorldError, + WriteComponentVec, +}; use paste::paste; use std::any::TypeId; use std::collections::HashSet; @@ -208,12 +211,15 @@ pub type SystemParameterResult = Result; /// A collection of `ecs::SystemParameter`s that can be passed to a `ecs::System`. pub trait SystemParameters: Send + Sync { + /// The type of borrowed data for all system parameters + type BorrowedData<'components>; + /// A description of all of the data that is accessed and how (read/write). fn component_accesses() -> Vec; } /// Something that can be passed to a [`System`]. -pub(crate) trait SystemParameter: Send + Sync + Sized { +pub trait SystemParameter: Send + Sync + Sized { /// Contains a borrow of components from `ecs::World`. type BorrowedData<'components>; @@ -229,7 +235,7 @@ pub(crate) trait SystemParameter: Send + Sync + Sized { unsafe fn fetch_parameter(borrowed: &mut Self::BorrowedData<'_>) -> Option>; /// A description of what data is accessed and how (read/write). - fn component_access() -> Option; + fn component_accesses() -> Vec; /// If the [`SystemParameter`] require that the system iterates over entities. /// The system will only run once if all [`SystemParameter`]'s `iterates_over_entities` is `false`. @@ -251,6 +257,11 @@ pub(crate) trait SystemParameter: Send + Sync + Sized { fn filter(universe: &HashSet, _world: &World) -> HashSet { universe.clone() } + + /// If a system with this [`SystemParameter`] can use intra-system parallelization. + fn support_parallelization() -> bool { + true + } } trait SystemParameterFunction: 'static {} @@ -317,8 +328,8 @@ impl SystemParameter for Read< None } - fn component_access() -> Option { - Some(ComponentAccessDescriptor::read::()) + fn component_accesses() -> Vec { + vec![ComponentAccessDescriptor::read::()] } fn iterates_over_entities() -> bool { @@ -375,8 +386,8 @@ impl SystemParameter for Write None } - fn component_access() -> Option { - Some(ComponentAccessDescriptor::write::()) + fn component_accesses() -> Vec { + vec![ComponentAccessDescriptor::write::()] } fn iterates_over_entities() -> bool { @@ -408,7 +419,80 @@ impl<'a, Component> DerefMut for Write<'a, Component> { } } +/// `Query` allows a system to access components from entities other than the currently iterated. +/// +/// # Example +/// ``` +/// # use ecs::filter::With; +/// # use ecs::systems::{Query, Read, Write}; +/// # #[derive(Debug)] struct Mass(f32); +/// # #[derive(Debug)] struct Velocity(f32); +/// fn print_largest_momentum(query: Query<(Read, Read)>) { +/// let mut largest_momentum = 0.0; +/// for (mass, velocity) in query { +/// let momentum = mass.0 * velocity.0; +/// if momentum > largest_momentum { +/// largest_momentum = momentum; +/// } +/// } +/// println!("The largest momentum of all entities is {largest_momentum} kg*m/s."); +/// } +/// ``` +#[derive(Debug)] +pub struct Query<'a, P: SystemParameters> { + phantom: PhantomData

, + world: &'a World, +} + +/// Iterator for [`Query`]. +#[derive(Debug)] +pub struct QueryIterator<'components, P: SystemParameters> { + borrowed: P::BorrowedData<'components>, + iterate_over_entities: bool, + iterated_once: bool, +} + +impl<'a, P: SystemParameters> SystemParameter for Query<'a, P> { + type BorrowedData<'components> = &'components World; + + fn borrow<'world>( + world: &'world World, + _: &[ArchetypeIndex], + ) -> SystemParameterResult> { + Ok(world) + } + + unsafe fn fetch_parameter(borrowed: &mut Self::BorrowedData<'_>) -> Option> { + #[allow(trivial_casts)] + let world = &*(*borrowed as *const World); + Some(Some(Self { + phantom: PhantomData::default(), + world, + })) + } + + fn component_accesses() -> Vec { + P::component_accesses() + } + + fn iterates_over_entities() -> bool { + false + } + + fn base_signature() -> Option { + None + } + + fn support_parallelization() -> bool { + Self::component_accesses() + .iter() + .all(|component_access| component_access.is_read()) + } +} + impl SystemParameters for () { + type BorrowedData<'components> = (); + fn component_accesses() -> Vec { vec![] } @@ -420,14 +504,77 @@ macro_rules! impl_system_parameter_function { ($($parameter:expr),*) => { paste! { impl<$([]: SystemParameter,)*> SystemParameters for ($([],)*) { + type BorrowedData<'components> = ($([]::BorrowedData<'components>,)*); + fn component_accesses() -> Vec { - [$([]::component_access(),)*] + [$([]::component_accesses(),)*] .into_iter() .flatten() .collect() } } + impl<'a, $([]: SystemParameter,)*> IntoIterator for Query<'a, ($([],)*)> { + type Item = ($([],)*); + type IntoIter = QueryIterator<'a, ($([],)*)>; + + fn into_iter(self) -> Self::IntoIter { + let base_signature: Vec = [$([]::base_signature(),)*] + .into_iter() + .flatten() + .collect(); + + let universe = self.world.get_archetype_indices(&base_signature); + + let archetypes_indices: Vec<_> = intersection_of_multiple_sets(&[ + universe.clone(), + $([]::filter(&universe, self.world),)* + ]) + .into_iter() + .collect(); + + let borrowed = ( + $([]::borrow(self.world, &archetypes_indices).unwrap(),)* + ); + + let iterate_over_entities = $([]::iterates_over_entities() ||)* false; + + Self::IntoIter { + borrowed, + iterate_over_entities, + iterated_once: false, + } + } + } + + impl<'a, $([]: SystemParameter,)*> Iterator for QueryIterator<'a, ($([],)*)> { + type Item = ($([],)*); + + fn next(&mut self) -> Option { + // SAFETY: This is safe because the result from fetch_parameter will not outlive borrowed + unsafe { + if self.iterate_over_entities { + while let ($(Some([]),)*) = ( + $([]::fetch_parameter(&mut self.borrowed.$parameter),)* + ) { + if let ($(Some([]),)*) = ( + $([],)* + ) { + return Some(($([],)*)); + } + } + } else if let (false, $(Some(Some([])),)*) = ( + self.iterated_once, + $([]::fetch_parameter(&mut self.borrowed.$parameter),)* + ) { + self.iterated_once = true; + return Some(($([],)*)); + } + None + } + } + } + impl]: SystemParameter,)*> SystemParameterFunction<($([],)*)> for F where F: Fn($([],)*) + 'static, {} } From 8f12e7c8bf1b50b1adf21481d392d7b23ccdd6e3 Mon Sep 17 00:00:00 2001 From: Edvin Nilsson <42613683+EdvinNilsson@users.noreply.github.com> Date: Fri, 31 Mar 2023 19:02:17 +0200 Subject: [PATCH 2/3] Rename lifetime 'a to 'world Co-authored-by: Martin --- crates/ecs/src/systems/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ecs/src/systems/mod.rs b/crates/ecs/src/systems/mod.rs index a96aa23..cfccfe4 100644 --- a/crates/ecs/src/systems/mod.rs +++ b/crates/ecs/src/systems/mod.rs @@ -439,9 +439,9 @@ impl<'a, Component> DerefMut for Write<'a, Component> { /// } /// ``` #[derive(Debug)] -pub struct Query<'a, P: SystemParameters> { +pub struct Query<'world, P: SystemParameters> { phantom: PhantomData

, - world: &'a World, + world: &'world World, } /// Iterator for [`Query`]. From 9c4fe47d2567966dd895b0622b94bcc748eeb8a1 Mon Sep 17 00:00:00 2001 From: Edvin Nillsson Date: Sat, 1 Apr 2023 20:46:12 +0200 Subject: [PATCH 3/3] Use QueryIterator in SequentiallyIterable implementation --- crates/ecs/src/systems/iteration.rs | 44 +++++++---------------------- crates/ecs/src/systems/mod.rs | 13 +++++++-- 2 files changed, 20 insertions(+), 37 deletions(-) diff --git a/crates/ecs/src/systems/iteration.rs b/crates/ecs/src/systems/iteration.rs index 21c6d96..08cc09e 100644 --- a/crates/ecs/src/systems/iteration.rs +++ b/crates/ecs/src/systems/iteration.rs @@ -1,7 +1,7 @@ //! Different ways to iterate over all queried entities in a [`System`]. use super::*; -use crate::{intersection_of_multiple_sets, World}; +use crate::World; /// Execution of a single [`System`] in a sequential order. pub trait SequentiallyIterable { @@ -31,40 +31,16 @@ macro_rules! impl_sequentially_iterable_system { { fn run(&self, world: &World) -> SystemResult<()> { - let base_signature: Vec = [$([]::base_signature(),)*] - .into_iter() - .flatten() - .collect(); - - let universe = world.get_archetype_indices(&base_signature); - - let archetypes_indices: Vec<_> = intersection_of_multiple_sets(&[ - universe.clone(), - $([]::filter(&universe, world),)* - ]) - .into_iter() - .collect(); - - $(let mut [] = []::borrow(world, &archetypes_indices).map_err(SystemError::MissingParameter)?;)* - - // SAFETY: This is safe because the result from fetch_parameter will not outlive borrowed - unsafe { - if $([]::iterates_over_entities() ||)* false { - while let ($(Some([]),)*) = ( - $([]::fetch_parameter(&mut []),)* - ) { - if let ($(Some([]),)*) = ( - $([],)* - ) { - (self.function)($([],)*); - } - } - } else if let ($(Some(Some([])),)*) = ( - $([]::fetch_parameter(&mut []),)* - ) { - (self.function)($([],)*); - } + let query: Query<($([],)*)> = Query { + phantom: PhantomData::default(), + world + }; + + let query_iterator = query.try_into_iter().map_err(SystemError::MissingParameter)?; + for ($([],)*) in query_iterator { + (self.function)($([],)*); } + Ok(()) } } diff --git a/crates/ecs/src/systems/mod.rs b/crates/ecs/src/systems/mod.rs index cfccfe4..610fe76 100644 --- a/crates/ecs/src/systems/mod.rs +++ b/crates/ecs/src/systems/mod.rs @@ -519,6 +519,13 @@ macro_rules! impl_system_parameter_function { type IntoIter = QueryIterator<'a, ($([],)*)>; fn into_iter(self) -> Self::IntoIter { + Self::try_into_iter(self) + .expect("creating `QueryIterator` should work if the archetypes are in a valid state") + } + } + + impl<'a, $([]: SystemParameter,)*> Query<'a, ($([],)*)> { + fn try_into_iter(self) -> SystemParameterResult],)*)>> { let base_signature: Vec = [$([]::base_signature(),)*] .into_iter() .flatten() @@ -534,16 +541,16 @@ macro_rules! impl_system_parameter_function { .collect(); let borrowed = ( - $([]::borrow(self.world, &archetypes_indices).unwrap(),)* + $([]::borrow(self.world, &archetypes_indices)?,)* ); let iterate_over_entities = $([]::iterates_over_entities() ||)* false; - Self::IntoIter { + Ok(QueryIterator { borrowed, iterate_over_entities, iterated_once: false, - } + }) } }