restructuring entity set query system for caching benefits

pull/5/head
Evan Hemsley 2019-12-22 17:16:54 -08:00
parent 9a1a210039
commit 4e3aaa7d47
4 changed files with 114 additions and 92 deletions

View File

@ -12,7 +12,8 @@ namespace Encompass
private readonly ComponentStore existingComponentStore;
private readonly ComponentStore pendingComponentStore;
private readonly Dictionary<Type, Dictionary<Entity, int>> typeToEntityToPendingComponentPriority = new Dictionary<Type, Dictionary<Entity, int>>(128);
private Dictionary<Type, int> typeToIndex;
public Dictionary<Type, int> TypeToIndex { get; }
public ComponentStore UpToDateComponentStore { get; private set; }
public ComponentUpdateManager(Dictionary<Type, int> typeToIndex)
@ -21,7 +22,7 @@ namespace Encompass
existingComponentStore = new ComponentStore(typeToIndex);
pendingComponentStore = new ComponentStore(typeToIndex);
UpToDateComponentStore = new ComponentStore(typeToIndex);
this.typeToIndex = typeToIndex;
TypeToIndex = typeToIndex;
}
public void RegisterComponentType<TComponent>() where TComponent : struct, IComponent
@ -196,54 +197,14 @@ namespace Encompass
UpToDateComponentStore.Remove<TComponent>(entity);
}
internal IEnumerable<Entity> QueryEntities(IEnumerable<Entity> entities, HashSet<Type> readTypes, HashSet<Type> readPendingTypes, IEnumerable<Type> withTypes, IEnumerable<Type> withoutTypes)
internal BitSet1024 PendingBits(Entity entity)
{
var pendingMask = BitSet1024Builder.Zeroes();
foreach (var type in readPendingTypes)
{
pendingMask = pendingMask.Set(typeToIndex[type]);
}
var readMask = BitSet1024Builder.Zeroes();
foreach (var type in readTypes)
{
readMask = readMask.Set(typeToIndex[type]);
}
var withMask = BitSet1024Builder.Zeroes();
foreach (var type in withTypes)
{
withMask = withMask.Set(typeToIndex[type]);
}
var withoutMask = BitSet1024Builder.Zeroes();
foreach (var type in withoutTypes)
{
withoutMask = withoutMask.Set(typeToIndex[type]);
}
var notWithMask = withMask.Not();
foreach (var entity in entities)
{
var pendingEntity = pendingComponentStore.EntityBitArray(entity);
var existingEntity = existingComponentStore.EntityBitArray(entity);
if (VerifyTypes(pendingEntity, existingEntity, readMask, pendingMask, withMask, withoutMask, notWithMask)) { yield return entity; }
}
return pendingComponentStore.EntityBitArray(entity);
}
internal bool VerifyTypes(BitSet1024 pendingEntity, BitSet1024 existingEntity, BitSet1024 readMask, BitSet1024 pendingMask, BitSet1024 withMask, BitSet1024 withoutMask, BitSet1024 notWithMask)
internal BitSet1024 ExistingBits(Entity entity)
{
var pending = pendingMask.And(withMask).And(pendingEntity);
var existing = readMask.And(withMask).And(existingEntity);
var withCheck = pending.Or(existing).Or(notWithMask);
var pendingForbidden = pendingMask.And(withoutMask).And(pendingEntity).Not();
var existingForbidden = readMask.And(withoutMask).And(existingEntity).Not();
var withoutCheck = pendingForbidden.And(existingForbidden);
return withCheck.And(withoutCheck).AllTrue();
return existingComponentStore.EntityBitArray(entity);
}
}
}

View File

@ -615,9 +615,14 @@ namespace Encompass
/// <summary>
/// Returns an empty EntitySetQuery. Can be modified and iterated over to obtain Entities that fit the given criteria.
/// </summary>
protected EntitySetQuery QueryEntities()
protected EntitySetQueryBuilder EntityQueryBuilder()
{
return new EntitySetQuery(entityManager, componentUpdateManager, readTypes, readPendingTypes);
return new EntitySetQueryBuilder(
entityManager,
componentUpdateManager,
readPendingTypes,
readTypes
);
}
}
}

View File

@ -1,61 +1,47 @@
using System;
using System.Collections;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
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 struct EntitySetQuery : IEnumerable<Entity>
{
private EntitySetQuery(EntityManager entityManager, ComponentUpdateManager componentUpdateManager, HashSet<Type> readTypes, HashSet<Type> readPendingTypes, ImmutableArray<Type> includes, ImmutableArray<Type> excludes)
{
EntityManager = entityManager;
ComponentUpdateManager = componentUpdateManager;
ReadTypes = readTypes;
ReadPendingTypes = readPendingTypes;
Includes = includes;
Excludes = excludes;
}
internal EntitySetQuery(EntityManager entityManager, ComponentUpdateManager componentUpdateManager, HashSet<Type> readTypes, HashSet<Type> readPendingTypes)
{
EntityManager = entityManager;
ComponentUpdateManager = componentUpdateManager;
ReadTypes = readTypes;
ReadPendingTypes = readPendingTypes;
Includes = ImmutableArray.Create<Type>();
Excludes = ImmutableArray.Create<Type>();
}
private EntityManager EntityManager { get; }
private ComponentUpdateManager ComponentUpdateManager { get; }
private HashSet<Type> ReadTypes { get; }
private HashSet<Type> ReadPendingTypes { get; }
private ImmutableArray<Type> Includes { get; }
private ImmutableArray<Type> Excludes { get; }
private BitSet1024 WithPendingMask { get; }
private BitSet1024 WithExistingMask { get; }
private BitSet1024 WithoutPendingMask { get; }
private BitSet1024 WithoutExistingMask { get; }
private BitSet1024 NotWithMask { get; }
/// <summary>
/// Designates that the given component type is required.
/// </summary>
public EntitySetQuery With<TComponent>() where TComponent : struct, IComponent
internal EntitySetQuery(EntityManager entityManager, ComponentUpdateManager componentUpdateManager, BitSet1024 withPendingMask, BitSet1024 withExistingMask, BitSet1024 withoutPendingMask, BitSet1024 withoutExistingMask, BitSet1024 notWithMask)
{
return new EntitySetQuery(EntityManager, ComponentUpdateManager, ReadTypes, ReadPendingTypes, Includes.Add(typeof(TComponent)), Excludes);
}
/// <summary>
/// Designates that the given component type is forbidden.
/// </summary>
public EntitySetQuery Without<TComponent>() where TComponent : struct, IComponent
{
return new EntitySetQuery(EntityManager, ComponentUpdateManager, ReadTypes, ReadPendingTypes, Includes, Excludes.Add(typeof(TComponent)));
EntityManager = entityManager;
ComponentUpdateManager = componentUpdateManager;
WithPendingMask = withPendingMask;
WithExistingMask = withExistingMask;
WithoutPendingMask = withoutPendingMask;
WithoutExistingMask = withoutExistingMask;
NotWithMask = notWithMask;
}
public IEnumerator<Entity> GetEnumerator()
{
return ComponentUpdateManager.QueryEntities(EntityManager.Entities, ReadTypes, ReadPendingTypes, Includes, Excludes).GetEnumerator();
foreach (var entity in EntityManager.Entities)
{
var pendingBits = ComponentUpdateManager.PendingBits(entity);
var existingBits = ComponentUpdateManager.ExistingBits(entity);
var pending = WithPendingMask.And(pendingBits);
var existing = WithExistingMask.And(existingBits);
var withCheck = pending.Or(existing).Or(NotWithMask);
var pendingForbidden = WithoutPendingMask.And(pendingBits);
var existingForbidden = WithoutExistingMask.And(existingBits);
var withoutCheck = pendingForbidden.And(existingForbidden);
if (withCheck.And(withoutCheck).AllTrue()) { yield return entity; }
}
}
IEnumerator IEnumerable.GetEnumerator()

View File

@ -0,0 +1,70 @@
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()
);
}
}
}