feature/archetype-ecs #1
@@ -15,13 +15,23 @@ public partial class ArcEntityTest : ITest
|
|||||||
public void Run()
|
public void Run()
|
||||||
{
|
{
|
||||||
var entity1 = _world.EntityManager.CreateEntity(ComponentTypeID<TransformData>.value);
|
var entity1 = _world.EntityManager.CreateEntity(ComponentTypeID<TransformData>.value);
|
||||||
Console.WriteLine($"{entity1} Has Transform: {_world.EntityManager.HasComponent<TransformData>(entity1)}");
|
|
||||||
var mesh = new MeshData { index = 1 };
|
var mesh = new MeshData { index = 1 };
|
||||||
_world.EntityManager.AddComponent<MeshData>(entity1, ref mesh);
|
_world.EntityManager.AddComponent<MeshData>(entity1, ref mesh);
|
||||||
Console.WriteLine($"{entity1} Has Mesh: {_world.EntityManager.HasComponent<MeshData>(entity1)}");
|
|
||||||
|
|
||||||
_world.EntityManager.DestoryEntity(entity1);
|
var queryID = new QueryBuilder().WithAll<TransformData>().Build(_world);
|
||||||
Console.WriteLine($"{entity1} Has Transform: {_world.EntityManager.HasComponent<TransformData>(entity1)}");
|
ref var query = ref _world.GetEntityQueryReference(queryID);
|
||||||
|
|
||||||
|
foreach (var item in query)
|
||||||
|
{
|
||||||
|
var transforms = item.GetComponentData<TransformData>();
|
||||||
|
Console.WriteLine($"Item Count: {item.Count}");
|
||||||
|
for (var i = 0; i < item.Count; i++)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Entity Position: {transforms[i].position}");
|
||||||
|
transforms[i].position = new float3(1, 2, 3);
|
||||||
|
Console.WriteLine($"Updated Entity Position: {transforms[i].position}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Cleanup()
|
public void Cleanup()
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ using System.Runtime.CompilerServices;
|
|||||||
|
|
||||||
namespace Ghost.Entities;
|
namespace Ghost.Entities;
|
||||||
|
|
||||||
internal unsafe struct Chuck : IDisposable
|
internal unsafe struct Chunk : IDisposable
|
||||||
{
|
{
|
||||||
public const int CHUNK_SIZE = 16384; // 16 KB
|
public const int CHUNK_SIZE = 16384; // 16 KB
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ internal unsafe struct Chuck : IDisposable
|
|||||||
|
|
||||||
public int Capacity => _capacity;
|
public int Capacity => _capacity;
|
||||||
|
|
||||||
public Chuck(int size, int capacity)
|
public Chunk(int size, int capacity)
|
||||||
{
|
{
|
||||||
_data = new UnsafeArray<byte>(size, Allocator.Persistent);
|
_data = new UnsafeArray<byte>(size, Allocator.Persistent);
|
||||||
_capacity = capacity;
|
_capacity = capacity;
|
||||||
@@ -59,11 +59,11 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
|
|||||||
private readonly Identifier<Archetype> _id;
|
private readonly Identifier<Archetype> _id;
|
||||||
private readonly Identifier<World> _worldID;
|
private readonly Identifier<World> _worldID;
|
||||||
|
|
||||||
private UnsafeBitSet _signature;
|
internal UnsafeBitSet _signature;
|
||||||
private UnsafeList<Chuck> _chunks;
|
internal UnsafeList<Chunk> _chunks;
|
||||||
private UnsafeArray<ComponentMemoryLayout> _layouts;
|
internal UnsafeArray<ComponentMemoryLayout> _layouts;
|
||||||
private UnsafeArray<int> _componentIDToOffset;
|
|
||||||
|
|
||||||
|
private UnsafeArray<int> _componentIDToOffset;
|
||||||
// TODO: Is hash map better?
|
// TODO: Is hash map better?
|
||||||
private UnsafeList<Edge> _edgesAdd;
|
private UnsafeList<Edge> _edgesAdd;
|
||||||
private UnsafeList<Edge> _edgesRemove;
|
private UnsafeList<Edge> _edgesRemove;
|
||||||
@@ -75,10 +75,6 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
|
|||||||
|
|
||||||
public Identifier<Archetype> ID => _id;
|
public Identifier<Archetype> ID => _id;
|
||||||
|
|
||||||
public UnsafeBitSet Signature => _signature;
|
|
||||||
public UnsafeList<Chuck> Chunks => _chunks;
|
|
||||||
public UnsafeArray<ComponentMemoryLayout> Layouts => _layouts;
|
|
||||||
|
|
||||||
public int EntityCapacity => _entityCapacity;
|
public int EntityCapacity => _entityCapacity;
|
||||||
public int ChunkCount => _chunks.Count;
|
public int ChunkCount => _chunks.Count;
|
||||||
|
|
||||||
@@ -90,14 +86,14 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
|
|||||||
if (componentIds.IsEmpty)
|
if (componentIds.IsEmpty)
|
||||||
{
|
{
|
||||||
_signature = new UnsafeBitSet(1, Allocator.Persistent, AllocationOption.Clear);
|
_signature = new UnsafeBitSet(1, Allocator.Persistent, AllocationOption.Clear);
|
||||||
_chunks = new UnsafeList<Chuck>(4, Allocator.Persistent);
|
_chunks = new UnsafeList<Chunk>(4, Allocator.Persistent);
|
||||||
|
|
||||||
_edgesAdd = new UnsafeList<Edge>(4, Allocator.Persistent);
|
_edgesAdd = new UnsafeList<Edge>(4, Allocator.Persistent);
|
||||||
_edgesRemove = new UnsafeList<Edge>(4, Allocator.Persistent);
|
_edgesRemove = new UnsafeList<Edge>(4, Allocator.Persistent);
|
||||||
|
|
||||||
_signature.ClearAll();
|
_signature.ClearAll();
|
||||||
|
|
||||||
_entityCapacity = Chuck.CHUNK_SIZE / sizeof(Entity);
|
_entityCapacity = Chunk.CHUNK_SIZE / sizeof(Entity);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -112,7 +108,7 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
_signature = new UnsafeBitSet(highestComponentID + 1, Allocator.Persistent, AllocationOption.Clear);
|
_signature = new UnsafeBitSet(highestComponentID + 1, Allocator.Persistent, AllocationOption.Clear);
|
||||||
_chunks = new UnsafeList<Chuck>(4, Allocator.Persistent);
|
_chunks = new UnsafeList<Chunk>(4, Allocator.Persistent);
|
||||||
|
|
||||||
_edgesAdd = new UnsafeList<Edge>(4, Allocator.Persistent);
|
_edgesAdd = new UnsafeList<Edge>(4, Allocator.Persistent);
|
||||||
_edgesRemove = new UnsafeList<Edge>(4, Allocator.Persistent);
|
_edgesRemove = new UnsafeList<Edge>(4, Allocator.Persistent);
|
||||||
@@ -148,7 +144,7 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
_maxComponentID = maxComponentID;
|
_maxComponentID = maxComponentID;
|
||||||
_entityCapacity = Chuck.CHUNK_SIZE / bytesPerEntity;
|
_entityCapacity = Chunk.CHUNK_SIZE / bytesPerEntity;
|
||||||
_layouts = new UnsafeArray<ComponentMemoryLayout>(components.Length, Allocator.Persistent);
|
_layouts = new UnsafeArray<ComponentMemoryLayout>(components.Length, Allocator.Persistent);
|
||||||
_componentIDToOffset = new UnsafeArray<int>(_maxComponentID + 1, Allocator.Persistent);
|
_componentIDToOffset = new UnsafeArray<int>(_maxComponentID + 1, Allocator.Persistent);
|
||||||
|
|
||||||
@@ -176,7 +172,7 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
|
|||||||
tempOffsets[i] = currentOffset;
|
tempOffsets[i] = currentOffset;
|
||||||
currentOffset += _entityCapacity * size;
|
currentOffset += _entityCapacity * size;
|
||||||
|
|
||||||
if (currentOffset > Chuck.CHUNK_SIZE)
|
if (currentOffset > Chunk.CHUNK_SIZE)
|
||||||
{
|
{
|
||||||
fits = false;
|
fits = false;
|
||||||
break;
|
break;
|
||||||
@@ -220,7 +216,7 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Need to allocate a new chunk
|
// Need to allocate a new chunk
|
||||||
var newChunk = new Chuck(Chuck.CHUNK_SIZE, _entityCapacity);
|
var newChunk = new Chunk(Chunk.CHUNK_SIZE, _entityCapacity);
|
||||||
|
|
||||||
rowIndex = 0;
|
rowIndex = 0;
|
||||||
newChunk.Count++;
|
newChunk.Count++;
|
||||||
@@ -234,9 +230,9 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
|
|||||||
{
|
{
|
||||||
var chunk = _chunks[chunkIndex];
|
var chunk = _chunks[chunkIndex];
|
||||||
var chunkBase = chunk.GetUnsafePtr();
|
var chunkBase = chunk.GetUnsafePtr();
|
||||||
var pEntity = chunkBase + _entityIdsOffset + (sizeof(Entity) * rowIndex);
|
var dst = chunkBase + _entityIdsOffset + (sizeof(Entity) * rowIndex);
|
||||||
|
|
||||||
MemoryUtility.MemCpy(&entity, pEntity, (nuint)sizeof(Entity));
|
MemoryUtility.MemCpy(&entity, dst, (nuint)sizeof(Entity));
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@@ -253,7 +249,20 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public ref Chuck GetChunkReference(int index)
|
public void* GetComponentDataPtr(int chunkIndex, int rowIndex, Identifier<IComponent> componentID)
|
||||||
|
{
|
||||||
|
var offset = _componentIDToOffset[componentID];
|
||||||
|
var chunk = _chunks[chunkIndex];
|
||||||
|
|
||||||
|
var chunkBase = chunk.GetUnsafePtr();
|
||||||
|
var size = ComponentRegister.GetComponentInfo(componentID).size;
|
||||||
|
var dst = chunkBase + offset + (size * rowIndex);
|
||||||
|
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public ref Chunk GetChunkReference(int index)
|
||||||
{
|
{
|
||||||
return ref _chunks[index];
|
return ref _chunks[index];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,13 +110,13 @@ public unsafe class EntityManager : IDisposable
|
|||||||
ref Archetype newArch, int newChunk, int newRow)
|
ref Archetype newArch, int newChunk, int newRow)
|
||||||
{
|
{
|
||||||
// Iterate every component type in the OLD archetype
|
// Iterate every component type in the OLD archetype
|
||||||
for (var i = 0; i < oldArch.Layouts.Count; i++)
|
for (var i = 0; i < oldArch._layouts.Count; i++)
|
||||||
{
|
{
|
||||||
var layout = oldArch.Layouts[i];
|
var layout = oldArch._layouts[i];
|
||||||
|
|
||||||
var src = oldArch.Chunks[oldChunk].GetUnsafePtr() + layout.offset + (layout.size * oldRow);
|
var src = oldArch._chunks[oldChunk].GetUnsafePtr() + layout.offset + (layout.size * oldRow);
|
||||||
var newOffset = newArch.GetOffset(layout.componentID); // O(1) Lookup
|
var newOffset = newArch.GetOffset(layout.componentID); // O(1) Lookup
|
||||||
var dst = oldArch.Chunks[oldChunk].GetUnsafePtr() + newOffset + (layout.size * newRow);
|
var dst = oldArch._chunks[oldChunk].GetUnsafePtr() + newOffset + (layout.size * newRow);
|
||||||
|
|
||||||
MemoryUtility.MemCpy(src, dst, (nuint)layout.size);
|
MemoryUtility.MemCpy(src, dst, (nuint)layout.size);
|
||||||
}
|
}
|
||||||
@@ -133,7 +133,7 @@ public unsafe class EntityManager : IDisposable
|
|||||||
|
|
||||||
// Build new archetype signature
|
// Build new archetype signature
|
||||||
ref var oldArchetype = ref _world.GetArchetypeReference(location.archetypeID);
|
ref var oldArchetype = ref _world.GetArchetypeReference(location.archetypeID);
|
||||||
var oldSignature = oldArchetype.Signature;
|
var oldSignature = oldArchetype._signature;
|
||||||
|
|
||||||
// TODO: Check edge cache first.
|
// TODO: Check edge cache first.
|
||||||
var newArcID = oldArchetype.GetEdgeAdd(componentID);
|
var newArcID = oldArchetype.GetEdgeAdd(componentID);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace Ghost.Entities;
|
namespace Ghost.Entities;
|
||||||
|
|
||||||
public unsafe class EntityQuery<T1, T2>
|
public unsafe class EntityQueryy<T1, T2>
|
||||||
where T1 : unmanaged, IComponent
|
where T1 : unmanaged, IComponent
|
||||||
where T2 : unmanaged, IComponent
|
where T2 : unmanaged, IComponent
|
||||||
{
|
{
|
||||||
|
|||||||
263
Ghost.Entities/Query.cs
Normal file
263
Ghost.Entities/Query.cs
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
using Ghost.Core;
|
||||||
|
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
|
|
||||||
|
namespace Ghost.Entities;
|
||||||
|
|
||||||
|
public struct EntityQueryMask : IDisposable
|
||||||
|
{
|
||||||
|
public UnsafeBitSet all;
|
||||||
|
public UnsafeBitSet any;
|
||||||
|
public UnsafeBitSet absent;
|
||||||
|
|
||||||
|
public bool Matches(UnsafeBitSet archetypeSignature)
|
||||||
|
{
|
||||||
|
// 1. Check All: Archetype must have ALL bits set
|
||||||
|
if (all.IsCreated && !archetypeSignature.All(all))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Check None: Archetype must have NONE of these bits set
|
||||||
|
if (absent.IsCreated && archetypeSignature.Any(absent))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Check Any: Archetype must have AT LEAST ONE of these bits (if Any is defined)
|
||||||
|
if (any.IsCreated && any.Count != 0 && !archetypeSignature.Any(any))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
var hash = 17;
|
||||||
|
if (all.IsCreated) hash = hash * 23 + all.GetHashCode();
|
||||||
|
if (absent.IsCreated) hash = hash * 23 + absent.GetHashCode();
|
||||||
|
if (any.IsCreated) hash = hash * 23 + any.GetHashCode();
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
all.Dispose();
|
||||||
|
any.Dispose();
|
||||||
|
absent.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe struct EntityQuery : IIdentifierType, IDisposable
|
||||||
|
{
|
||||||
|
public readonly ref struct QueryItem
|
||||||
|
{
|
||||||
|
private readonly ref Archetype _archetype;
|
||||||
|
private readonly ref Chunk _chunk;
|
||||||
|
|
||||||
|
public readonly int Count => _chunk.Count;
|
||||||
|
|
||||||
|
internal QueryItem(ref Archetype archetype, int chunkIndex)
|
||||||
|
{
|
||||||
|
_archetype = ref archetype;
|
||||||
|
_chunk = ref archetype.GetChunkReference(chunkIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly Span<T> GetComponentData<T>()
|
||||||
|
where T : unmanaged, IComponent
|
||||||
|
{
|
||||||
|
var offset = _archetype.GetOffset(ComponentTypeID<T>.value);
|
||||||
|
if (offset < 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Archetype does not contain component of type {typeof(T)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var ptr = (byte*)_chunk.GetUnsafePtr() + offset;
|
||||||
|
return new Span<T>(ptr, _chunk.Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ref struct ChunkEnumerator
|
||||||
|
{
|
||||||
|
private ReadOnlyUnsafeCollection<Identifier<Archetype>> _matchingArchetypes;
|
||||||
|
private World _world;
|
||||||
|
private int _archetypeIndex;
|
||||||
|
private int _chunkIndex;
|
||||||
|
|
||||||
|
internal ChunkEnumerator(ReadOnlyUnsafeCollection<Identifier<Archetype>> matchingArchetypes, World world)
|
||||||
|
{
|
||||||
|
_matchingArchetypes = matchingArchetypes;
|
||||||
|
_world = world;
|
||||||
|
_archetypeIndex = 0;
|
||||||
|
_chunkIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QueryItem Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
ref var archetype = ref _world.GetArchetypeReference(_matchingArchetypes[_archetypeIndex]);
|
||||||
|
return new QueryItem(ref archetype, _chunkIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
_chunkIndex++;
|
||||||
|
|
||||||
|
while (_archetypeIndex < _matchingArchetypes.Count)
|
||||||
|
{
|
||||||
|
ref var archetype = ref _world.GetArchetypeReference(_matchingArchetypes[_archetypeIndex]);
|
||||||
|
if (_chunkIndex < archetype.ChunkCount)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_chunkIndex = 0;
|
||||||
|
_archetypeIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
_archetypeIndex = 0;
|
||||||
|
_chunkIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Identifier<World> _worldID;
|
||||||
|
private EntityQueryMask _mask;
|
||||||
|
private UnsafeList<Identifier<Archetype>> _matchingArchetypes;
|
||||||
|
|
||||||
|
internal EntityQuery(Identifier<World> worldID, EntityQueryMask mask)
|
||||||
|
{
|
||||||
|
_worldID = worldID;
|
||||||
|
_mask = mask;
|
||||||
|
_matchingArchetypes = new UnsafeList<Identifier<Archetype>>(8, Allocator.Persistent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called by World when a new archetype is created
|
||||||
|
internal void AddArchetypeIfMatch(Archetype archetype)
|
||||||
|
{
|
||||||
|
if (_mask.Matches(archetype._signature))
|
||||||
|
{
|
||||||
|
_matchingArchetypes.Add(archetype.ID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChunkEnumerator GetEnumerator()
|
||||||
|
{
|
||||||
|
var world = World.GetWorld(_worldID).Value;
|
||||||
|
return new ChunkEnumerator(_matchingArchetypes.AsReadOnly(), world);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_mask.Dispose();
|
||||||
|
_matchingArchetypes.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ref struct QueryBuilder
|
||||||
|
{
|
||||||
|
private Stack.Scope _scope;
|
||||||
|
|
||||||
|
private UnsafeList<Identifier<IComponent>> _all;
|
||||||
|
private UnsafeList<Identifier<IComponent>> _any;
|
||||||
|
private UnsafeList<Identifier<IComponent>> _absent;
|
||||||
|
|
||||||
|
public QueryBuilder()
|
||||||
|
{
|
||||||
|
_scope = AllocationManager.CreateStackScope();
|
||||||
|
|
||||||
|
_all = new UnsafeList<Identifier<IComponent>>(4, Allocator.Stack);
|
||||||
|
_any = new UnsafeList<Identifier<IComponent>>(4, Allocator.Stack);
|
||||||
|
_absent = new UnsafeList<Identifier<IComponent>>(4, Allocator.Stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
public QueryBuilder WithAll<T>()
|
||||||
|
where T : unmanaged, IComponent
|
||||||
|
{
|
||||||
|
_all.Add(ComponentTypeID<T>.value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QueryBuilder WithAny<T>()
|
||||||
|
where T : unmanaged, IComponent
|
||||||
|
{
|
||||||
|
_any.Add(ComponentTypeID<T>.value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QueryBuilder WithNone<T>()
|
||||||
|
where T : unmanaged, IComponent
|
||||||
|
{
|
||||||
|
_absent.Add(ComponentTypeID<T>.value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Identifier<EntityQuery> Build(World world)
|
||||||
|
{
|
||||||
|
// 1. Calculate max component ID to size the BitSets
|
||||||
|
int maxID = 0;
|
||||||
|
FindMax(_all, ref maxID);
|
||||||
|
FindMax(_any, ref maxID);
|
||||||
|
FindMax(_absent, ref maxID);
|
||||||
|
|
||||||
|
// 2. Create the Mask
|
||||||
|
using var mask = new EntityQueryMask
|
||||||
|
{
|
||||||
|
all = new UnsafeBitSet(maxID + 1, Allocator.Stack),
|
||||||
|
any = new UnsafeBitSet(maxID + 1, Allocator.Stack),
|
||||||
|
absent = new UnsafeBitSet(maxID + 1, Allocator.Stack)
|
||||||
|
};
|
||||||
|
|
||||||
|
// 3. Fill BitSets
|
||||||
|
foreach (var id in _all) mask.all.SetBit(id);
|
||||||
|
foreach (var id in _any) mask.any.SetBit(id);
|
||||||
|
foreach (var id in _absent) mask.absent.SetBit(id);
|
||||||
|
|
||||||
|
// 4. Ask World for the Query (Cached)
|
||||||
|
var queryID = world.GetEntityQueryIDByMaskHash(mask.GetHashCode());
|
||||||
|
if (queryID.IsNotValid)
|
||||||
|
{
|
||||||
|
queryID = world.CreateEntityQuery(mask);
|
||||||
|
ref var query = ref world.GetEntityQueryReference(queryID);
|
||||||
|
for (var i = 0; i < world.ArchetypeCount; i++)
|
||||||
|
{
|
||||||
|
ref var archetype = ref world.GetArchetypeReference(i);
|
||||||
|
query.AddArchetypeIfMatch(archetype);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Dispose();
|
||||||
|
|
||||||
|
return queryID;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FindMax(UnsafeList<Identifier<IComponent>> list, ref int max)
|
||||||
|
{
|
||||||
|
foreach (var id in list)
|
||||||
|
{
|
||||||
|
if (id.value > max) max = id.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Dispose()
|
||||||
|
{
|
||||||
|
_all.Dispose();
|
||||||
|
_any.Dispose();
|
||||||
|
_absent.Dispose();
|
||||||
|
|
||||||
|
_scope.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,6 +32,23 @@ public partial class World
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Destroy(Identifier<World> id)
|
||||||
|
{
|
||||||
|
lock (s_worlds)
|
||||||
|
{
|
||||||
|
if (id.value < 0 || id.value >= s_worlds.Count)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var world = s_worlds[id.value];
|
||||||
|
if (world is not null)
|
||||||
|
{
|
||||||
|
world.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static Result<World, ResultStatus> GetWorld(Identifier<World> id)
|
public static Result<World, ResultStatus> GetWorld(Identifier<World> id)
|
||||||
{
|
{
|
||||||
@@ -54,13 +71,19 @@ public partial class World : IIdentifierType, IDisposable, IEquatable<World>
|
|||||||
{
|
{
|
||||||
private readonly Identifier<World> _id;
|
private readonly Identifier<World> _id;
|
||||||
|
|
||||||
private UnsafeList<Archetype> _archetypes;
|
|
||||||
private UnsafeHashMap<int, Identifier<Archetype>> _archetypeLookup; // Signature Hash to Archetype ID
|
|
||||||
private EntityManager _entityManager;
|
private EntityManager _entityManager;
|
||||||
private EntityCommandBuffer _entityCommandBuffer;
|
private EntityCommandBuffer _entityCommandBuffer;
|
||||||
|
|
||||||
|
private UnsafeList<Archetype> _archetypes;
|
||||||
|
private UnsafeList<EntityQuery> _entityQueries;
|
||||||
|
|
||||||
|
private UnsafeHashMap<int, Identifier<Archetype>> _archetypeLookup; // Signature Hash to Archetype ID
|
||||||
|
private UnsafeHashMap<int, Identifier<EntityQuery>> _querieLookup; // Query Mask Hash to Query ID
|
||||||
|
|
||||||
private bool _disposed = false;
|
private bool _disposed = false;
|
||||||
|
|
||||||
|
internal int ArchetypeCount => _archetypes.Count;
|
||||||
|
|
||||||
public Identifier<World> ID => _id;
|
public Identifier<World> ID => _id;
|
||||||
public EntityManager EntityManager => _entityManager;
|
public EntityManager EntityManager => _entityManager;
|
||||||
public EntityCommandBuffer EntityCommandBuffer => _entityCommandBuffer;
|
public EntityCommandBuffer EntityCommandBuffer => _entityCommandBuffer;
|
||||||
@@ -70,7 +93,10 @@ public partial class World : IIdentifierType, IDisposable, IEquatable<World>
|
|||||||
_id = id;
|
_id = id;
|
||||||
|
|
||||||
_archetypes = new UnsafeList<Archetype>(16, Allocator.Persistent);
|
_archetypes = new UnsafeList<Archetype>(16, Allocator.Persistent);
|
||||||
|
_entityQueries = new UnsafeList<EntityQuery>(16, Allocator.Persistent);
|
||||||
|
|
||||||
_archetypeLookup = new UnsafeHashMap<int, Identifier<Archetype>>(16, Allocator.Persistent);
|
_archetypeLookup = new UnsafeHashMap<int, Identifier<Archetype>>(16, Allocator.Persistent);
|
||||||
|
_querieLookup = new UnsafeHashMap<int, Identifier<EntityQuery>>(16, Allocator.Persistent);
|
||||||
|
|
||||||
_entityManager = new EntityManager(this, entityCapacity);
|
_entityManager = new EntityManager(this, entityCapacity);
|
||||||
_entityCommandBuffer = new EntityCommandBuffer(_entityManager);
|
_entityCommandBuffer = new EntityCommandBuffer(_entityManager);
|
||||||
@@ -90,12 +116,13 @@ public partial class World : IIdentifierType, IDisposable, IEquatable<World>
|
|||||||
_archetypes.Add(new Archetype(arcID, _id, componentTypeIDs));
|
_archetypes.Add(new Archetype(arcID, _id, componentTypeIDs));
|
||||||
_archetypeLookup.Add(signatureHash, arcID);
|
_archetypeLookup.Add(signatureHash, arcID);
|
||||||
|
|
||||||
return arcID;
|
for (int i = 0; i < _entityQueries.Count; i++)
|
||||||
}
|
{
|
||||||
|
ref var query = ref _entityQueries[i];
|
||||||
|
query.AddArchetypeIfMatch(_archetypes[arcID.value]);
|
||||||
|
}
|
||||||
|
|
||||||
internal ref Archetype GetArchetypeReference(Identifier<Archetype> id)
|
return arcID;
|
||||||
{
|
|
||||||
return ref _archetypes[id.value];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Identifier<Archetype> GetArchetypeIDBySignatureHash(int signatureHash)
|
internal Identifier<Archetype> GetArchetypeIDBySignatureHash(int signatureHash)
|
||||||
@@ -108,6 +135,35 @@ public partial class World : IIdentifierType, IDisposable, IEquatable<World>
|
|||||||
return Identifier<Archetype>.Invalid;
|
return Identifier<Archetype>.Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal ref Archetype GetArchetypeReference(Identifier<Archetype> id)
|
||||||
|
{
|
||||||
|
return ref _archetypes[id.value];
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Identifier<EntityQuery> CreateEntityQuery(EntityQueryMask mask)
|
||||||
|
{
|
||||||
|
var queryID = new Identifier<EntityQuery>(_entityQueries.Count);
|
||||||
|
_entityQueries.Add(new EntityQuery(_id, mask));
|
||||||
|
_querieLookup.Add(mask.GetHashCode(), queryID);
|
||||||
|
|
||||||
|
return queryID;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Identifier<EntityQuery> GetEntityQueryIDByMaskHash(int maskHash)
|
||||||
|
{
|
||||||
|
if (_querieLookup.TryGetValue(maskHash, out var queryID))
|
||||||
|
{
|
||||||
|
return queryID;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Identifier<EntityQuery>.Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ref EntityQuery GetEntityQueryReference(Identifier<EntityQuery> id)
|
||||||
|
{
|
||||||
|
return ref _entityQueries[id.value];
|
||||||
|
}
|
||||||
|
|
||||||
public bool Equals(World? other)
|
public bool Equals(World? other)
|
||||||
{
|
{
|
||||||
return other is not null && _id == other._id;
|
return other is not null && _id == other._id;
|
||||||
@@ -141,11 +197,15 @@ public partial class World : IIdentifierType, IDisposable, IEquatable<World>
|
|||||||
}
|
}
|
||||||
|
|
||||||
_entityManager.Dispose();
|
_entityManager.Dispose();
|
||||||
|
_entityCommandBuffer.Dispose();
|
||||||
|
|
||||||
_archetypes.Dispose();
|
_archetypes.Dispose();
|
||||||
|
_entityQueries.Dispose();
|
||||||
_archetypeLookup.Dispose();
|
_archetypeLookup.Dispose();
|
||||||
|
_querieLookup.Dispose();
|
||||||
|
|
||||||
s_freeWorldSlots.Enqueue(_id);
|
s_freeWorldSlots.Enqueue(_id);
|
||||||
|
s_worlds[_id] = null;
|
||||||
|
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user