From 5a2c8851ee1c329aaec6050e8582275cc8c74224 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Wed, 21 Dec 2022 11:35:15 -0800 Subject: [PATCH] experimenting with filtering relations --- src/EntityComponentReader.cs | 2 +- src/Filter.cs | 11 +++- src/FilterBuilder.cs | 45 +++++++++++--- src/FilterSignature.cs | 30 +++++++-- src/FilterStorage.cs | 114 +++++++++++++++++++++++++---------- src/RelationDepot.cs | 10 +++ src/RelationStorage.cs | 6 +- src/Snapshot.cs | 2 +- src/World.cs | 6 +- 9 files changed, 173 insertions(+), 53 deletions(-) diff --git a/src/EntityComponentReader.cs b/src/EntityComponentReader.cs index e55f6dd..2ae8914 100644 --- a/src/EntityComponentReader.cs +++ b/src/EntityComponentReader.cs @@ -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; diff --git a/src/Filter.cs b/src/Filter.cs index 4124428..0aeea2f 100644 --- a/src/Filter.cs +++ b/src/Filter.cs @@ -8,10 +8,15 @@ namespace MoonTools.ECS internal FilterSignature Signature; private FilterStorage FilterStorage; - internal Filter(FilterStorage filterStorage, HashSet included, HashSet excluded) - { + internal Filter( + FilterStorage filterStorage, + HashSet included, + HashSet excluded, + HashSet inRelations, + HashSet outRelations + ) { FilterStorage = filterStorage; - Signature = new FilterSignature(included, excluded); + Signature = new FilterSignature(included, excluded, inRelations, outRelations); } public ReverseSpanEnumerator Entities => FilterStorage.FilterEntities(Signature); diff --git a/src/FilterBuilder.cs b/src/FilterBuilder.cs index ed1562e..3a660cb 100644 --- a/src/FilterBuilder.cs +++ b/src/FilterBuilder.cs @@ -6,41 +6,72 @@ namespace MoonTools.ECS public struct FilterBuilder { private TypeIndices ComponentTypeIndices; + private TypeIndices RelationTypeIndices; private FilterStorage FilterStorage; private HashSet Included; private HashSet Excluded; + private HashSet InRelations; + private HashSet OutRelations; - internal FilterBuilder(FilterStorage filterStorage, TypeIndices componentTypeIndices) - { + internal FilterBuilder( + FilterStorage filterStorage, + TypeIndices componentTypeIndices, + TypeIndices relationTypeIndices + ) { FilterStorage = filterStorage; ComponentTypeIndices = componentTypeIndices; + RelationTypeIndices = relationTypeIndices; Included = new HashSet(); Excluded = new HashSet(); + InRelations = new HashSet(); + OutRelations = new HashSet(); } - private FilterBuilder(FilterStorage filterStorage, TypeIndices componentTypeIndices, HashSet included, HashSet excluded) - { + private FilterBuilder( + FilterStorage filterStorage, + TypeIndices componentTypeIndices, + TypeIndices relationTypeIndices, + HashSet included, + HashSet excluded, + HashSet inRelations, + HashSet outRelations + ) { FilterStorage = filterStorage; ComponentTypeIndices = componentTypeIndices; + RelationTypeIndices = relationTypeIndices; Included = included; Excluded = excluded; + InRelations = inRelations; + OutRelations = outRelations; } public FilterBuilder Include() where TComponent : unmanaged { Included.Add(ComponentTypeIndices.GetIndex()); - return new FilterBuilder(FilterStorage, ComponentTypeIndices, Included, Excluded); + return this; } public FilterBuilder Exclude() where TComponent : unmanaged { Excluded.Add(ComponentTypeIndices.GetIndex()); - return new FilterBuilder(FilterStorage, ComponentTypeIndices, Included, Excluded); + return this; + } + + public FilterBuilder WithInRelation() where TRelation : unmanaged + { + InRelations.Add(RelationTypeIndices.GetIndex()); + return this; + } + + public FilterBuilder WithOutRelation() where TRelation : unmanaged + { + OutRelations.Add(RelationTypeIndices.GetIndex()); + return this; } public Filter Build() { - return FilterStorage.CreateFilter(Included, Excluded); + return FilterStorage.CreateFilter(Included, Excluded, InRelations, OutRelations); } } } diff --git a/src/FilterSignature.cs b/src/FilterSignature.cs index 8fd65ec..4c0555b 100644 --- a/src/FilterSignature.cs +++ b/src/FilterSignature.cs @@ -3,15 +3,23 @@ using System.Collections.Generic; namespace MoonTools.ECS { - public struct FilterSignature : IEquatable + internal struct FilterSignature : IEquatable { public readonly HashSet Included; public readonly HashSet Excluded; + public readonly HashSet InRelations; + public readonly HashSet OutRelations; - public FilterSignature(HashSet included, HashSet excluded) - { + public FilterSignature( + HashSet included, + HashSet excluded, + HashSet inRelations, + HashSet 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; } diff --git a/src/FilterStorage.cs b/src/FilterStorage.cs index b8ea8f3..3196f12 100644 --- a/src/FilterStorage.cs +++ b/src/FilterStorage.cs @@ -6,44 +6,78 @@ namespace MoonTools.ECS internal class FilterStorage { private EntityStorage EntityStorage; + private RelationDepot RelationDepot; private TypeIndices ComponentTypeIndices; + private TypeIndices RelationTypeIndices; private Dictionary> filterSignatureToEntityIDs = new Dictionary>(); - private Dictionary> typeToFilterSignatures = new Dictionary>(); + private Dictionary> componentTypeToFilterSignatures = new Dictionary>(); + private Dictionary> relationTypeToFilterSignatures = new Dictionary>(); - 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 included, HashSet excluded) - { - var filterSignature = new FilterSignature(included, excluded); + public Filter CreateFilter( + HashSet included, + HashSet excluded, + HashSet inRelations, + HashSet outRelations + ) { + var filterSignature = new FilterSignature(included, excluded, inRelations, outRelations); if (!filterSignatureToEntityIDs.ContainsKey(filterSignature)) { filterSignatureToEntityIDs.Add(filterSignature, new IndexableSet()); foreach (var type in included) { - if (!typeToFilterSignatures.ContainsKey(type)) + if (!componentTypeToFilterSignatures.ContainsKey(type)) { - typeToFilterSignatures.Add(type, new HashSet()); + componentTypeToFilterSignatures.Add(type, new HashSet()); } - 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()); + componentTypeToFilterSignatures.Add(type, new HashSet()); } - typeToFilterSignatures[type].Add(filterSignature); + componentTypeToFilterSignatures[type].Add(filterSignature); + } + + foreach (var type in inRelations) + { + if (!relationTypeToFilterSignatures.ContainsKey(type)) + { + relationTypeToFilterSignatures.Add(type, new HashSet()); + } + + relationTypeToFilterSignatures[type].Add(filterSignature); + } + + foreach (var type in outRelations) + { + if (!relationTypeToFilterSignatures.ContainsKey(type)) + { + relationTypeToFilterSignatures.Add(type, new HashSet()); + } + + relationTypeToFilterSignatures[type].Add(filterSignature); } } - return new Filter(this, included, excluded); + + return new Filter(this, included, excluded, inRelations, outRelations); } public ReverseSpanEnumerator 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(int entityID) where TComponent : unmanaged { - Check(entityID, ComponentTypeIndices.GetIndex()); + CheckComponentChange(entityID, ComponentTypeIndices.GetIndex()); } 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) { diff --git a/src/RelationDepot.cs b/src/RelationDepot.cs index 06bb2fe..03258c6 100644 --- a/src/RelationDepot.cs +++ b/src/RelationDepot.cs @@ -86,6 +86,11 @@ namespace MoonTools.ECS return Lookup().HasOutRelation(entityID); } + public bool HasOutRelation(int entityID, int typeIndex) + { + return storages[typeIndex].HasOutRelation(entityID); + } + public ReverseSpanEnumerator InRelations(int entityID) where TRelationKind : unmanaged { return Lookup().InRelations(entityID); @@ -101,6 +106,11 @@ namespace MoonTools.ECS return Lookup().HasInRelation(entityID); } + public bool HasInRelation(int entityID, int typeIndex) + { + return storages[typeIndex].HasInRelation(entityID); + } + public int InRelationCount(int entityID) where TRelationKind : unmanaged { return Lookup().InRelationCount(entityID); diff --git a/src/RelationStorage.cs b/src/RelationStorage.cs index a069768..fd7a389 100644 --- a/src/RelationStorage.cs +++ b/src/RelationStorage.cs @@ -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; } diff --git a/src/Snapshot.cs b/src/Snapshot.cs index 6fd6bcb..6dfe34e 100644 --- a/src/Snapshot.cs +++ b/src/Snapshot.cs @@ -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)); } } diff --git a/src/World.cs b/src/World.cs index 17f487f..acea528 100644 --- a/src/World.cs +++ b/src/World.cs @@ -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)); }