Groundwork for dynamic types in archetypes

This commit is contained in:
SeanOMik 2023-12-09 11:40:41 -05:00
parent 808cb77040
commit e867aaeadb
Signed by: SeanOMik
GPG Key ID: 568F326C7EB33ACB
6 changed files with 186 additions and 44 deletions

View File

@ -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 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 { pub struct ComponentColumn {
data: RefCell<NonNull<u8>>, data: RefCell<NonNull<u8>>,
@ -16,8 +16,8 @@ impl Drop for ComponentColumn {
unsafe { unsafe {
// layout of current alloc // layout of current alloc
let layout = Layout::from_size_align_unchecked(self.info.layout.size() * self.capacity, let layout = Layout::from_size_align_unchecked(self.info.layout.size * self.capacity,
self.info.layout.align()); self.info.layout.alignment);
dealloc(data, layout); dealloc(data, layout);
} }
} }
@ -50,7 +50,7 @@ impl ComponentColumn {
} }
pub unsafe fn new(info: ComponentInfo, capacity: usize) -> Self { 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 { Self {
data: RefCell::new(data), data: RefCell::new(data),
@ -71,8 +71,8 @@ impl ComponentColumn {
let mut data = self.data.borrow_mut(); let mut data = self.data.borrow_mut();
let data = data.deref_mut(); let data = data.deref_mut();
let dest = NonNull::new_unchecked(data.as_ptr().add(entity_index * 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()); ptr::copy_nonoverlapping(comp_src.as_ptr(), dest.as_ptr(), self.info.layout.size);
self.len += 1; self.len += 1;
} }
@ -86,7 +86,7 @@ impl ComponentColumn {
let data = data.deref(); let data = data.deref();
let ptr = NonNull::new_unchecked(data.as_ptr() let ptr = NonNull::new_unchecked(data.as_ptr()
.add(entity_index * self.info.layout.size())) .add(entity_index * self.info.layout.size))
.cast(); .cast();
&*ptr.as_ptr() &*ptr.as_ptr()
} }
@ -102,7 +102,7 @@ impl ComponentColumn {
let p = data.as_ptr() let p = data.as_ptr()
.cast::<T>() .cast::<T>()
.add(entity_index * self.info.layout.size()); .add(entity_index * self.info.layout.size);
&mut *p &mut *p
} }
@ -120,17 +120,17 @@ impl ComponentColumn {
let mut data = self.data.borrow_mut(); let mut data = self.data.borrow_mut();
//let data = data.deref_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 { 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 // dont attempt to free if we weren't able to store anything anyway
if self.capacity != 0 { if self.capacity != 0 {
let old_layout = Layout::from_size_align_unchecked( let old_layout = Layout::from_size_align_unchecked(
self.info.layout.size().checked_mul(self.capacity).unwrap(), self.info.layout.size.checked_mul(self.capacity).unwrap(),
self.info.layout.align() self.info.layout.alignment
); );
mem::swap(data.deref_mut(), &mut new_ptr); mem::swap(data.deref_mut(), &mut new_ptr);
@ -148,14 +148,14 @@ impl ComponentColumn {
let data = data.deref_mut(); let data = data.deref_mut();
let mut old_comp_ptr = NonNull::new_unchecked(data.as_ptr() 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 = if entity_index != self.len - 1 {
let moved_index = self.len - 1; let moved_index = self.len - 1;
let mut new_comp_ptr = NonNull::new_unchecked(data.as_ptr() 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 mem::swap(&mut old_comp_ptr, &mut new_comp_ptr); // new_comp_ptr is now the old ptr
Some(moved_index) Some(moved_index)
@ -276,7 +276,7 @@ impl Archetype {
} }
/// Returns a boolean indicating whether this archetype can store the TypeIds given /// Returns a boolean indicating whether this archetype can store the TypeIds given
pub(crate) fn is_archetype_for(&self, types: Vec<TypeId>) -> bool { pub(crate) fn is_archetype_for(&self, types: Vec<DynTypeId>) -> bool {
self.columns.iter().all(|c| types.contains(&c.info.type_id)) self.columns.iter().all(|c| types.contains(&c.info.type_id))
} }

View File

@ -1,33 +1,33 @@
use std::{any::{TypeId, Any}, ptr::NonNull, mem::size_of}; 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 { pub trait Bundle {
/// Get a list of type ids that this bundle is storing /// Get a list of type ids that this bundle is storing
fn type_ids(&self) -> Vec<TypeId>; fn type_ids(&self) -> Vec<DynTypeId>;
/// Get ComponentInfo's for the components in this bundle /// Get ComponentInfo's for the components in this bundle
fn info(&self) -> Vec<ComponentInfo>; fn info(&self) -> Vec<ComponentInfo>;
/// Take the bundle by calling the closure with pointers to each component, its type and size. /// 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. /// The closure is expected to take ownership of the pointer.
fn take(self, f: impl FnMut(NonNull<u8>, TypeId, usize)); fn take(self, f: impl FnMut(NonNull<u8>, DynTypeId, usize));
} }
// The macro below can implement this for us, but this is here for development // The macro below can implement this for us, but this is here for development
impl<C1: Component> Bundle for (C1,) { impl<C1: Component> Bundle for (C1,) {
fn type_ids(&self) -> Vec<TypeId> { fn type_ids(&self) -> Vec<DynTypeId> {
vec![self.0.type_id()] vec![DynTypeId::of::<C1>()]
} }
fn info(&self) -> Vec<ComponentInfo> { fn info(&self) -> Vec<ComponentInfo> {
vec![ComponentInfo::new::<C1>()] vec![ComponentInfo::new::<C1>()]
} }
fn take(self, mut f: impl FnMut(NonNull<u8>, TypeId, usize)) { fn take(self, mut f: impl FnMut(NonNull<u8>, DynTypeId, usize)) {
let (c1, ) = self; let (c1, ) = self;
f(NonNull::from(&c1).cast(), TypeId::of::<C1>(), size_of::<C1>()); f(NonNull::from(&c1).cast(), DynTypeId::of::<C1>(), size_of::<C1>());
} }
} }
@ -35,21 +35,21 @@ macro_rules! impl_bundle_tuple {
( $($name: ident),+ ) => ( ( $($name: ident),+ ) => (
#[allow(non_snake_case)] #[allow(non_snake_case)]
impl<$($name: Component),+> Bundle for ($($name,)+) { impl<$($name: Component),+> Bundle for ($($name,)+) {
fn type_ids(&self) -> Vec<TypeId> { fn type_ids(&self) -> Vec<DynTypeId> {
// these names wont follow rust convention, but its a macro so deal with it // these names wont follow rust convention, but its a macro so deal with it
let ($($name),+) = self; let ($($name),+) = self;
vec![$($name.type_id()),+] vec![$(DynTypeId::of::<$name>()),+]
} }
fn info(&self) -> Vec<ComponentInfo> { fn info(&self) -> Vec<ComponentInfo> {
vec![$(ComponentInfo::new::<$name>()),+] vec![$(ComponentInfo::new::<$name>()),+]
} }
fn take(self, mut f: impl FnMut(NonNull<u8>, TypeId, usize)) { fn take(self, mut f: impl FnMut(NonNull<u8>, DynTypeId, usize)) {
// these names wont follow rust convention, but its a macro so deal with it // these names wont follow rust convention, but its a macro so deal with it
let ($($name),+) = self; 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>());)+
} }
} }
); );

View File

@ -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<Layout> for MemoryLayout {
type Error = LayoutError;
fn try_into(self) -> Result<Layout, Self::Error> {
Layout::from_size_align(self.size, self.alignment)
}
}
impl From<Layout> 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<DynTypeId> for u128 {
fn into(self) -> DynTypeId {
DynTypeId::Unknown(self)
}
}
impl From<TypeId> for DynTypeId {
fn from(value: TypeId) -> Self {
DynTypeId::Rust(value)
}
}
impl DynTypeId {
pub fn of<T: 'static>() -> Self {
Self::Rust(TypeId::of::<T>())
}
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct ComponentInfo { pub struct ComponentInfo {
pub type_id: TypeId, pub type_id: DynTypeId,
pub name: String, pub name: String,
pub layout: Layout, pub layout: MemoryLayout,
} }
impl ComponentInfo { impl ComponentInfo {
pub fn new<T: 'static>() -> Self { pub fn new<T: 'static>() -> Self {
Self { Self {
type_id: TypeId::of::<T>(), type_id: DynTypeId::from(TypeId::of::<T>()),
name: type_name::<T>().to_string(), name: type_name::<T>().to_string(),
layout: Layout::new::<T>(), layout: MemoryLayout::from(Layout::new::<T>()),
}
}
/// Create ComponentInfo from a type that is not known to rust
pub fn new_unknown<D>(type_id: D, name: &str, layout: MemoryLayout) -> Self
where
D: Into<DynTypeId>,
{
Self {
type_id: type_id.into(),
name: name.to_string(),
layout,
} }
} }
} }

View File

@ -1,6 +1,6 @@
use std::{marker::PhantomData, any::TypeId, ptr::NonNull, cell::{Ref, RefCell, RefMut}}; 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}; use super::{Fetch, Query, AsQuery};
@ -41,7 +41,7 @@ where
/// } /// }
/// ``` /// ```
pub struct QueryBorrow<T> { pub struct QueryBorrow<T> {
type_id: TypeId, type_id: DynTypeId,
_phantom: PhantomData<T> _phantom: PhantomData<T>
} }
@ -56,7 +56,7 @@ impl<T> Clone for QueryBorrow<T> {
impl<T: 'static> QueryBorrow<T> { impl<T: 'static> QueryBorrow<T> {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
type_id: TypeId::of::<T>(), type_id: DynTypeId::of::<T>(),
_phantom: PhantomData, _phantom: PhantomData,
} }
} }
@ -84,7 +84,7 @@ where
FetchBorrow { FetchBorrow {
col, col,
size: col.info.layout.size(), size: col.info.layout.size,
_phantom: PhantomData, _phantom: PhantomData,
} }
} }
@ -135,7 +135,7 @@ where
/// } /// }
/// ``` /// ```
pub struct QueryBorrowMut<T> { pub struct QueryBorrowMut<T> {
type_id: TypeId, type_id: DynTypeId,
_phantom: PhantomData<T> _phantom: PhantomData<T>
} }
@ -150,7 +150,7 @@ impl<T> Clone for QueryBorrowMut<T> {
impl<T: 'static> QueryBorrowMut<T> { impl<T: 'static> QueryBorrowMut<T> {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
type_id: TypeId::of::<T>(), type_id: DynTypeId::of::<T>(),
_phantom: PhantomData, _phantom: PhantomData,
} }
} }
@ -178,7 +178,7 @@ where
FetchBorrowMut { FetchBorrowMut {
col, col,
size: col.info.layout.size(), size: col.info.layout.size,
_phantom: PhantomData, _phantom: PhantomData,
} }
} }
@ -196,7 +196,7 @@ impl<T: 'static> AsQuery for &mut T {
mod tests { mod tests {
use std::{any::TypeId, mem::size_of, marker::PhantomData}; 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}; use super::{QueryBorrow, QueryBorrowMut, FetchBorrowMut};
@ -215,7 +215,7 @@ mod tests {
let world = prepare_world(); let world = prepare_world();
let borrow = QueryBorrow::<Vec2> { let borrow = QueryBorrow::<Vec2> {
type_id: TypeId::of::<Vec2>(), type_id: DynTypeId::of::<Vec2>(),
_phantom: std::marker::PhantomData, _phantom: std::marker::PhantomData,
}; };
let archetypes: Vec<&Archetype> = world.archetypes.values().collect(); let archetypes: Vec<&Archetype> = world.archetypes.values().collect();
@ -231,7 +231,7 @@ mod tests {
let world = prepare_world(); let world = prepare_world();
let borrow = QueryBorrowMut::<Vec2> { let borrow = QueryBorrowMut::<Vec2> {
type_id: TypeId::of::<Vec2>(), type_id: DynTypeId::of::<Vec2>(),
_phantom: std::marker::PhantomData, _phantom: std::marker::PhantomData,
}; };
let archetypes: Vec<&Archetype> = world.archetypes.values().collect(); let archetypes: Vec<&Archetype> = world.archetypes.values().collect();
@ -248,7 +248,7 @@ mod tests {
// Now make sure the changes were actually made // Now make sure the changes were actually made
let borrow = QueryBorrow::<Vec2> { let borrow = QueryBorrow::<Vec2> {
type_id: TypeId::of::<Vec2>(), type_id: DynTypeId::of::<Vec2>(),
_phantom: std::marker::PhantomData, _phantom: std::marker::PhantomData,
}; };
let archetypes: Vec<&Archetype> = world.archetypes.values().collect(); let archetypes: Vec<&Archetype> = world.archetypes.values().collect();
@ -273,7 +273,7 @@ mod tests {
}, (Vec2::rand(),)); }, (Vec2::rand(),));
} }
let col = a.columns.iter().find(|c| c.info.type_id == TypeId::of::<Vec2>()).unwrap(); let col = a.columns.iter().find(|c| c.info.type_id == DynTypeId::of::<Vec2>()).unwrap();
let mut bmut = FetchBorrowMut::<Vec2> { let mut bmut = FetchBorrowMut::<Vec2> {
col, col,

View File

@ -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<u8>,
}
pub struct FetchDynamicType {
info: Option<DynamicTypeInfo>,
}
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!()
}
}

View File

@ -19,6 +19,10 @@ pub mod resource;
#[allow(unused_imports)] #[allow(unused_imports)]
pub use resource::*; pub use resource::*;
pub mod dynamic;
#[allow(unused_imports)]
pub use dynamic::*;
/// A [`Fetch`]er implementation gets data out of an archetype. /// A [`Fetch`]er implementation gets data out of an archetype.
pub trait Fetch<'a> { pub trait Fetch<'a> {
/// The type that this Fetch yields /// The type that this Fetch yields