ecs: Added<T> queries

This commit is contained in:
Carter Anderson 2020-07-21 22:00:11 -07:00
parent 21cdaaf7eb
commit a6953049fb
4 changed files with 133 additions and 22 deletions

View file

@ -134,6 +134,13 @@ impl Archetype {
Some(unsafe { NonNull::new_unchecked(state.modified_entities.as_ptr() as *mut bool) }) Some(unsafe { NonNull::new_unchecked(state.modified_entities.as_ptr() as *mut bool) })
} }
#[allow(missing_docs)]
#[inline]
pub fn get_added<T: Component>(&self) -> Option<NonNull<bool>> {
let state = self.state.get(&TypeId::of::<T>())?;
Some(unsafe { NonNull::new_unchecked(state.added_entities.as_ptr() as *mut bool) })
}
#[allow(missing_docs)] #[allow(missing_docs)]
pub fn get_type_state_mut(&mut self, ty: TypeId) -> Option<&mut TypeState> { pub fn get_type_state_mut(&mut self, ty: TypeId) -> Option<&mut TypeState> {
self.state.get_mut(&ty) self.state.get_mut(&ty)
@ -258,6 +265,7 @@ impl Archetype {
for type_state in self.state.values_mut() { for type_state in self.state.values_mut() {
type_state.modified_entities.resize_with(count, || false); type_state.modified_entities.resize_with(count, || false);
type_state.added_entities.resize_with(count, || false);
} }
let old_data_size = mem::replace(&mut self.data_size, 0); let old_data_size = mem::replace(&mut self.data_size, 0);
@ -318,6 +326,7 @@ impl Archetype {
let type_state = self.state.get_mut(&ty.id).unwrap(); let type_state = self.state.get_mut(&ty.id).unwrap();
type_state.modified_entities[index as usize] = type_state.modified_entities[last as usize]; type_state.modified_entities[index as usize] = type_state.modified_entities[last as usize];
type_state.added_entities[index as usize] = type_state.added_entities[last as usize];
} }
} }
self.len = last; self.len = last;
@ -333,7 +342,7 @@ impl Archetype {
pub(crate) unsafe fn move_to( pub(crate) unsafe fn move_to(
&mut self, &mut self,
index: u32, index: u32,
mut f: impl FnMut(*mut u8, TypeId, usize, bool), mut f: impl FnMut(*mut u8, TypeId, usize, bool, bool),
) -> Option<u32> { ) -> Option<u32> {
let last = self.len - 1; let last = self.len - 1;
for ty in &self.types { for ty in &self.types {
@ -342,8 +351,9 @@ impl Archetype {
.unwrap() .unwrap()
.as_ptr(); .as_ptr();
let type_state = self.state.get(&ty.id).unwrap(); let type_state = self.state.get(&ty.id).unwrap();
let is_added= type_state.added_entities[index as usize];
let is_modified = type_state.modified_entities[index as usize]; let is_modified = type_state.modified_entities[index as usize];
f(moved, ty.id(), ty.layout().size(), is_modified); f(moved, ty.id(), ty.layout().size(), is_added, is_modified);
if index != last { if index != last {
ptr::copy_nonoverlapping( ptr::copy_nonoverlapping(
self.get_dynamic(ty.id, ty.layout.size(), last) self.get_dynamic(ty.id, ty.layout.size(), last)
@ -353,6 +363,7 @@ impl Archetype {
ty.layout.size(), ty.layout.size(),
); );
let type_state = self.state.get_mut(&ty.id).unwrap(); let type_state = self.state.get_mut(&ty.id).unwrap();
type_state.added_entities[index as usize] = type_state.added_entities[last as usize];
type_state.modified_entities[index as usize] = type_state.modified_entities[last as usize]; type_state.modified_entities[index as usize] = type_state.modified_entities[last as usize];
} }
} }
@ -366,11 +377,14 @@ impl Archetype {
} }
#[allow(missing_docs)] #[allow(missing_docs)]
pub unsafe fn put_dynamic(&mut self, component: *mut u8, ty: TypeId, size: usize, index: u32) { pub unsafe fn put_dynamic(&mut self, component: *mut u8, ty: TypeId, size: usize, index: u32, added: bool) {
let ptr = self let state = self.state.get_mut(&ty).unwrap();
.get_dynamic(ty, size, index) if added {
.unwrap() state.added_entities[index as usize] = true;
}
let ptr = (*self.data.get())
.as_ptr() .as_ptr()
.add(state.offset + size * index as usize)
.cast::<u8>(); .cast::<u8>();
ptr::copy_nonoverlapping(component, ptr, size); ptr::copy_nonoverlapping(component, ptr, size);
} }
@ -402,6 +416,7 @@ pub struct TypeState {
offset: usize, offset: usize,
borrow: AtomicBorrow, borrow: AtomicBorrow,
pub modified_entities: Vec<bool>, pub modified_entities: Vec<bool>,
pub added_entities: Vec<bool>,
} }
impl TypeState { impl TypeState {
@ -410,6 +425,7 @@ impl TypeState {
offset: 0, offset: 0,
borrow: AtomicBorrow::new(), borrow: AtomicBorrow::new(),
modified_entities: Vec::new(), modified_entities: Vec::new(),
added_entities: Vec::new(),
} }
} }
@ -417,6 +433,10 @@ impl TypeState {
for modified in self.modified_entities.iter_mut() { for modified in self.modified_entities.iter_mut() {
*modified = false; *modified = false;
} }
for added in self.added_entities.iter_mut() {
*added = false;
}
} }
} }

View file

@ -150,7 +150,6 @@ impl<T: Query> Query for Option<T> {
type Fetch = TryFetch<T::Fetch>; type Fetch = TryFetch<T::Fetch>;
} }
/// Unique borrow of an entity's component /// Unique borrow of an entity's component
pub struct Mut<'a, T: Component> { pub struct Mut<'a, T: Component> {
value: &'a mut T, value: &'a mut T,
@ -279,6 +278,61 @@ impl<'a, T: Component> Fetch<'a> for FetchChanged<T> {
} }
} }
#[allow(missing_docs)]
pub struct Added<'a, T> {
value: &'a T,
}
impl<'a, T: Component> Deref for Added<'a, T> {
type Target = T;
fn deref(&self) -> &T {
self.value
}
}
impl<'a, T: Component> Query for Added<'a, T> {
type Fetch = FetchAdded<T>;
}
#[doc(hidden)]
pub struct FetchAdded<T>(NonNull<T>, NonNull<bool>);
impl<'a, T: Component> Fetch<'a> for FetchAdded<T> {
type Item = Added<'a, T>;
fn access(archetype: &Archetype) -> Option<Access> {
if archetype.has::<T>() {
Some(Access::Read)
} else {
None
}
}
fn borrow(archetype: &Archetype) {
archetype.borrow::<T>();
}
unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option<Self> {
let components = NonNull::new_unchecked(archetype.get::<T>()?.as_ptr().add(offset));
let modified = NonNull::new_unchecked(archetype.get_added::<T>()?.as_ptr().add(offset));
Some(Self(components, modified))
}
fn release(archetype: &Archetype) {
archetype.release::<T>();
}
unsafe fn should_skip(&self) -> bool {
// skip if the current item wasn't changed
!*self.1.as_ref()
}
#[inline]
unsafe fn next(&mut self) -> Self::Item {
self.1 = NonNull::new_unchecked(self.1.as_ptr().add(1));
let value = self.0.as_ptr();
self.0 = NonNull::new_unchecked(value.add(1));
Added { value: &*value }
}
}
#[doc(hidden)] #[doc(hidden)]
pub struct TryFetch<T>(Option<T>); pub struct TryFetch<T>(Option<T>);
@ -782,6 +836,38 @@ mod tests {
struct B(usize); struct B(usize);
struct C; struct C;
#[test]
fn added_trackers() {
let mut world = World::default();
let e1 = world.spawn((A(0),));
fn get_added<Com: Component>(world: &World) -> Vec<Entity> {
world
.query::<(Added<Com>, Entity)>()
.iter()
.map(|(_added, e)| e)
.collect::<Vec<Entity>>()
};
assert_eq!(get_added::<A>(&world), vec![e1]);
world.insert(e1, (B(0),)).unwrap();
assert_eq!(get_added::<A>(&world), vec![e1]);
assert_eq!(get_added::<B>(&world), vec![e1]);
world.clear_trackers();
assert!(get_added::<A>(&world).is_empty());
let e2 = world.spawn((A(1), B(1)));
assert_eq!(get_added::<A>(&world), vec![e2]);
assert_eq!(get_added::<B>(&world), vec![e2]);
let added = world
.query::<(Entity, Added<A>, Added<B>)>()
.iter()
.map(|a| a.0)
.collect::<Vec<Entity>>();
assert_eq!(added, vec![e2]);
}
#[test] #[test]
fn modified_trackers() { fn modified_trackers() {
let mut world = World::default(); let mut world = World::default();

View file

@ -106,7 +106,7 @@ impl World {
unsafe { unsafe {
let index = archetype.allocate(entity.id()); let index = archetype.allocate(entity.id());
components.put(|ptr, ty, size| { components.put(|ptr, ty, size| {
archetype.put_dynamic(ptr, ty, size, index); archetype.put_dynamic(ptr, ty, size, index, true);
true true
}); });
self.entities.insert( self.entities.insert(
@ -371,7 +371,7 @@ impl World {
// Update components in the current archetype // Update components in the current archetype
let arch = &mut self.archetypes[loc.archetype as usize]; let arch = &mut self.archetypes[loc.archetype as usize];
components.put(|ptr, ty, size| { components.put(|ptr, ty, size| {
arch.put_dynamic(ptr, ty, size, loc.index); arch.put_dynamic(ptr, ty, size, loc.index, false);
true true
}); });
return Ok(()); return Ok(());
@ -386,14 +386,17 @@ impl World {
let target_index = target_arch.allocate(entity.id()); let target_index = target_arch.allocate(entity.id());
loc.archetype = target; loc.archetype = target;
let old_index = mem::replace(&mut loc.index, target_index); let old_index = mem::replace(&mut loc.index, target_index);
if let Some(moved) = source_arch.move_to(old_index, |ptr, ty, size, is_modified| { if let Some(moved) = source_arch.move_to(old_index, |ptr, ty, size, is_added, is_modified| {
target_arch.put_dynamic(ptr, ty, size, target_index); target_arch.put_dynamic(ptr, ty, size, target_index, false);
target_arch.get_type_state_mut(ty).unwrap().modified_entities[target_index as usize] = is_modified; let type_state = target_arch.get_type_state_mut(ty).unwrap();
type_state.added_entities[target_index as usize] = is_added;
type_state.modified_entities[target_index as usize] = is_modified;
}) { }) {
self.entities.get_mut(Entity::with_id(moved)).unwrap().index = old_index; self.entities.get_mut(Entity::with_id(moved)).unwrap().index = old_index;
} }
components.put(|ptr, ty, size| { components.put(|ptr, ty, size| {
target_arch.put_dynamic(ptr, ty, size, target_index); target_arch.put_dynamic(ptr, ty, size, target_index, true);
true true
}); });
} }
@ -464,11 +467,13 @@ impl World {
let target_index = target_arch.allocate(entity.id()); let target_index = target_arch.allocate(entity.id());
loc.archetype = target; loc.archetype = target;
loc.index = target_index; loc.index = target_index;
if let Some(moved) = source_arch.move_to(old_index, |src, ty, size, is_modified| { if let Some(moved) = source_arch.move_to(old_index, |src, ty, size, is_added, is_modified| {
// Only move the components present in the target archetype, i.e. the non-removed ones. // Only move the components present in the target archetype, i.e. the non-removed ones.
if let Some(dst) = target_arch.get_dynamic(ty, size, target_index) { if let Some(dst) = target_arch.get_dynamic(ty, size, target_index) {
ptr::copy_nonoverlapping(src, dst.as_ptr(), size); ptr::copy_nonoverlapping(src, dst.as_ptr(), size);
target_arch.get_type_state_mut(ty).unwrap().modified_entities[target_index as usize] = is_modified; let state = target_arch.get_type_state_mut(ty).unwrap();
state.added_entities[target_index as usize] = is_added;
state.modified_entities[target_index as usize] = is_modified;
} }
}) { }) {
self.entities.get_mut(Entity::with_id(moved)).unwrap().index = old_index; self.entities.get_mut(Entity::with_id(moved)).unwrap().index = old_index;
@ -746,7 +751,7 @@ where
unsafe { unsafe {
let index = self.archetype.allocate(entity.id()); let index = self.archetype.allocate(entity.id());
components.put(|ptr, ty, size| { components.put(|ptr, ty, size| {
self.archetype.put_dynamic(ptr, ty, size, index); self.archetype.put_dynamic(ptr, ty, size, index, true);
true true
}); });
self.entities.insert( self.entities.insert(

View file

@ -65,13 +65,13 @@ impl Resources {
}); });
let archetype = &mut data.archetype; let archetype = &mut data.archetype;
let mut added = false;
let index = match resource_index { let index = match resource_index {
ResourceIndex::Global => *data.default_index.get_or_insert_with(|| archetype.len()), ResourceIndex::Global => *data.default_index.get_or_insert_with(|| { added = true; archetype.len() }),
ResourceIndex::System(id) => *data ResourceIndex::System(id) => *data
.system_id_to_archetype_index .system_id_to_archetype_index
.entry(id.0) .entry(id.0)
.or_insert_with(|| archetype.len()), .or_insert_with(|| { added = true; archetype.len() }),
}; };
if index == archetype.len() { if index == archetype.len() {
@ -82,7 +82,7 @@ impl Resources {
unsafe { unsafe {
let resource_ptr = (&mut resource as *mut T).cast::<u8>(); let resource_ptr = (&mut resource as *mut T).cast::<u8>();
archetype.put_dynamic(resource_ptr, type_id, core::mem::size_of::<T>(), index); archetype.put_dynamic(resource_ptr, type_id, core::mem::size_of::<T>(), index, added);
std::mem::forget(resource); std::mem::forget(resource);
} }
} }