garbage collection optimizations
							parent
							
								
									01a9211ff4
								
							
						
					
					
						commit
						2f46af30fb
					
				|  | @ -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; | ||||
|  |  | |||
|  | @ -0,0 +1,40 @@ | |||
| using System; | ||||
| 
 | ||||
| namespace MoonTools.ECS | ||||
| { | ||||
| 	public class DynamicArray<T> where T : unmanaged | ||||
| 	{ | ||||
| 		private T[] Array; | ||||
| 		public int Count { get; private set; } | ||||
| 
 | ||||
| 		public Span<T> ToSpan() => new Span<T>(Array, 0, Count); | ||||
| 		public ReverseSpanEnumerator<T> GetEnumerator() => new ReverseSpanEnumerator<T>(new Span<T>(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; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -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); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -62,13 +62,18 @@ namespace MoonTools.ECS | |||
| 			return RelationDepot.Related<TRelationKind>(a.ID, b.ID); | ||||
| 		} | ||||
| 
 | ||||
| 		// relations go A->B, so given A, will give all outgoing B relations. | ||||
| 		protected IEnumerable<(Entity, TRelationKind)> OutRelations<TRelationKind>(in Entity entity) where TRelationKind : unmanaged | ||||
| 		protected TRelationKind GetRelationData<TRelationKind>(in Entity a, in Entity b) where TRelationKind : unmanaged | ||||
| 		{ | ||||
| 			return RelationDepot.Get<TRelationKind>(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<Entity> OutRelations<TRelationKind>(in Entity entity) where TRelationKind : unmanaged | ||||
| 		{ | ||||
| 			return RelationDepot.OutRelations<TRelationKind>(entity.ID); | ||||
| 		} | ||||
| 
 | ||||
| 		protected (Entity, TRelationKind) OutRelationSingleton<TRelationKind>(in Entity entity) where TRelationKind : unmanaged | ||||
| 		protected Entity OutRelationSingleton<TRelationKind>(in Entity entity) where TRelationKind : unmanaged | ||||
| 		{ | ||||
| 			return RelationDepot.OutRelationSingleton<TRelationKind>(entity.ID); | ||||
| 		} | ||||
|  | @ -83,13 +88,13 @@ namespace MoonTools.ECS | |||
| 			return RelationDepot.OutRelationCount<TRelationKind>(entity.ID); | ||||
| 		} | ||||
| 
 | ||||
| 		// Relations go A->B, so given B, will give all incoming A relations. | ||||
| 		protected IEnumerable<(Entity, TRelationKind)> InRelations<TRelationKind>(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<Entity> InRelations<TRelationKind>(in Entity entity) where TRelationKind : unmanaged | ||||
| 		{ | ||||
| 			return RelationDepot.InRelations<TRelationKind>(entity.ID); | ||||
| 		} | ||||
| 
 | ||||
| 		protected (Entity, TRelationKind) InRelationSingleton<TRelationKind>(in Entity entity) where TRelationKind : unmanaged | ||||
| 		protected Entity InRelationSingleton<TRelationKind>(in Entity entity) where TRelationKind : unmanaged | ||||
| 		{ | ||||
| 			return RelationDepot.InRelationSingleton<TRelationKind>(entity.ID); | ||||
| 		} | ||||
|  |  | |||
|  | @ -62,13 +62,12 @@ namespace MoonTools.ECS | |||
| 			EntityToRelationTypeIndices[entityId].Remove(relationIndex); | ||||
| 		} | ||||
| 
 | ||||
| 		// TODO: should these ints be ID types? | ||||
| 		public IEnumerable<int> ComponentTypeIndices(int entityID) | ||||
| 		public HashSet<int> ComponentTypeIndices(int entityID) | ||||
| 		{ | ||||
| 			return EntityToComponentTypeIndices[entityID]; | ||||
| 		} | ||||
| 
 | ||||
| 		public IEnumerable<int> RelationTypeIndices(int entityID) | ||||
| 		public HashSet<int> RelationTypeIndices(int entityID) | ||||
| 		{ | ||||
| 			return EntityToRelationTypeIndices[entityID]; | ||||
| 		} | ||||
|  |  | |||
|  | @ -0,0 +1,36 @@ | |||
| using System; | ||||
| using System.Runtime.CompilerServices; | ||||
| 
 | ||||
| namespace MoonTools.ECS | ||||
| { | ||||
| 	public ref struct ReverseSpanEnumerator<T> | ||||
| 	{ | ||||
| 		private ReadOnlySpan<T> Span; | ||||
| 		private int index; | ||||
| 
 | ||||
| 		public ReverseSpanEnumerator<T> 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<T> span) | ||||
| 		{ | ||||
| 			Span = span; | ||||
| 			index = span.Length; | ||||
| 		} | ||||
| 
 | ||||
| 		public static ReverseSpanEnumerator<T> Empty => new ReverseSpanEnumerator<T>(); | ||||
| 	} | ||||
| } | ||||
|  | @ -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<Entity> Entities => FilterStorage.FilterEntities(Signature); | ||||
| 		public IEnumerable<Entity> EntitiesInRandomOrder => FilterStorage.FilterEntitiesRandom(Signature); | ||||
| 		public ReverseSpanEnumerator<Entity> Entities => FilterStorage.FilterEntities(Signature); | ||||
| 		public LinearCongruentialEnumerator EntitiesInRandomOrder => FilterStorage.FilterEntitiesRandom(Signature); | ||||
| 		public Entity RandomEntity => FilterStorage.FilterRandomEntity(Signature); | ||||
| 
 | ||||
| 		public int Count => FilterStorage.FilterCount(Signature); | ||||
|  |  | |||
|  | @ -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<FilterSignature, IndexableSet<int>> filterSignatureToEntityIDs = new Dictionary<FilterSignature, IndexableSet<int>>(); | ||||
| 		private Dictionary<FilterSignature, IndexableSet<Entity>> filterSignatureToEntityIDs = new Dictionary<FilterSignature, IndexableSet<Entity>>(); | ||||
| 		private Dictionary<int, HashSet<FilterSignature>> typeToFilterSignatures = new Dictionary<int, HashSet<FilterSignature>>(); | ||||
| 
 | ||||
| 		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<int>()); | ||||
| 				filterSignatureToEntityIDs.Add(filterSignature, new IndexableSet<Entity>()); | ||||
| 
 | ||||
| 				foreach (var type in included) | ||||
| 				{ | ||||
|  | @ -46,20 +46,14 @@ namespace MoonTools.ECS | |||
| 			return new Filter(this, included, excluded); | ||||
| 		} | ||||
| 
 | ||||
| 		public IEnumerable<Entity> FilterEntities(FilterSignature filterSignature) | ||||
| 		public ReverseSpanEnumerator<Entity> FilterEntities(FilterSignature filterSignature) | ||||
| 		{ | ||||
| 			foreach (var id in filterSignatureToEntityIDs[filterSignature]) | ||||
| 			{ | ||||
| 				yield return new Entity(id); | ||||
| 			} | ||||
| 			return filterSignatureToEntityIDs[filterSignature].GetEnumerator(); | ||||
| 		} | ||||
| 
 | ||||
| 		public IEnumerable<Entity> 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); | ||||
| 				} | ||||
|  |  | |||
|  | @ -9,7 +9,7 @@ namespace MoonTools.ECS | |||
| 		private Dictionary<T, int> indices; | ||||
| 		private T[] array; | ||||
| 		public int Count { get; private set; } | ||||
| 		public Enumerator GetEnumerator() => new Enumerator(this); | ||||
| 		public ReverseSpanEnumerator<T> GetEnumerator() => new ReverseSpanEnumerator<T>(new Span<T>(array, 0, Count)); | ||||
| 
 | ||||
| 		public IndexableSet(int size = 32) | ||||
| 		{ | ||||
|  |  | |||
|  | @ -42,7 +42,7 @@ namespace MoonTools.ECS | |||
| 			return Lookup<TMessage>().First(); | ||||
| 		} | ||||
| 
 | ||||
| 		public IEnumerable<TMessage> WithEntity<TMessage>(int entityID) where TMessage : unmanaged | ||||
| 		public ReverseSpanEnumerator<TMessage> WithEntity<TMessage>(int entityID) where TMessage : unmanaged | ||||
| 		{ | ||||
| 			return Lookup<TMessage>().WithEntity(entityID); | ||||
| 		} | ||||
|  |  | |||
|  | @ -13,7 +13,8 @@ namespace MoonTools.ECS | |||
| 		private int count = 0; | ||||
| 		private int capacity = 128; | ||||
| 		private TMessage[] messages; | ||||
| 		private Dictionary<int, List<int>> entityToIndices = new Dictionary<int, List<int>>(); | ||||
| 		// duplicating storage here for fast iteration | ||||
| 		private Dictionary<int, DynamicArray<TMessage>> entityToMessages = new Dictionary<int, DynamicArray<TMessage>>(); | ||||
| 
 | ||||
| 		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<int>()); | ||||
| 				entityToMessages.Add(entityID, new DynamicArray<TMessage>()); | ||||
| 			} | ||||
| 			entityToIndices[entityID].Add(count); | ||||
| 			entityToMessages[entityID].Add(message); | ||||
| 
 | ||||
| 			Add(message); | ||||
| 		} | ||||
|  | @ -58,31 +59,32 @@ namespace MoonTools.ECS | |||
| 			return messages[0]; | ||||
| 		} | ||||
| 
 | ||||
| 		public IEnumerable<TMessage> WithEntity(int entityID) | ||||
| 		public ReverseSpanEnumerator<TMessage> 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<TMessage>.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(); | ||||
| 			} | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Runtime.CompilerServices; | ||||
| 
 | ||||
| namespace MoonTools.ECS | ||||
| { | ||||
|  | @ -20,7 +20,7 @@ namespace MoonTools.ECS | |||
| 		/// <summary> | ||||
| 		/// A psuedorandom nonrepeating sequence of integers from 0 to n. | ||||
| 		/// </summary> | ||||
| 		public static IEnumerable<int> 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; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -41,6 +41,11 @@ namespace MoonTools.ECS | |||
| 			Lookup<TRelationKind>().Set(relation, relationData); | ||||
| 		} | ||||
| 
 | ||||
| 		public TRelationKind Get<TRelationKind>(Relation relation) where TRelationKind : unmanaged | ||||
| 		{ | ||||
| 			return Lookup<TRelationKind>().Get(relation); | ||||
| 		} | ||||
| 
 | ||||
| 		public (bool, bool) Remove<TRelationKind>(Relation relation) where TRelationKind : unmanaged | ||||
| 		{ | ||||
| 			return Lookup<TRelationKind>().Remove(relation); | ||||
|  | @ -61,12 +66,12 @@ namespace MoonTools.ECS | |||
| 			return Lookup<TRelationKind>().Has(new Relation(idA, idB)); | ||||
| 		} | ||||
| 
 | ||||
| 		public IEnumerable<(Entity, TRelationKind)> OutRelations<TRelationKind>(int entityID) where TRelationKind : unmanaged | ||||
| 		public ReverseSpanEnumerator<Entity> OutRelations<TRelationKind>(int entityID) where TRelationKind : unmanaged | ||||
| 		{ | ||||
| 			return Lookup<TRelationKind>().OutRelations(entityID); | ||||
| 		} | ||||
| 
 | ||||
| 		public (Entity, TRelationKind) OutRelationSingleton<TRelationKind>(int entityID) where TRelationKind : unmanaged | ||||
| 		public Entity OutRelationSingleton<TRelationKind>(int entityID) where TRelationKind : unmanaged | ||||
| 		{ | ||||
| 			return Lookup<TRelationKind>().OutFirst(entityID); | ||||
| 		} | ||||
|  | @ -81,12 +86,12 @@ namespace MoonTools.ECS | |||
| 			return Lookup<TRelationKind>().HasOutRelation(entityID); | ||||
| 		} | ||||
| 
 | ||||
| 		public IEnumerable<(Entity, TRelationKind)> InRelations<TRelationKind>(int entityID) where TRelationKind : unmanaged | ||||
| 		public ReverseSpanEnumerator<Entity> InRelations<TRelationKind>(int entityID) where TRelationKind : unmanaged | ||||
| 		{ | ||||
| 			return Lookup<TRelationKind>().InRelations(entityID); | ||||
| 		} | ||||
| 
 | ||||
| 		public (Entity, TRelationKind) InRelationSingleton<TRelationKind>(int entityID) where TRelationKind : unmanaged | ||||
| 		public Entity InRelationSingleton<TRelationKind>(int entityID) where TRelationKind : unmanaged | ||||
| 		{ | ||||
| 			return Lookup<TRelationKind>().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<Entity> OutRelations(int entityID, int relationTypeIndex) | ||||
| 		{ | ||||
| 			return storages[relationTypeIndex].OutRelationIndices(entityID); | ||||
| 			return storages[relationTypeIndex].OutRelations(entityID); | ||||
| 		} | ||||
| 
 | ||||
| 		public void Clear() | ||||
|  |  | |||
|  | @ -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<Entity> OutRelations(int entityID); | ||||
| 		public abstract RelationStorage CreateStorage(); | ||||
| 		public abstract void Clear(); | ||||
| 	} | ||||
|  | @ -21,9 +22,9 @@ namespace MoonTools.ECS | |||
| 		private Dictionary<Relation, int> indices = new Dictionary<Relation, int>(16); | ||||
| 		private Relation[] relations = new Relation[16]; | ||||
| 		private TRelation[] relationDatas = new TRelation[16]; | ||||
| 		private Dictionary<int, IndexableSet<int>> outRelations = new Dictionary<int, IndexableSet<int>>(16); | ||||
| 		private Dictionary<int, IndexableSet<int>> inRelations = new Dictionary<int, IndexableSet<int>>(16); | ||||
| 		private Stack<IndexableSet<int>> listPool = new Stack<IndexableSet<int>>(); | ||||
| 		private Dictionary<int, IndexableSet<Entity>> outRelations = new Dictionary<int, IndexableSet<Entity>>(16); | ||||
| 		private Dictionary<int, IndexableSet<Entity>> inRelations = new Dictionary<int, IndexableSet<Entity>>(16); | ||||
| 		private Stack<IndexableSet<Entity>> listPool = new Stack<IndexableSet<Entity>>(); | ||||
| 
 | ||||
| 		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<Entity> 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<Entity>.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<Entity> 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<Entity>.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<int> AcquireHashSetFromPool() | ||||
| 		private IndexableSet<Entity> AcquireHashSetFromPool() | ||||
| 		{ | ||||
| 			if (listPool.Count == 0) | ||||
| 			{ | ||||
| 				listPool.Push(new IndexableSet<int>()); | ||||
| 				listPool.Push(new IndexableSet<Entity>()); | ||||
| 			} | ||||
| 
 | ||||
| 			return listPool.Pop(); | ||||
| 		} | ||||
| 
 | ||||
| 		private void ReturnHashSetToPool(IndexableSet<int> hashSet) | ||||
| 		private void ReturnHashSetToPool(IndexableSet<Entity> 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<TRelation> CreateStorage() | ||||
| 		{ | ||||
| 			return new RelationStorage<TRelation>(); | ||||
|  |  | |||
|  | @ -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)); | ||||
| 					} | ||||
|  |  | |||
|  | @ -43,7 +43,7 @@ namespace MoonTools.ECS | |||
| 			return MessageDepot.Some<TMessage>(); | ||||
| 		} | ||||
| 
 | ||||
| 		protected IEnumerable<TMessage> ReadMessagesWithEntity<TMessage>(in Entity entity) where TMessage : unmanaged | ||||
| 		protected ReverseSpanEnumerator<TMessage> ReadMessagesWithEntity<TMessage>(in Entity entity) where TMessage : unmanaged | ||||
| 		{ | ||||
| 			return MessageDepot.WithEntity<TMessage>(entity.ID); | ||||
| 		} | ||||
|  |  | |||
|  | @ -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<Type> Types => TypeToIndex.Keys; | ||||
| 		public Dictionary<Type, int>.KeyCollection Types => TypeToIndex.Keys; | ||||
| #endif | ||||
| 	} | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue