From 85ec31bb65af55a4e0a6c9c4cc0d8065d04d66c3 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Fri, 17 Jul 2020 19:13:56 -0700 Subject: [PATCH] ecs: change state now moves when an entity moves to a different archetype --- crates/bevy_ecs/hecs/src/archetype.rs | 22 ++++++++++++---- crates/bevy_ecs/hecs/src/world.rs | 6 +++-- crates/bevy_ecs/src/world/component.rs | 35 +++++++++++++++++--------- 3 files changed, 44 insertions(+), 19 deletions(-) diff --git a/crates/bevy_ecs/hecs/src/archetype.rs b/crates/bevy_ecs/hecs/src/archetype.rs index 99b41e4d24..c9d9503b8c 100644 --- a/crates/bevy_ecs/hecs/src/archetype.rs +++ b/crates/bevy_ecs/hecs/src/archetype.rs @@ -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(&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 { 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, + pub modified_entities: Vec, } impl TypeState { diff --git a/crates/bevy_ecs/hecs/src/world.rs b/crates/bevy_ecs/hecs/src/world.rs index 21a7f083e0..4969db4f65 100644 --- a/crates/bevy_ecs/hecs/src/world.rs +++ b/crates/bevy_ecs/hecs/src/world.rs @@ -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; diff --git a/crates/bevy_ecs/src/world/component.rs b/crates/bevy_ecs/src/world/component.rs index bb284b2d5b..10ec4110f5 100644 --- a/crates/bevy_ecs/src/world/component.rs +++ b/crates/bevy_ecs/src/world/component.rs @@ -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::>() - .iter() - .collect::>(); - assert_eq!(changed_entities, vec![e1, e3]); + fn get_changed(world: &World) -> Vec { + world + .query::>() + .iter() + .collect::>() + }; + + 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::>() - .iter() - .collect::>(); - 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();