From e867aaeadb61a816121ffb40cfdd2bfcddcbdbe6 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sat, 9 Dec 2023 11:40:41 -0500 Subject: [PATCH] Groundwork for dynamic types in archetypes --- lyra-ecs/src/archetype.rs | 32 ++++++------- lyra-ecs/src/bundle.rs | 22 ++++----- lyra-ecs/src/component_info.rs | 83 ++++++++++++++++++++++++++++++++-- lyra-ecs/src/query/borrow.rs | 24 +++++----- lyra-ecs/src/query/dynamic.rs | 65 ++++++++++++++++++++++++++ lyra-ecs/src/query/mod.rs | 4 ++ 6 files changed, 186 insertions(+), 44 deletions(-) create mode 100644 lyra-ecs/src/query/dynamic.rs diff --git a/lyra-ecs/src/archetype.rs b/lyra-ecs/src/archetype.rs index 6b302ab..6f9eb08 100644 --- a/lyra-ecs/src/archetype.rs +++ b/lyra-ecs/src/archetype.rs @@ -1,6 +1,6 @@ use std::{any::TypeId, ptr::{NonNull, self}, alloc::{self, Layout, alloc, dealloc}, mem, collections::HashMap, cell::{RefCell, Ref, RefMut, BorrowError, BorrowMutError}, ops::{DerefMut, Deref}}; -use crate::{world::{Entity, ArchetypeEntityId}, bundle::Bundle, component_info::ComponentInfo}; +use crate::{world::{Entity, ArchetypeEntityId}, bundle::Bundle, component_info::ComponentInfo, DynTypeId}; pub struct ComponentColumn { data: RefCell>, @@ -16,8 +16,8 @@ impl Drop for ComponentColumn { unsafe { // layout of current alloc - let layout = Layout::from_size_align_unchecked(self.info.layout.size() * self.capacity, - self.info.layout.align()); + let layout = Layout::from_size_align_unchecked(self.info.layout.size * self.capacity, + self.info.layout.alignment); dealloc(data, layout); } } @@ -50,7 +50,7 @@ impl ComponentColumn { } pub unsafe fn new(info: ComponentInfo, capacity: usize) -> Self { - let data = ComponentColumn::alloc(info.layout, capacity); + let data = ComponentColumn::alloc(info.layout.into_layout_unchecked(), capacity); Self { data: RefCell::new(data), @@ -71,8 +71,8 @@ impl ComponentColumn { let mut data = self.data.borrow_mut(); let data = data.deref_mut(); - let dest = NonNull::new_unchecked(data.as_ptr().add(entity_index * self.info.layout.size())); - ptr::copy_nonoverlapping(comp_src.as_ptr(), dest.as_ptr(), self.info.layout.size()); + let dest = NonNull::new_unchecked(data.as_ptr().add(entity_index * self.info.layout.size)); + ptr::copy_nonoverlapping(comp_src.as_ptr(), dest.as_ptr(), self.info.layout.size); self.len += 1; } @@ -86,7 +86,7 @@ impl ComponentColumn { let data = data.deref(); let ptr = NonNull::new_unchecked(data.as_ptr() - .add(entity_index * self.info.layout.size())) + .add(entity_index * self.info.layout.size)) .cast(); &*ptr.as_ptr() } @@ -102,7 +102,7 @@ impl ComponentColumn { let p = data.as_ptr() .cast::() - .add(entity_index * self.info.layout.size()); + .add(entity_index * self.info.layout.size); &mut *p } @@ -120,17 +120,17 @@ impl ComponentColumn { let mut data = self.data.borrow_mut(); //let data = data.deref_mut(); - let mut new_ptr = Self::alloc(self.info.layout, new_capacity); + let mut new_ptr = Self::alloc(self.info.layout.into_layout_unchecked(), new_capacity); if self.len > 0 { - ptr::copy_nonoverlapping(data.as_ptr(), new_ptr.as_ptr(), self.len * self.info.layout.size()); + ptr::copy_nonoverlapping(data.as_ptr(), new_ptr.as_ptr(), self.len * self.info.layout.size); } // dont attempt to free if we weren't able to store anything anyway if self.capacity != 0 { let old_layout = Layout::from_size_align_unchecked( - self.info.layout.size().checked_mul(self.capacity).unwrap(), - self.info.layout.align() + self.info.layout.size.checked_mul(self.capacity).unwrap(), + self.info.layout.alignment ); mem::swap(data.deref_mut(), &mut new_ptr); @@ -148,14 +148,14 @@ impl ComponentColumn { let data = data.deref_mut(); let mut old_comp_ptr = NonNull::new_unchecked(data.as_ptr() - .add(entity_index * self.info.layout.size())); + .add(entity_index * self.info.layout.size)); let moved_index = if entity_index != self.len - 1 { let moved_index = self.len - 1; let mut new_comp_ptr = NonNull::new_unchecked(data.as_ptr() - .add(moved_index * self.info.layout.size())); + .add(moved_index * self.info.layout.size)); - ptr::copy_nonoverlapping(new_comp_ptr.as_ptr(), old_comp_ptr.as_ptr(), self.info.layout.size()); + ptr::copy_nonoverlapping(new_comp_ptr.as_ptr(), old_comp_ptr.as_ptr(), self.info.layout.size); mem::swap(&mut old_comp_ptr, &mut new_comp_ptr); // new_comp_ptr is now the old ptr Some(moved_index) @@ -276,7 +276,7 @@ impl Archetype { } /// Returns a boolean indicating whether this archetype can store the TypeIds given - pub(crate) fn is_archetype_for(&self, types: Vec) -> bool { + pub(crate) fn is_archetype_for(&self, types: Vec) -> bool { self.columns.iter().all(|c| types.contains(&c.info.type_id)) } diff --git a/lyra-ecs/src/bundle.rs b/lyra-ecs/src/bundle.rs index da779cd..39698b4 100644 --- a/lyra-ecs/src/bundle.rs +++ b/lyra-ecs/src/bundle.rs @@ -1,33 +1,33 @@ use std::{any::{TypeId, Any}, ptr::NonNull, mem::size_of}; -use crate::{component::Component, component_info::ComponentInfo}; +use crate::{component::Component, component_info::ComponentInfo, DynTypeId}; pub trait Bundle { /// Get a list of type ids that this bundle is storing - fn type_ids(&self) -> Vec; + fn type_ids(&self) -> Vec; /// Get ComponentInfo's for the components in this bundle fn info(&self) -> Vec; /// Take the bundle by calling the closure with pointers to each component, its type and size. /// The closure is expected to take ownership of the pointer. - fn take(self, f: impl FnMut(NonNull, TypeId, usize)); + fn take(self, f: impl FnMut(NonNull, DynTypeId, usize)); } // The macro below can implement this for us, but this is here for development impl Bundle for (C1,) { - fn type_ids(&self) -> Vec { - vec![self.0.type_id()] + fn type_ids(&self) -> Vec { + vec![DynTypeId::of::()] } fn info(&self) -> Vec { vec![ComponentInfo::new::()] } - fn take(self, mut f: impl FnMut(NonNull, TypeId, usize)) { + fn take(self, mut f: impl FnMut(NonNull, DynTypeId, usize)) { let (c1, ) = self; - f(NonNull::from(&c1).cast(), TypeId::of::(), size_of::()); + f(NonNull::from(&c1).cast(), DynTypeId::of::(), size_of::()); } } @@ -35,21 +35,21 @@ macro_rules! impl_bundle_tuple { ( $($name: ident),+ ) => ( #[allow(non_snake_case)] impl<$($name: Component),+> Bundle for ($($name,)+) { - fn type_ids(&self) -> Vec { + fn type_ids(&self) -> Vec { // these names wont follow rust convention, but its a macro so deal with it let ($($name),+) = self; - vec![$($name.type_id()),+] + vec![$(DynTypeId::of::<$name>()),+] } fn info(&self) -> Vec { vec![$(ComponentInfo::new::<$name>()),+] } - fn take(self, mut f: impl FnMut(NonNull, TypeId, usize)) { + fn take(self, mut f: impl FnMut(NonNull, DynTypeId, usize)) { // these names wont follow rust convention, but its a macro so deal with it let ($($name),+) = self; - $(f(NonNull::from(&$name).cast(), TypeId::of::<$name>(), size_of::<$name>());)+ + $(f(NonNull::from(&$name).cast(), DynTypeId::of::<$name>(), size_of::<$name>());)+ } } ); diff --git a/lyra-ecs/src/component_info.rs b/lyra-ecs/src/component_info.rs index ed6c8e1..aec19c5 100644 --- a/lyra-ecs/src/component_info.rs +++ b/lyra-ecs/src/component_info.rs @@ -1,18 +1,91 @@ -use std::{any::{TypeId, type_name}, alloc::Layout}; +use std::{any::{TypeId, type_name}, alloc::{Layout, LayoutError}}; + +#[derive(Clone, Copy, Debug)] +pub struct MemoryLayout { + pub size: usize, + pub alignment: usize +} + +impl TryInto for MemoryLayout { + type Error = LayoutError; + + fn try_into(self) -> Result { + Layout::from_size_align(self.size, self.alignment) + } +} + +impl From for MemoryLayout { + fn from(value: Layout) -> Self { + Self { + size: value.size(), + alignment: value.align(), + } + } +} + +impl MemoryLayout { + pub fn new(size: usize, alignment: usize) -> Self { + MemoryLayout { + size, + alignment + } + } + + pub unsafe fn into_layout_unchecked(self) -> Layout { + Layout::from_size_align_unchecked(self.size, self.alignment) + } +} + +/// A dynamic type id. Supports types that are not known to Rust. +#[derive(Clone, Copy, Hash, Debug, PartialEq, Eq)] +pub enum DynTypeId { + Rust(TypeId), + Unknown(u128), +} + +impl Into for u128 { + fn into(self) -> DynTypeId { + DynTypeId::Unknown(self) + } +} + +impl From for DynTypeId { + fn from(value: TypeId) -> Self { + DynTypeId::Rust(value) + } +} + +impl DynTypeId { + pub fn of() -> Self { + Self::Rust(TypeId::of::()) + } +} #[derive(Clone, Debug)] pub struct ComponentInfo { - pub type_id: TypeId, + pub type_id: DynTypeId, pub name: String, - pub layout: Layout, + pub layout: MemoryLayout, } impl ComponentInfo { pub fn new() -> Self { Self { - type_id: TypeId::of::(), + type_id: DynTypeId::from(TypeId::of::()), name: type_name::().to_string(), - layout: Layout::new::(), + layout: MemoryLayout::from(Layout::new::()), + } + } + + /// Create ComponentInfo from a type that is not known to rust + pub fn new_unknown(type_id: D, name: &str, layout: MemoryLayout) -> Self + where + D: Into, + { + Self { + type_id: type_id.into(), + name: name.to_string(), + layout, } } } \ No newline at end of file diff --git a/lyra-ecs/src/query/borrow.rs b/lyra-ecs/src/query/borrow.rs index 9104034..1ee7af4 100644 --- a/lyra-ecs/src/query/borrow.rs +++ b/lyra-ecs/src/query/borrow.rs @@ -1,6 +1,6 @@ use std::{marker::PhantomData, any::TypeId, ptr::NonNull, cell::{Ref, RefCell, RefMut}}; -use crate::{world::World, ComponentColumn}; +use crate::{world::World, ComponentColumn, DynTypeId}; use super::{Fetch, Query, AsQuery}; @@ -41,7 +41,7 @@ where /// } /// ``` pub struct QueryBorrow { - type_id: TypeId, + type_id: DynTypeId, _phantom: PhantomData } @@ -56,7 +56,7 @@ impl Clone for QueryBorrow { impl QueryBorrow { pub fn new() -> Self { Self { - type_id: TypeId::of::(), + type_id: DynTypeId::of::(), _phantom: PhantomData, } } @@ -84,7 +84,7 @@ where FetchBorrow { col, - size: col.info.layout.size(), + size: col.info.layout.size, _phantom: PhantomData, } } @@ -135,7 +135,7 @@ where /// } /// ``` pub struct QueryBorrowMut { - type_id: TypeId, + type_id: DynTypeId, _phantom: PhantomData } @@ -150,7 +150,7 @@ impl Clone for QueryBorrowMut { impl QueryBorrowMut { pub fn new() -> Self { Self { - type_id: TypeId::of::(), + type_id: DynTypeId::of::(), _phantom: PhantomData, } } @@ -178,7 +178,7 @@ where FetchBorrowMut { col, - size: col.info.layout.size(), + size: col.info.layout.size, _phantom: PhantomData, } } @@ -196,7 +196,7 @@ impl AsQuery for &mut T { mod tests { use std::{any::TypeId, mem::size_of, marker::PhantomData}; - use crate::{world::{World, Entity, EntityId}, archetype::{Archetype, ArchetypeId}, query::View, tests::Vec2, bundle::Bundle, Fetch}; + use crate::{world::{World, Entity, EntityId}, archetype::{Archetype, ArchetypeId}, query::View, tests::Vec2, bundle::Bundle, Fetch, DynTypeId}; use super::{QueryBorrow, QueryBorrowMut, FetchBorrowMut}; @@ -215,7 +215,7 @@ mod tests { let world = prepare_world(); let borrow = QueryBorrow:: { - type_id: TypeId::of::(), + type_id: DynTypeId::of::(), _phantom: std::marker::PhantomData, }; let archetypes: Vec<&Archetype> = world.archetypes.values().collect(); @@ -231,7 +231,7 @@ mod tests { let world = prepare_world(); let borrow = QueryBorrowMut:: { - type_id: TypeId::of::(), + type_id: DynTypeId::of::(), _phantom: std::marker::PhantomData, }; let archetypes: Vec<&Archetype> = world.archetypes.values().collect(); @@ -248,7 +248,7 @@ mod tests { // Now make sure the changes were actually made let borrow = QueryBorrow:: { - type_id: TypeId::of::(), + type_id: DynTypeId::of::(), _phantom: std::marker::PhantomData, }; let archetypes: Vec<&Archetype> = world.archetypes.values().collect(); @@ -273,7 +273,7 @@ mod tests { }, (Vec2::rand(),)); } - let col = a.columns.iter().find(|c| c.info.type_id == TypeId::of::()).unwrap(); + let col = a.columns.iter().find(|c| c.info.type_id == DynTypeId::of::()).unwrap(); let mut bmut = FetchBorrowMut:: { col, diff --git a/lyra-ecs/src/query/dynamic.rs b/lyra-ecs/src/query/dynamic.rs new file mode 100644 index 0000000..b27313a --- /dev/null +++ b/lyra-ecs/src/query/dynamic.rs @@ -0,0 +1,65 @@ +use std::ptr::NonNull; + +use crate::{Fetch, world::World, Query}; + +#[derive(Clone, Copy, Hash)] +enum TypeId { + Rust(std::any::TypeId), + Unknown(u128), +} + +#[derive(Clone, Copy, Hash)] +pub struct DynamicTypeInfo { + id: TypeId, + size: usize, + alignment: usize, +} + +/// Data that is unknown to rust +pub struct DynamicType { + info: DynamicTypeInfo, + ptr: NonNull, +} + +pub struct FetchDynamicType { + info: Option, +} + +impl<'a> Fetch<'a> for FetchDynamicType { + type Item = DynamicType; + + fn dangling() -> Self { + Self { + info: None, + } + } + + unsafe fn get_item(&mut self, entity: crate::ArchetypeEntityId) -> Self::Item { + todo!() + } +} + +#[derive(Clone, Copy)] +pub struct QueryDynamicType { + info: DynamicTypeInfo, +} + +impl Query for QueryDynamicType { + type Item<'a> = DynamicType; + + type Fetch<'a> = FetchDynamicType; + + const ALWAYS_FETCHES: bool = false; + + fn new() -> Self { + todo!() + } + + fn can_visit_archetype(&self, _archetype: &crate::archetype::Archetype) -> bool { + todo!() + } + + unsafe fn fetch<'a>(&self, world: &'a World, _arch_id: crate::archetype::ArchetypeId, _archetype: &'a crate::archetype::Archetype) -> Self::Fetch<'a> { + todo!() + } +} \ No newline at end of file diff --git a/lyra-ecs/src/query/mod.rs b/lyra-ecs/src/query/mod.rs index b007897..b338381 100644 --- a/lyra-ecs/src/query/mod.rs +++ b/lyra-ecs/src/query/mod.rs @@ -19,6 +19,10 @@ pub mod resource; #[allow(unused_imports)] pub use resource::*; +pub mod dynamic; +#[allow(unused_imports)] +pub use dynamic::*; + /// A [`Fetch`]er implementation gets data out of an archetype. pub trait Fetch<'a> { /// The type that this Fetch yields