From 70c7d745f918dd23343599963a619539f4f880cb Mon Sep 17 00:00:00 2001 From: HampusM Date: Thu, 1 Aug 2024 12:36:35 +0200 Subject: refactor(ecs): add & use component metadata struct --- ecs/src/archetype.rs | 25 +++++++++++- ecs/src/component.rs | 29 ++++++++++++-- ecs/src/component/storage.rs | 93 +++++++++++++++++++++++++++++--------------- ecs/src/query.rs | 31 ++++----------- 4 files changed, 118 insertions(+), 60 deletions(-) diff --git a/ecs/src/archetype.rs b/ecs/src/archetype.rs index a33de66..808d006 100644 --- a/ecs/src/archetype.rs +++ b/ecs/src/archetype.rs @@ -1,6 +1,10 @@ use std::hash::{DefaultHasher, Hash, Hasher}; -use crate::component::Id as ComponentId; +use crate::component::{ + Id as ComponentId, + IsOptional as ComponentIsOptional, + Metadata as ComponentMetadata, +}; /// Archetype ID. #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] @@ -11,8 +15,25 @@ pub struct Id impl Id { + pub fn from_components_metadata( + components_metadata: impl IntoIterator, + ) -> Self + { + Self::new( + components_metadata + .into_iter() + .filter_map(|component_metadata| { + if component_metadata.is_optional == ComponentIsOptional::Yes { + return None; + } + + Some(component_metadata.id) + }), + ) + } + /// Returns the ID of a archetype with the given components. - pub fn new(component_ids: impl IntoIterator) -> Self + fn new(component_ids: impl IntoIterator) -> Self { let mut hasher = DefaultHasher::new(); diff --git a/ecs/src/component.rs b/ecs/src/component.rs index be5756f..67ae453 100644 --- a/ecs/src/component.rs +++ b/ecs/src/component.rs @@ -139,13 +139,33 @@ pub trait Sequence fn into_vec(self) -> Vec>; - fn ids() -> Vec<(Id, IsOptional)>; + fn metadata() -> Vec; fn from_components<'component>( components: impl Iterator, ) -> Self::Refs<'component>; } +/// [`Component`] metadata. +#[derive(Debug, Clone)] +#[non_exhaustive] +pub struct Metadata +{ + pub id: Id, + pub is_optional: IsOptional, +} + +impl Metadata +{ + pub fn of(component: &ComponentT) -> Self + { + Self { + id: component.id(), + is_optional: component.is_optional().into(), + } + } +} + /// Whether or not a `Component` is optional. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum IsOptional @@ -201,11 +221,14 @@ macro_rules! inner { Vec::from_iter([#(Box::new(self.I) as Box,)*]) } - fn ids() -> Vec<(Id, IsOptional)> + fn metadata() -> Vec { vec![ #( - (Id::of::(), is_optional::().into()), + Metadata { + id: Id::of::(), + is_optional: is_optional::().into() + }, )* ] } diff --git a/ecs/src/component/storage.rs b/ecs/src/component/storage.rs index 70981a3..76701ac 100644 --- a/ecs/src/component/storage.rs +++ b/ecs/src/component/storage.rs @@ -3,7 +3,12 @@ use std::collections::{HashMap, HashSet}; use std::slice::Iter as SliceIter; use crate::archetype::Id as ArchetypeId; -use crate::component::{Component, Id as ComponentId, IsOptional as ComponentIsOptional}; +use crate::component::{ + Component, + Id as ComponentId, + IsOptional as ComponentIsOptional, + Metadata as ComponentMetadata, +}; use crate::lock::Lock; use crate::type_name::TypeName; use crate::EntityComponent; @@ -13,28 +18,18 @@ pub struct Storage { archetypes: Vec, archetype_lookup: HashMap>, - pending_archetype_lookup_entries: Vec>, + pending_archetype_lookup_entries: Vec>, } impl Storage { pub fn find_entities( &self, - component_ids: &[(ComponentId, ComponentIsOptional)], + components_metadata: impl IntoIterator, ) -> ArchetypeRefIter<'_> { - let ids = component_ids - .iter() - .filter_map(|(component_id, is_optional)| { - if *is_optional == ComponentIsOptional::Yes { - return None; - } - - Some(*component_id) - }); - self.archetype_lookup - .get(&ArchetypeId::new(ids)) + .get(&ArchetypeId::from_components_metadata(components_metadata)) .map_or_else(ArchetypeRefIter::new_empty, |archetypes_indices| { ArchetypeRefIter { inner: archetypes_indices.iter(), @@ -58,11 +53,10 @@ impl Storage let archetype_indices = self .archetype_lookup - .entry(ArchetypeId::new( + .entry(ArchetypeId::from_components_metadata( components .iter() - .filter(|component| !component.is_optional()) - .map(|component| component.id()), + .map(|component| ComponentMetadata::of(&**component)), )) .or_insert_with(|| { self.archetypes.push(Archetype::new( @@ -102,10 +96,13 @@ impl Storage ); } - pub fn add_archetype_lookup_entry(&mut self, component_ids: &[ComponentId]) + pub fn add_archetype_lookup_entry( + &mut self, + components_metadata: impl IntoIterator, + ) { self.pending_archetype_lookup_entries - .push(component_ids.to_vec()); + .push(components_metadata.into_iter().collect()); } pub fn make_archetype_lookup_entries(&mut self) @@ -115,14 +112,22 @@ impl Storage self.archetype_lookup.clear(); for pending_entry in self.pending_archetype_lookup_entries.drain(..) { - let components_set: HashSet<_> = pending_entry.iter().copied().collect(); + let ids = pending_entry.iter().filter_map(|component_metadata| { + if component_metadata.is_optional == ComponentIsOptional::Yes { + return None; + } + + Some(component_metadata.id) + }); + + let ids_set: HashSet<_> = ids.collect(); let matching_archetype_indices = self .archetypes .iter() .enumerate() .filter_map(|(index, archetype)| { - if archetype.component_ids.is_superset(&components_set) { + if archetype.component_ids.is_superset(&ids_set) { return Some(index); } @@ -131,7 +136,9 @@ impl Storage let archetype_indices = self .archetype_lookup - .entry(ArchetypeId::new(pending_entry.into_iter())) + .entry(ArchetypeId::from_components_metadata( + pending_entry.into_iter(), + )) .or_default(); archetype_indices.extend(matching_archetype_indices); @@ -205,7 +212,11 @@ mod tests use super::{Archetype, Storage}; use crate::archetype::Id as ArchetypeId; - use crate::component::Id as ComponentId; + use crate::component::{ + Id as ComponentId, + IsOptional as ComponentIsOptional, + Metadata as ComponentMetadata, + }; use crate::lock::Lock; use crate::{self as ecs, EntityComponent}; @@ -269,9 +280,15 @@ mod tests let lookup = component_storage .archetype_lookup - .get(&ArchetypeId::new([ - ComponentId::of::(), - ComponentId::of::(), + .get(&ArchetypeId::from_components_metadata([ + ComponentMetadata { + id: ComponentId::of::(), + is_optional: ComponentIsOptional::No, + }, + ComponentMetadata { + id: ComponentId::of::(), + is_optional: ComponentIsOptional::No, + }, ])) .expect("Expected entry in archetype lookup map"); @@ -343,9 +360,15 @@ mod tests ], }); - component_storage.add_archetype_lookup_entry(&[ - ComponentId::of::(), - ComponentId::of::(), + component_storage.add_archetype_lookup_entry([ + ComponentMetadata { + id: ComponentId::of::(), + is_optional: ComponentIsOptional::No, + }, + ComponentMetadata { + id: ComponentId::of::(), + is_optional: ComponentIsOptional::No, + }, ]); assert_eq!(component_storage.pending_archetype_lookup_entries.len(), 1); @@ -356,9 +379,15 @@ mod tests let archetypes = component_storage .archetype_lookup - .get(&ArchetypeId::new([ - ComponentId::of::(), - ComponentId::of::(), + .get(&ArchetypeId::from_components_metadata([ + ComponentMetadata { + id: ComponentId::of::(), + is_optional: ComponentIsOptional::No, + }, + ComponentMetadata { + id: ComponentId::of::(), + is_optional: ComponentIsOptional::No, + }, ])) .expect(concat!( "Expected a archetype for IronBoots & Hookshot to be found in the ", diff --git a/ecs/src/query.rs b/ecs/src/query.rs index 2c52206..bc98ac0 100644 --- a/ecs/src/query.rs +++ b/ecs/src/query.rs @@ -9,11 +9,7 @@ use crate::component::storage::{ ArchetypeRefIter, Storage as ComponentStorage, }; -use crate::component::{ - Id as ComponentId, - IsOptional as ComponentIsOptional, - Sequence as ComponentSequence, -}; +use crate::component::{Metadata as ComponentMetadata, Sequence as ComponentSequence}; use crate::lock::{Lock, ReadGuard}; use crate::query::options::Options; use crate::system::{ @@ -52,7 +48,7 @@ where ComponentIter { entities: self .component_storage - .find_entities(&Comps::ids()) + .find_entities(Comps::metadata()) .map((|archetype| archetype.components.as_slice()) as ComponentIterMapFn) .flatten() .filter(|components| OptionsT::entity_filter(*components)), @@ -124,7 +120,7 @@ where fn get_comparable() -> Box { - Box::new(QueryComponentIds { component_ids: Comps::ids() }) + Box::new(QueryComponentIds { component_ids: Comps::metadata() }) } fn prepare(world_data: &WorldData) @@ -140,18 +136,7 @@ where std::any::type_name::() ); - component_storage_lock.add_archetype_lookup_entry( - &Comps::ids() - .into_iter() - .filter_map(|(component_id, is_optional)| { - if is_optional == ComponentIsOptional::Yes { - return None; - } - - Some(component_id) - }) - .collect::>(), - ); + component_storage_lock.add_archetype_lookup_entry(Comps::metadata()); } } @@ -183,7 +168,7 @@ where #[derive(Debug)] struct QueryComponentIds { - component_ids: Vec<(ComponentId, ComponentIsOptional)>, + component_ids: Vec, } impl QueryComponentIds @@ -192,13 +177,13 @@ impl QueryComponentIds where OtherComps: ComponentSequence, { - let other_ids = OtherComps::ids() + let other_ids = OtherComps::metadata() .into_iter() - .map(|(id, _)| id) + .map(|component_metadata| component_metadata.id) .collect::>(); self.component_ids .iter() - .all(|(id, _)| other_ids.contains(id)) + .all(|component_metadata| other_ids.contains(&component_metadata.id)) } } -- cgit v1.2.3-18-g5258