From 272fd6b492a953de4850241acfc63ff7cb117ec4 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Fri, 20 Oct 2023 17:24:35 -0700 Subject: [PATCH] more rev2 implementation --- src/Rev2/Archetype.cs | 3 + src/Rev2/ArchetypeRecord.cs | 4 - src/Rev2/Column.cs | 1 + src/Rev2/Filter.cs | 76 ++++++++++++++++ src/Rev2/IForEach.cs | 6 ++ src/Rev2/World.cs | 174 ++++++++++++++++++++++-------------- 6 files changed, 192 insertions(+), 72 deletions(-) delete mode 100644 src/Rev2/ArchetypeRecord.cs create mode 100644 src/Rev2/Filter.cs create mode 100644 src/Rev2/IForEach.cs diff --git a/src/Rev2/Archetype.cs b/src/Rev2/Archetype.cs index ff3a435..9f6b777 100644 --- a/src/Rev2/Archetype.cs +++ b/src/Rev2/Archetype.cs @@ -8,6 +8,9 @@ namespace MoonTools.ECS.Rev2 public ArchetypeId Id { get; private set; } public List Components = new List(); public List RowToEntity = new List(); + + public Dictionary ComponentToColumnIndex = + new Dictionary(); public SortedDictionary Edges = new SortedDictionary(); public int Count; diff --git a/src/Rev2/ArchetypeRecord.cs b/src/Rev2/ArchetypeRecord.cs deleted file mode 100644 index 784699f..0000000 --- a/src/Rev2/ArchetypeRecord.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace MoonTools.ECS.Rev2 -{ - public readonly record struct ArchetypeRecord(int ColumnIndex); -} diff --git a/src/Rev2/Column.cs b/src/Rev2/Column.cs index 532ac73..1a7d984 100644 --- a/src/Rev2/Column.cs +++ b/src/Rev2/Column.cs @@ -27,6 +27,7 @@ namespace MoonTools.ECS.Rev2 Elements = (nint) NativeMemory.Realloc((void*) Elements, (nuint) (ElementSize * Capacity)); } + // Fills gap by copying final element to the deleted index public void Delete(int index) { if (Count > 1) diff --git a/src/Rev2/Filter.cs b/src/Rev2/Filter.cs new file mode 100644 index 0000000..3bdb9ee --- /dev/null +++ b/src/Rev2/Filter.cs @@ -0,0 +1,76 @@ +using System; +using System.Runtime.InteropServices; + +namespace MoonTools.ECS.Rev2 +{ + // TODO: do we want to get fancy with queries beyond Include and Exclude? + // TODO: need an edge iterator as part of this nested horseshit + public class Filter + { + private Archetype Start; + + public EntityEnumerator Entities => new EntityEnumerator(Start); + + private ref struct FilterEnumerator + { + private Archetype CurrentArchetype; + private bool Active; + + public FilterEnumerator(Archetype start) + { + CurrentArchetype = start; + Active = false; + } + + public bool MoveNext() + { + if (!Active) + { + Active = true; + return true; + } + + // TODO: go to next available edge + } + + public Archetype Current => CurrentArchetype; + } + + public ref struct EntityEnumerator + { + private FilterEnumerator FilterEnumerator; + private ReverseSpanEnumerator EntityListEnumerator; + private bool EntityListEnumeratorActive; + + public EntityEnumerator GetEnumerator() => this; + + public EntityEnumerator(Archetype start) + { + FilterEnumerator = new FilterEnumerator(start); + } + + public bool MoveNext() + { + if (!EntityListEnumeratorActive || !EntityListEnumerator.MoveNext()) + { + if (!FilterEnumerator.MoveNext()) + { + return false; + } + + if (FilterEnumerator.Current.RowToEntity.Count != 0) + { + EntityListEnumerator = new ReverseSpanEnumerator(CollectionsMarshal.AsSpan(FilterEnumerator.Current.RowToEntity)); + EntityListEnumeratorActive = true; + } + + return MoveNext(); + } + + return true; + } + + public EntityId Current => EntityListEnumerator.Current; + } + } +} diff --git a/src/Rev2/IForEach.cs b/src/Rev2/IForEach.cs new file mode 100644 index 0000000..361509e --- /dev/null +++ b/src/Rev2/IForEach.cs @@ -0,0 +1,6 @@ +namespace MoonTools.ECS.Rev2; + +public interface IForEach where T1 : unmanaged where T2 : unmanaged +{ + public void Update(ref T1 t1, ref T2 t2); +} diff --git a/src/Rev2/World.cs b/src/Rev2/World.cs index b6ac0a0..9f4d86a 100644 --- a/src/Rev2/World.cs +++ b/src/Rev2/World.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace MoonTools.ECS.Rev2 { @@ -12,8 +13,8 @@ namespace MoonTools.ECS.Rev2 // Going from EntityId to Archetype and storage row Dictionary EntityIndex = new Dictionary(); - // Going from ComponentId to an Archetype storage column index - Dictionary> ComponentIndex = new Dictionary>(); + // Going from ComponentId to Archetype list + Dictionary> ComponentIndex = new Dictionary>(); // Get ComponentId from a Type Dictionary TypeToComponentId = new Dictionary(); @@ -49,7 +50,8 @@ namespace MoonTools.ECS.Rev2 for (int i = 0; i < signature.Count; i += 1) { var componentId = signature[i]; - ComponentIndex[componentId].Add(archetypeId, new ArchetypeRecord(i)); + ComponentIndex[componentId].Add(archetype); //, new ArchetypeRecord(i)); + archetype.ComponentToColumnIndex.Add(componentId, archetype.Components.Count); archetype.Components.Add(new Column(ElementSizes[componentId])); } @@ -70,7 +72,7 @@ namespace MoonTools.ECS.Rev2 { var componentId = ComponentIdAssigner.Assign(); TypeToComponentId.Add(typeof(T), componentId); - ComponentIndex.Add(componentId, new Dictionary()); + ComponentIndex.Add(componentId, new List()); ElementSizes.Add(componentId, Unsafe.SizeOf()); } @@ -82,67 +84,51 @@ namespace MoonTools.ECS.Rev2 } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private ComponentId GetComponentId() where T : unmanaged { return TypeToComponentId[typeof(T)]; } - internal ArchetypeRecord GetArchetypeRecord(Archetype archetype) where T : unmanaged + public bool Has(EntityId entityId) where T : unmanaged { var componentId = GetComponentId(); - return ComponentIndex[componentId][archetype.Id]; + var record = EntityIndex[entityId]; + return record.Archetype.ComponentToColumnIndex.ContainsKey(componentId); } - public bool HasComponent(EntityId entityId) where T : unmanaged + // will throw if non-existent + public unsafe ref T Get(EntityId entityId) where T : unmanaged { var componentId = GetComponentId(); var record = EntityIndex[entityId]; - var archetypes = ComponentIndex[componentId]; - return archetypes.ContainsKey(record.Archetype.Id); + var columnIndex = record.Archetype.ComponentToColumnIndex[componentId]; + var column = record.Archetype.Components[columnIndex]; + + return ref ((T*) column.Elements)[record.Row]; } - public unsafe T GetComponent(EntityId entityId) where T : unmanaged - { - var componentId = GetComponentId(); - - var record = EntityIndex[entityId]; - var archetype = record.Archetype; - - var archetypes = ComponentIndex[componentId]; - if (!archetypes.ContainsKey(archetype.Id)) - { - return default; // FIXME: maybe throw in debug mode? - } - - var archetypeRecord = archetypes[archetype.Id]; - var column = archetype.Components[archetypeRecord.ColumnIndex]; - - return ((T*) column.Elements)[record.Row]; - } - - public unsafe void SetComponent(EntityId entityId, T component) where T : unmanaged + public unsafe void Set(in EntityId entityId, in T component) where T : unmanaged { TryRegisterComponentId(); var componentId = GetComponentId(); - if (HasComponent(entityId)) + if (Has(entityId)) { var record = EntityIndex[entityId]; - var archetype = record.Archetype; - var archetypes = ComponentIndex[componentId]; - var archetypeRecord = archetypes[archetype.Id]; - var column = archetype.Components[archetypeRecord.ColumnIndex]; + var columnIndex = record.Archetype.ComponentToColumnIndex[componentId]; + var column = record.Archetype.Components[columnIndex]; ((T*) column.Elements)[record.Row] = component; } else { - AddComponent(entityId, component); + Add(entityId, component); } } - private void AddComponent(EntityId entityId, T component) where T : unmanaged + private void Add(EntityId entityId, in T component) where T : unmanaged { Archetype? nextArchetype; @@ -176,20 +162,18 @@ namespace MoonTools.ECS.Rev2 MoveEntityToHigherArchetype(entityId, record.Row, archetype, nextArchetype); // add the new component to the new archetype - var archetypes = ComponentIndex[componentId]; - var archetypeRecord = archetypes[nextArchetype.Id]; - var column = nextArchetype.Components[archetypeRecord.ColumnIndex]; + var columnIndex = nextArchetype.ComponentToColumnIndex[componentId]; + var column = nextArchetype.Components[columnIndex]; column.Append(component); } - public void RemoveComponent(EntityId entityId) where T : unmanaged + public void Remove(EntityId entityId) where T : unmanaged { Archetype? nextArchetype; var componentId = GetComponentId(); - var record = EntityIndex[entityId]; - var archetype = record.Archetype; + var (archetype, row) = EntityIndex[entityId]; if (archetype.Edges.TryGetValue(componentId, out var edge)) { @@ -212,7 +196,30 @@ namespace MoonTools.ECS.Rev2 nextArchetype.Edges.Add(componentId, newEdge); } - MoveEntityToLowerArchetype(entityId, record.Row, archetype, nextArchetype, componentId); + MoveEntityToLowerArchetype(entityId, row, archetype, nextArchetype, componentId); + } + + public void Destroy(EntityId entityId) + { + var record = EntityIndex[entityId]; + var archetype = record.Archetype; + var row = record.Row; + + for (int i = 0; i < archetype.Components.Count; i += 1) + { + archetype.Components[i].Delete(row); + } + + if (archetype.Count > 1) + { + // update row to entity lookup on archetype + archetype.RowToEntity[row] = archetype.RowToEntity[archetype.Count - 1]; + archetype.RowToEntity.RemoveAt(archetype.Count - 1); + } + + archetype.Count -= 1; + EntityIndex.Remove(entityId); + EntityIdAssigner.Unassign(entityId); } private void MoveEntityToHigherArchetype(EntityId entityId, int row, Archetype from, Archetype to) @@ -220,21 +227,21 @@ namespace MoonTools.ECS.Rev2 for (int i = 0; i < from.Components.Count; i += 1) { var componentId = from.Signature[i]; - var destinationColumnIndex = ComponentIndex[componentId][to.Id].ColumnIndex; + var destinationColumnIndex = to.ComponentToColumnIndex[componentId]; // copy all components to higher archetype from.Components[i].CopyToEnd(row, to.Components[destinationColumnIndex]); // delete row on from archetype from.Components[i].Delete(row); + } - if (from.Count > 1) - { - // update row to entity lookup on from archetype - from.RowToEntity[row] = from.RowToEntity[from.Count - 1]; - from.RowToEntity.RemoveAt(from.Count - 1); - EntityIndex[from.RowToEntity[row]] = new Record(from, row); - } + if (from.Count > 1) + { + // update row to entity lookup on from archetype + from.RowToEntity[row] = from.RowToEntity[from.Count - 1]; + from.RowToEntity.RemoveAt(from.Count - 1); + EntityIndex[from.RowToEntity[row]] = new Record(from, row); } // update row to entity lookup on to archetype @@ -257,19 +264,19 @@ namespace MoonTools.ECS.Rev2 // if this isn't the removed component, copy to the lower archetype if (componentId != removed) { - var destinationColumnIndex = ComponentIndex[componentId][to.Id].ColumnIndex; + var destinationColumnIndex = to.ComponentToColumnIndex[componentId]; from.Components[i].CopyToEnd(row, to.Components[destinationColumnIndex]); - - if (from.Count > 0) - { - // update row to entity lookup on from archetype - from.RowToEntity[row] = from.RowToEntity[from.Count - 1]; - from.RowToEntity.RemoveAt(from.Count - 1); - EntityIndex[from.RowToEntity[row]] = new Record(from, row); - } } } + if (from.Count > 1) + { + // update row to entity lookup on from archetype + from.RowToEntity[row] = from.RowToEntity[from.Count - 1]; + from.RowToEntity.RemoveAt(from.Count - 1); + EntityIndex[from.RowToEntity[row]] = new Record(from, row); + } + // update row to entity lookup on to archetype EntityIndex[entityId] = new Record(to, to.Count); to.RowToEntity.Add(entityId); @@ -278,19 +285,46 @@ namespace MoonTools.ECS.Rev2 from.Count -= 1; } + public unsafe void ForEachEntity(ArchetypeSignature signature, + T rowForEachContainer) where T : IForEach where T1 : unmanaged where T2 : unmanaged + { + var archetype = ArchetypeIndex[signature]; + + var componentIdOne = signature[0]; + var columnIndexOne = archetype.ComponentToColumnIndex[componentIdOne]; + var columnOneElements = archetype.Components[columnIndexOne].Elements; + + var componentIdTwo = signature[1]; + var columnIndexTwo = archetype.ComponentToColumnIndex[componentIdTwo]; + var columnTwoElements = archetype.Components[columnIndexTwo].Elements; + + for (int i = archetype.Count - 1; i >= 0; i -= 1) + { + rowForEachContainer.Update(ref ((T1*) columnOneElements)[i], ref ((T2*) columnTwoElements)[i]); + } + + foreach (var edge in archetype.Edges.Values) + { + if (edge.Add != archetype) + { + ForEachEntity(edge.Add.Signature, rowForEachContainer); + } + } + } + public unsafe void ForEachEntity(ArchetypeSignature signature, RefAction rowAction) where T1 : unmanaged where T2 : unmanaged { var archetype = ArchetypeIndex[signature]; var componentIdOne = signature[0]; - var columnIndexOne = ComponentIndex[componentIdOne][archetype.Id].ColumnIndex; + var columnIndexOne = archetype.ComponentToColumnIndex[componentIdOne]; var columnOneElements = archetype.Components[columnIndexOne].Elements; var componentIdTwo = signature[1]; - var columnIndexTwo = ComponentIndex[componentIdTwo][archetype.Id].ColumnIndex; + var columnIndexTwo = archetype.ComponentToColumnIndex[componentIdTwo]; var columnTwoElements = archetype.Components[columnIndexTwo].Elements; - for (int i = 0; i < archetype.Count; i += 1) + for (int i = archetype.Count - 1; i >= 0; i -= 1) { rowAction(ref ((T1*) columnOneElements)[i], ref ((T2*) columnTwoElements)[i]); } @@ -304,15 +338,13 @@ namespace MoonTools.ECS.Rev2 } } - /* - public void ForEachEntity(ArchetypeSignature signature, Action rowAction) + public void ForEachEntity(ArchetypeSignature signature, Action rowAction) { var archetype = ArchetypeIndex[signature]; for (int i = 0; i < archetype.Count; i += 1) { - var entity = new Entity(this, archetype, i, archetype.RowToEntity[i]); - rowAction(entity); + rowAction(archetype.RowToEntity[i]); } // recursion might get too hairy here @@ -324,7 +356,13 @@ namespace MoonTools.ECS.Rev2 } } } - */ + + public ReverseSpanEnumerator Entities(ArchetypeSignature signature) + { + var archetype = ArchetypeIndex[signature]; + return new ReverseSpanEnumerator( + CollectionsMarshal.AsSpan(archetype.RowToEntity)); + } protected virtual void Dispose(bool disposing) {