diff --git a/src/DynamicArray.cs b/src/NativeArray.cs similarity index 58% rename from src/DynamicArray.cs rename to src/NativeArray.cs index 08400fc..9cc855f 100644 --- a/src/DynamicArray.cs +++ b/src/NativeArray.cs @@ -9,17 +9,19 @@ namespace MoonTools.ECS.Collections private T* Array; private int count; private int capacity; + private int elementSize; public int Count => count; - public ReverseSpanEnumerator GetEnumerator() => new ReverseSpanEnumerator(new Span(Array, Count)); + public Span.Enumerator GetEnumerator() => new Span(Array, count).GetEnumerator(); private bool disposed; public NativeArray(int capacity = 16) { this.capacity = capacity; - Array = (T*) NativeMemory.Alloc((nuint) (capacity * Unsafe.SizeOf())); + elementSize = Unsafe.SizeOf(); + Array = (T*) NativeMemory.Alloc((nuint) (capacity * elementSize)); count = 0; } @@ -37,11 +39,51 @@ namespace MoonTools.ECS.Collections count += 1; } + public void RemoveLastElement() + { + count -= 1; + } + + public bool TryPop(out T element) + { + if (count > 0) + { + element = Array[count - 1]; + count -= 1; + return true; + } + + element = default; + return false; + } + public void Clear() { count = 0; } + private void ResizeTo(int size) + { + capacity = size; + Array = (T*) NativeMemory.Realloc((void*) Array, (nuint) (elementSize * capacity)); + } + + public void CopyTo(NativeArray other) + { + if (count >= other.capacity) + { + other.ResizeTo(Count); + } + + NativeMemory.Copy( + (void*) Array, + (void*) other.Array, + (nuint) (elementSize * Count) + ); + + other.count = count; + } + protected virtual void Dispose(bool disposing) { if (!disposed) diff --git a/src/Rev2/Archetype.cs b/src/Rev2/Archetype.cs index 31ae816..65ee1c1 100644 --- a/src/Rev2/Archetype.cs +++ b/src/Rev2/Archetype.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using MoonTools.ECS.Collections; namespace MoonTools.ECS.Rev2; @@ -6,8 +7,8 @@ internal class Archetype { public World World; public ArchetypeSignature Signature; - public List ComponentColumns = new List(); - public List RowToEntity = new List(); + public Column[] ComponentColumns; + public NativeArray RowToEntity = new NativeArray(); public Dictionary ComponentToColumnIndex = new Dictionary(); @@ -19,11 +20,12 @@ internal class Archetype { World = world; Signature = signature; + ComponentColumns = new Column[signature.Count]; } public void ClearAll() { - for (int i = 0; i < ComponentColumns.Count; i += 1) + for (int i = 0; i < ComponentColumns.Length; i += 1) { ComponentColumns[i].Count = 0; } diff --git a/src/Rev2/Filter.cs b/src/Rev2/Filter.cs index 62683c2..eab57cc 100644 --- a/src/Rev2/Filter.cs +++ b/src/Rev2/Filter.cs @@ -83,16 +83,6 @@ namespace MoonTools.ECS.Rev2 } } - public void TakeSnapshot(Snapshot snapshot) - { - snapshot.Reset(); - - foreach (var archetype in Archetypes) - { - snapshot.TakeArchetypeSnapshot(archetype); - } - } - internal Filter(Archetype emptyArchetype, HashSet included, HashSet excluded) { EmptyArchetype = emptyArchetype; diff --git a/src/Rev2/IdAssigner.cs b/src/Rev2/IdAssigner.cs index f326a62..4f40105 100644 --- a/src/Rev2/IdAssigner.cs +++ b/src/Rev2/IdAssigner.cs @@ -1,26 +1,31 @@ -using System.Collections.Generic; +using MoonTools.ECS.Collections; -namespace MoonTools.ECS.Rev2 +namespace MoonTools.ECS.Rev2; + +internal class IdAssigner { - internal class IdAssigner + ulong Next; + NativeArray AvailableIds = new NativeArray(); + + public Id Assign() { - ulong Next; - Queue AvailableIds = new Queue(); - - public Id Assign() + if (!AvailableIds.TryPop(out var id)) { - if (!AvailableIds.TryDequeue(out var id)) - { - id = Next; - Next += 1; - } - - return new Id(id); + id = Next; + Next += 1; } - public void Unassign(Id id) - { - AvailableIds.Enqueue(id.Value); - } + return new Id(id); + } + + public void Unassign(Id id) + { + AvailableIds.Add(id.Value); + } + + public void CopyTo(IdAssigner other) + { + AvailableIds.CopyTo(other.AvailableIds); + other.Next = Next; } } diff --git a/src/Rev2/Snapshot.cs b/src/Rev2/Snapshot.cs index e3fb33b..5c21251 100644 --- a/src/Rev2/Snapshot.cs +++ b/src/Rev2/Snapshot.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using MoonTools.ECS.Collections; namespace MoonTools.ECS.Rev2; @@ -7,6 +8,9 @@ public class Snapshot private Dictionary ArchetypeSnapshots = new Dictionary(); + private Dictionary EntityIndex = new Dictionary(); + private IdAssigner IdAssigner = new IdAssigner(); + public int Count { get @@ -24,18 +28,40 @@ public class Snapshot public void Restore(World world) { + // restore archetype storage foreach (var (archetypeSignature, archetypeSnapshot) in ArchetypeSnapshots) { var archetype = world.ArchetypeIndex[archetypeSignature]; RestoreArchetypeSnapshot(archetype); } + + // restore entity index + world.EntityIndex.Clear(); + foreach (var (id, record) in EntityIndex) + { + world.EntityIndex[id] = record; + } + + // restore id assigner state + IdAssigner.CopyTo(world.IdAssigner); } - internal void Reset() + public void Take(World world) { - foreach (var archetypeSnapshot in ArchetypeSnapshots.Values) + // copy id assigner state + world.IdAssigner.CopyTo(IdAssigner); + + // copy entity index + EntityIndex.Clear(); + foreach (var (id, record) in world.EntityIndex) { - archetypeSnapshot.Count = 0; + EntityIndex[id] = record; + } + + // copy archetypes + foreach (var archetype in world.ArchetypeIndex.Values) + { + TakeArchetypeSnapshot(archetype); } } @@ -59,59 +85,48 @@ public class Snapshot private class ArchetypeSnapshot { public ArchetypeSignature Signature; - public readonly List ComponentColumns; + private readonly Column[] ComponentColumns; + private readonly NativeArray RowToEntity; - public int Count; + public int Count => RowToEntity.Count; public ArchetypeSnapshot(ArchetypeSignature signature) { Signature = signature; - ComponentColumns = new List(signature.Count); + ComponentColumns = new Column[signature.Count]; + RowToEntity = new NativeArray(); for (int i = 0; i < signature.Count; i += 1) { var componentId = signature[i]; - ComponentColumns.Add(new Column(World.ElementSizes[componentId])); + ComponentColumns[i] = new Column(World.ElementSizes[componentId]); } } + public void Clear() + { + RowToEntity.Clear(); + } + public void Take(Archetype archetype) { - for (int i = 0; i < ComponentColumns.Count; i += 1) + for (int i = 0; i < ComponentColumns.Length; i += 1) { archetype.ComponentColumns[i].CopyAllTo(ComponentColumns[i]); } - Count = archetype.Count; + archetype.RowToEntity.CopyTo(RowToEntity); } public void Restore(Archetype archetype) { // Copy all component data - for (int i = 0; i < ComponentColumns.Count; i += 1) + for (int i = 0; i < ComponentColumns.Length; i += 1) { ComponentColumns[i].CopyAllTo(archetype.ComponentColumns[i]); } - var archetypeCount = archetype.Count; - - if (Count < archetypeCount) - { - // if snapshot has fewer entities than archetype, remove extra entities - for (int i = archetypeCount - 1; i >= Count; i -= 1) - { - archetype.World.FreeEntity(archetype.RowToEntity[i]); - archetype.RowToEntity.RemoveAt(i); - } - } - else if (Count > archetypeCount) - { - // if snapshot has more entities than archetype, add entities - for (int i = archetypeCount; i < Count; i += 1) - { - archetype.World.CreateEntityOnArchetype(archetype); - } - } + RowToEntity.CopyTo(archetype.RowToEntity); } } } diff --git a/src/Rev2/World.cs b/src/Rev2/World.cs index dd4c00d..d02d063 100644 --- a/src/Rev2/World.cs +++ b/src/Rev2/World.cs @@ -15,22 +15,22 @@ namespace MoonTools.ECS.Rev2 internal Dictionary ArchetypeIndex = new Dictionary(); // Going from EntityId to Archetype and storage row - Dictionary EntityIndex = new Dictionary(); + internal Dictionary EntityIndex = new Dictionary(); // Going from ComponentId to Archetype list Dictionary> ComponentIndex = new Dictionary>(); // ID Management - IdAssigner IdAssigner = new IdAssigner(); + internal IdAssigner IdAssigner = new IdAssigner(); internal readonly Archetype EmptyArchetype; public FilterBuilder FilterBuilder => new FilterBuilder(this); - private bool IsDisposed; - public delegate void RefAction(ref T1 arg1, ref T2 arg2); + private bool IsDisposed; + public World() { // Create the Empty Archetype @@ -39,10 +39,7 @@ namespace MoonTools.ECS.Rev2 internal Archetype CreateArchetype(ArchetypeSignature signature) { - var archetype = new Archetype(this, signature) - { - ComponentColumns = new List(signature.Count) - }; + var archetype = new Archetype(this, signature); ArchetypeIndex.Add(signature, archetype); @@ -50,8 +47,8 @@ namespace MoonTools.ECS.Rev2 { var componentId = signature[i]; ComponentIndex[componentId].Add(archetype); - archetype.ComponentToColumnIndex.Add(componentId, archetype.ComponentColumns.Count); - archetype.ComponentColumns.Add(new Column(ElementSizes[componentId])); + archetype.ComponentToColumnIndex.Add(componentId, i); + archetype.ComponentColumns[i] = new Column(ElementSizes[componentId]); } return archetype; @@ -69,7 +66,6 @@ namespace MoonTools.ECS.Rev2 internal void CreateEntityOnArchetype(Archetype archetype) { var entityId = IdAssigner.Assign(); - EntityIndex.Add(entityId, new Record(archetype, archetype.Count)); archetype.RowToEntity.Add(entityId); } @@ -330,7 +326,7 @@ namespace MoonTools.ECS.Rev2 var archetype = record.Archetype; var row = record.Row; - for (int i = 0; i < archetype.ComponentColumns.Count; i += 1) + for (int i = 0; i < archetype.Signature.Count; i += 1) { archetype.ComponentColumns[i].Delete(row); } @@ -343,14 +339,14 @@ namespace MoonTools.ECS.Rev2 EntityIndex[lastRowEntity] = new Record(archetype, row); } - archetype.RowToEntity.RemoveAt(archetype.Count - 1); + archetype.RowToEntity.RemoveLastElement(); EntityIndex.Remove(entityId); IdAssigner.Unassign(entityId); } private void MoveEntityToHigherArchetype(Id entityId, int row, Archetype from, Archetype to) { - for (int i = 0; i < from.ComponentColumns.Count; i += 1) + for (int i = 0; i < from.Signature.Count; i += 1) { var componentId = from.Signature[i]; var destinationColumnIndex = to.ComponentToColumnIndex[componentId]; @@ -370,7 +366,7 @@ namespace MoonTools.ECS.Rev2 EntityIndex[lastRowEntity] = new Record(from, row); } - from.RowToEntity.RemoveAt(from.Count - 1); + from.RowToEntity.RemoveLastElement(); // update row to entity lookup on to archetype EntityIndex[entityId] = new Record(to, to.Count); @@ -379,7 +375,7 @@ namespace MoonTools.ECS.Rev2 private void MoveEntityToLowerArchetype(Id entityId, int row, Archetype from, Archetype to, Id removed) { - for (int i = 0; i < from.ComponentColumns.Count; i += 1) + for (int i = 0; i < from.Signature.Count; i += 1) { var componentId = from.Signature[i]; @@ -402,7 +398,7 @@ namespace MoonTools.ECS.Rev2 EntityIndex[lastRowEntity] = new Record(from, row); } - from.RowToEntity.RemoveAt(from.Count - 1); + from.RowToEntity.RemoveLastElement(); // update row to entity lookup on to archetype EntityIndex[entityId] = new Record(to, to.Count);