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 EntityStorage EntityStorage => World.EntityStorage;
internal ComponentDepot ComponentDepot => World.ComponentDepot; internal ComponentDepot ComponentDepot => World.ComponentDepot;
internal RelationDepot RelationDepot => World.RelationDepot; 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 FilterStorage FilterStorage => World.FilterStorage;
internal TypeIndices ComponentTypeIndices => World.ComponentTypeIndices; internal TypeIndices ComponentTypeIndices => World.ComponentTypeIndices;
internal TypeIndices RelationTypeIndices => World.RelationTypeIndices; internal TypeIndices RelationTypeIndices => World.RelationTypeIndices;

View File

@ -8,10 +8,15 @@ namespace MoonTools.ECS
internal FilterSignature Signature; internal FilterSignature Signature;
private FilterStorage FilterStorage; 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; FilterStorage = filterStorage;
Signature = new FilterSignature(included, excluded); Signature = new FilterSignature(included, excluded, inRelations, outRelations);
} }
public ReverseSpanEnumerator<Entity> Entities => FilterStorage.FilterEntities(Signature); public ReverseSpanEnumerator<Entity> Entities => FilterStorage.FilterEntities(Signature);

View File

@ -6,41 +6,72 @@ namespace MoonTools.ECS
public struct FilterBuilder public struct FilterBuilder
{ {
private TypeIndices ComponentTypeIndices; private TypeIndices ComponentTypeIndices;
private TypeIndices RelationTypeIndices;
private FilterStorage FilterStorage; private FilterStorage FilterStorage;
private HashSet<int> Included; private HashSet<int> Included;
private HashSet<int> Excluded; 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; FilterStorage = filterStorage;
ComponentTypeIndices = componentTypeIndices; ComponentTypeIndices = componentTypeIndices;
RelationTypeIndices = relationTypeIndices;
Included = new HashSet<int>(); Included = new HashSet<int>();
Excluded = 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; FilterStorage = filterStorage;
ComponentTypeIndices = componentTypeIndices; ComponentTypeIndices = componentTypeIndices;
RelationTypeIndices = relationTypeIndices;
Included = included; Included = included;
Excluded = excluded; Excluded = excluded;
InRelations = inRelations;
OutRelations = outRelations;
} }
public FilterBuilder Include<TComponent>() where TComponent : unmanaged public FilterBuilder Include<TComponent>() where TComponent : unmanaged
{ {
Included.Add(ComponentTypeIndices.GetIndex<TComponent>()); Included.Add(ComponentTypeIndices.GetIndex<TComponent>());
return new FilterBuilder(FilterStorage, ComponentTypeIndices, Included, Excluded); return this;
} }
public FilterBuilder Exclude<TComponent>() where TComponent : unmanaged public FilterBuilder Exclude<TComponent>() where TComponent : unmanaged
{ {
Excluded.Add(ComponentTypeIndices.GetIndex<TComponent>()); 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() 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 namespace MoonTools.ECS
{ {
public struct FilterSignature : IEquatable<FilterSignature> internal struct FilterSignature : IEquatable<FilterSignature>
{ {
public readonly HashSet<int> Included; public readonly HashSet<int> Included;
public readonly HashSet<int> Excluded; 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; Included = included;
Excluded = excluded; Excluded = excluded;
InRelations = inRelations;
OutRelations = outRelations;
} }
public override bool Equals(object? obj) public override bool Equals(object? obj)
@ -21,7 +29,11 @@ namespace MoonTools.ECS
public bool Equals(FilterSignature other) 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() public override int GetHashCode()
@ -38,6 +50,16 @@ namespace MoonTools.ECS
hashcode = HashCode.Combine(hashcode, type); 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; return hashcode;
} }

View File

@ -6,44 +6,78 @@ namespace MoonTools.ECS
internal class FilterStorage internal class FilterStorage
{ {
private EntityStorage EntityStorage; private EntityStorage EntityStorage;
private RelationDepot RelationDepot;
private TypeIndices ComponentTypeIndices; private TypeIndices ComponentTypeIndices;
private TypeIndices RelationTypeIndices;
private Dictionary<FilterSignature, IndexableSet<Entity>> filterSignatureToEntityIDs = new Dictionary<FilterSignature, IndexableSet<Entity>>(); 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; EntityStorage = entityStorage;
RelationDepot = relationDepot;
ComponentTypeIndices = componentTypeIndices; ComponentTypeIndices = componentTypeIndices;
RelationTypeIndices = relationTypeIndices;
} }
public Filter CreateFilter(HashSet<int> included, HashSet<int> excluded) public Filter CreateFilter(
{ HashSet<int> included,
var filterSignature = new FilterSignature(included, excluded); HashSet<int> excluded,
HashSet<int> inRelations,
HashSet<int> outRelations
) {
var filterSignature = new FilterSignature(included, excluded, inRelations, outRelations);
if (!filterSignatureToEntityIDs.ContainsKey(filterSignature)) if (!filterSignatureToEntityIDs.ContainsKey(filterSignature))
{ {
filterSignatureToEntityIDs.Add(filterSignature, new IndexableSet<Entity>()); filterSignatureToEntityIDs.Add(filterSignature, new IndexableSet<Entity>());
foreach (var type in included) 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) 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) public ReverseSpanEnumerator<Entity> FilterEntities(FilterSignature filterSignature)
@ -72,9 +106,20 @@ namespace MoonTools.ECS
return filterSignatureToEntityIDs[filterSignature].Count; 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) foreach (var filterSignature in filterSignatures)
{ {
@ -85,7 +130,7 @@ namespace MoonTools.ECS
public void Check<TComponent>(int entityID) where TComponent : unmanaged 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) 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; return true;
} }
private void CheckFilter(int entityID, FilterSignature filterSignature) 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);
{
filterSignatureToEntityIDs[filterSignature].Remove(entityID);
return;
}
} }
else
foreach (var type in filterSignature.Excluded)
{ {
if (EntityStorage.HasComponent(entityID, type)) filterSignatureToEntityIDs[filterSignature].Remove(entityID);
{
filterSignatureToEntityIDs[filterSignature].Remove(entityID);
return;
}
} }
filterSignatureToEntityIDs[filterSignature].Add(entityID);
} }
public void RemoveEntity(int entityID, int componentTypeIndex) 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) foreach (var filterSignature in filterSignatures)
{ {

View File

@ -86,6 +86,11 @@ namespace MoonTools.ECS
return Lookup<TRelationKind>().HasOutRelation(entityID); 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 public ReverseSpanEnumerator<Entity> InRelations<TRelationKind>(int entityID) where TRelationKind : unmanaged
{ {
return Lookup<TRelationKind>().InRelations(entityID); return Lookup<TRelationKind>().InRelations(entityID);
@ -101,6 +106,11 @@ namespace MoonTools.ECS
return Lookup<TRelationKind>().HasInRelation(entityID); 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 public int InRelationCount<TRelationKind>(int entityID) where TRelationKind : unmanaged
{ {
return Lookup<TRelationKind>().InRelationCount(entityID); return Lookup<TRelationKind>().InRelationCount(entityID);

View File

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

View File

@ -87,7 +87,7 @@ namespace MoonTools.ECS
foreach (var componentTypeIndex in SnapshotEntityStorage.ComponentTypeIndices(i)) foreach (var componentTypeIndex in SnapshotEntityStorage.ComponentTypeIndices(i))
{ {
World.EntityStorage.SetComponent(entity.ID, componentTypeIndex); 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)); 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 MessageDepot MessageDepot = new MessageDepot();
internal readonly RelationDepot RelationDepot; internal readonly RelationDepot RelationDepot;
internal readonly FilterStorage FilterStorage; 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 TemplateStorage TemplateStorage = new TemplateStorage();
internal readonly ComponentDepot TemplateComponentDepot; internal readonly ComponentDepot TemplateComponentDepot;
@ -20,7 +20,7 @@ namespace MoonTools.ECS
{ {
ComponentDepot = new ComponentDepot(ComponentTypeIndices); ComponentDepot = new ComponentDepot(ComponentTypeIndices);
RelationDepot = new RelationDepot(RelationTypeIndices); RelationDepot = new RelationDepot(RelationTypeIndices);
FilterStorage = new FilterStorage(EntityStorage, ComponentTypeIndices); FilterStorage = new FilterStorage(EntityStorage, RelationDepot, ComponentTypeIndices, RelationTypeIndices);
TemplateComponentDepot = new ComponentDepot(ComponentTypeIndices); TemplateComponentDepot = new ComponentDepot(ComponentTypeIndices);
} }
@ -66,7 +66,7 @@ namespace MoonTools.ECS
foreach (var componentTypeIndex in TemplateStorage.ComponentTypeIndices(template.ID)) foreach (var componentTypeIndex in TemplateStorage.ComponentTypeIndices(template.ID))
{ {
EntityStorage.SetComponent(entity.ID, componentTypeIndex); 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)); ComponentDepot.Set(entity.ID, componentTypeIndex, TemplateComponentDepot.UntypedGet(template.ID, componentTypeIndex));
} }