From 31083994ab66fe5ee7d95b197a4742596cc0759f Mon Sep 17 00:00:00 2001
From: cosmonaut <evan@moonside.games>
Date: Sun, 3 Apr 2022 14:12:13 -0700
Subject: [PATCH 1/3] storage tweak

---
 src/ComponentDepot.cs        | 24 +++++++++++++-----------
 src/ComponentStorage.cs      | 30 +++++++++++-------------------
 src/DebugSystem.cs           |  2 +-
 src/EntityComponentReader.cs | 10 ++--------
 4 files changed, 27 insertions(+), 39 deletions(-)

diff --git a/src/ComponentDepot.cs b/src/ComponentDepot.cs
index 275a48f..dae13fb 100644
--- a/src/ComponentDepot.cs
+++ b/src/ComponentDepot.cs
@@ -10,11 +10,18 @@ internal class ComponentDepot
 
 	private Dictionary<int, HashSet<Type>> entityComponentMap = new Dictionary<int, HashSet<Type>>();
 
+	#if DEBUG
+	private Dictionary<Type, Filter> singleComponentFilters = new Dictionary<Type, Filter>();
+	#endif
+
 	internal void Register<TComponent>() where TComponent : struct
 	{
 		if (!storages.ContainsKey(typeof(TComponent)))
 		{
 			storages.Add(typeof(TComponent), new ComponentStorage<TComponent>());
+			#if DEBUG
+			singleComponentFilters.Add(typeof(TComponent), CreateFilter(new HashSet<Type>() { typeof(TComponent) }, new HashSet<Type>()));
+			#endif
 		}
 	}
 
@@ -79,14 +86,9 @@ internal class ComponentDepot
 		}
 	}
 
-	public ref readonly Entity GetEntity<TComponent>() where TComponent : struct
+	public Entity GetSingletonEntity<TComponent>() where TComponent : struct
 	{
-		return ref Lookup<TComponent>().FirstEntity();
-	}
-
-	public ReadOnlySpan<Entity> ReadEntities<TComponent>() where TComponent : struct
-	{
-		return Lookup<TComponent>().AllEntities();
+		return Lookup<TComponent>().FirstEntity();
 	}
 
 	public ReadOnlySpan<TComponent> ReadComponents<TComponent>() where TComponent : struct
@@ -226,8 +228,7 @@ internal class ComponentDepot
 		filterSignatureToEntityIDs[filterSignature].Add(entityID);
 	}
 
-	// debug use only!
-
+	#if DEBUG
 	public IEnumerable<object> Debug_GetAllComponents(int entityID)
 	{
 		foreach (var (type, storage) in storages)
@@ -239,9 +240,9 @@ internal class ComponentDepot
 		}
 	}
 
-	public ReadOnlySpan<Entity> Debug_GetEntities(Type componentType)
+	public IEnumerable<Entity> Debug_GetEntities(Type componentType)
 	{
-		return Lookup(componentType).AllEntities();
+		return singleComponentFilters[componentType].Entities;
 	}
 
 	public IEnumerable<Type> Debug_SearchComponentType(string typeString)
@@ -254,4 +255,5 @@ internal class ComponentDepot
 			}
 		}
 	}
+	#endif
 }
diff --git a/src/ComponentStorage.cs b/src/ComponentStorage.cs
index c5d86a2..0d7ba6e 100644
--- a/src/ComponentStorage.cs
+++ b/src/ComponentStorage.cs
@@ -4,8 +4,6 @@ internal abstract class ComponentStorage
 {
 	public abstract bool Has(int entityID);
 	public abstract void Remove(int entityID);
-	public abstract ReadOnlySpan<Entity> AllEntities();
-
 	public abstract object Debug_Get(int entityID);
 }
 
@@ -14,9 +12,9 @@ internal class ComponentStorage<TComponent> : ComponentStorage where TComponent
 {
 	private int nextID;
 	private IDStorage idStorage = new IDStorage();
-	private readonly Dictionary<int, int> entityIDToStorageIndex = new Dictionary<int, int>();
-	private Entity[] storageIndexToEntities = new Entity[64];
-	private TComponent[] components = new TComponent[64];
+	private readonly Dictionary<int, int> entityIDToStorageIndex = new Dictionary<int, int>(16);
+	private int[] entityIDs = new int[16];
+	private TComponent[] components = new TComponent[16];
 
 	public bool Any()
 	{
@@ -59,11 +57,11 @@ internal class ComponentStorage<TComponent> : ComponentStorage where TComponent
 			if (index >= components.Length)
 			{
 				Array.Resize(ref components, components.Length * 2);
-				Array.Resize(ref storageIndexToEntities, storageIndexToEntities.Length * 2);
+				Array.Resize(ref entityIDs, entityIDs.Length * 2);
 			}
 
 			entityIDToStorageIndex[entityID] = index;
-			storageIndexToEntities[index] = new Entity(entityID);
+			entityIDs[index] = entityID;
 		}
 
 		components[entityIDToStorageIndex[entityID]] = component;
@@ -79,13 +77,12 @@ internal class ComponentStorage<TComponent> : ComponentStorage where TComponent
 			var lastElementIndex = nextID - 1;
 
 			// move a component into the hole to maintain contiguous memory
-			if (entityIDToStorageIndex.Count > 0 && storageIndex != lastElementIndex)
+			if (lastElementIndex != storageIndex)
 			{
-				var lastEntity = storageIndexToEntities[lastElementIndex];
-
-				entityIDToStorageIndex[lastEntity.ID] = storageIndex;
-				storageIndexToEntities[storageIndex] = lastEntity;
+				var lastEntityID = entityIDs[lastElementIndex];
+				entityIDToStorageIndex[lastEntityID] = storageIndex;
 				components[storageIndex] = components[lastElementIndex];
+				entityIDs[storageIndex] = lastEntityID;
 			}
 
 			nextID -= 1;
@@ -98,18 +95,13 @@ internal class ComponentStorage<TComponent> : ComponentStorage where TComponent
 		entityIDToStorageIndex.Clear();
 	}
 
-	public override ReadOnlySpan<Entity> AllEntities()
-	{
-		return new ReadOnlySpan<Entity>(storageIndexToEntities, 0, nextID);
-	}
-
 	public ReadOnlySpan<TComponent> AllComponents()
 	{
 		return new ReadOnlySpan<TComponent>(components, 0, nextID);
 	}
 
-	public ref readonly Entity FirstEntity()
+	public Entity FirstEntity()
 	{
-		return ref storageIndexToEntities[0];
+		return new Entity(entityIDs[0]);
 	}
 }
diff --git a/src/DebugSystem.cs b/src/DebugSystem.cs
index 0697800..b4836cd 100644
--- a/src/DebugSystem.cs
+++ b/src/DebugSystem.cs
@@ -13,7 +13,7 @@ namespace MoonTools.ECS
 			return ComponentDepot.Debug_GetAllComponents(entity.ID);
 		}
 
-		protected ReadOnlySpan<Entity> Debug_GetEntities(Type componentType)
+		protected IEnumerable<Entity> Debug_GetEntities(Type componentType)
 		{
 			return ComponentDepot.Debug_GetEntities(componentType);
 		}
diff --git a/src/EntityComponentReader.cs b/src/EntityComponentReader.cs
index 6ef1469..4146d94 100644
--- a/src/EntityComponentReader.cs
+++ b/src/EntityComponentReader.cs
@@ -16,12 +16,6 @@ public abstract class EntityComponentReader
 		ComponentDepot = componentDepot;
 	}
 
-	// TODO: is this faster or slower than a single-component Filter?
-	protected ReadOnlySpan<Entity> ReadEntities<TComponent>() where TComponent : struct
-	{
-		return ComponentDepot.ReadEntities<TComponent>();
-	}
-
 	protected ReadOnlySpan<TComponent> ReadComponents<TComponent>() where TComponent : struct
 	{
 		return ComponentDepot.ReadComponents<TComponent>();
@@ -47,9 +41,9 @@ public abstract class EntityComponentReader
 		return ref ComponentDepot.Get<TComponent>();
 	}
 
-	protected ref readonly Entity GetEntity<TComponent>() where TComponent : struct
+	protected Entity GetSingletonEntity<TComponent>() where TComponent : struct
 	{
-		return ref ComponentDepot.GetEntity<TComponent>();
+		return ComponentDepot.GetSingletonEntity<TComponent>();
 	}
 
 	protected bool Exists(in Entity entity)
-- 
2.25.1


From 1c184c016639208ba92a243d907adfbc67ee129c Mon Sep 17 00:00:00 2001
From: cosmonaut <evan@moonside.games>
Date: Sun, 3 Apr 2022 14:14:50 -0700
Subject: [PATCH 2/3] component storage doesnt need ID storage

---
 src/ComponentStorage.cs | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/ComponentStorage.cs b/src/ComponentStorage.cs
index 0d7ba6e..a777494 100644
--- a/src/ComponentStorage.cs
+++ b/src/ComponentStorage.cs
@@ -11,7 +11,6 @@ internal abstract class ComponentStorage
 internal class ComponentStorage<TComponent> : ComponentStorage where TComponent : struct
 {
 	private int nextID;
-	private IDStorage idStorage = new IDStorage();
 	private readonly Dictionary<int, int> entityIDToStorageIndex = new Dictionary<int, int>(16);
 	private int[] entityIDs = new int[16];
 	private TComponent[] components = new TComponent[16];
-- 
2.25.1


From e3a313323d72a8957992943b6de43a48e3014cd7 Mon Sep 17 00:00:00 2001
From: cosmonaut <evan@moonside.games>
Date: Sun, 3 Apr 2022 14:43:51 -0700
Subject: [PATCH 3/3] only build DebugSystem in debug mode

---
 src/DebugSystem.cs | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/DebugSystem.cs b/src/DebugSystem.cs
index b4836cd..6db9882 100644
--- a/src/DebugSystem.cs
+++ b/src/DebugSystem.cs
@@ -1,5 +1,7 @@
 // NOTE: these methods are very inefficient
 // this class should only be used in debugging contexts!!
+
+#if DEBUG
 namespace MoonTools.ECS
 {
 	public abstract class DebugSystem : System
@@ -24,3 +26,4 @@ namespace MoonTools.ECS
 		}
 	}
 }
+#endif
-- 
2.25.1