snapshot and restore relations
parent
4d40103a6f
commit
9032eff699
|
@ -7,7 +7,7 @@ namespace MoonTools.ECS.Rev2
|
||||||
{
|
{
|
||||||
public static ArchetypeSignature Empty = new ArchetypeSignature(0);
|
public static ArchetypeSignature Empty = new ArchetypeSignature(0);
|
||||||
|
|
||||||
List<ulong> Ids;
|
List<uint> Ids;
|
||||||
|
|
||||||
public int Count => Ids.Count;
|
public int Count => Ids.Count;
|
||||||
|
|
||||||
|
@ -15,12 +15,12 @@ namespace MoonTools.ECS.Rev2
|
||||||
|
|
||||||
public ArchetypeSignature()
|
public ArchetypeSignature()
|
||||||
{
|
{
|
||||||
Ids = new List<ulong>();
|
Ids = new List<uint>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArchetypeSignature(int capacity)
|
public ArchetypeSignature(int capacity)
|
||||||
{
|
{
|
||||||
Ids = new List<ulong>(capacity);
|
Ids = new List<uint>(capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Maintains sorted order
|
// Maintains sorted order
|
||||||
|
|
|
@ -10,7 +10,7 @@ namespace MoonTools.ECS.Rev2
|
||||||
public int Count;
|
public int Count;
|
||||||
|
|
||||||
private int Capacity;
|
private int Capacity;
|
||||||
private readonly int ElementSize;
|
public readonly int ElementSize;
|
||||||
|
|
||||||
private bool IsDisposed;
|
private bool IsDisposed;
|
||||||
|
|
||||||
|
@ -23,6 +23,11 @@ namespace MoonTools.ECS.Rev2
|
||||||
Elements = (nint) NativeMemory.Alloc((nuint) (ElementSize * Capacity));
|
Elements = (nint) NativeMemory.Alloc((nuint) (ElementSize * Capacity));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Span<T> ToSpan<T>()
|
||||||
|
{
|
||||||
|
return new Span<T>((void*) Elements, Count);
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public ref T Get<T>(int i) where T : unmanaged
|
public ref T Get<T>(int i) where T : unmanaged
|
||||||
{
|
{
|
||||||
|
|
|
@ -24,13 +24,15 @@ namespace MoonTools.ECS.Rev2
|
||||||
|
|
||||||
public FilterBuilder Include<T>() where T : unmanaged
|
public FilterBuilder Include<T>() where T : unmanaged
|
||||||
{
|
{
|
||||||
Included.Add(World.TypeToComponentId[typeof(T)]);
|
World.TryRegisterTypeId<T>();
|
||||||
|
Included.Add(World.TypeToId[typeof(T)]);
|
||||||
return new FilterBuilder(World, Included, Excluded);
|
return new FilterBuilder(World, Included, Excluded);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FilterBuilder Exclude<T>() where T : unmanaged
|
public FilterBuilder Exclude<T>() where T : unmanaged
|
||||||
{
|
{
|
||||||
Excluded.Add(World.TypeToComponentId[typeof(T)]);
|
World.TryRegisterTypeId<T>();
|
||||||
|
Excluded.Add(World.TypeToId[typeof(T)]);
|
||||||
return new FilterBuilder(World, Included, Excluded);
|
return new FilterBuilder(World, Included, Excluded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,27 +2,8 @@
|
||||||
|
|
||||||
namespace MoonTools.ECS.Rev2;
|
namespace MoonTools.ECS.Rev2;
|
||||||
|
|
||||||
public readonly record struct Id : IComparable<Id>
|
public readonly record struct Id(uint Value) : IComparable<Id>
|
||||||
{
|
{
|
||||||
public readonly ulong Value;
|
|
||||||
|
|
||||||
private const ulong HI = 0xFFFFFFFF00000000;
|
|
||||||
private const ulong LO = 0xFFFFFFFF;
|
|
||||||
|
|
||||||
public bool IsPair => (HI & Value) != 0;
|
|
||||||
public uint Low => (uint)(LO & Value);
|
|
||||||
public uint High => (uint)((HI & Value) >>> 32);
|
|
||||||
|
|
||||||
public Id(ulong value)
|
|
||||||
{
|
|
||||||
Value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Id(uint relation, uint target)
|
|
||||||
{
|
|
||||||
Value = (relation << 31) | target;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int CompareTo(Id other)
|
public int CompareTo(Id other)
|
||||||
{
|
{
|
||||||
return Value.CompareTo(other.Value);
|
return Value.CompareTo(other.Value);
|
||||||
|
|
|
@ -4,8 +4,8 @@ namespace MoonTools.ECS.Rev2;
|
||||||
|
|
||||||
internal class IdAssigner
|
internal class IdAssigner
|
||||||
{
|
{
|
||||||
ulong Next;
|
uint Next;
|
||||||
NativeArray<ulong> AvailableIds = new NativeArray<ulong>();
|
NativeArray<uint> AvailableIds = new NativeArray<uint>();
|
||||||
|
|
||||||
public Id Assign()
|
public Id Assign()
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,28 +5,27 @@ using MoonTools.ECS.Collections;
|
||||||
|
|
||||||
namespace MoonTools.ECS.Rev2;
|
namespace MoonTools.ECS.Rev2;
|
||||||
|
|
||||||
internal class Relation
|
// TODO: implement this entire class with NativeMemory equivalents, can just memcpy for snapshots
|
||||||
|
internal class RelationStorage
|
||||||
{
|
{
|
||||||
private int count = 0;
|
internal Column relations;
|
||||||
private int capacity = 16;
|
internal Column relationDatas;
|
||||||
private Column relations;
|
internal Dictionary<(Id, Id), int> indices = new Dictionary<(Id, Id), int>(16);
|
||||||
private Column relationDatas;
|
internal Dictionary<Id, IndexableSet<Id>> outRelations = new Dictionary<Id, IndexableSet<Id>>(16);
|
||||||
private Dictionary<(Id, Id), int> indices = new Dictionary<(Id, Id), int>(16);
|
internal Dictionary<Id, IndexableSet<Id>> inRelations = new Dictionary<Id, IndexableSet<Id>>(16);
|
||||||
private Dictionary<Id, IndexableSet<Id>> outRelations = new Dictionary<Id, IndexableSet<Id>>(16);
|
|
||||||
private Dictionary<Id, IndexableSet<Id>> inRelations = new Dictionary<Id, IndexableSet<Id>>(16);
|
|
||||||
private Stack<IndexableSet<Id>> listPool = new Stack<IndexableSet<Id>>();
|
private Stack<IndexableSet<Id>> listPool = new Stack<IndexableSet<Id>>();
|
||||||
|
|
||||||
private bool disposed;
|
private bool disposed;
|
||||||
|
|
||||||
public Relation(int relationDataSize)
|
public RelationStorage(int relationDataSize)
|
||||||
{
|
{
|
||||||
relations = new Column(Unsafe.SizeOf<(Id, Id)>());
|
relations = new Column(Unsafe.SizeOf<(Id, Id)>());
|
||||||
relationDatas = new Column(relationDataSize);
|
relationDatas = new Column(relationDataSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe ReverseSpanEnumerator<(Id, Id)> All()
|
public ReverseSpanEnumerator<(Id, Id)> All()
|
||||||
{
|
{
|
||||||
return new ReverseSpanEnumerator<(Id, Id)>(new Span<(Id, Id)>((void*) relations.Elements, count));
|
return new ReverseSpanEnumerator<(Id, Id)>(relations.ToSpan<(Id, Id)>());
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void Set<T>(in Id entityA, in Id entityB, in T relationData) where T : unmanaged
|
public unsafe void Set<T>(in Id entityA, in Id entityB, in T relationData) where T : unmanaged
|
||||||
|
@ -51,16 +50,9 @@ internal class Relation
|
||||||
}
|
}
|
||||||
inRelations[entityB].Add(entityA);
|
inRelations[entityB].Add(entityA);
|
||||||
|
|
||||||
if (count >= capacity)
|
relations.Append(relation);
|
||||||
{
|
relationDatas.Append(relationData);
|
||||||
relations.Resize();
|
indices.Add(relation, relations.Count - 1);
|
||||||
relationDatas.Resize();
|
|
||||||
}
|
|
||||||
|
|
||||||
(((Id, Id)*) relationDatas.Elements)[count] = relation;
|
|
||||||
((T*) relationDatas.Elements)[count] = relationData;
|
|
||||||
indices.Add(relation, count);
|
|
||||||
count += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ref T Get<T>(in Id entityA, in Id entityB) where T : unmanaged
|
public ref T Get<T>(in Id entityA, in Id entityB) where T : unmanaged
|
||||||
|
@ -69,9 +61,9 @@ internal class Relation
|
||||||
return ref relationDatas.Get<T>(relationIndex);
|
return ref relationDatas.Get<T>(relationIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Has((Id, Id) relation)
|
public bool Has(Id entityA, Id entityB)
|
||||||
{
|
{
|
||||||
return indices.ContainsKey(relation);
|
return indices.ContainsKey((entityA, entityB));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReverseSpanEnumerator<Id> OutRelations(Id entityID)
|
public ReverseSpanEnumerator<Id> OutRelations(Id entityID)
|
||||||
|
@ -177,11 +169,11 @@ internal class Relation
|
||||||
|
|
||||||
if (indices.TryGetValue(relation, out var index))
|
if (indices.TryGetValue(relation, out var index))
|
||||||
{
|
{
|
||||||
|
var lastElementIndex = relations.Count - 1;
|
||||||
|
|
||||||
relationDatas.Delete(index);
|
relationDatas.Delete(index);
|
||||||
relations.Delete(index);
|
relations.Delete(index);
|
||||||
|
|
||||||
var lastElementIndex = count - 1;
|
|
||||||
|
|
||||||
// move an element into the hole
|
// move an element into the hole
|
||||||
if (index != lastElementIndex)
|
if (index != lastElementIndex)
|
||||||
{
|
{
|
||||||
|
@ -189,14 +181,38 @@ internal class Relation
|
||||||
indices[lastRelation] = index;
|
indices[lastRelation] = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
count -= 1;
|
|
||||||
indices.Remove(relation);
|
indices.Remove(relation);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (aEmpty, bEmpty);
|
return (aEmpty, bEmpty);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IndexableSet<Id> AcquireHashSetFromPool()
|
public void RemoveEntity(Id entity)
|
||||||
|
{
|
||||||
|
if (outRelations.TryGetValue(entity, out var entityOutRelations))
|
||||||
|
{
|
||||||
|
foreach (var entityB in entityOutRelations)
|
||||||
|
{
|
||||||
|
Remove(entity, entityB);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnHashSetToPool(entityOutRelations);
|
||||||
|
outRelations.Remove(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inRelations.TryGetValue(entity, out var entityInRelations))
|
||||||
|
{
|
||||||
|
foreach (var entityA in entityInRelations)
|
||||||
|
{
|
||||||
|
Remove(entityA, entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnHashSetToPool(entityInRelations);
|
||||||
|
inRelations.Remove(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal IndexableSet<Id> AcquireHashSetFromPool()
|
||||||
{
|
{
|
||||||
if (listPool.Count == 0)
|
if (listPool.Count == 0)
|
||||||
{
|
{
|
||||||
|
@ -214,7 +230,6 @@ internal class Relation
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
count = 0;
|
|
||||||
indices.Clear();
|
indices.Clear();
|
||||||
|
|
||||||
foreach (var set in inRelations.Values)
|
foreach (var set in inRelations.Values)
|
||||||
|
@ -228,6 +243,9 @@ internal class Relation
|
||||||
ReturnHashSetToPool(set);
|
ReturnHashSetToPool(set);
|
||||||
}
|
}
|
||||||
outRelations.Clear();
|
outRelations.Clear();
|
||||||
|
|
||||||
|
relations.Count = 0;
|
||||||
|
relationDatas.Count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using MoonTools.ECS.Collections;
|
using MoonTools.ECS.Collections;
|
||||||
|
|
||||||
namespace MoonTools.ECS.Rev2;
|
namespace MoonTools.ECS.Rev2;
|
||||||
|
@ -8,7 +9,14 @@ public class Snapshot
|
||||||
private Dictionary<ArchetypeSignature, ArchetypeSnapshot> ArchetypeSnapshots =
|
private Dictionary<ArchetypeSignature, ArchetypeSnapshot> ArchetypeSnapshots =
|
||||||
new Dictionary<ArchetypeSignature, ArchetypeSnapshot>();
|
new Dictionary<ArchetypeSignature, ArchetypeSnapshot>();
|
||||||
|
|
||||||
|
private Dictionary<Id, RelationSnapshot> RelationSnapshots =
|
||||||
|
new Dictionary<Id, RelationSnapshot>();
|
||||||
|
|
||||||
private Dictionary<Id, Record> EntityIndex = new Dictionary<Id, Record>();
|
private Dictionary<Id, Record> EntityIndex = new Dictionary<Id, Record>();
|
||||||
|
|
||||||
|
private Dictionary<Id, IndexableSet<Id>> EntityRelationIndex =
|
||||||
|
new Dictionary<Id, IndexableSet<Id>>();
|
||||||
|
|
||||||
private IdAssigner IdAssigner = new IdAssigner();
|
private IdAssigner IdAssigner = new IdAssigner();
|
||||||
|
|
||||||
public int Count
|
public int Count
|
||||||
|
@ -32,7 +40,7 @@ public class Snapshot
|
||||||
foreach (var (archetypeSignature, archetypeSnapshot) in ArchetypeSnapshots)
|
foreach (var (archetypeSignature, archetypeSnapshot) in ArchetypeSnapshots)
|
||||||
{
|
{
|
||||||
var archetype = world.ArchetypeIndex[archetypeSignature];
|
var archetype = world.ArchetypeIndex[archetypeSignature];
|
||||||
RestoreArchetypeSnapshot(archetype);
|
archetypeSnapshot.Restore(archetype);
|
||||||
}
|
}
|
||||||
|
|
||||||
// restore entity index
|
// restore entity index
|
||||||
|
@ -44,6 +52,25 @@ public class Snapshot
|
||||||
|
|
||||||
// restore id assigner state
|
// restore id assigner state
|
||||||
IdAssigner.CopyTo(world.IdAssigner);
|
IdAssigner.CopyTo(world.IdAssigner);
|
||||||
|
|
||||||
|
// restore relation state
|
||||||
|
foreach (var (typeId, relationSnapshot) in RelationSnapshots)
|
||||||
|
{
|
||||||
|
var relationStorage = world.RelationIndex[typeId];
|
||||||
|
relationSnapshot.Restore(relationStorage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// restore entity relation index state
|
||||||
|
// FIXME: arghhhh this is so slow
|
||||||
|
foreach (var (id, relationTypeSet) in EntityRelationIndex)
|
||||||
|
{
|
||||||
|
world.EntityRelationIndex[id].Clear();
|
||||||
|
|
||||||
|
foreach (var typeId in relationTypeSet)
|
||||||
|
{
|
||||||
|
world.EntityRelationIndex[id].Add(typeId);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Take(World world)
|
public void Take(World world)
|
||||||
|
@ -63,9 +90,32 @@ public class Snapshot
|
||||||
{
|
{
|
||||||
TakeArchetypeSnapshot(archetype);
|
TakeArchetypeSnapshot(archetype);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// copy relations
|
||||||
|
foreach (var (typeId, relationStorage) in world.RelationIndex)
|
||||||
|
{
|
||||||
|
TakeRelationSnapshot(typeId, relationStorage);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void TakeArchetypeSnapshot(Archetype archetype)
|
// copy entity relation index
|
||||||
|
// FIXME: arghhhh this is so slow
|
||||||
|
foreach (var (id, relationTypeSet) in world.EntityRelationIndex)
|
||||||
|
{
|
||||||
|
if (!EntityRelationIndex.ContainsKey(id))
|
||||||
|
{
|
||||||
|
EntityRelationIndex.Add(id, new IndexableSet<Id>());
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityRelationIndex[id].Clear();
|
||||||
|
|
||||||
|
foreach (var typeId in relationTypeSet)
|
||||||
|
{
|
||||||
|
EntityRelationIndex[id].Add(typeId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TakeArchetypeSnapshot(Archetype archetype)
|
||||||
{
|
{
|
||||||
if (!ArchetypeSnapshots.TryGetValue(archetype.Signature, out var archetypeSnapshot))
|
if (!ArchetypeSnapshots.TryGetValue(archetype.Signature, out var archetypeSnapshot))
|
||||||
{
|
{
|
||||||
|
@ -76,15 +126,19 @@ public class Snapshot
|
||||||
archetypeSnapshot.Take(archetype);
|
archetypeSnapshot.Take(archetype);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RestoreArchetypeSnapshot(Archetype archetype)
|
private void TakeRelationSnapshot(Id typeId, RelationStorage relationStorage)
|
||||||
{
|
{
|
||||||
var archetypeSnapshot = ArchetypeSnapshots[archetype.Signature];
|
if (!RelationSnapshots.TryGetValue(typeId, out var snapshot))
|
||||||
archetypeSnapshot.Restore(archetype);
|
{
|
||||||
|
snapshot = new RelationSnapshot(World.ElementSizes[typeId]);
|
||||||
|
RelationSnapshots.Add(typeId, snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshot.Take(relationStorage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ArchetypeSnapshot
|
private class ArchetypeSnapshot
|
||||||
{
|
{
|
||||||
public ArchetypeSignature Signature;
|
|
||||||
private readonly Column[] ComponentColumns;
|
private readonly Column[] ComponentColumns;
|
||||||
private readonly NativeArray<Id> RowToEntity;
|
private readonly NativeArray<Id> RowToEntity;
|
||||||
|
|
||||||
|
@ -92,7 +146,6 @@ public class Snapshot
|
||||||
|
|
||||||
public ArchetypeSnapshot(ArchetypeSignature signature)
|
public ArchetypeSnapshot(ArchetypeSignature signature)
|
||||||
{
|
{
|
||||||
Signature = signature;
|
|
||||||
ComponentColumns = new Column[signature.Count];
|
ComponentColumns = new Column[signature.Count];
|
||||||
RowToEntity = new NativeArray<Id>();
|
RowToEntity = new NativeArray<Id>();
|
||||||
|
|
||||||
|
@ -103,11 +156,6 @@ public class Snapshot
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
RowToEntity.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Take(Archetype archetype)
|
public void Take(Archetype archetype)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < ComponentColumns.Length; i += 1)
|
for (int i = 0; i < ComponentColumns.Length; i += 1)
|
||||||
|
@ -129,4 +177,54 @@ public class Snapshot
|
||||||
RowToEntity.CopyTo(archetype.RowToEntity);
|
RowToEntity.CopyTo(archetype.RowToEntity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class RelationSnapshot
|
||||||
|
{
|
||||||
|
private Column Relations;
|
||||||
|
private Column RelationDatas;
|
||||||
|
|
||||||
|
public RelationSnapshot(int elementSize)
|
||||||
|
{
|
||||||
|
Relations = new Column(Unsafe.SizeOf<(Id, Id)>());
|
||||||
|
RelationDatas = new Column(elementSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Take(RelationStorage relationStorage)
|
||||||
|
{
|
||||||
|
relationStorage.relations.CopyAllTo(Relations);
|
||||||
|
relationStorage.relationDatas.CopyAllTo(RelationDatas);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Restore(RelationStorage relationStorage)
|
||||||
|
{
|
||||||
|
relationStorage.Clear();
|
||||||
|
|
||||||
|
Relations.CopyAllTo(relationStorage.relations);
|
||||||
|
RelationDatas.CopyAllTo(relationStorage.relationDatas);
|
||||||
|
|
||||||
|
for (int index = 0; index < Relations.Count; index += 1)
|
||||||
|
{
|
||||||
|
var relation = Relations.Get<(Id, Id)>(index);
|
||||||
|
relationStorage.indices[relation] = index;
|
||||||
|
|
||||||
|
relationStorage.indices[relation] = index;
|
||||||
|
|
||||||
|
if (!relationStorage.outRelations.ContainsKey(relation.Item1))
|
||||||
|
{
|
||||||
|
relationStorage.outRelations[relation.Item1] =
|
||||||
|
relationStorage.AcquireHashSetFromPool();
|
||||||
|
}
|
||||||
|
|
||||||
|
relationStorage.outRelations[relation.Item1].Add(relation.Item2);
|
||||||
|
|
||||||
|
if (!relationStorage.inRelations.ContainsKey(relation.Item2))
|
||||||
|
{
|
||||||
|
relationStorage.inRelations[relation.Item2] =
|
||||||
|
relationStorage.AcquireHashSetFromPool();
|
||||||
|
}
|
||||||
|
|
||||||
|
relationStorage.inRelations[relation.Item2].Add(relation.Item1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using MoonTools.ECS.Collections;
|
||||||
|
|
||||||
namespace MoonTools.ECS.Rev2
|
namespace MoonTools.ECS.Rev2
|
||||||
{
|
{
|
||||||
public class World : IDisposable
|
public class World : IDisposable
|
||||||
{
|
{
|
||||||
// Get ComponentId from a Type
|
// Get ComponentId from a Type
|
||||||
internal static Dictionary<Type, Id> TypeToComponentId = new Dictionary<Type, Id>();
|
internal static Dictionary<Type, Id> TypeToId = new Dictionary<Type, Id>();
|
||||||
// Get element size from a ComponentId
|
// Get element size from a ComponentId
|
||||||
internal static Dictionary<Id, int> ElementSizes = new Dictionary<Id, int>();
|
internal static Dictionary<Id, int> ElementSizes = new Dictionary<Id, int>();
|
||||||
|
|
||||||
|
@ -20,7 +22,16 @@ namespace MoonTools.ECS.Rev2
|
||||||
// Going from ComponentId to Archetype list
|
// Going from ComponentId to Archetype list
|
||||||
Dictionary<Id, List<Archetype>> ComponentIndex = new Dictionary<Id, List<Archetype>>();
|
Dictionary<Id, List<Archetype>> ComponentIndex = new Dictionary<Id, List<Archetype>>();
|
||||||
|
|
||||||
|
// Relation Storages
|
||||||
|
internal Dictionary<Id, RelationStorage> RelationIndex =
|
||||||
|
new Dictionary<Id, RelationStorage>();
|
||||||
|
|
||||||
|
// Entity Relation Tracking
|
||||||
|
internal Dictionary<Id, IndexableSet<Id>> EntityRelationIndex =
|
||||||
|
new Dictionary<Id, IndexableSet<Id>>();
|
||||||
|
|
||||||
// ID Management
|
// ID Management
|
||||||
|
// FIXME: Entity and Type Ids should be separated
|
||||||
internal IdAssigner IdAssigner = new IdAssigner();
|
internal IdAssigner IdAssigner = new IdAssigner();
|
||||||
|
|
||||||
internal readonly Archetype EmptyArchetype;
|
internal readonly Archetype EmptyArchetype;
|
||||||
|
@ -59,58 +70,66 @@ namespace MoonTools.ECS.Rev2
|
||||||
var entityId = IdAssigner.Assign();
|
var entityId = IdAssigner.Assign();
|
||||||
EntityIndex.Add(entityId, new Record(EmptyArchetype, EmptyArchetype.Count));
|
EntityIndex.Add(entityId, new Record(EmptyArchetype, EmptyArchetype.Count));
|
||||||
EmptyArchetype.RowToEntity.Add(entityId);
|
EmptyArchetype.RowToEntity.Add(entityId);
|
||||||
|
|
||||||
|
if (!EntityRelationIndex.ContainsKey(entityId))
|
||||||
|
{
|
||||||
|
EntityRelationIndex.Add(entityId, new IndexableSet<Id>());
|
||||||
|
}
|
||||||
|
|
||||||
return entityId;
|
return entityId;
|
||||||
}
|
}
|
||||||
|
|
||||||
// used as a fast path by snapshot restore
|
internal void TryRegisterTypeId<T>() where T : unmanaged
|
||||||
internal void CreateEntityOnArchetype(Archetype archetype)
|
|
||||||
{
|
{
|
||||||
var entityId = IdAssigner.Assign();
|
if (!TypeToId.ContainsKey(typeof(T)))
|
||||||
archetype.RowToEntity.Add(entityId);
|
{
|
||||||
|
var typeId = IdAssigner.Assign();
|
||||||
|
TypeToId.Add(typeof(T), typeId);
|
||||||
|
ElementSizes.Add(typeId, Unsafe.SizeOf<T>());
|
||||||
}
|
}
|
||||||
|
|
||||||
// used as a fast path by Archetype.ClearAll and snapshot restore
|
|
||||||
internal void FreeEntity(Id entityId)
|
|
||||||
{
|
|
||||||
EntityIndex.Remove(entityId);
|
|
||||||
IdAssigner.Unassign(entityId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RegisterTypeId(Id typeId, int elementSize)
|
|
||||||
{
|
|
||||||
ComponentIndex.Add(typeId, new List<Archetype>());
|
|
||||||
ElementSizes.Add(typeId, elementSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: would be much more efficient to do all this at load time somehow
|
// FIXME: would be much more efficient to do all this at load time somehow
|
||||||
private void RegisterComponent<T>() where T : unmanaged
|
private void RegisterComponent(Id typeId)
|
||||||
{
|
{
|
||||||
var componentId = IdAssigner.Assign();
|
ComponentIndex.Add(typeId, new List<Archetype>());
|
||||||
TypeToComponentId.Add(typeof(T), componentId);
|
|
||||||
ComponentIndex.Add(componentId, new List<Archetype>());
|
|
||||||
ElementSizes.Add(componentId, Unsafe.SizeOf<T>());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TryRegisterTypeId(Id typeId, int elementSize)
|
|
||||||
{
|
|
||||||
if (!ComponentIndex.ContainsKey(typeId))
|
|
||||||
{
|
|
||||||
RegisterTypeId(typeId, elementSize);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TryRegisterComponentId<T>() where T : unmanaged
|
private void TryRegisterComponentId<T>() where T : unmanaged
|
||||||
{
|
{
|
||||||
if (!TypeToComponentId.ContainsKey(typeof(T)))
|
TryRegisterTypeId<T>();
|
||||||
|
var typeId = TypeToId[typeof(T)];
|
||||||
|
if (!ComponentIndex.ContainsKey(typeId))
|
||||||
{
|
{
|
||||||
RegisterComponent<T>();
|
RegisterComponent(typeId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private Id GetComponentId<T>() where T : unmanaged
|
private Id GetComponentId<T>() where T : unmanaged
|
||||||
{
|
{
|
||||||
return TypeToComponentId[typeof(T)];
|
return TypeToId[typeof(T)];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RegisterRelationType(Id typeId)
|
||||||
|
{
|
||||||
|
RelationIndex.Add(typeId, new RelationStorage(ElementSizes[typeId]));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TryRegisterRelationType<T>() where T : unmanaged
|
||||||
|
{
|
||||||
|
TryRegisterTypeId<T>();
|
||||||
|
var typeId = TypeToId[typeof(T)];
|
||||||
|
if (!RelationIndex.ContainsKey(typeId))
|
||||||
|
{
|
||||||
|
RegisterRelationType(typeId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private RelationStorage GetRelationStorage<T>() where T : unmanaged
|
||||||
|
{
|
||||||
|
return RelationIndex[TypeToId[typeof(T)]];
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Has<T>(Id entityId) where T : unmanaged
|
public bool Has<T>(Id entityId) where T : unmanaged
|
||||||
|
@ -222,43 +241,49 @@ namespace MoonTools.ECS.Rev2
|
||||||
MoveEntityToLowerArchetype(entityId, row, archetype, nextArchetype, componentId);
|
MoveEntityToLowerArchetype(entityId, row, archetype, nextArchetype, componentId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Id Pair(in Id relation, in Id target)
|
|
||||||
{
|
|
||||||
return new Id(relation.Low, target.Low);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Relate<T>(in Id entityA, in Id entityB, in T relation) where T : unmanaged
|
public void Relate<T>(in Id entityA, in Id entityB, in T relation) where T : unmanaged
|
||||||
{
|
{
|
||||||
TryRegisterComponentId<T>();
|
TryRegisterRelationType<T>();
|
||||||
var relationDataTypeId = GetComponentId<T>();
|
var relationStorage = GetRelationStorage<T>();
|
||||||
|
relationStorage.Set(entityA, entityB, relation);
|
||||||
|
EntityRelationIndex[entityA].Add(TypeToId[typeof(T)]);
|
||||||
|
EntityRelationIndex[entityB].Add(TypeToId[typeof(T)]);
|
||||||
|
}
|
||||||
|
|
||||||
var typeId = Pair(relationDataTypeId, entityB);
|
public void Unrelate<T>(in Id entityA, in Id entityB) where T : unmanaged
|
||||||
|
{
|
||||||
TryRegisterTypeId(typeId, Unsafe.SizeOf<T>());
|
var relationStorage = GetRelationStorage<T>();
|
||||||
SetRelationData(entityA, typeId, relation);
|
relationStorage.Remove(entityA, entityB);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Related<T>(in Id entityA, in Id entityB) where T : unmanaged
|
public bool Related<T>(in Id entityA, in Id entityB) where T : unmanaged
|
||||||
{
|
{
|
||||||
var relationDataTypeId = GetComponentId<T>();
|
var relationStorage = GetRelationStorage<T>();
|
||||||
var typeId = Pair(relationDataTypeId, entityB);
|
return relationStorage.Has(entityA, entityB);
|
||||||
return Has(entityA, typeId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public T GetRelationData<T>(in Id entityA, in Id entityB) where T : unmanaged
|
public T GetRelationData<T>(in Id entityA, in Id entityB) where T : unmanaged
|
||||||
{
|
{
|
||||||
var relationDataTypeId = GetComponentId<T>();
|
var relationStorage = GetRelationStorage<T>();
|
||||||
var typeId = Pair(relationDataTypeId, entityB);
|
return relationStorage.Get<T>(entityA, entityB);
|
||||||
return Get<T>(entityA, typeId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe ref T Get<T>(Id entityId, Id typeId) where T : unmanaged
|
public ReverseSpanEnumerator<(Id, Id)> Relations<T>() where T : unmanaged
|
||||||
{
|
{
|
||||||
var record = EntityIndex[entityId];
|
var relationStorage = GetRelationStorage<T>();
|
||||||
var columnIndex = record.Archetype.ComponentToColumnIndex[typeId];
|
return relationStorage.All();
|
||||||
var column = record.Archetype.ComponentColumns[columnIndex];
|
}
|
||||||
|
|
||||||
return ref ((T*) column.Elements)[record.Row];
|
public ReverseSpanEnumerator<Id> OutRelations<T>(Id entity) where T : unmanaged
|
||||||
|
{
|
||||||
|
var relationStorage = GetRelationStorage<T>();
|
||||||
|
return relationStorage.OutRelations(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReverseSpanEnumerator<Id> InRelations<T>(Id entity) where T : unmanaged
|
||||||
|
{
|
||||||
|
var relationStorage = GetRelationStorage<T>();
|
||||||
|
return relationStorage.InRelations(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool Has(Id entityId, Id typeId)
|
private bool Has(Id entityId, Id typeId)
|
||||||
|
@ -267,57 +292,19 @@ namespace MoonTools.ECS.Rev2
|
||||||
return record.Archetype.ComponentToColumnIndex.ContainsKey(typeId);
|
return record.Archetype.ComponentToColumnIndex.ContainsKey(typeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Add<T>(Id entityId, Id typeId, in T component) where T : unmanaged
|
// used as a fast path by Archetype.ClearAll and snapshot restore
|
||||||
|
internal void FreeEntity(Id entityId)
|
||||||
{
|
{
|
||||||
Archetype? nextArchetype;
|
EntityIndex.Remove(entityId);
|
||||||
|
IdAssigner.Unassign(entityId);
|
||||||
|
|
||||||
// move the entity to the new archetype
|
foreach (var relationTypeIndex in EntityRelationIndex[entityId])
|
||||||
var record = EntityIndex[entityId];
|
|
||||||
var archetype = record.Archetype;
|
|
||||||
|
|
||||||
if (archetype.Edges.TryGetValue(typeId, out var edge))
|
|
||||||
{
|
{
|
||||||
nextArchetype = edge.Add;
|
var relationStorage = RelationIndex[relationTypeIndex];
|
||||||
}
|
relationStorage.RemoveEntity(entityId);
|
||||||
else
|
|
||||||
{
|
|
||||||
// FIXME: pool the signatures
|
|
||||||
var nextSignature = new ArchetypeSignature(archetype.Signature.Count + 1);
|
|
||||||
archetype.Signature.CopyTo(nextSignature);
|
|
||||||
nextSignature.Insert(typeId);
|
|
||||||
|
|
||||||
if (!ArchetypeIndex.TryGetValue(nextSignature, out nextArchetype))
|
|
||||||
{
|
|
||||||
nextArchetype = CreateArchetype(nextSignature);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var newEdge = new ArchetypeEdge(nextArchetype, archetype);
|
EntityRelationIndex[entityId].Clear();
|
||||||
archetype.Edges.Add(typeId, newEdge);
|
|
||||||
nextArchetype.Edges.Add(typeId, newEdge);
|
|
||||||
}
|
|
||||||
|
|
||||||
MoveEntityToHigherArchetype(entityId, record.Row, archetype, nextArchetype);
|
|
||||||
|
|
||||||
// add the new component to the new archetype
|
|
||||||
var columnIndex = nextArchetype.ComponentToColumnIndex[typeId];
|
|
||||||
var column = nextArchetype.ComponentColumns[columnIndex];
|
|
||||||
column.Append(component);
|
|
||||||
}
|
|
||||||
|
|
||||||
private unsafe void SetRelationData<T>(in Id entityId, in Id typeId, T data) where T : unmanaged
|
|
||||||
{
|
|
||||||
if (Has(entityId, typeId))
|
|
||||||
{
|
|
||||||
var record = EntityIndex[entityId];
|
|
||||||
var columnIndex = record.Archetype.ComponentToColumnIndex[typeId];
|
|
||||||
var column = record.Archetype.ComponentColumns[columnIndex];
|
|
||||||
|
|
||||||
((T*) column.Elements)[record.Row] = data;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Add(entityId, typeId, data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Destroy(Id entityId)
|
public void Destroy(Id entityId)
|
||||||
|
@ -342,6 +329,14 @@ namespace MoonTools.ECS.Rev2
|
||||||
archetype.RowToEntity.RemoveLastElement();
|
archetype.RowToEntity.RemoveLastElement();
|
||||||
EntityIndex.Remove(entityId);
|
EntityIndex.Remove(entityId);
|
||||||
IdAssigner.Unassign(entityId);
|
IdAssigner.Unassign(entityId);
|
||||||
|
|
||||||
|
foreach (var relationTypeIndex in EntityRelationIndex[entityId])
|
||||||
|
{
|
||||||
|
var relationStorage = RelationIndex[relationTypeIndex];
|
||||||
|
relationStorage.RemoveEntity(entityId);
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityRelationIndex[entityId].Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MoveEntityToHigherArchetype(Id entityId, int row, Archetype from, Archetype to)
|
private void MoveEntityToHigherArchetype(Id entityId, int row, Archetype from, Archetype to)
|
||||||
|
|
Loading…
Reference in New Issue