diff --git a/src/archetypes.rs b/src/archetypes.rs index d598513b..e3442738 100644 --- a/src/archetypes.rs +++ b/src/archetypes.rs @@ -95,7 +95,7 @@ impl Archetypes { /// `components` must be sorted. /// /// Ensures the `exclusive` property of any relations are satisfied - pub(crate) fn find( + pub(crate) fn find_create( &mut self, components: impl IntoIterator, ) -> (ArchetypeId, &mut Archetype) { diff --git a/src/world.rs b/src/world.rs index fd433364..f8ce489a 100644 --- a/src/world.rs +++ b/src/world.rs @@ -2,9 +2,7 @@ use alloc::{borrow::ToOwned, collections::BTreeMap, sync::Arc, vec::Vec}; use core::{ fmt, fmt::Formatter, - iter::once, mem::{self, MaybeUninit}, - ptr, sync::atomic::{AtomicBool, AtomicU32, Ordering, Ordering::Relaxed}, }; use once_cell::unsync::OnceCell; @@ -14,7 +12,7 @@ use atomic_refcell::{AtomicRef, BorrowError, BorrowMutError}; use itertools::Itertools; use crate::{ - archetype::{Archetype, ArchetypeInfo}, + archetype::{Archetype, ArchetypeInfo, Slot}, archetypes::Archetypes, buffer::ComponentBuffer, component::dummy, @@ -28,7 +26,7 @@ use crate::{ format::{EntitiesFormatter, HierarchyFormatter, WorldFormatter}, is_static, relation::Relation, - writer::{self, ComponentWriter, MigrateEntity}, + writer::{self, EntityWriter, Replace, ReplaceDyn, SingleComponentWriter}, ArchetypeId, BatchSpawn, Component, ComponentInfo, ComponentKey, ComponentVTable, ComponentValue, Error, Fetch, Query, RefMut, RelationExt, }; @@ -56,6 +54,23 @@ impl EntityStores { } } +pub(crate) fn update_entity_loc( + world: &mut World, + id: Entity, + loc: EntityLocation, + swapped: Option<(Entity, Slot)>, +) { + if let Some((swapped, slot)) = swapped { + // The last entity in src was moved into the slot occupied by id + let swapped_ns = world.entities.init(swapped.kind()); + swapped_ns.get_mut(swapped).expect("Invalid entity id").slot = slot; + } + + let ns = world.entities.init(id.kind()); + + *ns.get_mut(id).expect("Entity is not valid") = loc; +} + /// Holds the entities and components of the ECS. pub struct World { entities: EntityStores, @@ -126,7 +141,7 @@ impl World { let change_tick = self.advance_change_tick(); - let (arch_id, arch) = self.archetypes.find(batch.components()); + let (arch_id, arch) = self.archetypes.find_create(batch.components()); let base = arch.len(); let store = self.entities.init(EntityKind::empty()); @@ -199,7 +214,7 @@ impl World { self.init_component(component); } - let (arch_id, _) = self.archetypes.find(buffer.components().copied()); + let (arch_id, _) = self.archetypes.find_create(buffer.components().copied()); let (loc, arch) = self.spawn_at_inner(id, arch_id)?; for (info, src) in buffer.drain() { @@ -218,7 +233,7 @@ impl World { } let change_tick = self.advance_change_tick(); - let (arch_id, _) = self.archetypes.find(buffer.components().copied()); + let (arch_id, _) = self.archetypes.find_create(buffer.components().copied()); let (id, _, arch) = self.spawn_inner(arch_id, EntityKind::empty()); @@ -274,7 +289,7 @@ impl World { let dst_components: SmallVec<[ComponentInfo; 8]> = src.components().filter(|v| f(v.key())).collect(); - let (dst_id, _) = self.archetypes.find(dst_components); + let (dst_id, _) = self.archetypes.find_create(dst_components); let (src, dst) = self.archetypes.get_disjoint(loc.arch_id, dst_id).unwrap(); @@ -445,7 +460,7 @@ impl World { !(key.id == id || key.object == Some(id)) }); - let (dst_id, dst) = self.archetypes.find(components); + let (dst_id, dst) = self.archetypes.find_create(components); for (id, slot) in src.move_all(dst, change_tick) { *self.location_mut(id).expect("Entity id was not valid") = EntityLocation { @@ -495,53 +510,20 @@ impl World { info: ComponentInfo, value: *mut u8, ) -> Result { - let loc = self.set_impl( - id, - writer::ReplaceDyn { - info, - value, - _marker: core::marker::PhantomData, - }, - )?; + let loc = self.set_impl(id, SingleComponentWriter::new(info, ReplaceDyn { value }))?; Ok(loc) } #[inline] - fn set_impl(&mut self, id: Entity, updater: U) -> Result { + fn set_impl(&mut self, id: Entity, writer: U) -> Result { // We know things will change either way let change_tick = self.advance_change_tick(); let src_loc = self.init_location(id)?; - let src = self.archetypes.get_mut(src_loc.arch_id); - eprintln!("loc: {src_loc:?}"); - if let Some(writer) = updater.write(src, id, src_loc.slot, change_tick) { - // Move to a new archetype - let (dst_loc, swapped) = - writer.migrate(self, src_loc.arch_id, src_loc.slot, change_tick); - - debug_assert_eq!( - self.archetypes.get(dst_loc.arch_id).entity(dst_loc.slot), - Some(id) - ); - - if let Some((swapped, slot)) = swapped { - // The last entity in src was moved into the slot occupied by id - let swapped_ns = self.entities.init(swapped.kind()); - swapped_ns.get_mut(swapped).expect("Invalid entity id").slot = slot; - } - self.archetypes.prune_arch(src_loc.arch_id); - - let ns = self.entities.init(id.kind()); - - *ns.get_mut(id).expect("Entity is not valid") = dst_loc; - - Ok(dst_loc) - } else { - Ok(src_loc) - } + Ok(writer.write(self, id, src_loc, change_tick)) } /// TODO benchmark with fully generic function @@ -556,11 +538,7 @@ impl World { let loc = self.set_impl( id, - writer::Replace { - component, - value, - output: &mut output, - }, + SingleComponentWriter::new(component.info(), Replace::new(value, &mut output)), )?; Ok((output, loc)) @@ -599,7 +577,7 @@ impl World { .filter(|v| v.key != component.key()) .collect_vec(); - let (dst_id, _) = self.archetypes.find(components); + let (dst_id, _) = self.archetypes.find_create(components); dst_id } @@ -850,7 +828,7 @@ impl World { let change_tick = self.advance_change_tick(); - let (arch_id, arch) = self.archetypes.find(batch.components()); + let (arch_id, arch) = self.archetypes.find_create(batch.components()); let base = arch.len(); for (idx, &id) in ids.iter().enumerate() { @@ -1347,14 +1325,18 @@ mod tests { let mut world = World::new(); // () -> (a) -> (ab) -> (abc) - let (_, archetype) = world.archetypes.find([a().info(), b().info(), c().info()]); + let (_, archetype) = world + .archetypes + .find_create([a().info(), b().info(), c().info()]); assert!(!archetype.has(d().key())); assert!(archetype.has(a().key())); assert!(archetype.has(b().key())); // () -> (a) -> (ab) -> (abc) // -> (abd) - let (_, archetype) = world.archetypes.find([a().info(), b().info(), d().info()]); + let (_, archetype) = world + .archetypes + .find_create([a().info(), b().info(), d().info()]); assert!(archetype.has(d().key())); assert!(!archetype.has(c().key())); } diff --git a/src/writer.rs b/src/writer.rs index 13785d11..2368a7c4 100644 --- a/src/writer.rs +++ b/src/writer.rs @@ -1,292 +1,207 @@ -use core::{marker::PhantomData, mem, ptr, slice}; +use core::{mem, ptr, slice}; -use itertools::{Either, Itertools}; -use serde::ser::SerializeTupleStruct; +use itertools::Itertools; use crate::{ - archetype::{Archetype, Slice, Slot}, + archetype::{Cell, Slice, Slot}, buffer::ComponentBuffer, entity::EntityLocation, metadata::exclusive, - ArchetypeId, Component, ComponentInfo, ComponentValue, Entity, World, + world::update_entity_loc, + ArchetypeId, ComponentInfo, ComponentValue, Entity, World, }; /// Describes a modification to the components of an entity within the context of an archetype pub(crate) trait ComponentWriter { - /// If returned, will be used to migrate the entity to a new archetype - type Writer: MigrateEntity; - - /// Performs write operations against the target entity and archetype. + /// Performs write operations against the target entity + fn update(self, cell: &mut Cell, slot: Slot, id: Entity, tick: u32); + /// # Safety /// - /// A migration to another archetype may be induced by returning an entity migrator - fn write( - self, - archetype: &mut Archetype, - id: Entity, - slot: Slot, - tick: u32, - ) -> Option; + /// The cell **must** be extended with valid component data for the new entity + unsafe fn push(self, cell: &mut Cell, id: Entity, tick: u32); } /// # Safety -/// *All* components of the new slot must be initialized -pub(crate) unsafe trait MigrateEntity { - fn migrate( - self, - world: &mut World, - src_id: ArchetypeId, - src_slot: Slot, - tick: u32, - ) -> (EntityLocation, Option<(Entity, Slot)>); +/// +/// The entity must be fully initialized and all bookkepping updated +pub unsafe trait EntityWriter { + fn write(self, world: &mut World, id: Entity, loc: EntityLocation, tick: u32) + -> EntityLocation; } -pub(crate) struct Replace<'a, T: ComponentValue> { - pub(crate) component: Component, - pub(crate) value: T, - pub(crate) output: &'a mut Option, +pub(crate) struct SingleComponentWriter { + info: ComponentInfo, + writer: W, } -impl<'a, T: ComponentValue> ComponentWriter for Replace<'a, T> { - type Writer = ReplaceWriter; +impl SingleComponentWriter { + pub(crate) fn new(info: ComponentInfo, writer: W) -> Self { + Self { info, writer } + } +} +unsafe impl EntityWriter for SingleComponentWriter { fn write( self, - arch: &mut Archetype, + world: &mut World, id: Entity, - slot: Slot, + src_loc: EntityLocation, tick: u32, - ) -> Option { - let info = self.component.info(); - let key = self.component.key(); - - if let Some(cell) = arch.cell_mut(key) { - let data = cell.data.get_mut(); - - let storage = data.storage.downcast_mut::(); - let old = mem::replace(&mut storage[slot], self.value); - - data.set_modified(&[id], Slice::single(slot), tick); + ) -> EntityLocation { + let key = self.info.key(); - *self.output = Some(old); + let arch = world.archetypes.get_mut(src_loc.arch_id); - None - } else if let Some(&dst) = arch.outgoing.get(&key) { - eprintln!("Outgoing edge: {:?}", self.component); + if let Some(cell) = arch.cell_mut(key) { + self.writer.update(cell, src_loc.slot, id, tick); + return src_loc; + } + let (src, dst, dst_id) = if let Some(&dst_id) = arch.outgoing.get(&key) { + eprintln!("Outgoing edge: {:?}", self.info); - Some(ReplaceWriter { - dst: Either::Left(dst), - component: self.component, - value: self.value, - }) + let (src, dst) = world + .archetypes + .get_disjoint(src_loc.arch_id, dst_id) + .unwrap(); + (src, dst, dst_id) } else { // Oh no! The archetype is missing the component eprintln!( "Missing component: {:?} found:{:?}", - info, + self.info, arch.components().collect_vec() ); - let exclusive = if info.meta_ref().has(exclusive()) { - slice::from_ref(&info.key.id) + let exclusive = if self.info.meta_ref().has(exclusive()) { + slice::from_ref(&self.info.key.id) } else { &[] }; - Some(ReplaceWriter { - dst: Either::Right(find_archetype(arch, [info], exclusive)), - component: self.component, - value: self.value, - }) - } - } -} + let (components, superset) = + find_archetype_components(arch.components(), [self.info], exclusive); -pub(crate) struct ReplaceWriter { - dst: Either, bool)>, - component: Component, - value: T, -} + world.init_component(self.info); + let (dst_id, _) = world.archetypes.find_create(components.iter().copied()); -unsafe impl MigrateEntity for ReplaceWriter { - fn migrate( - self, - world: &mut World, - src_id: ArchetypeId, - src_slot: Slot, - tick: u32, - ) -> (EntityLocation, Option<(Entity, Slot)>) { - let key = self.component.key(); + // Add a quick edge to refer to later + let reserved_id = world.archetypes.reserved; + let (src, dst) = world + .archetypes + .get_disjoint(src_loc.arch_id, dst_id) + .unwrap(); - let (src, dst, dst_id) = match &self.dst { - &Either::Left(dst_id) => { - let (src, dst) = world.archetypes.get_disjoint(src_id, dst_id).unwrap(); - (src, dst, dst_id) + if superset && src_loc.arch_id != reserved_id { + src.add_outgoing(key, dst_id); + dst.add_incoming(key, src_loc.arch_id); + } else { + eprintln!("Not a superset") } - Either::Right((components, superset)) => { - // Initialize component - world.init_component(self.component.info()); - - let (dst_id, _) = world.archetypes.find(components.iter().copied()); - - // Add a quick edge to refer to later - let reserved_id = world.archetypes.reserved; - let (src, dst) = world.archetypes.get_disjoint(src_id, dst_id).unwrap(); - - if *superset && src_id != reserved_id { - src.add_outgoing(key, dst_id); - dst.add_incoming(key, src_id); - } else { - eprintln!("Not a superset") - } - (src, dst, dst_id) - } + (src, dst, dst_id) }; - let (dst_slot, swapped) = unsafe { src.move_to(dst, src_slot, |c, ptr| c.drop(ptr), tick) }; + let (dst_slot, swapped) = + unsafe { src.move_to(dst, src_loc.slot, |c, ptr| c.drop(ptr), tick) }; // Insert the missing component unsafe { - let mut value = self.value; - dst.push(key, &mut value as *mut T as *mut u8, tick); - mem::forget(value); + let cell = dst + .cell_mut(key) + .expect("Missing component in new archetype"); + + cell.data.get_mut().storage.reserve(1); + self.writer.push(cell, id, tick); } - ( - EntityLocation { - slot: dst_slot, - arch_id: dst_id, - }, - swapped, - ) - } -} + let dst_loc = EntityLocation { + arch_id: dst_id, + slot: dst_slot, + }; -pub(crate) struct ReplaceDyn<'a> { - pub(crate) info: ComponentInfo, - pub(crate) value: *mut u8, - pub(crate) _marker: PhantomData<&'a mut ()>, -} + update_entity_loc(world, id, dst_loc, swapped); -impl<'a> ComponentWriter for ReplaceDyn<'a> { - type Writer = ReplaceWriterDyn<'a>; + dst_loc + } +} - fn write( +/// # Safety +/// *All* components of the new slot must be initialized +pub(crate) unsafe trait MigrateEntity { + fn migrate( self, - arch: &mut Archetype, - id: Entity, - slot: Slot, + world: &mut World, + src_id: ArchetypeId, + src_slot: Slot, tick: u32, - ) -> Option { - let key = self.info.key(); + ) -> (EntityLocation, Option<(Entity, Slot)>); +} - if let Some(cell) = arch.cell_mut(key) { - let data = cell.data.get_mut(); +pub(crate) struct Replace<'a, T: ComponentValue> { + pub(crate) value: T, + pub(crate) output: &'a mut Option, +} - let storage = &mut data.storage; - unsafe { - let dst = storage.at_mut(slot).unwrap(); +impl<'a, T: ComponentValue> Replace<'a, T> { + pub(crate) fn new(value: T, output: &'a mut Option) -> Self { + Self { value, output } + } +} - self.info.drop(dst); +impl<'a, T: ComponentValue> ComponentWriter for Replace<'a, T> { + fn update(self, cell: &mut Cell, slot: Slot, id: Entity, tick: u32) { + let data = cell.data.get_mut(); - ptr::copy_nonoverlapping(self.value, dst, self.info.size()); - } + let storage = data.storage.downcast_mut::(); + let old = mem::replace(&mut storage[slot], self.value); - data.set_modified(&[id], Slice::single(slot), tick); + data.set_modified(&[id], Slice::single(slot), tick); - None - } else if let Some(&dst) = arch.outgoing.get(&key) { - eprintln!("Outgoing edge: {:?}", self.info); + *self.output = Some(old); + } - Some(ReplaceWriterDyn { - dst: Either::Left(dst), - info: self.info, - value: self.value, - _marker: self._marker, - }) - } else { - // Oh no! The archetype is missing the component + unsafe fn push(mut self, cell: &mut Cell, id: Entity, tick: u32) { + let data = cell.data.get_mut(); - eprintln!( - "Missing component: {:?} found:{:?}", - self.info, - arch.components().collect_vec() - ); + let slot = data.storage.len(); - let exclusive = if self.info.meta_ref().has(exclusive()) { - slice::from_ref(&self.info.key.id) - } else { - &[] - }; + data.storage.extend(&mut self.value as *mut T as *mut u8, 1); - Some(ReplaceWriterDyn { - dst: Either::Right(find_archetype(arch, [self.info], exclusive)), - info: self.info, - value: self.value, - _marker: self._marker, - }) - } + mem::forget(self.value); + + data.set_added(&[id], Slice::single(slot), tick); } } -pub(crate) struct ReplaceWriterDyn<'a> { - dst: Either, bool)>, - info: ComponentInfo, - value: *mut u8, - _marker: PhantomData<&'a mut ()>, +pub(crate) struct ReplaceDyn { + pub(crate) value: *mut u8, } -unsafe impl<'a> MigrateEntity for ReplaceWriterDyn<'a> { - fn migrate( - self, - world: &mut World, - src_id: ArchetypeId, - src_slot: Slot, - tick: u32, - ) -> (EntityLocation, Option<(Entity, Slot)>) { - let key = self.info.key(); +impl ComponentWriter for ReplaceDyn { + fn update(self, cell: &mut Cell, slot: Slot, id: Entity, tick: u32) { + let info = cell.info(); - let (src, dst, dst_id) = match &self.dst { - &Either::Left(dst_id) => { - let (src, dst) = world.archetypes.get_disjoint(src_id, dst_id).unwrap(); - (src, dst, dst_id) - } - Either::Right((components, superset)) => { - // Initialize component - world.init_component(self.info); + let data = cell.data.get_mut(); - let (dst_id, _) = world.archetypes.find(components.iter().copied()); + unsafe { + let dst = data.storage.at_mut(slot).unwrap(); - // Add a quick edge to refer to later - let reserved_id = world.archetypes.reserved; - let (src, dst) = world.archetypes.get_disjoint(src_id, dst_id).unwrap(); + info.drop(dst); - if *superset && src_id != reserved_id { - eprintln!("Adding edge: {:?} -> {:?} {:?}", src_id, dst_id, self.info); - src.add_outgoing(key, dst_id); - dst.add_incoming(key, src_id); - } + ptr::copy_nonoverlapping(self.value, dst, info.size()); + } - (src, dst, dst_id) - } - }; + data.set_modified(&[id], Slice::single(slot), tick); + } - let (dst_slot, swapped) = unsafe { src.move_to(dst, src_slot, |c, ptr| c.drop(ptr), tick) }; + unsafe fn push(self, cell: &mut Cell, id: Entity, tick: u32) { + let data = cell.data.get_mut(); - // Insert the missing component - unsafe { - let value = self.value; - dst.push(key, value, tick); - } + let slot = data.storage.len(); + data.storage.extend(self.value, 1); - ( - EntityLocation { - slot: dst_slot, - arch_id: dst_id, - }, - swapped, - ) + data.set_added(&[id], Slice::single(slot), tick); } } @@ -300,17 +215,17 @@ impl<'a> Buffered<'a> { } } -impl<'a> ComponentWriter for Buffered<'a> { - type Writer = BufferedMigrate<'a>; - +unsafe impl<'a> EntityWriter for Buffered<'a> { fn write( self, - arch: &mut Archetype, + world: &mut World, id: Entity, - slot: Slot, + src_loc: EntityLocation, tick: u32, - ) -> Option { + ) -> EntityLocation { let mut exclusive_relations = Vec::new(); + + let arch = world.archetypes.get_mut(src_loc.arch_id); unsafe { self.buffer.retain(|info, src| { let key = info.key; @@ -319,11 +234,11 @@ impl<'a> ComponentWriter for Buffered<'a> { if let Some(cell) = arch.cell_mut(key) { let data = cell.data.get_mut(); - let dst = data.storage.at_mut(slot).unwrap(); + let dst = data.storage.at_mut(src_loc.slot).unwrap(); info.drop(dst); ptr::copy_nonoverlapping(src, dst, info.size()); - data.set_modified(&[id], Slice::single(slot), tick); + data.set_modified(&[id], Slice::single(src_loc.slot), tick); false } else { // Component does not exist yet, so defer a move @@ -344,69 +259,52 @@ impl<'a> ComponentWriter for Buffered<'a> { if self.buffer.is_empty() { eprintln!("Archetype fully matched"); - None - } else { - // Add the existing components, making sure new exclusive relations are favored - - let (components, _) = find_archetype( - arch, - self.buffer.components().copied(), - &exclusive_relations, - ); - - Some(BufferedMigrate { - components, - buffer: self.buffer, - }) + return src_loc; } - } -} -pub(crate) struct BufferedMigrate<'a> { - components: Vec, - buffer: &'a mut ComponentBuffer, -} + // Add the existing components, making sure new exclusive relations are favored + let (components, _) = find_archetype_components( + arch.components(), + self.buffer.components().copied(), + &exclusive_relations, + ); -unsafe impl<'a> MigrateEntity for BufferedMigrate<'a> { - fn migrate( - self, - world: &mut World, - src_id: ArchetypeId, - src_slot: Slot, - tick: u32, - ) -> (EntityLocation, Option<(Entity, Slot)>) { for &info in self.buffer.components() { eprintln!("Initializing component {:?}", info); world.init_component(info); } - let (dst_id, _) = world.archetypes.find(self.components.iter().copied()); + let (dst_id, _) = world.archetypes.find_create(components); + + let (src, dst) = world + .archetypes + .get_disjoint(src_loc.arch_id, dst_id) + .unwrap(); - let (src, dst) = world.archetypes.get_disjoint(src_id, dst_id).unwrap(); - let (dst_slot, swapped) = unsafe { src.move_to(dst, src_slot, |c, ptr| c.drop(ptr), tick) }; + let (dst_slot, swapped) = + unsafe { src.move_to(dst, src_loc.slot, |c, ptr| c.drop(ptr), tick) }; // Insert the missing components for (info, src) in self.buffer.drain() { unsafe { - // src moves into herer dst.push(info.key, src, tick); } } - eprintln!("Buffer retained {} items", self.buffer.len()); + let dst_loc = EntityLocation { + arch_id: dst_id, + slot: dst_slot, + }; + + update_entity_loc(world, id, dst_loc, swapped); + world.archetypes.prune_arch(src_loc.arch_id); - ( - EntityLocation { - slot: dst_slot, - arch_id: dst_id, - }, - swapped, - ) + dst_loc } } -fn find_archetype( - arch: &Archetype, +fn find_archetype_components( + current_components: impl IntoIterator, new_components: impl IntoIterator, // Subset of `new_components` exclusive: &[Entity], @@ -414,7 +312,7 @@ fn find_archetype( let mut superset = true; let res = new_components .into_iter() - .chain(arch.components().filter(|v| { + .chain(current_components.into_iter().filter(|v| { if exclusive.contains(&v.key.id) { superset = false; false