experimenting with filtering relations

filter_relations
cosmonaut 2022-12-21 11:35:15 -08:00
parent 93ed5c8dd2
commit 5a2c8851ee
9 changed files with 173 additions and 53 deletions

View File

@ -9,7 +9,7 @@ namespace MoonTools.ECS
internal EntityStorage EntityStorage => World.EntityStorage;
internal ComponentDepot ComponentDepot => World.ComponentDepot;
internal RelationDepot RelationDepot => World.RelationDepot;
protected FilterBuilder FilterBuilder => new FilterBuilder(FilterStorage, ComponentTypeIndices);
protected FilterBuilder FilterBuilder => new FilterBuilder(FilterStorage, ComponentTypeIndices, RelationTypeIndices);
internal FilterStorage FilterStorage => World.FilterStorage;
internal TypeIndices ComponentTypeIndices => World.ComponentTypeIndices;
internal TypeIndices RelationTypeIndices => World.RelationTypeIndices;

View File

@ -8,10 +8,15 @@ namespace MoonTools.ECS
internal FilterSignature Signature;
private FilterStorage FilterStorage;
internal Filter(FilterStorage filterStorage, HashSet<int> included, HashSet<int> excluded)
{
internal Filter(
FilterStorage filterStorage,
HashSet<int> included,
HashSet<int> excluded,
HashSet<int> inRelations,
HashSet<int> outRelations
) {
FilterStorage = filterStorage;
Signature = new FilterSignature(included, excluded);
Signature = new FilterSignature(included, excluded, inRelations, outRelations);
}
public ReverseSpanEnumerator<Entity> Entities => FilterStorage.FilterEntities(Signature);

View File

@ -6,41 +6,72 @@ namespace MoonTools.ECS
public struct FilterBuilder
{
private TypeIndices ComponentTypeIndices;
private TypeIndices RelationTypeIndices;
private FilterStorage FilterStorage;
private HashSet<int> Included;
private HashSet<int> Excluded;
private HashSet<int> InRelations;
private HashSet<int> OutRelations;
internal FilterBuilder(FilterStorage filterStorage, TypeIndices componentTypeIndices)
{
internal FilterBuilder(
FilterStorage filterStorage,
TypeIndices componentTypeIndices,
TypeIndices relationTypeIndices
) {
FilterStorage = filterStorage;
ComponentTypeIndices = componentTypeIndices;
RelationTypeIndices = relationTypeIndices;
Included = new HashSet<int>();
Excluded = new HashSet<int>();
InRelations = new HashSet<int>();
OutRelations = new HashSet<int>();
}
private FilterBuilder(FilterStorage filterStorage, TypeIndices componentTypeIndices, HashSet<int> included, HashSet<int> excluded)
{
private FilterBuilder(
FilterStorage filterStorage,
TypeIndices componentTypeIndices,
TypeIndices relationTypeIndices,
HashSet<int> included,
HashSet<int> excluded,
HashSet<int> inRelations,
HashSet<int> outRelations
) {
FilterStorage = filterStorage;
ComponentTypeIndices = componentTypeIndices;
RelationTypeIndices = relationTypeIndices;
Included = included;
Excluded = excluded;
InRelations = inRelations;
OutRelations = outRelations;
}
public FilterBuilder Include<TComponent>() where TComponent : unmanaged
{
Included.Add(ComponentTypeIndices.GetIndex<TComponent>());
return new FilterBuilder(FilterStorage, ComponentTypeIndices, Included, Excluded);
return this;
}
public FilterBuilder Exclude<TComponent>() where TComponent : unmanaged
{
Excluded.Add(ComponentTypeIndices.GetIndex<TComponent>());
return new FilterBuilder(FilterStorage, ComponentTypeIndices, Included, Excluded);
return this;
}
public FilterBuilder WithInRelation<TRelation>() where TRelation : unmanaged
{
InRelations.Add(RelationTypeIndices.GetIndex<TRelation>());
return this;
}
public FilterBuilder WithOutRelation<TRelation>() where TRelation : unmanaged
{
OutRelations.Add(RelationTypeIndices.GetIndex<TRelation>());
return this;
}
public Filter Build()
{
return FilterStorage.CreateFilter(Included, Excluded);
return FilterStorage.CreateFilter(Included, Excluded, InRelations, OutRelations);
}
}
}

View File

@ -3,15 +3,23 @@ using System.Collections.Generic;
namespace MoonTools.ECS
{
public struct FilterSignature : IEquatable<FilterSignature>
internal struct FilterSignature : IEquatable<FilterSignature>
{
public readonly HashSet<int> Included;
public readonly HashSet<int> Excluded;
public readonly HashSet<int> InRelations;
public readonly HashSet<int> OutRelations;
public FilterSignature(HashSet<int> included, HashSet<int> excluded)
{
public FilterSignature(
HashSet<int> included,
HashSet<int> excluded,
HashSet<int> inRelations,
HashSet<int> outRelations
) {
Included = included;
Excluded = excluded;
InRelations = inRelations;
OutRelations = outRelations;
}
public override bool Equals(object? obj)
@ -21,7 +29,11 @@ namespace MoonTools.ECS
public bool Equals(FilterSignature other)
{
return Included.SetEquals(other.Included) && Excluded.SetEquals(other.Excluded);
return
Included.SetEquals(other.Included) &&
Excluded.SetEquals(other.Excluded) &&
InRelations.SetEquals(other.InRelations) &&
OutRelations.SetEquals(other.OutRelations);
}
public override int GetHashCode()
@ -38,6 +50,16 @@ namespace MoonTools.ECS
hashcode = HashCode.Combine(hashcode, type);
}
foreach (var type in InRelations)
{
hashcode = HashCode.Combine(hashcode, type);
}
foreach (var type in OutRelations)
{
hashcode = HashCode.Combine(hashcode, type);
}
return hashcode;
}

View File

@ -6,44 +6,78 @@ namespace MoonTools.ECS
internal class FilterStorage
{
private EntityStorage EntityStorage;
private RelationDepot RelationDepot;
private TypeIndices ComponentTypeIndices;
private TypeIndices RelationTypeIndices;
private Dictionary<FilterSignature, IndexableSet<Entity>> filterSignatureToEntityIDs = new Dictionary<FilterSignature, IndexableSet<Entity>>();
private Dictionary<int, HashSet<FilterSignature>> typeToFilterSignatures = new Dictionary<int, HashSet<FilterSignature>>();
private Dictionary<int, HashSet<FilterSignature>> componentTypeToFilterSignatures = new Dictionary<int, HashSet<FilterSignature>>();
private Dictionary<int, HashSet<FilterSignature>> relationTypeToFilterSignatures = new Dictionary<int, HashSet<FilterSignature>>();
public FilterStorage(EntityStorage entityStorage, TypeIndices componentTypeIndices)
{
public FilterStorage(
EntityStorage entityStorage,
RelationDepot relationDepot,
TypeIndices componentTypeIndices,
TypeIndices relationTypeIndices
) {
EntityStorage = entityStorage;
RelationDepot = relationDepot;
ComponentTypeIndices = componentTypeIndices;
RelationTypeIndices = relationTypeIndices;
}
public Filter CreateFilter(HashSet<int> included, HashSet<int> excluded)
{
var filterSignature = new FilterSignature(included, excluded);
public Filter CreateFilter(
HashSet<int> included,
HashSet<int> excluded,
HashSet<int> inRelations,
HashSet<int> outRelations
) {
var filterSignature = new FilterSignature(included, excluded, inRelations, outRelations);
if (!filterSignatureToEntityIDs.ContainsKey(filterSignature))
{
filterSignatureToEntityIDs.Add(filterSignature, new IndexableSet<Entity>());
foreach (var type in included)
{
if (!typeToFilterSignatures.ContainsKey(type))
if (!componentTypeToFilterSignatures.ContainsKey(type))
{
typeToFilterSignatures.Add(type, new HashSet<FilterSignature>());
componentTypeToFilterSignatures.Add(type, new HashSet<FilterSignature>());
}
typeToFilterSignatures[type].Add(filterSignature);
componentTypeToFilterSignatures[type].Add(filterSignature);
}
foreach (var type in excluded)
{
if (!typeToFilterSignatures.ContainsKey(type))
if (!componentTypeToFilterSignatures.ContainsKey(type))
{
typeToFilterSignatures.Add(type, new HashSet<FilterSignature>());
componentTypeToFilterSignatures.Add(type, new HashSet<FilterSignature>());
}
typeToFilterSignatures[type].Add(filterSignature);
componentTypeToFilterSignatures[type].Add(filterSignature);
}
foreach (var type in inRelations)
{
if (!relationTypeToFilterSignatures.ContainsKey(type))
{
relationTypeToFilterSignatures.Add(type, new HashSet<FilterSignature>());
}
relationTypeToFilterSignatures[type].Add(filterSignature);
}
foreach (var type in outRelations)
{
if (!relationTypeToFilterSignatures.ContainsKey(type))
{
relationTypeToFilterSignatures.Add(type, new HashSet<FilterSignature>());
}
relationTypeToFilterSignatures[type].Add(filterSignature);
}
}
return new Filter(this, included, excluded);
return new Filter(this, included, excluded, inRelations, outRelations);
}
public ReverseSpanEnumerator<Entity> FilterEntities(FilterSignature filterSignature)
@ -72,9 +106,20 @@ namespace MoonTools.ECS
return filterSignatureToEntityIDs[filterSignature].Count;
}
public void Check(int entityID, int componentTypeIndex)
public void CheckComponentChange(int entityID, int componentTypeIndex)
{
if (typeToFilterSignatures.TryGetValue(componentTypeIndex, out var filterSignatures))
if (componentTypeToFilterSignatures.TryGetValue(componentTypeIndex, out var filterSignatures))
{
foreach (var filterSignature in filterSignatures)
{
CheckFilter(entityID, filterSignature);
}
}
}
public void CheckRelationChange(int entityID, int relationTypeIndex)
{
if (relationTypeToFilterSignatures.TryGetValue(relationTypeIndex, out var filterSignatures))
{
foreach (var filterSignature in filterSignatures)
{
@ -85,7 +130,7 @@ namespace MoonTools.ECS
public void Check<TComponent>(int entityID) where TComponent : unmanaged
{
Check(entityID, ComponentTypeIndices.GetIndex<TComponent>());
CheckComponentChange(entityID, ComponentTypeIndices.GetIndex<TComponent>());
}
public bool CheckSatisfied(int entityID, FilterSignature filterSignature)
@ -106,35 +151,40 @@ namespace MoonTools.ECS
}
}
foreach (var type in filterSignature.InRelations)
{
if (!RelationDepot.HasInRelation(entityID, type))
{
return false;
}
}
foreach (var type in filterSignature.OutRelations)
{
if (!RelationDepot.HasOutRelation(entityID, type))
{
return false;
}
}
return true;
}
private void CheckFilter(int entityID, FilterSignature filterSignature)
{
foreach (var type in filterSignature.Included)
if (CheckSatisfied(entityID, filterSignature))
{
if (!EntityStorage.HasComponent(entityID, type))
{
filterSignatureToEntityIDs[filterSignature].Remove(entityID);
return;
}
filterSignatureToEntityIDs[filterSignature].Remove(entityID);
}
foreach (var type in filterSignature.Excluded)
else
{
if (EntityStorage.HasComponent(entityID, type))
{
filterSignatureToEntityIDs[filterSignature].Remove(entityID);
return;
}
filterSignatureToEntityIDs[filterSignature].Remove(entityID);
}
filterSignatureToEntityIDs[filterSignature].Add(entityID);
}
public void RemoveEntity(int entityID, int componentTypeIndex)
{
if (typeToFilterSignatures.TryGetValue(componentTypeIndex, out var filterSignatures))
if (componentTypeToFilterSignatures.TryGetValue(componentTypeIndex, out var filterSignatures))
{
foreach (var filterSignature in filterSignatures)
{

View File

@ -86,6 +86,11 @@ namespace MoonTools.ECS
return Lookup<TRelationKind>().HasOutRelation(entityID);
}
public bool HasOutRelation(int entityID, int typeIndex)
{
return storages[typeIndex].HasOutRelation(entityID);
}
public ReverseSpanEnumerator<Entity> InRelations<TRelationKind>(int entityID) where TRelationKind : unmanaged
{
return Lookup<TRelationKind>().InRelations(entityID);
@ -101,6 +106,11 @@ namespace MoonTools.ECS
return Lookup<TRelationKind>().HasInRelation(entityID);
}
public bool HasInRelation(int entityID, int typeIndex)
{
return storages[typeIndex].HasInRelation(entityID);
}
public int InRelationCount<TRelationKind>(int entityID) where TRelationKind : unmanaged
{
return Lookup<TRelationKind>().InRelationCount(entityID);

View File

@ -5,6 +5,8 @@ namespace MoonTools.ECS
{
internal abstract class RelationStorage
{
public abstract bool HasInRelation(int entityID);
public abstract bool HasOutRelation(int entityID);
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);
@ -101,7 +103,7 @@ namespace MoonTools.ECS
return outRelations[entityID][0];
}
public bool HasOutRelation(int entityID)
public override bool HasOutRelation(int entityID)
{
return outRelations.ContainsKey(entityID) && outRelations[entityID].Count > 0;
}
@ -135,7 +137,7 @@ namespace MoonTools.ECS
return inRelations[entityID][0];
}
public bool HasInRelation(int entityID)
public override bool HasInRelation(int entityID)
{
return inRelations.ContainsKey(entityID) && inRelations[entityID].Count > 0;
}

View File

@ -87,7 +87,7 @@ namespace MoonTools.ECS
foreach (var componentTypeIndex in SnapshotEntityStorage.ComponentTypeIndices(i))
{
World.EntityStorage.SetComponent(entity.ID, componentTypeIndex);
World.FilterStorage.Check(entity.ID, componentTypeIndex);
World.FilterStorage.CheckComponentChange(entity.ID, componentTypeIndex);
World.ComponentDepot.Set(entity.ID, componentTypeIndex, SnapshotComponentDepot.UntypedGet(i, componentTypeIndex));
}
}

View File

@ -11,7 +11,7 @@ namespace MoonTools.ECS
internal readonly MessageDepot MessageDepot = new MessageDepot();
internal readonly RelationDepot RelationDepot;
internal readonly FilterStorage FilterStorage;
public FilterBuilder FilterBuilder => new FilterBuilder(FilterStorage, ComponentTypeIndices);
public FilterBuilder FilterBuilder => new FilterBuilder(FilterStorage, ComponentTypeIndices, RelationTypeIndices);
internal readonly TemplateStorage TemplateStorage = new TemplateStorage();
internal readonly ComponentDepot TemplateComponentDepot;
@ -20,7 +20,7 @@ namespace MoonTools.ECS
{
ComponentDepot = new ComponentDepot(ComponentTypeIndices);
RelationDepot = new RelationDepot(RelationTypeIndices);
FilterStorage = new FilterStorage(EntityStorage, ComponentTypeIndices);
FilterStorage = new FilterStorage(EntityStorage, RelationDepot, ComponentTypeIndices, RelationTypeIndices);
TemplateComponentDepot = new ComponentDepot(ComponentTypeIndices);
}
@ -66,7 +66,7 @@ namespace MoonTools.ECS
foreach (var componentTypeIndex in TemplateStorage.ComponentTypeIndices(template.ID))
{
EntityStorage.SetComponent(entity.ID, componentTypeIndex);
FilterStorage.Check(entity.ID, componentTypeIndex);
FilterStorage.CheckComponentChange(entity.ID, componentTypeIndex);
ComponentDepot.Set(entity.ID, componentTypeIndex, TemplateComponentDepot.UntypedGet(template.ID, componentTypeIndex));
}