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 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<NonNull<u8>>,
@ -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::<T>()
.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<TypeId>) -> bool {
pub(crate) fn is_archetype_for(&self, types: Vec<DynTypeId>) -> bool {
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 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<TypeId>;
fn type_ids(&self) -> Vec<DynTypeId>;
/// Get ComponentInfo's for the components in this bundle
fn info(&self) -> Vec<ComponentInfo>;
/// 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<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
impl<C1: Component> Bundle for (C1,) {
fn type_ids(&self) -> Vec<TypeId> {
vec![self.0.type_id()]
fn type_ids(&self) -> Vec<DynTypeId> {
vec![DynTypeId::of::<C1>()]
}
fn info(&self) -> Vec<ComponentInfo> {
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;
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),+ ) => (
#[allow(non_snake_case)]
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
let ($($name),+) = self;
vec![$($name.type_id()),+]
vec![$(DynTypeId::of::<$name>()),+]
}
fn info(&self) -> Vec<ComponentInfo> {
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
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)]
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<T: 'static>() -> Self {
Self {
type_id: TypeId::of::<T>(),
type_id: DynTypeId::from(TypeId::of::<T>()),
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 crate::{world::World, ComponentColumn};
use crate::{world::World, ComponentColumn, DynTypeId};
use super::{Fetch, Query, AsQuery};
@ -41,7 +41,7 @@ where
/// }
/// ```
pub struct QueryBorrow<T> {
type_id: TypeId,
type_id: DynTypeId,
_phantom: PhantomData<T>
}
@ -56,7 +56,7 @@ impl<T> Clone for QueryBorrow<T> {
impl<T: 'static> QueryBorrow<T> {
pub fn new() -> Self {
Self {
type_id: TypeId::of::<T>(),
type_id: DynTypeId::of::<T>(),
_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<T> {
type_id: TypeId,
type_id: DynTypeId,
_phantom: PhantomData<T>
}
@ -150,7 +150,7 @@ impl<T> Clone for QueryBorrowMut<T> {
impl<T: 'static> QueryBorrowMut<T> {
pub fn new() -> Self {
Self {
type_id: TypeId::of::<T>(),
type_id: DynTypeId::of::<T>(),
_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<T: 'static> 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::<Vec2> {
type_id: TypeId::of::<Vec2>(),
type_id: DynTypeId::of::<Vec2>(),
_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::<Vec2> {
type_id: TypeId::of::<Vec2>(),
type_id: DynTypeId::of::<Vec2>(),
_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::<Vec2> {
type_id: TypeId::of::<Vec2>(),
type_id: DynTypeId::of::<Vec2>(),
_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::<Vec2>()).unwrap();
let col = a.columns.iter().find(|c| c.info.type_id == DynTypeId::of::<Vec2>()).unwrap();
let mut bmut = FetchBorrowMut::<Vec2> {
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)]
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