From 2f46af30fba8613fb084efd1557c7b84b24b995f Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Tue, 13 Dec 2022 00:34:35 -0800 Subject: [PATCH] garbage collection optimizations --- src/ComponentStorage.cs | 3 +- src/DynamicArray.cs | 40 +++++++++ src/Entity.cs | 10 +++ src/EntityComponentReader.cs | 17 ++-- src/EntityStorage.cs | 5 +- src/Enumerators/ReverseSpanEnumerator.cs | 36 ++++++++ src/Filter.cs | 7 +- src/FilterStorage.cs | 28 +++--- src/IndexableSet.cs | 2 +- src/MessageDepot.cs | 2 +- src/MessageStorage.cs | 28 +++--- src/Random.cs | 42 +++++++-- src/Relation.cs | 6 -- src/RelationDepot.cs | 22 +++-- src/RelationStorage.cs | 110 +++++++++++------------ src/Snapshot.cs | 8 +- src/System.cs | 2 +- src/TypeIndices.cs | 4 +- 18 files changed, 245 insertions(+), 127 deletions(-) create mode 100644 src/DynamicArray.cs create mode 100644 src/Enumerators/ReverseSpanEnumerator.cs diff --git a/src/ComponentStorage.cs b/src/ComponentStorage.cs index 5ea989f..41117bc 100644 --- a/src/ComponentStorage.cs +++ b/src/ComponentStorage.cs @@ -83,9 +83,8 @@ namespace MoonTools.ECS // Returns true if the entity had this component. public override bool Remove(int entityID) { - if (entityIDToStorageIndex.ContainsKey(entityID)) + if (entityIDToStorageIndex.TryGetValue(entityID, out int storageIndex)) { - var storageIndex = entityIDToStorageIndex[entityID]; entityIDToStorageIndex.Remove(entityID); var lastElementIndex = nextID - 1; diff --git a/src/DynamicArray.cs b/src/DynamicArray.cs new file mode 100644 index 0000000..c16b697 --- /dev/null +++ b/src/DynamicArray.cs @@ -0,0 +1,40 @@ +using System; + +namespace MoonTools.ECS +{ + public class DynamicArray where T : unmanaged + { + private T[] Array; + public int Count { get; private set; } + + public Span ToSpan() => new Span(Array, 0, Count); + public ReverseSpanEnumerator GetEnumerator() => new ReverseSpanEnumerator(new Span(Array, 0, Count)); + + public DynamicArray(int capacity = 16) + { + Array = new T[capacity]; + Count = 0; + } + + public ref T this[int i] + { + get { return ref Array[i]; } + } + + public void Add(T item) + { + if (Count >= Array.Length) + { + global::System.Array.Resize(ref Array, Array.Length * 2); + } + + Array[Count] = item; + Count += 1; + } + + public void Clear() + { + Count = 0; + } + } +} diff --git a/src/Entity.cs b/src/Entity.cs index ba87a1e..e91da6e 100644 --- a/src/Entity.cs +++ b/src/Entity.cs @@ -35,5 +35,15 @@ namespace MoonTools.ECS { return !a.Equals(b); } + + public static implicit operator int(Entity e) + { + return e.ID; + } + + public static implicit operator Entity(int i) + { + return new Entity(i); + } } } diff --git a/src/EntityComponentReader.cs b/src/EntityComponentReader.cs index 1678317..efdf50d 100644 --- a/src/EntityComponentReader.cs +++ b/src/EntityComponentReader.cs @@ -62,13 +62,18 @@ namespace MoonTools.ECS return RelationDepot.Related(a.ID, b.ID); } - // relations go A->B, so given A, will give all outgoing B relations. - protected IEnumerable<(Entity, TRelationKind)> OutRelations(in Entity entity) where TRelationKind : unmanaged + protected TRelationKind GetRelationData(in Entity a, in Entity b) where TRelationKind : unmanaged + { + return RelationDepot.Get(new Relation(a.ID, b.ID)); + } + + // relations go A->B, so given A, will give all entities in outgoing relations of this kind. + protected ReverseSpanEnumerator OutRelations(in Entity entity) where TRelationKind : unmanaged { return RelationDepot.OutRelations(entity.ID); } - protected (Entity, TRelationKind) OutRelationSingleton(in Entity entity) where TRelationKind : unmanaged + protected Entity OutRelationSingleton(in Entity entity) where TRelationKind : unmanaged { return RelationDepot.OutRelationSingleton(entity.ID); } @@ -83,13 +88,13 @@ namespace MoonTools.ECS return RelationDepot.OutRelationCount(entity.ID); } - // Relations go A->B, so given B, will give all incoming A relations. - protected IEnumerable<(Entity, TRelationKind)> InRelations(in Entity entity) where TRelationKind : unmanaged + // Relations go A->B, so given B, will give all entities in incoming A relations of this kind. + protected ReverseSpanEnumerator InRelations(in Entity entity) where TRelationKind : unmanaged { return RelationDepot.InRelations(entity.ID); } - protected (Entity, TRelationKind) InRelationSingleton(in Entity entity) where TRelationKind : unmanaged + protected Entity InRelationSingleton(in Entity entity) where TRelationKind : unmanaged { return RelationDepot.InRelationSingleton(entity.ID); } diff --git a/src/EntityStorage.cs b/src/EntityStorage.cs index 99d8b6c..d2f912d 100644 --- a/src/EntityStorage.cs +++ b/src/EntityStorage.cs @@ -62,13 +62,12 @@ namespace MoonTools.ECS EntityToRelationTypeIndices[entityId].Remove(relationIndex); } - // TODO: should these ints be ID types? - public IEnumerable ComponentTypeIndices(int entityID) + public HashSet ComponentTypeIndices(int entityID) { return EntityToComponentTypeIndices[entityID]; } - public IEnumerable RelationTypeIndices(int entityID) + public HashSet RelationTypeIndices(int entityID) { return EntityToRelationTypeIndices[entityID]; } diff --git a/src/Enumerators/ReverseSpanEnumerator.cs b/src/Enumerators/ReverseSpanEnumerator.cs new file mode 100644 index 0000000..ce526a5 --- /dev/null +++ b/src/Enumerators/ReverseSpanEnumerator.cs @@ -0,0 +1,36 @@ +using System; +using System.Runtime.CompilerServices; + +namespace MoonTools.ECS +{ + public ref struct ReverseSpanEnumerator + { + private ReadOnlySpan Span; + private int index; + + public ReverseSpanEnumerator GetEnumerator() => this; + + public T Current => Span[index]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + if (index > 0) + { + index -= 1; + return true; + } + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReverseSpanEnumerator(Span span) + { + Span = span; + index = span.Length; + } + + public static ReverseSpanEnumerator Empty => new ReverseSpanEnumerator(); + } +} diff --git a/src/Filter.cs b/src/Filter.cs index ef70da6..4124428 100644 --- a/src/Filter.cs +++ b/src/Filter.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; namespace MoonTools.ECS { @@ -13,8 +14,8 @@ namespace MoonTools.ECS Signature = new FilterSignature(included, excluded); } - public IEnumerable Entities => FilterStorage.FilterEntities(Signature); - public IEnumerable EntitiesInRandomOrder => FilterStorage.FilterEntitiesRandom(Signature); + public ReverseSpanEnumerator Entities => FilterStorage.FilterEntities(Signature); + public LinearCongruentialEnumerator EntitiesInRandomOrder => FilterStorage.FilterEntitiesRandom(Signature); public Entity RandomEntity => FilterStorage.FilterRandomEntity(Signature); public int Count => FilterStorage.FilterCount(Signature); diff --git a/src/FilterStorage.cs b/src/FilterStorage.cs index e380234..b8ea8f3 100644 --- a/src/FilterStorage.cs +++ b/src/FilterStorage.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; namespace MoonTools.ECS @@ -7,7 +7,7 @@ namespace MoonTools.ECS { private EntityStorage EntityStorage; private TypeIndices ComponentTypeIndices; - private Dictionary> filterSignatureToEntityIDs = new Dictionary>(); + private Dictionary> filterSignatureToEntityIDs = new Dictionary>(); private Dictionary> typeToFilterSignatures = new Dictionary>(); public FilterStorage(EntityStorage entityStorage, TypeIndices componentTypeIndices) @@ -21,7 +21,7 @@ namespace MoonTools.ECS var filterSignature = new FilterSignature(included, excluded); if (!filterSignatureToEntityIDs.ContainsKey(filterSignature)) { - filterSignatureToEntityIDs.Add(filterSignature, new IndexableSet()); + filterSignatureToEntityIDs.Add(filterSignature, new IndexableSet()); foreach (var type in included) { @@ -46,20 +46,14 @@ namespace MoonTools.ECS return new Filter(this, included, excluded); } - public IEnumerable FilterEntities(FilterSignature filterSignature) + public ReverseSpanEnumerator FilterEntities(FilterSignature filterSignature) { - foreach (var id in filterSignatureToEntityIDs[filterSignature]) - { - yield return new Entity(id); - } + return filterSignatureToEntityIDs[filterSignature].GetEnumerator(); } - public IEnumerable FilterEntitiesRandom(FilterSignature filterSignature) + public LinearCongruentialEnumerator FilterEntitiesRandom(FilterSignature filterSignature) { - foreach (var index in RandomGenerator.LinearCongruentialGenerator(FilterCount(filterSignature))) - { - yield return new Entity(filterSignatureToEntityIDs[filterSignature][index]); - } + return RandomGenerator.LinearCongruentialGenerator(FilterCount(filterSignature)); } public Entity FilterNthEntity(FilterSignature filterSignature, int index) @@ -80,9 +74,9 @@ namespace MoonTools.ECS public void Check(int entityID, int componentTypeIndex) { - if (typeToFilterSignatures.ContainsKey(componentTypeIndex)) + if (typeToFilterSignatures.TryGetValue(componentTypeIndex, out var filterSignatures)) { - foreach (var filterSignature in typeToFilterSignatures[componentTypeIndex]) + foreach (var filterSignature in filterSignatures) { CheckFilter(entityID, filterSignature); } @@ -140,9 +134,9 @@ namespace MoonTools.ECS public void RemoveEntity(int entityID, int componentTypeIndex) { - if (typeToFilterSignatures.ContainsKey(componentTypeIndex)) + if (typeToFilterSignatures.TryGetValue(componentTypeIndex, out var filterSignatures)) { - foreach (var filterSignature in typeToFilterSignatures[componentTypeIndex]) + foreach (var filterSignature in filterSignatures) { filterSignatureToEntityIDs[filterSignature].Remove(entityID); } diff --git a/src/IndexableSet.cs b/src/IndexableSet.cs index 14689e1..1a79f71 100644 --- a/src/IndexableSet.cs +++ b/src/IndexableSet.cs @@ -9,7 +9,7 @@ namespace MoonTools.ECS private Dictionary indices; private T[] array; public int Count { get; private set; } - public Enumerator GetEnumerator() => new Enumerator(this); + public ReverseSpanEnumerator GetEnumerator() => new ReverseSpanEnumerator(new Span(array, 0, Count)); public IndexableSet(int size = 32) { diff --git a/src/MessageDepot.cs b/src/MessageDepot.cs index 5af175f..63b9e2d 100644 --- a/src/MessageDepot.cs +++ b/src/MessageDepot.cs @@ -42,7 +42,7 @@ namespace MoonTools.ECS return Lookup().First(); } - public IEnumerable WithEntity(int entityID) where TMessage : unmanaged + public ReverseSpanEnumerator WithEntity(int entityID) where TMessage : unmanaged { return Lookup().WithEntity(entityID); } diff --git a/src/MessageStorage.cs b/src/MessageStorage.cs index 545da25..b2f29d0 100644 --- a/src/MessageStorage.cs +++ b/src/MessageStorage.cs @@ -13,7 +13,8 @@ namespace MoonTools.ECS private int count = 0; private int capacity = 128; private TMessage[] messages; - private Dictionary> entityToIndices = new Dictionary>(); + // duplicating storage here for fast iteration + private Dictionary> entityToMessages = new Dictionary>(); public MessageStorage() { @@ -34,11 +35,11 @@ namespace MoonTools.ECS public void Add(int entityID, in TMessage message) { - if (!entityToIndices.ContainsKey(entityID)) + if (!entityToMessages.ContainsKey(entityID)) { - entityToIndices.Add(entityID, new List()); + entityToMessages.Add(entityID, new DynamicArray()); } - entityToIndices[entityID].Add(count); + entityToMessages[entityID].Add(message); Add(message); } @@ -58,31 +59,32 @@ namespace MoonTools.ECS return messages[0]; } - public IEnumerable WithEntity(int entityID) + public ReverseSpanEnumerator WithEntity(int entityID) { - if (entityToIndices.ContainsKey(entityID)) + if (entityToMessages.TryGetValue(entityID, out var messages)) { - foreach (var index in entityToIndices[entityID]) - { - yield return messages[index]; - } + return messages.GetEnumerator(); + } + else + { + return ReverseSpanEnumerator.Empty; } } public ref readonly TMessage FirstWithEntity(int entityID) { - return ref messages[entityToIndices[entityID][0]]; + return ref entityToMessages[entityID][0]; } public bool SomeWithEntity(int entityID) { - return entityToIndices.ContainsKey(entityID) && entityToIndices[entityID].Count > 0; + return entityToMessages.ContainsKey(entityID) && entityToMessages[entityID].Count > 0; } public override void Clear() { count = 0; - foreach (var set in entityToIndices.Values) + foreach (var set in entityToMessages.Values) { set.Clear(); } diff --git a/src/Random.cs b/src/Random.cs index 97c0ddd..324eccd 100644 --- a/src/Random.cs +++ b/src/Random.cs @@ -1,5 +1,5 @@ using System; -using System.Collections.Generic; +using System.Runtime.CompilerServices; namespace MoonTools.ECS { @@ -20,7 +20,7 @@ namespace MoonTools.ECS /// /// A psuedorandom nonrepeating sequence of integers from 0 to n. /// - public static IEnumerable LinearCongruentialGenerator(int n) + public static LinearCongruentialEnumerator LinearCongruentialGenerator(int n) { var x = Primes[random.Next(Primes.Length - 1)]; while (x % n == 0) @@ -29,12 +29,44 @@ namespace MoonTools.ECS x = Primes[random.Next(Primes.Length - 1)]; } - var start = random.Next(n); + return new LinearCongruentialEnumerator(random.Next(n), x, n); + } + } - for (var i = start; i < start + n; i++) + public struct LinearCongruentialEnumerator + { + private readonly int start; + private readonly int count; + private readonly int prime; + private int current; + + public LinearCongruentialEnumerator GetEnumerator() => this; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal LinearCongruentialEnumerator(int start, int prime, int count) + { + current = start; + this.start = start; + this.prime = prime; + this.count = count; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + current += 1; + if (current < start + count) { - yield return (i * x) % n; + return true; } + + return false; + } + + public int Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (current * prime) % count; } } } diff --git a/src/Relation.cs b/src/Relation.cs index 165aca6..6dccd3a 100644 --- a/src/Relation.cs +++ b/src/Relation.cs @@ -13,12 +13,6 @@ namespace MoonTools.ECS B = entityB; } - internal Relation(int idA, int idB) - { - A = new Entity(idA); - B = new Entity(idB); - } - public override bool Equals(object? obj) { return obj is Relation relation && Equals(relation); diff --git a/src/RelationDepot.cs b/src/RelationDepot.cs index 4baa35c..abea455 100644 --- a/src/RelationDepot.cs +++ b/src/RelationDepot.cs @@ -41,6 +41,11 @@ namespace MoonTools.ECS Lookup().Set(relation, relationData); } + public TRelationKind Get(Relation relation) where TRelationKind : unmanaged + { + return Lookup().Get(relation); + } + public (bool, bool) Remove(Relation relation) where TRelationKind : unmanaged { return Lookup().Remove(relation); @@ -61,12 +66,12 @@ namespace MoonTools.ECS return Lookup().Has(new Relation(idA, idB)); } - public IEnumerable<(Entity, TRelationKind)> OutRelations(int entityID) where TRelationKind : unmanaged + public ReverseSpanEnumerator OutRelations(int entityID) where TRelationKind : unmanaged { return Lookup().OutRelations(entityID); } - public (Entity, TRelationKind) OutRelationSingleton(int entityID) where TRelationKind : unmanaged + public Entity OutRelationSingleton(int entityID) where TRelationKind : unmanaged { return Lookup().OutFirst(entityID); } @@ -81,12 +86,12 @@ namespace MoonTools.ECS return Lookup().HasOutRelation(entityID); } - public IEnumerable<(Entity, TRelationKind)> InRelations(int entityID) where TRelationKind : unmanaged + public ReverseSpanEnumerator InRelations(int entityID) where TRelationKind : unmanaged { return Lookup().InRelations(entityID); } - public (Entity, TRelationKind) InRelationSingleton(int entityID) where TRelationKind : unmanaged + public Entity InRelationSingleton(int entityID) where TRelationKind : unmanaged { return Lookup().InFirst(entityID); } @@ -108,6 +113,11 @@ namespace MoonTools.ECS storages[relationTypeIndex].Set(entityA, entityB, relationData); } + public int GetStorageIndex(int relationTypeIndex, int entityA, int entityB) + { + return storages[relationTypeIndex].GetStorageIndex(entityA, entityB); + } + public unsafe void* Get(int relationTypeIndex, int relationStorageIndex) { return storages[relationTypeIndex].Get(relationStorageIndex); @@ -118,9 +128,9 @@ namespace MoonTools.ECS storages[relationTypeIndex].UnrelateAll(entityID); } - public IEnumerable<(int, int)> OutRelationIndices(int entityID, int relationTypeIndex) + public ReverseSpanEnumerator OutRelations(int entityID, int relationTypeIndex) { - return storages[relationTypeIndex].OutRelationIndices(entityID); + return storages[relationTypeIndex].OutRelations(entityID); } public void Clear() diff --git a/src/RelationStorage.cs b/src/RelationStorage.cs index c7ed957..7c522c5 100644 --- a/src/RelationStorage.cs +++ b/src/RelationStorage.cs @@ -6,9 +6,10 @@ namespace MoonTools.ECS internal abstract class RelationStorage { public abstract unsafe void Set(int entityA, int entityB, void* relationData); + public abstract int GetStorageIndex(int entityA, int entityB); public abstract unsafe void* Get(int relationStorageIndex); public abstract void UnrelateAll(int entityID); - public abstract IEnumerable<(int, int)> OutRelationIndices(int entityID); + public abstract ReverseSpanEnumerator OutRelations(int entityID); public abstract RelationStorage CreateStorage(); public abstract void Clear(); } @@ -21,9 +22,9 @@ namespace MoonTools.ECS private Dictionary indices = new Dictionary(16); private Relation[] relations = new Relation[16]; private TRelation[] relationDatas = new TRelation[16]; - private Dictionary> outRelations = new Dictionary>(16); - private Dictionary> inRelations = new Dictionary>(16); - private Stack> listPool = new Stack>(); + private Dictionary> outRelations = new Dictionary>(16); + private Dictionary> inRelations = new Dictionary>(16); + private Stack> listPool = new Stack>(); public IEnumerable<(Entity, Entity, TRelation)> All() { @@ -36,9 +37,8 @@ namespace MoonTools.ECS public void Set(Relation relation, TRelation relationData) { - if (indices.ContainsKey(relation)) + if (indices.TryGetValue(relation, out var index)) { - var index = indices[relation]; relationDatas[index] = relationData; return; } @@ -70,34 +70,37 @@ namespace MoonTools.ECS count += 1; } + public TRelation Get(Relation relation) + { + return relationDatas[indices[relation]]; + } + public bool Has(Relation relation) { return indices.ContainsKey(relation); } - // FIXME: creating the new Relation in here is slightly deranged - public IEnumerable<(Entity, TRelation)> OutRelations(int entityID) + public override ReverseSpanEnumerator OutRelations(int entityID) { - if (outRelations.ContainsKey(entityID)) + if (outRelations.TryGetValue(entityID, out var entityOutRelations)) { - foreach (var id in outRelations[entityID]) - { - var relation = new Relation(entityID, id); - yield return (relation.B, relationDatas[indices[relation]]); - } + return entityOutRelations.GetEnumerator(); + } + else + { + return ReverseSpanEnumerator.Empty; } } - public (Entity, TRelation) OutFirst(int entityID) + public Entity OutFirst(int entityID) { #if DEBUG - if (!outRelations.ContainsKey(entityID)) + if (!outRelations.ContainsKey(entityID) || outRelations[entityID].Count == 0) { throw new KeyNotFoundException("No out relations to this entity!"); } #endif - var relation = new Relation(entityID, outRelations[entityID][0]); - return (relation.B, relationDatas[indices[relation]]); + return outRelations[entityID][0]; } public bool HasOutRelation(int entityID) @@ -107,32 +110,31 @@ namespace MoonTools.ECS public int OutRelationCount(int entityID) { - return outRelations.ContainsKey(entityID) ? outRelations[entityID].Count : 0; + return outRelations.TryGetValue(entityID, out var entityOutRelations) ? entityOutRelations.Count : 0; } - public IEnumerable<(Entity, TRelation)> InRelations(int entityID) + public ReverseSpanEnumerator InRelations(int entityID) { - if (inRelations.ContainsKey(entityID)) + if (inRelations.TryGetValue(entityID, out var entityInRelations)) { - foreach (var id in inRelations[entityID]) - { - var relation = new Relation(id, entityID); - yield return (relation.A, relationDatas[indices[relation]]); - } + return entityInRelations.GetEnumerator(); + } + else + { + return ReverseSpanEnumerator.Empty; } } - public (Entity, TRelation) InFirst(int entityID) + public Entity InFirst(int entityID) { #if DEBUG - if (!inRelations.ContainsKey(entityID)) + if (!inRelations.ContainsKey(entityID) || inRelations[entityID].Count == 0) { throw new KeyNotFoundException("No out relations to this entity!"); } #endif - var relation = new Relation(inRelations[entityID][0], entityID); - return (relation.A, relationDatas[indices[relation]]); + return inRelations[entityID][0]; } public bool HasInRelation(int entityID) @@ -142,7 +144,7 @@ namespace MoonTools.ECS public int InRelationCount(int entityID) { - return inRelations.ContainsKey(entityID) ? inRelations[entityID].Count : 0; + return inRelations.TryGetValue(entityID, out var entityInRelations) ? entityInRelations.Count : 0; } public (bool, bool) Remove(Relation relation) @@ -150,27 +152,26 @@ namespace MoonTools.ECS var aEmpty = false; var bEmpty = false; - if (outRelations.ContainsKey(relation.A.ID)) + if (outRelations.TryGetValue(relation.A.ID, out var entityOutRelations)) { - outRelations[relation.A.ID].Remove(relation.B.ID); + entityOutRelations.Remove(relation.B.ID); if (outRelations[relation.A.ID].Count == 0) { aEmpty = true; } } - if (inRelations.ContainsKey(relation.B.ID)) + if (inRelations.TryGetValue(relation.B.ID, out var entityInRelations)) { - inRelations[relation.B.ID].Remove(relation.A.ID); + entityInRelations.Remove(relation.A.ID); if (inRelations[relation.B.ID].Count == 0) { bEmpty = true; } } - if (indices.ContainsKey(relation)) + if (indices.TryGetValue(relation, out var index)) { - var index = indices[relation]; var lastElementIndex = count - 1; // move an element into the hole @@ -189,17 +190,17 @@ namespace MoonTools.ECS return (aEmpty, bEmpty); } - private IndexableSet AcquireHashSetFromPool() + private IndexableSet AcquireHashSetFromPool() { if (listPool.Count == 0) { - listPool.Push(new IndexableSet()); + listPool.Push(new IndexableSet()); } return listPool.Pop(); } - private void ReturnHashSetToPool(IndexableSet hashSet) + private void ReturnHashSetToPool(IndexableSet hashSet) { hashSet.Clear(); listPool.Push(hashSet); @@ -212,6 +213,11 @@ namespace MoonTools.ECS Set(new Relation(entityA, entityB), *((TRelation*) relationData)); } + public override int GetStorageIndex(int entityA, int entityB) + { + return indices[new Relation(entityA, entityB)]; + } + public override unsafe void* Get(int relationStorageIndex) { fixed (void* p = &relations[relationStorageIndex]) @@ -222,41 +228,29 @@ namespace MoonTools.ECS public override void UnrelateAll(int entityID) { - if (outRelations.ContainsKey(entityID)) + if (outRelations.TryGetValue(entityID, out var entityOutRelations)) { - foreach (var entityB in outRelations[entityID]) + foreach (var entityB in entityOutRelations) { Remove(new Relation(entityID, entityB)); } - ReturnHashSetToPool(outRelations[entityID]); + ReturnHashSetToPool(entityOutRelations); outRelations.Remove(entityID); } - if (inRelations.ContainsKey(entityID)) + if (inRelations.TryGetValue(entityID, out var entityInRelations)) { - foreach (var entityA in inRelations[entityID]) + foreach (var entityA in entityInRelations) { Remove(new Relation(entityA, entityID)); } - ReturnHashSetToPool(inRelations[entityID]); + ReturnHashSetToPool(entityInRelations); inRelations.Remove(entityID); } } - public override IEnumerable<(int, int)> OutRelationIndices(int entityID) - { - if (outRelations.ContainsKey(entityID)) - { - foreach (var id in outRelations[entityID]) - { - var relation = new Relation(entityID, id); - yield return (id, indices[relation]); - } - } - } - public override RelationStorage CreateStorage() { return new RelationStorage(); diff --git a/src/Snapshot.cs b/src/Snapshot.cs index 6c8be13..6fd6bcb 100644 --- a/src/Snapshot.cs +++ b/src/Snapshot.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; namespace MoonTools.ECS @@ -50,7 +50,7 @@ namespace MoonTools.ECS { SnapshotEntityStorage.AddRelationKind(snapshotEntityID, relationTypeIndex); - foreach (var (otherEntityID, relationStorageIndex) in World.RelationDepot.OutRelationIndices(worldEntity.ID, relationTypeIndex)) + foreach (var otherEntityID in World.RelationDepot.OutRelations(worldEntity.ID, relationTypeIndex)) { #if DEBUG if (!World.FilterStorage.CheckSatisfied(otherEntityID, Filter.Signature)) @@ -58,6 +58,7 @@ namespace MoonTools.ECS throw new InvalidOperationException($"Snapshot entity {worldEntity.ID} is related to non-snapshot entity {otherEntityID}!"); } #endif + var relationStorageIndex = World.RelationDepot.GetStorageIndex(relationTypeIndex, worldEntity, otherEntityID); var otherSnapshotID = WorldToSnapshotID[otherEntityID]; SnapshotEntityStorage.AddRelationKind(otherSnapshotID, relationTypeIndex); SnapshotRelationDepot.Set(snapshotEntityID, otherSnapshotID, relationTypeIndex, World.RelationDepot.Get(relationTypeIndex, relationStorageIndex)); @@ -99,8 +100,9 @@ namespace MoonTools.ECS { World.EntityStorage.AddRelationKind(worldID, relationTypeIndex); - foreach (var (otherEntityID, relationStorageIndex) in SnapshotRelationDepot.OutRelationIndices(i, relationTypeIndex)) + foreach (var otherEntityID in SnapshotRelationDepot.OutRelations(i, relationTypeIndex)) { + var relationStorageIndex = SnapshotRelationDepot.GetStorageIndex(relationTypeIndex, i, otherEntityID); var otherEntityWorldID = SnapshotToWorldID[otherEntityID]; World.RelationDepot.Set(worldID, otherEntityWorldID, relationTypeIndex, SnapshotRelationDepot.Get(relationTypeIndex, relationStorageIndex)); } diff --git a/src/System.cs b/src/System.cs index 9e98e6a..35c44a6 100644 --- a/src/System.cs +++ b/src/System.cs @@ -43,7 +43,7 @@ namespace MoonTools.ECS return MessageDepot.Some(); } - protected IEnumerable ReadMessagesWithEntity(in Entity entity) where TMessage : unmanaged + protected ReverseSpanEnumerator ReadMessagesWithEntity(in Entity entity) where TMessage : unmanaged { return MessageDepot.WithEntity(entity.ID); } diff --git a/src/TypeIndices.cs b/src/TypeIndices.cs index 53bf700..2f43850 100644 --- a/src/TypeIndices.cs +++ b/src/TypeIndices.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; namespace MoonTools.ECS @@ -27,7 +27,7 @@ namespace MoonTools.ECS #if DEBUG - public IEnumerable Types => TypeToIndex.Keys; + public Dictionary.KeyCollection Types => TypeToIndex.Keys; #endif } }