use std::{ptr::{NonNull, self}, alloc::{self, Layout, alloc, dealloc}, mem, collections::HashMap, cell::{RefCell, Ref, RefMut, BorrowError, BorrowMutError}, ops::DerefMut}; use crate::{world::ArchetypeEntityId, bundle::Bundle, component_info::ComponentInfo, DynTypeId, Tick, Entity}; #[derive(Clone)] pub struct ComponentColumn { pub data: RefCell>, pub len: usize, pub capacity: usize, pub info: ComponentInfo, /// The last tick that this component was modified at. pub entity_ticks: Vec, } impl Drop for ComponentColumn { fn drop(&mut self) { let data = self.data.borrow_mut(); let data = data.as_ptr(); unsafe { // TODO: trigger drop on the components // SAFETY: The size of the data buffer is the capcity times the size of a component let size = self.info.layout().size() * self.capacity; let layout = Layout::from_size_align_unchecked(size, self.info.layout().align()); dealloc(data, layout); } } } #[allow(dead_code)] impl ComponentColumn { pub unsafe fn alloc(component_layout: Layout, capacity: usize) -> NonNull { let new_layout = Layout::from_size_align( component_layout.size().checked_mul(capacity).unwrap(), component_layout.align() ).unwrap(); if let Some(data) = NonNull::new(alloc(new_layout)) { data } else { alloc::handle_alloc_error(new_layout) } } pub unsafe fn new(info: ComponentInfo, capacity: usize) -> Self { let data = ComponentColumn::alloc(info.layout(), capacity); Self { data: RefCell::new(data), capacity, info, len: 0, entity_ticks: Vec::new(), } } /// Set a component from pointer at an entity index. /// /// # Safety /// This column must have space to fit the component, if it does not have room it will panic. pub unsafe fn set_at(&mut self, entity_index: usize, comp_src: NonNull, tick: Tick) { assert!(entity_index < self.capacity); let mut data = self.data.borrow_mut(); let data = data.deref_mut(); let size = self.info.layout().size(); let dest = NonNull::new_unchecked(data.as_ptr().add(entity_index * size)); ptr::copy_nonoverlapping(comp_src.as_ptr(), dest.as_ptr(), size); // check if a component spot is being set twice and that the entity's tick is // already stored if self.entity_ticks.len() > entity_index { self.entity_ticks[entity_index].tick_to(&tick); } else { self.entity_ticks.push(tick); } } /// Inserts an entity and its component at a specific index. pub unsafe fn insert_entity(&mut self, entity_index: usize, comp_src: NonNull, tick: Tick) { assert!(entity_index < self.capacity); let mut data = self.data.borrow_mut(); let data = data.deref_mut(); let size = self.info.layout().size(); let dest = NonNull::new_unchecked(data.as_ptr().add(entity_index * size)); ptr::copy_nonoverlapping(comp_src.as_ptr(), dest.as_ptr(), size); // check if a component spot is being set twice and that the entity's tick is // already stored self.entity_ticks.push(tick); self.len += 1; } /// Get a component at an entities index. /// /// # Safety /// /// This column MUST have the entity. If it does not, it WILL NOT panic and will cause UB. pub unsafe fn get(&self, entity_index: usize) -> Ref { let data = self.data.borrow(); Ref::map(data, |data| { let ptr = NonNull::new_unchecked(data.as_ptr() .add(entity_index * self.info.layout().size())) .cast(); &*ptr.as_ptr() }) } /// Get a mutable borrow to the component at an entities index, ticking the entity. /// /// # Safety /// /// This column must have the entity. pub unsafe fn get_mut(&mut self, entity_index: usize, tick: &Tick) -> RefMut { self.entity_ticks[entity_index].tick_to(tick); let data = self.data.borrow_mut(); RefMut::map(data, |data| { let ptr = NonNull::new_unchecked(data.as_ptr() .add(entity_index * self.info.layout().size())) .cast(); &mut *ptr.as_ptr() }) } /// Grow the column to fit `new_capacity` amount of components. /// /// Parameters: /// * `new_capacity` - The new capacity of components that can fit in this column. /// /// Note: This does not modify the Tick of this column, since no components were actually modified. /// /// # Safety /// /// Will panic if `new_capacity` is less than the current capacity of the column. pub unsafe fn grow(&mut self, new_capacity: usize) { assert!(new_capacity > self.capacity); let mut data = self.data.borrow_mut(); let layout = self.info.layout(); let mut new_ptr = Self::alloc(layout, new_capacity); if self.len > 0 { ptr::copy_nonoverlapping(data.as_ptr(), new_ptr.as_ptr(), self.len * layout.size()); } // dont attempt to free if we weren't able to store anything anyway if self.capacity != 0 { // create a layout with the same alignment, but expand the size of the buffer. let old_layout = Layout::from_size_align_unchecked( layout.size().checked_mul(self.capacity).unwrap(), layout.align() ); mem::swap(data.deref_mut(), &mut new_ptr); dealloc(new_ptr.as_ptr(), old_layout); } else { *data = new_ptr; } self.capacity = new_capacity; } /// Removes a component from the column, freeing it, and returning the old index of the entity that took its place in the column. pub unsafe fn remove_component(&mut self, entity_index: usize, tick: &Tick) -> Option { let _ = tick; // may be used at some point debug_assert!(self.len > 0, "There are no entities in the Archetype to remove from!"); let mut data = self.data.borrow_mut(); let data = data.deref_mut(); let size = self.info.layout().size(); let mut old_comp_ptr = NonNull::new_unchecked(data.as_ptr() .add(entity_index * 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 * size)); ptr::copy_nonoverlapping(new_comp_ptr.as_ptr(), old_comp_ptr.as_ptr(), size); mem::swap(&mut old_comp_ptr, &mut new_comp_ptr); // new_comp_ptr is now the old ptr // make sure to keep entity indexes correct in the ticks list as well self.entity_ticks.swap_remove(entity_index); Some(moved_index) } else { None }; self.len -= 1; moved_index } pub fn borrow_ptr(&self) -> Ref> { self.data.borrow() } pub fn borrow_mut_ptr(&self) -> RefMut> { self.data.borrow_mut() } pub fn try_borrow_ptr(&self) -> Result>, BorrowError> { self.data.try_borrow() } pub fn try_borrow_mut_ptr(&self) -> Result>, BorrowMutError> { self.data.try_borrow_mut() } } /// An id of an Archetype #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)] pub struct ArchetypeId(pub u64); impl ArchetypeId { /// Increments the id and returns a new id with the value it was before incrementing. pub(crate) fn increment(&mut self) -> Self { let v = self.0; self.0 += 1; ArchetypeId(v) } } /// Stores a group of entities with matching components. /// /// An Archetype can be thought of as a table, with entities as the rows and the entity's /// components as each column. This means you can have tightly packed components of entities and /// quickly iterate through entities with the same components. #[derive(Clone, Default)] pub struct Archetype { id: ArchetypeId, /// The indexes of the entities in the archetype. pub(crate) entity_ids: HashMap, /// map an Archetype entity id to an entity //pub(crate) ids_to_entity: HashMap, /// The entities in the Archetype. /// /// Can be used to map `ArchetypeEntityId` to an Entity since `ArchetypeEntityId` has /// the index that the entity is stored at. pub(crate) entities: Vec, pub(crate) columns: Vec, capacity: usize, } /// The default capacity of the columns const DEFAULT_CAPACITY: usize = 32; impl Archetype { /// Returns the id of the Archetype pub fn id(&self) -> ArchetypeId { self.id } /// Returns the max amount of Entities that the Archetype can store without reallocating. pub fn capacity(&self) -> usize { self.capacity } pub fn from_bundle_info(new_id: ArchetypeId, bundle_info: Vec) -> Archetype { let columns = bundle_info.into_iter().map(|i| { unsafe { ComponentColumn::new(i, DEFAULT_CAPACITY) } }).collect(); Archetype { id: new_id, entity_ids: HashMap::new(), entities: Vec::new(), columns, capacity: DEFAULT_CAPACITY, } } /// Add an entity and its component bundle to the Archetype /// /// # Safety: /// /// Archetype must contain all of the components pub(crate) fn add_entity(&mut self, entity: Entity, bundle: B, tick: &Tick) -> ArchetypeEntityId where B: Bundle { if self.capacity == self.entity_ids.len() { let new_cap = self.capacity * 2; self.grow_columns(new_cap); self.capacity = new_cap; } debug_assert_eq!(self.entity_ids.len(), self.entities.len(), "Somehow the Archetype's entity storage got unsynced"); let entity_index = ArchetypeEntityId(self.entity_ids.len() as u64); self.entity_ids.insert(entity, entity_index); self.entities.push(entity); bundle.take(|data, type_id, _size| { let col = self.get_column_mut(type_id).unwrap(); unsafe { col.insert_entity(entity_index.0 as usize, data, *tick); } }); entity_index } /// Removes an entity from the Archetype and frees its components. Returns the entity record /// that took its place in the component column. pub(crate) fn remove_entity(&mut self, entity: Entity, tick: &Tick) -> Option<(Entity, ArchetypeEntityId)> { let entity_index = self.entity_ids.remove(&entity) .expect("The entity is not in this Archetype!"); let mut removed_entity: Option<(Entity, ArchetypeEntityId)> = None; for c in self.columns.iter_mut() { let moved_entity = unsafe { c.remove_component(entity_index.0 as usize, tick) }; if let Some(moved_idx) = moved_entity { if let Some((_, aid)) = removed_entity { // Make sure that the moved entity is the same as what was moved in other columns. assert!(moved_idx as u64 == aid.0); } else { // This is the first move, so find the Entity that was moved into this index. let just_removed = self.entities[moved_idx]; removed_entity = Some((just_removed, ArchetypeEntityId(moved_idx as u64))); } } else { // If there wasn't a moved entity, make sure no other columns moved something. assert!(removed_entity.is_none()); } } // safe from the .expect at the start of this method. //self.entity_ids.remove(&entity).unwrap(); // update the archetype index of the moved entity if let Some((moved, _old_idx)) = removed_entity { self.entity_ids.insert(moved, entity_index); } let removed = self.entities.swap_remove(entity_index.0 as _); assert_eq!(removed, entity); // now change the ArchetypeEntityId to be the index that the moved entity was moved into. removed_entity.map(|(e, _a)| (e, entity_index)) } /// Returns a boolean indicating whether this archetype can store the TypeIds given pub fn is_archetype_for(&self, types: &Vec) -> bool { if types.len() == self.columns.len() { self.columns.iter().all(|c| types.contains(&c.info.type_id())) } else { false } } /// Returns a boolean indicating whether this archetype has a column for `comp_type` pub fn has_column>(&self, comp_type: I) -> bool { let comp_type = comp_type.into(); self.columns.iter().any(|c| comp_type == c.info.type_id()) } /// Returns a boolean indicating whether this archetype is empty or not. pub fn is_empty(&self) -> bool { self.entity_ids.is_empty() } /// Returns the amount of entities that are stored in the archetype. pub fn len(&self) -> usize { self.entity_ids.len() } /// Grows columns in the archetype /// /// Parameters: /// * `new_capacity` - The new capacity of components that can fit in this column. /// /// # Safety /// /// Will panic if new_capacity is less than the current capacity fn grow_columns(&mut self, new_capacity: usize) { assert!(new_capacity > self.capacity); for c in self.columns.iter_mut() { unsafe { c.grow(new_capacity); } } self.capacity = new_capacity; } /// Attempts to find the column storing components of `type_id` pub fn get_column>(&self, type_id: I) -> Option<&ComponentColumn> { let type_id = type_id.into(); self.columns.iter().find(|c| c.info.type_id() == type_id) } /// Returns a mutable borrow to a component column for `type_id`. /// /// Note: This does not modify the tick for the column! pub fn get_column_mut>(&mut self, type_id: I) -> Option<&mut ComponentColumn> { let type_id = type_id.into(); self.columns.iter_mut().find(|c| c.info.type_id() == type_id) } /// Reserves a slot in the columns for an entity and returns the index of that reserved spot pub fn reserve_one(&mut self, entity: Entity) -> ArchetypeEntityId { if self.capacity == self.entity_ids.len() { let new_cap = self.capacity * 2; self.grow_columns(new_cap); self.capacity = new_cap; } debug_assert_eq!(self.entity_ids.len(), self.entities.len(), "Somehow the Archetype's entity storage got unsynced"); let entity_index = ArchetypeEntityId(self.entity_ids.len() as u64); self.entity_ids.insert(entity, entity_index); self.entities.push(entity); for col in self.columns.iter_mut() { col.len += 1; } entity_index } /// Ensure that the internal entity lists are synced in length pub(crate) fn ensure_synced(&self) { debug_assert_eq!(self.entity_ids.len(), self.entities.len(), "Somehow the Archetype's entity storage got unsynced"); } /// Moves the entity from this archetype into another one. /// /// # Safety /// The entity IS NOT removed from the old archetype. You must manually call [`Archetype::remove_entity`](crate::Archetype). /// It was done this way because I had some borrow check issues when writing [`World::insert`](crate::World) /// related to borrowing mutably from self more than once. /* pub fn move_into(&self, into_arch: &mut Archetype, entity: Entity, new_components: B) where B: Bundle { let new_index = into_arch.reserve_one(entity); //let infos: Vec = into_arch.columns.iter().map(|c| c.info).collect(); // move the existing components into the new archetype for col in self.columns.iter() { let into_col = into_arch.get_column_mut(col.info.type_id).unwrap(); // copy from the old column into the new column, then remove it from the old one unsafe { let ptr = col.borrow_ptr(); let ptr = NonNull::new_unchecked(ptr.as_ptr() .add(new_index.0 as usize * col.info.layout.size)); into_col.set_at(new_index.0 as _, ptr); } } // now move the new components into the new archetype new_components.take(|data, type_id, _size| { let col = into_arch.get_column_mut(type_id).unwrap(); unsafe { col.set_at(new_index.0 as _, data); } col.len += 1; }); //self.remove_entity(entity); } */ /// Returns a borrow to the map used to find the column indices of the entity. pub fn entity_indexes(&self) -> &HashMap { &self.entity_ids } /// Returns the Entity that is stored at the column matching `id`. pub fn entity_at_index(&self, id: ArchetypeEntityId) -> Option { self.entities.get(id.0 as usize).cloned() } /// Returns a boolean indicating if the Archetype is storing the entity. pub fn has_entity(&self, e: Entity) -> bool { self.entity_ids.contains_key(&e) } /// Extend the Archetype by adding more columns. /// /// In order to extend the Archetype, the archetype needs the components for the entities /// it already has. These are provided through the `new_columns` parameter. **If the Vec /// does not have the same amount of bundles in it as the amount of entities in the /// Archetype, it will panic!** pub fn extend(&mut self, tick: &Tick, new_columns: Vec) { debug_assert_eq!(new_columns.len(), self.len(), "The amount of provided column does not \ match the amount of entities"); let column_info = new_columns.iter() .next() .unwrap() .info(); for coli in column_info.into_iter() { let col = unsafe { ComponentColumn::new(coli, self.capacity) }; self.columns.push(col); } for (eid, bundle) in new_columns.into_iter().enumerate() { bundle.take(|ptr, tyid, _size| { unsafe { let col = self.get_column_mut(tyid).unwrap(); col.insert_entity(eid, ptr, tick.clone()); } }); } } } #[cfg(test)] mod tests { use std::{alloc::Layout, cell::Ref, ptr::NonNull}; use rand::Rng; use crate::{bundle::Bundle, tests::{Vec2, Vec3}, ComponentInfo, DynTypeId, DynamicBundle, Entity, EntityId, Tick}; use super::Archetype; #[test] fn one_entity_one_component() { let bundle = (Vec2::new(10.0, 20.0),); let entity = Entity { id: EntityId(0), generation: 0 }; let mut a = Archetype::from_bundle_info(super::ArchetypeId(0), bundle.info()); let entity_arch_id = a.add_entity(entity, bundle, &Tick::default()); let col = a.columns.get(0).unwrap(); let vec2: Ref = unsafe { col.get(entity_arch_id.0 as usize) }; assert_eq!(vec2.clone(), bundle.0); } #[test] fn one_entity_two_component() { let bundle = (Vec2::new(10.0, 20.0),Vec3::new(15.0, 54.0, 84.0)); let entity = Entity { id: EntityId(0), generation: 0 }; let mut a = Archetype::from_bundle_info(super::ArchetypeId(0), bundle.info()); let entity_arch_id = a.add_entity(entity, bundle, &Tick::default()); let col = a.columns.get(0).unwrap(); let vec2: Ref = unsafe { col.get(entity_arch_id.0 as usize) }; assert_eq!(vec2.clone(), bundle.0); let col = a.columns.get(1).unwrap(); let vec3: Ref = unsafe { col.get(entity_arch_id.0 as usize) }; assert_eq!(vec3.clone(), bundle.1); } #[test] fn two_entity_one_component() { let b1 = (Vec2::new(10.0, 20.0),); let e1 = Entity { id: EntityId(0), generation: 0 }; let b2 = (Vec2::new(19.0, 43.0),); let e2 = Entity { id: EntityId(1), generation: 0 }; let mut a = Archetype::from_bundle_info(super::ArchetypeId(0), b1.info()); let earch1 = a.add_entity(e1, b1, &Tick::default()); let earch2 = a.add_entity(e2, b2, &Tick::default()); let col = a.columns.get(0).unwrap(); let vec2: Ref = unsafe { col.get(earch1.0 as usize) }; assert_eq!(vec2.clone(), b1.0); let vec2: Ref = unsafe { col.get(earch2.0 as usize) }; assert_eq!(vec2.clone(), b2.0); } #[test] fn two_entity_two_component() { let b1 = (Vec2::new(10.0, 20.0), Vec3::new(84.0, 283.0, 28.0)); let e1 = Entity { id: EntityId(0), generation: 0 }; let b2 = (Vec2::new(19.0, 43.0), Vec3::new(74.0, 28.0, 93.0)); let e2 = Entity { id: EntityId(1), generation: 0 }; let mut a = Archetype::from_bundle_info(super::ArchetypeId(0), b1.info()); let earch1 = a.add_entity(e1, b1, &Tick::default()); let earch2 = a.add_entity(e2, b2, &Tick::default()); let col = a.columns.get(0).unwrap(); let vec2: Ref = unsafe { col.get(earch1.0 as usize) }; assert_eq!(vec2.clone(), b1.0); let vec2: Ref = unsafe { col.get(earch2.0 as usize) }; assert_eq!(vec2.clone(), b2.0); let col = a.columns.get(1).unwrap(); let vec3: Ref = unsafe { col.get(earch1.0 as usize) }; assert_eq!(vec3.clone(), b1.1); let vec3: Ref = unsafe { col.get(earch2.0 as usize) }; assert_eq!(vec3.clone(), b2.1); } /// Tests manual growth of archetypes #[test] fn archetype_growth() { let info = (Vec2::new(0.0, 0.0),).info(); let mut a = Archetype::from_bundle_info(super::ArchetypeId(0), info); a.grow_columns(64); a.grow_columns(128); a.grow_columns(256); } /// Tests the auto growth of archetypes when adding entities #[test] fn auto_archetype_growth() { let mut rng = rand::thread_rng(); let bundle_count = rng.gen_range(50..150); let mut bundles: Vec<(Vec2,)> = vec![]; bundles.reserve(bundle_count); let info = (Vec2::new(0.0, 0.0),).info(); let mut a = Archetype::from_bundle_info(super::ArchetypeId(0), info); for i in 0..bundle_count { let c = (Vec2::rand(),); bundles.push(c); a.add_entity( Entity { id: EntityId(i as u64), generation: 0 }, c, &Tick::default() ); } println!("Inserted {} entities", bundle_count); let col = a.columns.get(0).unwrap(); for i in 0..bundle_count { let vec2: Ref = unsafe { col.get(i) }; assert_eq!(vec2.clone(), bundles[i].0); } } #[test] fn remove_entity() { let bundles = vec![Vec2::rand(), Vec2::rand(), Vec2::rand()]; println!("Bundles: {:?}", bundles); let info = (Vec2::new(0.0, 0.0),).info(); let mut a = Archetype::from_bundle_info(super::ArchetypeId(0), info); // add the entities to the archetype for i in 0..bundles.len() { a.add_entity( Entity { id: EntityId(i as u64), generation: 0 }, (bundles[i],), &Tick::default() ); } // Remove the 'middle' entity in the column let moved_entity = a.remove_entity( Entity { id: EntityId(1u64), generation: 0, }, &Tick::default() ).expect("No entity was moved"); // The last entity in the column should have been moved assert!(moved_entity.0.id.0 == 2); assert!(moved_entity.1.0 == 1); // make sure that the entities' component was actually moved in the column let col = &a.columns[0]; let comp = unsafe { col.get::(1) }; assert_eq!(comp.clone(), bundles[2]); } /// This test simulates an archetype that stores types that rust does not know about. #[test] fn dynamic_archetype() { let layout = Layout::new::(); let info = ComponentInfo::new_unknown(DynTypeId::Unknown(100), "u32", layout); let infos = vec![info.clone()]; let mut a = Archetype::from_bundle_info(super::ArchetypeId(0), infos); let mut dynamic_bundle = DynamicBundle::default(); let comp = 50u32; let ptr = NonNull::from(&comp).cast::(); dynamic_bundle.push_unknown(ptr, info.clone()); a.add_entity( Entity { id: EntityId(0), generation: 0 }, dynamic_bundle, &Tick::default() ); let col = a.columns.iter().next().unwrap(); let ptr = col.borrow_ptr(); assert_eq!(unsafe { *ptr.cast::().as_ref() }, comp); assert_eq!(col.info, info); } /// Tests removing an entity from the Archetype when it is the only entity in it. #[test] fn remove_single_entity() { let info = (Vec2::new(0.0, 0.0),).info(); let mut a = Archetype::from_bundle_info(super::ArchetypeId(0), info); let ae = Entity { id: EntityId(0), generation: 0 }; a.add_entity( ae, Vec2::new(10.0, 50.0), &Tick::default() ); a.remove_entity(ae, &Tick::default()); } }