restructure entity queries to enable caching

pull/5/head
Evan Hemsley 2019-12-22 18:13:35 -08:00
parent 4e3aaa7d47
commit 1e73351b07
9 changed files with 156 additions and 133 deletions

View File

@ -0,0 +1,28 @@
using Encompass.Exceptions;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Encompass
{
[AttributeUsage(AttributeTargets.Class)]
public class QueryWith : Attribute
{
public readonly HashSet<Type> queryWithTypes = new HashSet<Type>();
public QueryWith(params Type[] queryWithTypes)
{
foreach (var queryWithType in queryWithTypes)
{
var isComponent = queryWithType.GetInterfaces().Contains(typeof(IComponent));
if (!isComponent)
{
throw new IllegalReadTypeException("{0} must be a Component", queryWithType.Name);
}
this.queryWithTypes.Add(queryWithType);
}
}
}
}

View File

@ -0,0 +1,28 @@
using Encompass.Exceptions;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Encompass
{
[AttributeUsage(AttributeTargets.Class)]
public class QueryWithout : Attribute
{
public readonly HashSet<Type> queryWithoutTypes = new HashSet<Type>();
public QueryWithout(params Type[] queryWithoutTypes)
{
foreach (var type in queryWithoutTypes)
{
var isComponent = type.GetInterfaces().Contains(typeof(IComponent));
if (!isComponent)
{
throw new IllegalReadTypeException("{0} must be a Component", type.Name);
}
this.queryWithoutTypes.Add(type);
}
}
}
}

View File

@ -7,11 +7,11 @@ namespace Encompass
internal class ComponentStore internal class ComponentStore
{ {
private Dictionary<Type, TypedComponentStore> Stores = new Dictionary<Type, TypedComponentStore>(512); private Dictionary<Type, TypedComponentStore> Stores = new Dictionary<Type, TypedComponentStore>(512);
private ComponentBitSet componentBitSet; public ComponentBitSet ComponentBitSet { get; private set; }
public ComponentStore(Dictionary<Type, int> typeToIndex) public ComponentStore(Dictionary<Type, int> typeToIndex)
{ {
componentBitSet = new ComponentBitSet(typeToIndex); ComponentBitSet = new ComponentBitSet(typeToIndex);
} }
public IEnumerable<(Type, TypedComponentStore)> StoresEnumerable() public IEnumerable<(Type, TypedComponentStore)> StoresEnumerable()
@ -33,7 +33,7 @@ namespace Encompass
public void FinishRegistering() public void FinishRegistering()
{ {
componentBitSet.FinishRegistering(); ComponentBitSet.FinishRegistering();
} }
private TypedComponentStore<TComponent> Lookup<TComponent>() where TComponent : struct, IComponent private TypedComponentStore<TComponent> Lookup<TComponent>() where TComponent : struct, IComponent
@ -54,7 +54,7 @@ namespace Encompass
public BitSet1024 EntityBitArray(Entity entity) public BitSet1024 EntityBitArray(Entity entity)
{ {
return componentBitSet.EntityBitArray(entity); return ComponentBitSet.EntityBitArray(entity);
} }
public TComponent Get<TComponent>(Entity entity) where TComponent : struct, IComponent public TComponent Get<TComponent>(Entity entity) where TComponent : struct, IComponent
@ -65,18 +65,18 @@ namespace Encompass
public void Set<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent public void Set<TComponent>(Entity entity, TComponent component) where TComponent : struct, IComponent
{ {
Lookup<TComponent>().Set(entity, component); Lookup<TComponent>().Set(entity, component);
componentBitSet.Set<TComponent>(entity); ComponentBitSet.Set<TComponent>(entity);
} }
public bool Set<TComponent>(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent public bool Set<TComponent>(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent
{ {
componentBitSet.Set<TComponent>(entity); ComponentBitSet.Set<TComponent>(entity);
return Lookup<TComponent>().Set(entity, component, priority); return Lookup<TComponent>().Set(entity, component, priority);
} }
public void Remove<TComponent>(Entity entity) where TComponent : struct, IComponent public void Remove<TComponent>(Entity entity) where TComponent : struct, IComponent
{ {
componentBitSet.RemoveComponent<TComponent>(entity); ComponentBitSet.RemoveComponent<TComponent>(entity);
Lookup<TComponent>().Remove(entity); Lookup<TComponent>().Remove(entity);
} }
@ -86,7 +86,7 @@ namespace Encompass
{ {
entry.Remove(entity); entry.Remove(entity);
} }
componentBitSet.RemoveEntity(entity); ComponentBitSet.RemoveEntity(entity);
} }
public bool Any<TComponent>() where TComponent : struct, IComponent public bool Any<TComponent>() where TComponent : struct, IComponent
@ -117,7 +117,7 @@ namespace Encompass
public void ClearAll() public void ClearAll()
{ {
componentBitSet.Clear(); ComponentBitSet.Clear();
foreach (var store in Stores.Values) foreach (var store in Stores.Values)
{ {
store.Clear(); store.Clear();
@ -127,7 +127,7 @@ namespace Encompass
public void SwapWith(ComponentStore other) public void SwapWith(ComponentStore other)
{ {
(Stores, other.Stores) = (other.Stores, Stores); (Stores, other.Stores) = (other.Stores, Stores);
(componentBitSet, other.componentBitSet) = (other.componentBitSet, componentBitSet); (ComponentBitSet, other.ComponentBitSet) = (other.ComponentBitSet, ComponentBitSet);
} }
} }
} }

View File

@ -197,14 +197,7 @@ namespace Encompass
UpToDateComponentStore.Remove<TComponent>(entity); UpToDateComponentStore.Remove<TComponent>(entity);
} }
internal BitSet1024 PendingBits(Entity entity) internal ComponentBitSet PendingBits { get { return pendingComponentStore.ComponentBitSet; } }
{ internal ComponentBitSet ExistingBits { get { return existingComponentStore.ComponentBitSet; } }
return pendingComponentStore.EntityBitArray(entity);
}
internal BitSet1024 ExistingBits(Entity entity)
{
return existingComponentStore.EntityBitArray(entity);
}
} }
} }

View File

@ -3,6 +3,7 @@ using System.Reflection;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Encompass.Exceptions; using Encompass.Exceptions;
using Encompass.Collections;
namespace Encompass namespace Encompass
{ {
@ -21,6 +22,8 @@ namespace Encompass
internal readonly HashSet<Type> receiveTypes = new HashSet<Type>(); internal readonly HashSet<Type> receiveTypes = new HashSet<Type>();
internal readonly HashSet<Type> writeTypes = new HashSet<Type>(); internal readonly HashSet<Type> writeTypes = new HashSet<Type>();
internal readonly HashSet<Type> writePendingTypes = new HashSet<Type>(); internal readonly HashSet<Type> writePendingTypes = new HashSet<Type>();
internal readonly HashSet<Type> queryWithTypes = new HashSet<Type>();
internal readonly HashSet<Type> queryWithoutTypes = new HashSet<Type>();
internal readonly Dictionary<Type, int> writePriorities = new Dictionary<Type, int>(); internal readonly Dictionary<Type, int> writePriorities = new Dictionary<Type, int>();
internal readonly int defaultWritePriority = 0; internal readonly int defaultWritePriority = 0;
@ -36,6 +39,8 @@ namespace Encompass
private ComponentUpdateManager componentUpdateManager; private ComponentUpdateManager componentUpdateManager;
private TimeManager timeManager; private TimeManager timeManager;
private EntitySetQuery entityQuery;
protected Engine() protected Engine()
{ {
ID = Guid.NewGuid(); ID = Guid.NewGuid();
@ -82,6 +87,18 @@ namespace Encompass
{ {
readPendingTypes = readsPendingAttribute.readPendingTypes; readPendingTypes = readsPendingAttribute.readPendingTypes;
} }
var queryWithAttribute = GetType().GetCustomAttribute<QueryWith>(false);
if (queryWithAttribute != null)
{
queryWithTypes = queryWithAttribute.queryWithTypes;
}
var queryWithoutAttribute = GetType().GetCustomAttribute<QueryWithout>(false);
if (queryWithoutAttribute != null)
{
queryWithoutTypes = queryWithoutAttribute.queryWithoutTypes;
}
} }
public override bool Equals(object obj) public override bool Equals(object obj)
@ -612,16 +629,49 @@ namespace Encompass
timeManager.ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime, easeOutFunction); timeManager.ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime, easeOutFunction);
} }
protected IEnumerable<Entity> QueryEntities()
{
foreach (var entity in entityQuery.FilterEntities(entityManager.Entities, componentUpdateManager.PendingBits, componentUpdateManager.ExistingBits))
{
yield return entity;
}
}
/// <summary> /// <summary>
/// Returns an empty EntitySetQuery. Can be modified and iterated over to obtain Entities that fit the given criteria. /// Returns an empty EntitySetQuery. Can be modified and iterated over to obtain Entities that fit the given criteria.
/// </summary> /// </summary>
protected EntitySetQueryBuilder EntityQueryBuilder() internal void BuildEntityQuery()
{ {
return new EntitySetQueryBuilder( var withMask = BitSet1024Builder.Zeroes();
entityManager, foreach (var type in queryWithTypes)
componentUpdateManager, {
readPendingTypes, withMask = withMask.Set(componentUpdateManager.TypeToIndex[type]);
readTypes }
var withoutMask = BitSet1024Builder.Zeroes();
foreach (var type in queryWithoutTypes)
{
withoutMask = withoutMask.Set(componentUpdateManager.TypeToIndex[type]);
}
var pendingMask = BitSet1024Builder.Zeroes();
foreach (var type in readPendingTypes)
{
pendingMask = pendingMask.Set(componentUpdateManager.TypeToIndex[type]);
}
var existingMask = BitSet1024Builder.Zeroes();
foreach (var type in readTypes)
{
existingMask = existingMask.Set(componentUpdateManager.TypeToIndex[type]);
}
entityQuery = new EntitySetQuery(
withMask.And(pendingMask),
withMask.And(existingMask),
withoutMask.And(pendingMask),
withoutMask.And(existingMask),
withMask.Not()
); );
} }
} }

View File

@ -4,20 +4,16 @@ using Encompass.Collections;
namespace Encompass namespace Encompass
{ {
public struct EntitySetQuery : IEnumerable<Entity> internal struct EntitySetQuery
{ {
private EntityManager EntityManager { get; }
private ComponentUpdateManager ComponentUpdateManager { get; }
private BitSet1024 WithPendingMask { get; } private BitSet1024 WithPendingMask { get; }
private BitSet1024 WithExistingMask { get; } private BitSet1024 WithExistingMask { get; }
private BitSet1024 WithoutPendingMask { get; } private BitSet1024 WithoutPendingMask { get; }
private BitSet1024 WithoutExistingMask { get; } private BitSet1024 WithoutExistingMask { get; }
private BitSet1024 NotWithMask { get; } private BitSet1024 NotWithMask { get; }
internal EntitySetQuery(EntityManager entityManager, ComponentUpdateManager componentUpdateManager, BitSet1024 withPendingMask, BitSet1024 withExistingMask, BitSet1024 withoutPendingMask, BitSet1024 withoutExistingMask, BitSet1024 notWithMask) internal EntitySetQuery(BitSet1024 withPendingMask, BitSet1024 withExistingMask, BitSet1024 withoutPendingMask, BitSet1024 withoutExistingMask, BitSet1024 notWithMask)
{ {
EntityManager = entityManager;
ComponentUpdateManager = componentUpdateManager;
WithPendingMask = withPendingMask; WithPendingMask = withPendingMask;
WithExistingMask = withExistingMask; WithExistingMask = withExistingMask;
WithoutPendingMask = withoutPendingMask; WithoutPendingMask = withoutPendingMask;
@ -25,28 +21,23 @@ namespace Encompass
NotWithMask = notWithMask; NotWithMask = notWithMask;
} }
public IEnumerator<Entity> GetEnumerator() public IEnumerable<Entity> FilterEntities(IEnumerable<Entity> entities, ComponentBitSet pendingBitLookup, ComponentBitSet existingBitLookup)
{ {
foreach (var entity in EntityManager.Entities) foreach (var entity in entities)
{ {
var pendingBits = ComponentUpdateManager.PendingBits(entity); var pendingBits = pendingBitLookup.EntityBitArray(entity);
var existingBits = ComponentUpdateManager.ExistingBits(entity); var existingBits = existingBitLookup.EntityBitArray(entity);
var pending = WithPendingMask.And(pendingBits); var pending = WithPendingMask.And(pendingBits);
var existing = WithExistingMask.And(existingBits); var existing = WithExistingMask.And(existingBits);
var withCheck = pending.Or(existing).Or(NotWithMask); var withCheck = pending.Or(existing).Or(NotWithMask);
var pendingForbidden = WithoutPendingMask.And(pendingBits); var pendingForbidden = WithoutPendingMask.And(pendingBits).Not();
var existingForbidden = WithoutExistingMask.And(existingBits); var existingForbidden = WithoutExistingMask.And(existingBits).Not();
var withoutCheck = pendingForbidden.And(existingForbidden); var withoutCheck = pendingForbidden.And(existingForbidden);
if (withCheck.And(withoutCheck).AllTrue()) { yield return entity; } if (withCheck.And(withoutCheck).AllTrue()) { yield return entity; }
} }
} }
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
} }
} }

View File

@ -1,70 +0,0 @@
using System;
using System.Collections.Generic;
using Encompass.Collections;
namespace Encompass
{
/// <summary>
/// EntitySetQuery is used to efficiently obtain a set of Entities that have all required Components and do not have any forbidden Components.
/// </summary>
public class EntitySetQueryBuilder
{
internal EntitySetQueryBuilder(EntityManager entityManager, ComponentUpdateManager componentUpdateManager, IEnumerable<Type> pendingTypes, IEnumerable<Type> existingTypes)
{
EntityManager = entityManager;
ComponentUpdateManager = componentUpdateManager;
WithMask = BitSet1024Builder.Zeroes();
WithoutMask = BitSet1024Builder.Zeroes();
PendingMask = BitSet1024Builder.Zeroes();
foreach (var type in pendingTypes)
{
PendingMask = PendingMask.Set(componentUpdateManager.TypeToIndex[type]);
}
ExistingMask = BitSet1024Builder.Zeroes();
foreach (var type in existingTypes)
{
ExistingMask = ExistingMask.Set(componentUpdateManager.TypeToIndex[type]);
}
}
private EntityManager EntityManager { get; }
private ComponentUpdateManager ComponentUpdateManager { get; }
private BitSet1024 WithMask { get; set; }
private BitSet1024 WithoutMask { get; set; }
private BitSet1024 PendingMask { get; set; }
private BitSet1024 ExistingMask { get; set; }
/// <summary>
/// Designates that the given component type is required.
/// </summary>
public EntitySetQueryBuilder With<TComponent>() where TComponent : struct, IComponent
{
WithMask = WithMask.Set(ComponentUpdateManager.TypeToIndex[typeof(TComponent)]);
return this;
}
/// <summary>
/// Designates that the given component type is forbidden.
/// </summary>
public EntitySetQueryBuilder Without<TComponent>() where TComponent : struct, IComponent
{
WithoutMask = WithoutMask.Set(ComponentUpdateManager.TypeToIndex[typeof(TComponent)]);
return this;
}
public EntitySetQuery Build()
{
return new EntitySetQuery(
EntityManager,
ComponentUpdateManager,
WithMask.And(PendingMask),
WithMask.And(ExistingMask),
WithoutMask.And(PendingMask),
WithoutMask.And(ExistingMask),
WithMask.Not()
);
}
}
}

View File

@ -173,9 +173,6 @@ namespace Encompass
typeToReaders[receiveType].Add(engine); typeToReaders[receiveType].Add(engine);
} }
// System.Runtime.CompilerServices.RuntimeHelpers.PrepareMethod(typeof(TEngine).GetRuntimeMethod("Update", new Type[] { typeof(double) }).MethodHandle);
// typeof(TEngine).GetMethod("Update", new Type[] { typeof(double) }).MethodHandle.GetFunctionPointer();
return engine; return engine;
} }
@ -385,6 +382,7 @@ namespace Encompass
foreach (var engine in engineGraph.TopologicalSort()) foreach (var engine in engineGraph.TopologicalSort())
{ {
engineOrder.Add(engine); engineOrder.Add(engine);
engine.BuildEntityQuery();
} }
var world = new World( var world = new World(

View File

@ -1095,6 +1095,7 @@ namespace Tests
struct MockComponentD : IComponent { } struct MockComponentD : IComponent { }
[Reads(typeof(MockComponent), typeof(MockComponentB))] [Reads(typeof(MockComponent), typeof(MockComponentB))]
[QueryWith(typeof(MockComponent), typeof(MockComponentB))]
class EntityQueryWithComponentsEngine : Engine class EntityQueryWithComponentsEngine : Engine
{ {
private List<Entity> entities; private List<Entity> entities;
@ -1107,8 +1108,10 @@ namespace Tests
public override void Update(double dt) public override void Update(double dt)
{ {
entities.Clear(); entities.Clear();
foreach (var entity in QueryEntities())
entities.AddRange(QueryEntities().With<MockComponent>().With<MockComponentB>()); {
entities.Add(entity);
}
} }
} }
@ -1140,6 +1143,7 @@ namespace Tests
} }
[Reads(typeof(MockComponent))] [Reads(typeof(MockComponent))]
[QueryWithout(typeof(MockComponent))]
class EntityQueryWithoutComponentsEngine : Engine class EntityQueryWithoutComponentsEngine : Engine
{ {
private List<Entity> entities; private List<Entity> entities;
@ -1152,8 +1156,7 @@ namespace Tests
public override void Update(double dt) public override void Update(double dt)
{ {
entities.Clear(); entities.Clear();
entities.AddRange(QueryEntities());
entities.AddRange(QueryEntities().Without<MockComponent>());
} }
} }
@ -1185,6 +1188,8 @@ namespace Tests
} }
[Reads(typeof(MockComponent), typeof(MockComponentB), typeof(MockComponentD))] [Reads(typeof(MockComponent), typeof(MockComponentB), typeof(MockComponentD))]
[QueryWith(typeof(MockComponent), typeof(MockComponentB))]
[QueryWithout(typeof(MockComponentD))]
class EntityQueryWithandWithoutComponentsEngine : Engine class EntityQueryWithandWithoutComponentsEngine : Engine
{ {
private List<Entity> entities; private List<Entity> entities;
@ -1198,9 +1203,7 @@ namespace Tests
{ {
entities.Clear(); entities.Clear();
entities.AddRange(QueryEntities().With<MockComponent>() entities.AddRange(QueryEntities());
.With<MockComponentB>()
.Without<MockComponentD>());
} }
} }
@ -1246,7 +1249,7 @@ namespace Tests
{ {
public override void Update(double dt) public override void Update(double dt)
{ {
foreach (var entity in QueryEntities().With<MockComponent>()) foreach (var entity in ReadEntities<MockComponent>())
{ {
SetComponent(entity, new MockComponentB()); SetComponent(entity, new MockComponentB());
} }
@ -1254,6 +1257,7 @@ namespace Tests
} }
[ReadsPending(typeof(MockComponentB))] [ReadsPending(typeof(MockComponentB))]
[QueryWith(typeof(MockComponentB))]
class EntityQueryWithPendingComponentsEngine : Engine class EntityQueryWithPendingComponentsEngine : Engine
{ {
private List<Entity> entities; private List<Entity> entities;
@ -1266,8 +1270,7 @@ namespace Tests
public override void Update(double dt) public override void Update(double dt)
{ {
entities.Clear(); entities.Clear();
entities.AddRange(QueryEntities());
entities.AddRange(QueryEntities().With<MockComponentB>());
} }
} }
@ -1293,6 +1296,7 @@ namespace Tests
} }
[ReadsPending(typeof(MockComponentB))] [ReadsPending(typeof(MockComponentB))]
[QueryWithout(typeof(MockComponentB))]
class EntityQueryWithoutPendingComponentsEngine : Engine class EntityQueryWithoutPendingComponentsEngine : Engine
{ {
private List<Entity> entities; private List<Entity> entities;
@ -1305,8 +1309,7 @@ namespace Tests
public override void Update(double dt) public override void Update(double dt)
{ {
entities.Clear(); entities.Clear();
entities.AddRange(QueryEntities());
entities.AddRange(QueryEntities().Without<MockComponentB>());
} }
} }
@ -1339,12 +1342,12 @@ namespace Tests
{ {
public override void Update(double dt) public override void Update(double dt)
{ {
foreach (var entity in QueryEntities().With<MockComponentC>()) foreach (var entity in ReadEntities<MockComponentC>())
{ {
SetComponent(entity, new MockComponent()); SetComponent(entity, new MockComponent());
} }
foreach (var entity in QueryEntities().With<MockComponentD>()) foreach (var entity in ReadEntities<MockComponentD>())
{ {
SetComponent(entity, new MockComponent()); SetComponent(entity, new MockComponent());
SetComponent(entity, new MockComponentB()); SetComponent(entity, new MockComponentB());
@ -1353,6 +1356,8 @@ namespace Tests
} }
[ReadsPending(typeof(MockComponent), typeof(MockComponentB))] [ReadsPending(typeof(MockComponent), typeof(MockComponentB))]
[QueryWith(typeof(MockComponent))]
[QueryWithout(typeof(MockComponentB))]
class EntityQueryWithAndWithoutPendingComponentsEngine : Engine class EntityQueryWithAndWithoutPendingComponentsEngine : Engine
{ {
private List<Entity> entities; private List<Entity> entities;
@ -1366,7 +1371,7 @@ namespace Tests
{ {
entities.Clear(); entities.Clear();
entities.AddRange(QueryEntities().With<MockComponent>().Without<MockComponentB>()); entities.AddRange(QueryEntities());
} }
} }
@ -1400,7 +1405,7 @@ namespace Tests
{ {
public override void Update(double dt) public override void Update(double dt)
{ {
foreach (var entity in QueryEntities().With<MockComponentC>()) foreach (var entity in ReadEntities<MockComponentC>())
{ {
SetComponent(entity, new MockComponentB()); SetComponent(entity, new MockComponentB());
} }
@ -1409,6 +1414,7 @@ namespace Tests
[ReadsPending(typeof(MockComponentB))] [ReadsPending(typeof(MockComponentB))]
[Reads(typeof(MockComponent))] [Reads(typeof(MockComponent))]
[QueryWith(typeof(MockComponent), typeof(MockComponentB))]
class EntityQueryWithPendingAndNonPendingComponents : Engine class EntityQueryWithPendingAndNonPendingComponents : Engine
{ {
private List<Entity> entities; private List<Entity> entities;
@ -1421,8 +1427,7 @@ namespace Tests
public override void Update(double dt) public override void Update(double dt)
{ {
entities.Clear(); entities.Clear();
entities.AddRange(QueryEntities());
entities.AddRange(QueryEntities().With<MockComponent>().With<MockComponentB>());
} }
} }