ecs: change state now moves when an entity moves to a different archetype

This commit is contained in:
Carter Anderson 2020-07-17 19:13:56 -07:00
parent 31d00ad861
commit 85ec31bb65
3 changed files with 44 additions and 19 deletions

View File

@ -133,6 +133,11 @@ impl Archetype {
Some(unsafe { NonNull::new_unchecked(state.modified_entities.as_ptr() as *mut bool) })
}
#[allow(missing_docs)]
pub fn get_type_state_mut(&mut self, ty: TypeId) -> Option<&mut TypeState> {
self.state.get_mut(&ty)
}
#[allow(missing_docs)]
pub fn borrow<T: Component>(&self) {
if self
@ -229,6 +234,7 @@ impl Archetype {
self.entities.len() as u32
}
#[allow(missing_docs)]
pub fn clear_trackers(&mut self) {
for type_state in self.state.values_mut() {
type_state.clear_trackers();
@ -302,6 +308,9 @@ impl Archetype {
removed,
ty.layout.size(),
);
let type_state = self.state.get_mut(&ty.id).unwrap();
type_state.modified_entities[index as usize] = type_state.modified_entities[last as usize];
}
}
self.len = last;
@ -317,7 +326,7 @@ impl Archetype {
pub(crate) unsafe fn move_to(
&mut self,
index: u32,
mut f: impl FnMut(*mut u8, TypeId, usize),
mut f: impl FnMut(*mut u8, TypeId, usize, bool),
) -> Option<u32> {
let last = self.len - 1;
for ty in &self.types {
@ -325,8 +334,9 @@ impl Archetype {
.get_dynamic(ty.id, ty.layout.size(), index)
.unwrap()
.as_ptr();
f(moved, ty.id(), ty.layout().size());
// TODO: copy component tracker state here
let type_state = self.state.get(&ty.id).unwrap();
let is_modified = type_state.modified_entities[index as usize];
f(moved, ty.id(), ty.layout().size(), is_modified);
if index != last {
ptr::copy_nonoverlapping(
self.get_dynamic(ty.id, ty.layout.size(), last)
@ -335,6 +345,8 @@ impl Archetype {
moved,
ty.layout.size(),
);
let type_state = self.state.get_mut(&ty.id).unwrap();
type_state.modified_entities[index as usize] = type_state.modified_entities[last as usize];
}
}
self.len -= 1;
@ -379,10 +391,10 @@ impl Drop for Archetype {
}
}
struct TypeState {
pub struct TypeState {
offset: usize,
borrow: AtomicBorrow,
modified_entities: Vec<bool>,
pub modified_entities: Vec<bool>,
}
impl TypeState {

View File

@ -386,8 +386,9 @@ impl World {
let target_index = target_arch.allocate(entity.id());
loc.archetype = target;
let old_index = mem::replace(&mut loc.index, target_index);
if let Some(moved) = source_arch.move_to(old_index, |ptr, ty, size| {
if let Some(moved) = source_arch.move_to(old_index, |ptr, ty, size, is_modified| {
target_arch.put_dynamic(ptr, ty, size, target_index);
target_arch.get_type_state_mut(ty).unwrap().modified_entities[target_index as usize] = is_modified;
}) {
self.entities.get_mut(Entity::with_id(moved)).unwrap().index = old_index;
}
@ -463,10 +464,11 @@ impl World {
let target_index = target_arch.allocate(entity.id());
loc.archetype = target;
loc.index = target_index;
if let Some(moved) = source_arch.move_to(old_index, |src, ty, size| {
if let Some(moved) = source_arch.move_to(old_index, |src, ty, size, is_modified| {
// 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) {
ptr::copy_nonoverlapping(src, dst.as_ptr(), size);
target_arch.get_type_state_mut(ty).unwrap().modified_entities[target_index as usize] = is_modified;
}
}) {
self.entities.get_mut(Entity::with_id(moved)).unwrap().index = old_index;

View File

@ -136,7 +136,7 @@ mod tests {
fn modified_trackers() {
let mut world = World::default();
let e1 = world.spawn((A(0), B));
world.spawn((A(0), B));
let e2 = world.spawn((A(0), B));
let e3 = world.spawn((A(0), B));
world.spawn((A(0), B));
@ -146,20 +146,31 @@ mod tests {
}
}
let changed_entities = world
.query::<Changed<A, Entity>>()
.iter()
.collect::<Vec<Entity>>();
assert_eq!(changed_entities, vec![e1, e3]);
fn get_changed(world: &World) -> Vec<Entity> {
world
.query::<Changed<A, Entity>>()
.iter()
.collect::<Vec<Entity>>()
};
assert_eq!(get_changed(&world), vec![e1, e3]);
// ensure changing an entity's archetypes also moves its modified state
world.insert(e1, (C,)).unwrap();
let changed_entities = world
.query::<Changed<A, Entity>>()
.iter()
.collect::<Vec<Entity>>();
assert_eq!(changed_entities, vec![e1, e3]);
assert_eq!(get_changed(&world), vec![e3, e1], "changed entities list should not change (although the order will due to archetype moves)");
// spawning a new A entity should not change existing modified state
world.insert(e1, (A(0), B)).unwrap();
assert_eq!(get_changed(&world), vec![e3, e1], "changed entities list should not change");
// removing an unchanged entity should not change modified state
world.despawn(e2).unwrap();
assert_eq!(get_changed(&world), vec![e3, e1], "changed entities list should not change");
// removing a changed entity should remove it from enumeration
world.despawn(e1).unwrap();
assert_eq!(get_changed(&world), vec![e3], "e1 should no longer be returned");
world.clear_trackers();