snapshot refinements

filter_relations
cosmonaut 2022-12-06 22:06:02 -08:00
parent 8061590195
commit f628735025
8 changed files with 216 additions and 35 deletions

View File

@ -47,12 +47,6 @@ namespace MoonTools.ECS
return ref Lookup<TComponent>().Get(entityID); return ref Lookup<TComponent>().Get(entityID);
} }
// used for debugging and template instantiation
internal object UntypedGet(int entityID, int componentTypeIndex)
{
return storages[componentTypeIndex].UntypedGet(entityID);
}
public ref readonly TComponent GetFirst<TComponent>() where TComponent : unmanaged public ref readonly TComponent GetFirst<TComponent>() where TComponent : unmanaged
{ {
return ref Lookup<TComponent>().GetFirst(); return ref Lookup<TComponent>().GetFirst();
@ -63,10 +57,7 @@ namespace MoonTools.ECS
Lookup<TComponent>().Set(entityID, component); Lookup<TComponent>().Set(entityID, component);
} }
internal void Set(int entityID, int componentTypeIndex, object component)
{
storages[componentTypeIndex].Set(entityID, component);
}
public Entity GetSingletonEntity<TComponent>() where TComponent : unmanaged public Entity GetSingletonEntity<TComponent>() where TComponent : unmanaged
{ {
@ -99,8 +90,21 @@ namespace MoonTools.ECS
} }
} }
// used to fill snapshot depot with correct storages // these methods used to implement snapshots, templates, and debugging
public void FillMissingStorages(ComponentDepot other)
// FIXME: use unsafe pointers instead of object
internal object UntypedGet(int entityID, int componentTypeIndex)
{
return storages[componentTypeIndex].UntypedGet(entityID);
}
// FIXME: use unsafe pointers instead of object
internal void Set(int entityID, int componentTypeIndex, object component)
{
storages[componentTypeIndex].Set(entityID, component);
}
public void CreateMissingStorages(ComponentDepot other)
{ {
for (var i = 0; i < ComponentTypeIndices.Count; i += 1) for (var i = 0; i < ComponentTypeIndices.Count; i += 1)
{ {

View File

@ -52,7 +52,7 @@ namespace MoonTools.ECS
return EntityToComponentTypeIndices[entityID].Remove(componentTypeIndex); return EntityToComponentTypeIndices[entityID].Remove(componentTypeIndex);
} }
public void AddRelation(int entityID, int relationIndex) public void AddRelationKind(int entityID, int relationIndex)
{ {
EntityToRelationTypeIndices[entityID].Add(relationIndex); EntityToRelationTypeIndices[entityID].Add(relationIndex);
} }

View File

@ -24,11 +24,6 @@ namespace MoonTools.ECS
return Included.SetEquals(other.Included) && Excluded.SetEquals(other.Excluded); return Included.SetEquals(other.Included) && Excluded.SetEquals(other.Excluded);
} }
private int GuidToInt(Guid guid)
{
return BitConverter.ToInt32(guid.ToByteArray());
}
public override int GetHashCode() public override int GetHashCode()
{ {
var hashcode = 1; var hashcode = 1;

View File

@ -94,6 +94,27 @@ namespace MoonTools.ECS
Check(entityID, ComponentTypeIndices.GetIndex<TComponent>()); Check(entityID, ComponentTypeIndices.GetIndex<TComponent>());
} }
public bool CheckSatisfied(int entityID, FilterSignature filterSignature)
{
foreach (var type in filterSignature.Included)
{
if (!EntityStorage.HasComponent(entityID, type))
{
return false;
}
}
foreach (var type in filterSignature.Excluded)
{
if (EntityStorage.HasComponent(entityID, type))
{
return false;
}
}
return true;
}
private void CheckFilter(int entityID, FilterSignature filterSignature) private void CheckFilter(int entityID, FilterSignature filterSignature)
{ {
foreach (var type in filterSignature.Included) foreach (var type in filterSignature.Included)

View File

@ -51,11 +51,6 @@ namespace MoonTools.ECS
Lookup<TRelationKind>().UnrelateAll(entityID); Lookup<TRelationKind>().UnrelateAll(entityID);
} }
public void UnrelateAll(int entityID, int relationStorageIndex)
{
storages[relationStorageIndex].UnrelateAll(entityID);
}
public IEnumerable<(Entity, Entity, TRelationKind)> Relations<TRelationKind>() where TRelationKind : unmanaged public IEnumerable<(Entity, Entity, TRelationKind)> Relations<TRelationKind>() where TRelationKind : unmanaged
{ {
return Lookup<TRelationKind>().All(); return Lookup<TRelationKind>().All();
@ -105,5 +100,49 @@ namespace MoonTools.ECS
{ {
return Lookup<TRelationKind>().InRelationCount(entityID); return Lookup<TRelationKind>().InRelationCount(entityID);
} }
// untyped methods used for destroying and snapshots
public void Set(int entityA, int entityB, int relationTypeIndex, object relationData)
{
storages[relationTypeIndex].Set(entityA, entityB, relationData);
}
public void UnrelateAll(int entityID, int relationTypeIndex)
{
storages[relationTypeIndex].UnrelateAll(entityID);
}
public IEnumerable<(int, object)> InRelations(int entityID, int relationTypeIndex)
{
return storages[relationTypeIndex].UntypedInRelations(entityID);
}
public IEnumerable<(int, object)> OutRelations(int entityID, int relationTypeIndex)
{
return storages[relationTypeIndex].UntypedOutRelations(entityID);
}
public void Clear()
{
for (var i = 0; i < RelationTypeIndices.Count; i += 1)
{
if (storages[i] != null)
{
storages[i].Clear();
}
}
}
public void CreateMissingStorages(RelationDepot other)
{
for (var i = 0; i < RelationTypeIndices.Count; i += 1)
{
if (storages[i] == null && other.storages[i] != null)
{
storages[i] = other.storages[i].CreateStorage();
}
}
}
} }
} }

View File

@ -1,12 +1,17 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace MoonTools.ECS namespace MoonTools.ECS
{ {
internal abstract class RelationStorage internal abstract class RelationStorage
{ {
public abstract void Set(int entityA, int entityB, object relationData);
public abstract void UnrelateAll(int entityID); public abstract void UnrelateAll(int entityID);
public abstract IEnumerable<(int, object)> UntypedInRelations(int entityID);
public abstract IEnumerable<(int, object)> UntypedOutRelations(int entityID);
// used to create correctly typed storage on snapshot
public abstract RelationStorage CreateStorage();
public abstract void Clear();
} }
// Relation is the two entities, A related to B. // Relation is the two entities, A related to B.
@ -185,6 +190,29 @@ namespace MoonTools.ECS
return (aEmpty, bEmpty); return (aEmpty, bEmpty);
} }
private IndexableSet<int> AcquireHashSetFromPool()
{
if (listPool.Count == 0)
{
listPool.Push(new IndexableSet<int>());
}
return listPool.Pop();
}
private void ReturnHashSetToPool(IndexableSet<int> hashSet)
{
hashSet.Clear();
listPool.Push(hashSet);
}
// untyped methods used for internal implementation
public override void Set(int entityA, int entityB, object relationData)
{
Set(new Relation(entityA, entityB), (TRelation) relationData);
}
public override void UnrelateAll(int entityID) public override void UnrelateAll(int entityID)
{ {
if (outRelations.ContainsKey(entityID)) if (outRelations.ContainsKey(entityID))
@ -210,20 +238,43 @@ namespace MoonTools.ECS
} }
} }
private IndexableSet<int> AcquireHashSetFromPool() public override IEnumerable<(int, object)> UntypedInRelations(int entityID)
{ {
if (listPool.Count == 0) foreach (var (entity, relationData) in InRelations(entityID))
{ {
listPool.Push(new IndexableSet<int>()); yield return (entity.ID, relationData);
} }
return listPool.Pop();
} }
private void ReturnHashSetToPool(IndexableSet<int> hashSet) public override IEnumerable<(int, object)> UntypedOutRelations(int entityID)
{ {
hashSet.Clear(); foreach (var (entity, relationData) in OutRelations(entityID))
listPool.Push(hashSet); {
yield return (entity.ID, relationData);
}
}
public override RelationStorage<TRelation> CreateStorage()
{
return new RelationStorage<TRelation>();
}
public override void Clear()
{
count = 0;
indices.Clear();
foreach (var set in inRelations.Values)
{
ReturnHashSetToPool(set);
}
inRelations.Clear();
foreach (var set in outRelations.Values)
{
ReturnHashSetToPool(set);
}
outRelations.Clear();
} }
} }
} }

View File

@ -1,3 +1,6 @@
using System;
using System.Collections.Generic;
namespace MoonTools.ECS namespace MoonTools.ECS
{ {
public class Snapshot public class Snapshot
@ -7,29 +10,71 @@ namespace MoonTools.ECS
private EntityStorage SnapshotEntityStorage; private EntityStorage SnapshotEntityStorage;
private ComponentDepot SnapshotComponentDepot; private ComponentDepot SnapshotComponentDepot;
private RelationDepot SnapshotRelationDepot;
private List<int> SnapshotToWorldID = new List<int>();
private Dictionary<int, int> WorldToSnapshotID = new Dictionary<int, int>();
internal Snapshot(World world) internal Snapshot(World world)
{ {
World = world; World = world;
SnapshotEntityStorage = new EntityStorage(); SnapshotEntityStorage = new EntityStorage();
SnapshotComponentDepot = new ComponentDepot(World.ComponentTypeIndices); SnapshotComponentDepot = new ComponentDepot(World.ComponentTypeIndices);
SnapshotRelationDepot = new RelationDepot(World.RelationTypeIndices);
} }
public void Take(Filter filter) public void Take(Filter filter)
{ {
Clear(); Clear();
Filter = filter; Filter = filter;
SnapshotComponentDepot.FillMissingStorages(World.ComponentDepot); SnapshotComponentDepot.CreateMissingStorages(World.ComponentDepot);
SnapshotRelationDepot.CreateMissingStorages(World.RelationDepot);
foreach (var worldEntity in filter.Entities) foreach (var worldEntity in filter.Entities)
{ {
var snapshotEntity = SnapshotEntityStorage.Create(); var snapshotEntity = SnapshotEntityStorage.Create();
WorldToSnapshotID.Add(worldEntity.ID, snapshotEntity.ID);
foreach (var componentTypeIndex in World.EntityStorage.ComponentTypeIndices(worldEntity.ID)) foreach (var componentTypeIndex in World.EntityStorage.ComponentTypeIndices(worldEntity.ID))
{ {
SnapshotEntityStorage.SetComponent(snapshotEntity.ID, componentTypeIndex); SnapshotEntityStorage.SetComponent(snapshotEntity.ID, componentTypeIndex);
SnapshotComponentDepot.Set(snapshotEntity.ID, componentTypeIndex, World.ComponentDepot.UntypedGet(worldEntity.ID, componentTypeIndex)); SnapshotComponentDepot.Set(snapshotEntity.ID, componentTypeIndex, World.ComponentDepot.UntypedGet(worldEntity.ID, componentTypeIndex));
} }
} }
foreach (var worldEntity in filter.Entities)
{
var snapshotEntityID = WorldToSnapshotID[worldEntity.ID];
foreach (var relationTypeIndex in World.EntityStorage.RelationTypeIndices(worldEntity.ID))
{
SnapshotEntityStorage.AddRelationKind(snapshotEntityID, relationTypeIndex);
foreach (var (otherEntityID, relationData) in World.RelationDepot.InRelations(worldEntity.ID, relationTypeIndex))
{
#if DEBUG
if (!World.FilterStorage.CheckSatisfied(otherEntityID, Filter.Signature))
{
throw new InvalidOperationException($"Snapshot entity {worldEntity.ID} is related to non-snapshot entity {otherEntityID}!");
}
#endif
var otherSnapshotID = WorldToSnapshotID[otherEntityID];
SnapshotRelationDepot.Set(otherSnapshotID, snapshotEntityID, relationTypeIndex, relationData);
}
foreach (var (otherEntityID, relationData) in World.RelationDepot.OutRelations(worldEntity.ID, relationTypeIndex))
{
#if DEBUG
if (!World.FilterStorage.CheckSatisfied(otherEntityID, Filter.Signature))
{
throw new InvalidOperationException($"Snapshot entity {worldEntity.ID} is related to non-snapshot entity {otherEntityID}!");
}
#endif
var otherSnapshotID = WorldToSnapshotID[otherEntityID];
SnapshotRelationDepot.Set(snapshotEntityID, otherSnapshotID, relationTypeIndex, relationData);
}
}
}
} }
public void Restore() public void Restore()
@ -47,6 +92,7 @@ namespace MoonTools.ECS
for (var i = 0; i < SnapshotEntityStorage.Count; i += 1) for (var i = 0; i < SnapshotEntityStorage.Count; i += 1)
{ {
var entity = World.CreateEntity(); var entity = World.CreateEntity();
SnapshotToWorldID.Add(entity.ID);
foreach (var componentTypeIndex in SnapshotEntityStorage.ComponentTypeIndices(i)) foreach (var componentTypeIndex in SnapshotEntityStorage.ComponentTypeIndices(i))
{ {
@ -55,12 +101,37 @@ namespace MoonTools.ECS
World.ComponentDepot.Set(entity.ID, componentTypeIndex, SnapshotComponentDepot.UntypedGet(i, componentTypeIndex)); World.ComponentDepot.Set(entity.ID, componentTypeIndex, SnapshotComponentDepot.UntypedGet(i, componentTypeIndex));
} }
} }
for (var i = 0; i < SnapshotEntityStorage.Count; i += 1)
{
var worldID = SnapshotToWorldID[i];
foreach (var relationTypeIndex in SnapshotEntityStorage.RelationTypeIndices(i))
{
World.EntityStorage.AddRelationKind(worldID, relationTypeIndex);
foreach (var (otherEntityID, relationData) in SnapshotRelationDepot.InRelations(i, relationTypeIndex))
{
var otherEntityWorldID = SnapshotToWorldID[otherEntityID];
World.RelationDepot.Set(otherEntityWorldID, worldID, relationTypeIndex, relationData);
}
foreach (var (otherEntityID, relationData) in SnapshotRelationDepot.OutRelations(i, relationTypeIndex))
{
var otherEntityWorldID = SnapshotToWorldID[otherEntityID];
World.RelationDepot.Set(worldID, otherEntityWorldID, relationTypeIndex, relationData);
}
}
}
} }
private void Clear() private void Clear()
{ {
SnapshotEntityStorage.Clear(); SnapshotEntityStorage.Clear();
SnapshotComponentDepot.Clear(); SnapshotComponentDepot.Clear();
SnapshotRelationDepot.Clear();
SnapshotToWorldID.Clear();
WorldToSnapshotID.Clear();
} }
} }
} }

View File

@ -67,8 +67,8 @@ namespace MoonTools.ECS
{ {
RelationDepot.Set<TRelationKind>(new Relation(entityA, entityB), relationData); RelationDepot.Set<TRelationKind>(new Relation(entityA, entityB), relationData);
var relationTypeIndex = RelationTypeIndices.GetIndex<TRelationKind>(); var relationTypeIndex = RelationTypeIndices.GetIndex<TRelationKind>();
EntityStorage.AddRelation(entityA.ID, relationTypeIndex); EntityStorage.AddRelationKind(entityA.ID, relationTypeIndex);
EntityStorage.AddRelation(entityB.ID, relationTypeIndex); EntityStorage.AddRelationKind(entityB.ID, relationTypeIndex);
} }
protected void Unrelate<TRelationKind>(in Entity entityA, in Entity entityB) where TRelationKind : unmanaged protected void Unrelate<TRelationKind>(in Entity entityA, in Entity entityB) where TRelationKind : unmanaged