Entity Iteration Rework (#1)

Filters are now the canonical way to look up Entities. The only exception is the new GetSingletonEntity.
pull/2/head
cosmonaut 2022-04-03 21:46:52 +00:00
parent b4c862d568
commit d7e795309f
4 changed files with 30 additions and 40 deletions

View File

@ -10,11 +10,18 @@ internal class ComponentDepot
private Dictionary<int, HashSet<Type>> entityComponentMap = new Dictionary<int, HashSet<Type>>(); 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 internal void Register<TComponent>() where TComponent : struct
{ {
if (!storages.ContainsKey(typeof(TComponent))) if (!storages.ContainsKey(typeof(TComponent)))
{ {
storages.Add(typeof(TComponent), new ComponentStorage<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(); return Lookup<TComponent>().FirstEntity();
}
public ReadOnlySpan<Entity> ReadEntities<TComponent>() where TComponent : struct
{
return Lookup<TComponent>().AllEntities();
} }
public ReadOnlySpan<TComponent> ReadComponents<TComponent>() where TComponent : struct public ReadOnlySpan<TComponent> ReadComponents<TComponent>() where TComponent : struct
@ -226,8 +228,7 @@ internal class ComponentDepot
filterSignatureToEntityIDs[filterSignature].Add(entityID); filterSignatureToEntityIDs[filterSignature].Add(entityID);
} }
// debug use only! #if DEBUG
public IEnumerable<object> Debug_GetAllComponents(int entityID) public IEnumerable<object> Debug_GetAllComponents(int entityID)
{ {
foreach (var (type, storage) in storages) 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) public IEnumerable<Type> Debug_SearchComponentType(string typeString)
@ -254,4 +255,5 @@ internal class ComponentDepot
} }
} }
} }
#endif
} }

View File

@ -4,8 +4,6 @@ internal abstract class ComponentStorage
{ {
public abstract bool Has(int entityID); public abstract bool Has(int entityID);
public abstract void Remove(int entityID); public abstract void Remove(int entityID);
public abstract ReadOnlySpan<Entity> AllEntities();
public abstract object Debug_Get(int entityID); public abstract object Debug_Get(int entityID);
} }
@ -13,10 +11,9 @@ internal abstract class ComponentStorage
internal class ComponentStorage<TComponent> : ComponentStorage where TComponent : struct internal class ComponentStorage<TComponent> : ComponentStorage where TComponent : struct
{ {
private int nextID; private int nextID;
private IDStorage idStorage = new IDStorage(); private readonly Dictionary<int, int> entityIDToStorageIndex = new Dictionary<int, int>(16);
private readonly Dictionary<int, int> entityIDToStorageIndex = new Dictionary<int, int>(); private int[] entityIDs = new int[16];
private Entity[] storageIndexToEntities = new Entity[64]; private TComponent[] components = new TComponent[16];
private TComponent[] components = new TComponent[64];
public bool Any() public bool Any()
{ {
@ -59,11 +56,11 @@ internal class ComponentStorage<TComponent> : ComponentStorage where TComponent
if (index >= components.Length) if (index >= components.Length)
{ {
Array.Resize(ref components, components.Length * 2); Array.Resize(ref components, components.Length * 2);
Array.Resize(ref storageIndexToEntities, storageIndexToEntities.Length * 2); Array.Resize(ref entityIDs, entityIDs.Length * 2);
} }
entityIDToStorageIndex[entityID] = index; entityIDToStorageIndex[entityID] = index;
storageIndexToEntities[index] = new Entity(entityID); entityIDs[index] = entityID;
} }
components[entityIDToStorageIndex[entityID]] = component; components[entityIDToStorageIndex[entityID]] = component;
@ -79,13 +76,12 @@ internal class ComponentStorage<TComponent> : ComponentStorage where TComponent
var lastElementIndex = nextID - 1; var lastElementIndex = nextID - 1;
// move a component into the hole to maintain contiguous memory // move a component into the hole to maintain contiguous memory
if (entityIDToStorageIndex.Count > 0 && storageIndex != lastElementIndex) if (lastElementIndex != storageIndex)
{ {
var lastEntity = storageIndexToEntities[lastElementIndex]; var lastEntityID = entityIDs[lastElementIndex];
entityIDToStorageIndex[lastEntityID] = storageIndex;
entityIDToStorageIndex[lastEntity.ID] = storageIndex;
storageIndexToEntities[storageIndex] = lastEntity;
components[storageIndex] = components[lastElementIndex]; components[storageIndex] = components[lastElementIndex];
entityIDs[storageIndex] = lastEntityID;
} }
nextID -= 1; nextID -= 1;
@ -98,18 +94,13 @@ internal class ComponentStorage<TComponent> : ComponentStorage where TComponent
entityIDToStorageIndex.Clear(); entityIDToStorageIndex.Clear();
} }
public override ReadOnlySpan<Entity> AllEntities()
{
return new ReadOnlySpan<Entity>(storageIndexToEntities, 0, nextID);
}
public ReadOnlySpan<TComponent> AllComponents() public ReadOnlySpan<TComponent> AllComponents()
{ {
return new ReadOnlySpan<TComponent>(components, 0, nextID); return new ReadOnlySpan<TComponent>(components, 0, nextID);
} }
public ref readonly Entity FirstEntity() public Entity FirstEntity()
{ {
return ref storageIndexToEntities[0]; return new Entity(entityIDs[0]);
} }
} }

View File

@ -1,5 +1,7 @@
// NOTE: these methods are very inefficient // NOTE: these methods are very inefficient
// this class should only be used in debugging contexts!! // this class should only be used in debugging contexts!!
#if DEBUG
namespace MoonTools.ECS namespace MoonTools.ECS
{ {
public abstract class DebugSystem : System public abstract class DebugSystem : System
@ -13,7 +15,7 @@ namespace MoonTools.ECS
return ComponentDepot.Debug_GetAllComponents(entity.ID); 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); return ComponentDepot.Debug_GetEntities(componentType);
} }
@ -24,3 +26,4 @@ namespace MoonTools.ECS
} }
} }
} }
#endif

View File

@ -16,12 +16,6 @@ public abstract class EntityComponentReader
ComponentDepot = componentDepot; 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 protected ReadOnlySpan<TComponent> ReadComponents<TComponent>() where TComponent : struct
{ {
return ComponentDepot.ReadComponents<TComponent>(); return ComponentDepot.ReadComponents<TComponent>();
@ -47,9 +41,9 @@ public abstract class EntityComponentReader
return ref ComponentDepot.Get<TComponent>(); 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) protected bool Exists(in Entity entity)