Implement relationships in the ECS #3

Merged
SeanOMik merged 5 commits from feature/ecs-relations into main 2024-03-03 21:22:40 +00:00
19 changed files with 78 additions and 90 deletions
Showing only changes of commit 6210778e9d - Show all commits

View File

@ -18,9 +18,8 @@ impl Drop for ComponentColumn {
let data = data.as_ptr();
unsafe {
// layout of current alloc
let layout = Layout::from_size_align_unchecked(self.info.layout.size() * self.capacity,
self.info.layout.align());
// TODO: trigger drop on the components
let layout = self.info.layout();
dealloc(data, layout);
}
}
@ -42,7 +41,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(), capacity);
Self {
data: RefCell::new(data),
@ -64,7 +63,7 @@ impl ComponentColumn {
let mut data = self.data.borrow_mut();
let data = data.deref_mut();
let size = self.info.layout.size();
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);
@ -87,27 +86,28 @@ impl ComponentColumn {
Ref::map(data, |data| {
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()
})
}
/// Get a component at an entities index.
/// 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<T>(&mut self, entity_index: usize, tick: &Tick) -> &mut T {
pub unsafe fn get_mut<T>(&mut self, entity_index: usize, tick: &Tick) -> RefMut<T> {
self.entity_ticks[entity_index].tick_to(tick);
let mut data = self.data.borrow_mut();
let data = data.deref_mut();
let data = self.data.borrow_mut();
let p = data.as_ptr()
.cast::<T>()
.add(entity_index * self.info.layout.size());
&mut *p
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.
@ -125,17 +125,19 @@ impl ComponentColumn {
let mut data = self.data.borrow_mut();
let mut new_ptr = Self::alloc(self.info.layout, new_capacity);
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 * self.info.layout.size());
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(
self.info.layout.size().checked_mul(self.capacity).unwrap(),
self.info.layout.align()
layout.size().checked_mul(self.capacity).unwrap(),
layout.align()
);
mem::swap(data.deref_mut(), &mut new_ptr);
@ -154,7 +156,7 @@ impl ComponentColumn {
let mut data = self.data.borrow_mut();
let data = data.deref_mut();
let size = self.info.layout.size();
let size = self.info.layout().size();
let mut old_comp_ptr = NonNull::new_unchecked(data.as_ptr()
.add(entity_index * size));
@ -328,13 +330,14 @@ impl Archetype {
/// Returns a boolean indicating whether this archetype can store the TypeIds given
pub fn is_archetype_for(&self, types: &Vec<DynTypeId>) -> bool {
if types.len() == self.columns.len() {
self.columns.iter().all(|c| types.contains(&c.info.type_id))
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: DynTypeId) -> bool {
self.columns.iter().any(|c| comp_type == c.info.type_id)
pub fn has_column<I: Into<DynTypeId>>(&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.
@ -368,7 +371,7 @@ impl Archetype {
/// Attempts to find the column storing components of `type_id`
pub fn get_column<I: Into<DynTypeId>>(&self, type_id: I) -> Option<&ComponentColumn> {
let type_id = type_id.into();
self.columns.iter().find(|c| c.info.type_id == type_id)
self.columns.iter().find(|c| c.info.type_id() == type_id)
}
/// Returns a mutable borrow to a component column for `type_id`.
@ -376,7 +379,7 @@ impl Archetype {
/// Note: This does not modify the tick for the column!
pub fn get_column_mut<I: Into<DynTypeId>>(&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)
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

View File

@ -143,7 +143,7 @@ impl DynamicBundle {
impl Bundle for DynamicBundle {
fn type_ids(&self) -> Vec<DynTypeId> {
self.bundle.iter().map(|b| b.1.type_id).collect()
self.bundle.iter().map(|b| b.1.type_id()).collect()
}
fn info(&self) -> Vec<ComponentInfo> {
@ -152,7 +152,7 @@ impl Bundle for DynamicBundle {
fn take(self, mut f: impl FnMut(NonNull<u8>, DynTypeId, usize)) {
for (data, info) in self.bundle.iter() {
f(*data, info.type_id, info.layout.size());
f(*data, info.type_id(), info.layout().size());
}
}

View File

@ -54,8 +54,8 @@ impl DynTypeId {
/// Some information about a component.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ComponentInfo {
pub type_id: DynTypeId,
pub(crate) layout: Layout,
type_id: DynTypeId,
layout: Layout,
}
impl ComponentInfo {
@ -77,4 +77,12 @@ impl ComponentInfo {
layout,
}
}
pub fn type_id(&self) -> DynTypeId {
self.type_id
}
pub fn layout(&self) -> Layout {
self.layout
}
}

View File

@ -1,13 +1,12 @@
use std::{marker::PhantomData, ptr::NonNull, cell::{Ref, RefMut}};
use crate::{world::World, ComponentColumn, DynTypeId, Tick, Component};
use crate::{World, ComponentColumn, DynTypeId, Tick, Component};
use super::{Fetch, Query, AsQuery};
/// Fetcher for borrowing components from archetypes.
pub struct FetchBorrow<'a, T> {
col: &'a ComponentColumn,
size: usize,
_phantom: PhantomData<&'a T>
}
@ -22,13 +21,7 @@ where
}
unsafe fn get_item(&mut self, entity: crate::world::ArchetypeEntityId) -> Self::Item {
let ptr = self.col.borrow_ptr();
Ref::map(ptr, |ptr| {
let ptr = NonNull::new_unchecked(ptr.as_ptr()
.add(entity.0 as usize * self.size))
.cast();
&*ptr.as_ptr()
})
self.col.get(entity.0 as _)
}
}
@ -81,17 +74,16 @@ where
}
fn can_visit_archetype(&self, archetype: &crate::archetype::Archetype) -> bool {
archetype.columns.iter().any(|c| c.info.type_id == self.type_id)
archetype.has_column(self.type_id)
}
unsafe fn fetch<'a>(&self, _world: &'a World, archetype: &'a crate::archetype::Archetype, tick: crate::Tick) -> Self::Fetch<'a> {
let _ = tick;
let col = archetype.columns.iter().find(|c| c.info.type_id == self.type_id)
let col = archetype.get_column(self.type_id)
.expect("You ignored 'can_visit_archetype'!");
FetchBorrow {
col,
size: col.info.layout.size(),
_phantom: PhantomData,
}
}
@ -108,7 +100,6 @@ impl<T: Component> AsQuery for &T {
/// A fetcher for mutably borrowing components from archetypes.
pub struct FetchBorrowMut<'a, T> {
col: NonNull<ComponentColumn>,
size: usize,
tick: Tick,
_phantom: PhantomData<&'a T>
}
@ -125,15 +116,7 @@ where
unsafe fn get_item(&mut self, entity: crate::world::ArchetypeEntityId) -> Self::Item {
let col = unsafe { self.col.as_mut() };
col.entity_ticks[entity.0 as usize] = self.tick;
let ptr = col.borrow_mut_ptr();
RefMut::map(ptr, |ptr| {
let ptr = NonNull::new_unchecked(ptr.as_ptr()
.add(entity.0 as usize * self.size))
.cast();
&mut *ptr.as_ptr()
})
col.get_mut(entity.0 as _, &self.tick)
}
}
@ -188,13 +171,12 @@ where
}
fn can_visit_archetype(&self, archetype: &crate::archetype::Archetype) -> bool {
archetype.columns.iter().any(|c| c.info.type_id == self.type_id)
archetype.has_column(self.type_id)
}
unsafe fn fetch<'a>(&self, _world: &'a World, archetype: &'a crate::archetype::Archetype, tick: crate::Tick) -> Self::Fetch<'a> {
let col = archetype.columns.iter().find(|c| c.info.type_id == self.type_id)
let col = archetype.get_column(self.type_id)
.expect("You ignored 'can_visit_archetype'!");
let layout_size = col.info.layout.size();
let col = NonNull::from(col);
// TODO: find a way to get the component column mutable with a borrowed archetype so its tick can be updated.
@ -202,7 +184,6 @@ where
FetchBorrowMut {
col,
size: layout_size,
tick,
_phantom: PhantomData,
}
@ -219,9 +200,9 @@ impl<T: Component> AsQuery for &mut T {
#[cfg(test)]
mod tests {
use std::{mem::size_of, marker::PhantomData, ptr::NonNull};
use std::{marker::PhantomData, ptr::NonNull};
use crate::{archetype::{Archetype, ArchetypeId}, bundle::Bundle, query::{Fetch, ViewState}, tests::Vec2, world::World, DynTypeId, Entity, EntityId, Tick};
use crate::{archetype::{Archetype, ArchetypeId}, bundle::Bundle, query::{Fetch, ViewState}, tests::Vec2, World, DynTypeId, Entity, EntityId, Tick};
use super::{QueryBorrow, QueryBorrowMut, FetchBorrowMut};
@ -298,12 +279,11 @@ mod tests {
}, (Vec2::rand(),), &Tick::default());
}
let col = a.columns.iter().find(|c| c.info.type_id == DynTypeId::of::<Vec2>()).unwrap();
let col = a.get_column(DynTypeId::of::<Vec2>()).unwrap();
let mut bmut = FetchBorrowMut::<Vec2> {
col: NonNull::from(col),
tick: Tick::default(),
size: size_of::<Vec2>(),
_phantom: PhantomData,
};
let item = unsafe { bmut.get_item(crate::ArchetypeEntityId(0)) };

View File

@ -1,6 +1,6 @@
use std::ptr::NonNull;
use crate::{world::World, ComponentColumn, ComponentInfo};
use crate::{World, ComponentColumn, ComponentInfo};
mod view;
pub use view::*;
@ -15,7 +15,7 @@ pub struct DynamicType {
impl DynamicType {
pub fn is<T: 'static>(&self) -> bool {
self.info.type_id.is::<T>()
self.info.type_id().is::<T>()
}
}
@ -38,7 +38,7 @@ impl<'a> Fetch<'a> for FetchDynamicType<'a> {
unsafe fn get_item(&mut self, entity: crate::ArchetypeEntityId) -> Self::Item {
let ptr = self.col.borrow_ptr();
let ptr = NonNull::new_unchecked(ptr.as_ptr()
.add(entity.0 as usize * self.info.layout.size()));
.add(entity.0 as usize * self.info.layout().size()));
DynamicType {
info: self.info,
@ -63,11 +63,11 @@ impl QueryDynamicType {
}
pub fn can_visit_archetype(&self, archetype: &crate::archetype::Archetype) -> bool {
archetype.has_column(self.info.type_id)
archetype.has_column(self.info.type_id())
}
pub unsafe fn fetch<'a>(&self, _world: &'a World, _arch_id: crate::archetype::ArchetypeId, archetype: &'a crate::archetype::Archetype) -> FetchDynamicType<'a> {
let col = archetype.columns.iter().find(|c| c.info.type_id == self.info.type_id)
let col = archetype.get_column(self.info.type_id())
.expect("You ignored 'can_visit_archetype'!");
FetchDynamicType {

View File

@ -1,6 +1,6 @@
use std::ops::Range;
use crate::{world::World, Archetype, ArchetypeEntityId, ArchetypeId, query::Fetch};
use crate::{World, Archetype, ArchetypeEntityId, ArchetypeId, query::Fetch};
use super::{QueryDynamicType, FetchDynamicType, DynamicType};
@ -104,7 +104,7 @@ impl<'a> Iterator for DynamicViewIter<'a> {
mod tests {
use std::{alloc::Layout, ptr::NonNull};
use crate::{world::World, ComponentInfo, DynTypeId, DynamicBundle, query::dynamic::QueryDynamicType};
use crate::{World, ComponentInfo, DynTypeId, DynamicBundle, query::dynamic::QueryDynamicType};
use super::DynamicView;

View File

@ -1,4 +1,4 @@
use crate::{archetype::Archetype, world::World, Entity};
use crate::{archetype::Archetype, World, Entity};
use super::{Fetch, Query, AsQuery};

View File

@ -125,7 +125,7 @@ impl AsQuery for () {
#[cfg(test)]
mod tests {
use crate::{world::World, archetype::Archetype, tests::Vec2};
use crate::{World, archetype::Archetype, tests::Vec2};
use super::{ViewState, Entities};

View File

@ -1,6 +1,6 @@
use std::{marker::PhantomData, cell::{Ref, RefMut}};
use crate::{world::World, resource::ResourceObject};
use crate::{World, resource::ResourceObject};
use super::{Query, Fetch, AsQuery};
@ -192,7 +192,7 @@ impl<'a, T: ResourceObject> AsQuery for ResMut<'a, T> {
mod tests {
use std::ops::{Deref, DerefMut};
use crate::{query::{Res, ResMut}, tests::{Vec2, Vec3}, world::World};
use crate::{query::{Res, ResMut}, tests::{Vec2, Vec3}, World};
struct SomeCounter(u32);

View File

@ -1,6 +1,6 @@
use std::marker::PhantomData;
use crate::{ComponentColumn, Tick, DynTypeId, world::World};
use crate::{ComponentColumn, Tick, DynTypeId, World};
use super::{Query, Fetch, AsQuery};
@ -90,11 +90,11 @@ where
}
fn can_visit_archetype(&self, archetype: &crate::archetype::Archetype) -> bool {
archetype.columns.iter().any(|c| c.info.type_id == self.type_id)
archetype.has_column(self.type_id)
}
unsafe fn fetch<'a>(&self, _world: &'a World, archetype: &'a crate::archetype::Archetype, _tick: crate::Tick) -> Self::Fetch<'a> {
let col = archetype.columns.iter().find(|c| c.info.type_id == self.type_id)
let col = archetype.get_column(self.type_id)
.expect("You ignored 'can_visit_archetype'!");
FetchTickOf {

View File

@ -1,4 +1,4 @@
use crate::world::World;
use crate::World;
use super::{Query, Fetch, AsQuery};
@ -175,7 +175,7 @@ impl_bundle_tuple! { Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13, Q14
#[cfg(test)]
mod tests {
use crate::{world::World, tests::{Vec2, Vec3}};
use crate::{World, tests::{Vec2, Vec3}};
#[test]
fn tuple_queries() {

View File

@ -1,4 +1,4 @@
use std::{any::TypeId, cell::Ref, marker::PhantomData};
use std::{any::{Any, TypeId}, cell::Ref, marker::PhantomData};
use crate::{query::{AsQuery, Fetch, Query}, Archetype, ComponentColumn, Entity, World};
@ -69,8 +69,7 @@ where
unsafe fn fetch<'a>(&self, _world: &'a World, archetype: &'a crate::archetype::Archetype, tick: crate::Tick) -> Self::Fetch<'a> {
let _ = tick;
let tyid = crate::DynTypeId::Rust(TypeId::of::<RelationOriginComponent<R>>());
let col = archetype.columns.iter().find(|c| c.info.type_id == tyid)
let col = archetype.get_column(self.type_id())
.expect("You ignored 'can_visit_archetype'!");
FetchRelatePair {

View File

@ -70,14 +70,12 @@ where
}
fn can_visit_archetype(&self, archetype: &crate::archetype::Archetype) -> bool {
let tyid = crate::DynTypeId::Rust(TypeId::of::<RelationOriginComponent<R>>());
archetype.has_column(tyid)
archetype.has_column(TypeId::of::<RelationOriginComponent<R>>())
}
unsafe fn fetch<'a>(&self, _world: &'a World, archetype: &'a crate::archetype::Archetype, tick: crate::Tick) -> Self::Fetch<'a> {
let _ = tick;
let tyid = crate::DynTypeId::Rust(TypeId::of::<RelationOriginComponent<R>>());
let col = archetype.columns.iter().find(|c| c.info.type_id == tyid)
let col = archetype.get_column(TypeId::of::<RelationOriginComponent<R>>())
.expect("You ignored 'can_visit_archetype'!");
FetchRelatesTo {

View File

@ -1,4 +1,4 @@
use lyra_ecs::world::World;
use lyra_ecs::World;
use crate::Access;

View File

@ -1,6 +1,6 @@
use std::ptr::NonNull;
use lyra_ecs::world::World;
use lyra_ecs::World;
/// An enum that is used to control if the Criteria was met or not.
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]

View File

@ -1,7 +1,7 @@
use std::{any::Any, marker::PhantomData, ptr::NonNull};
use paste::paste;
use crate::{world::World, Access, ResourceObject, query::{Query, ViewState, ResMut, Res}};
use crate::{World, Access, ResourceObject, query::{Query, ViewState, ResMut, Res}};
use super::{System, IntoSystem};
@ -239,7 +239,7 @@ impl<R: ResourceObject> FnArgFetcher for ResMut<'_, R> {
mod tests {
use std::ptr::NonNull;
use crate::{tests::{Vec2, Vec3}, world::World, query::{QueryBorrow, ViewState, ResMut}};
use crate::{tests::{Vec2, Vec3}, World, query::{QueryBorrow, ViewState, ResMut}};
use super::{System, IntoSystem};
struct SomeCounter(u32);

View File

@ -2,7 +2,7 @@ use std::{collections::{HashMap, VecDeque, HashSet}, ptr::NonNull};
use super::System;
use crate::{world::World, CommandQueue, Commands};
use crate::{World, CommandQueue, Commands};
#[derive(thiserror::Error, Debug)]
pub enum GraphExecutorError {
@ -140,7 +140,7 @@ impl GraphExecutor {
mod tests {
use std::ptr::NonNull;
use crate::{query::{ResMut, View}, system::IntoSystem, world::World};
use crate::{query::{ResMut, View}, system::IntoSystem, World};
use super::GraphExecutor;

View File

@ -1,6 +1,6 @@
use std::ptr::NonNull;
use crate::{world::World, Access};
use crate::{World, Access};
mod graph;
pub use graph::*;

View File

@ -143,7 +143,7 @@ impl World {
let record = self.entities.entity_record(entity).unwrap();
let current_arch = self.archetypes.get(&record.id).unwrap();
let mut col_types: Vec<DynTypeId> = current_arch.columns.iter().map(|c| c.info.type_id).collect();
let mut col_types: Vec<DynTypeId> = current_arch.columns.iter().map(|c| c.info.type_id()).collect();
let orig_col = col_types.clone();
col_types.extend(bundle.type_ids());
@ -158,7 +158,7 @@ impl World {
for (col_type, (col_ptr, col_info)) in orig_col.into_iter().zip(col_ptrs.into_iter()) {
unsafe {
let ptr = NonNull::new_unchecked(col_ptr.as_ptr()
.add(res_index.0 as usize * col_info.layout.size()));
.add(res_index.0 as usize * col_info.layout().size()));
let col = arch.get_column_mut(col_type).unwrap();
col.set_at(res_index.0 as _, ptr, tick);
}