diff --git a/Ghost.Entities.Test/ArcEntityTest.cs b/Ghost.Entities.Test/ArcEntityTest.cs index d378400..387d0c6 100644 --- a/Ghost.Entities.Test/ArcEntityTest.cs +++ b/Ghost.Entities.Test/ArcEntityTest.cs @@ -14,24 +14,29 @@ public partial class ArcEntityTest : ITest public void Run() { - var entity1 = _world.EntityManager.CreateEntity(ComponentTypeID.value); - var mesh = new MeshData { index = 1 }; - _world.EntityManager.AddComponent(entity1, ref mesh); + var entity1 = _world.EntityManager.CreateEntity(ComponentTypeID.value); + Console.WriteLine(entity1); + _world.EntityManager.AddComponent(entity1, new Mesh { index = 1 }); - var queryID = new QueryBuilder().WithAll().Build(_world); + var queryID = new QueryBuilder().WithAll().Build(_world); ref var query = ref _world.GetEntityQueryReference(queryID); - foreach (var item in query) + foreach (var chunk in query.GetChunkIterator()) { - var transforms = item.GetComponentData(); - Console.WriteLine($"Item Count: {item.Count}"); - for (var i = 0; i < item.Count; i++) + var transforms = chunk.GetComponentData(); + var entities = chunk.GetEntities(); + + for (var i = 0; i < chunk.Count; i++) { - Console.WriteLine($"Entity Position: {transforms[i].position}"); + Console.WriteLine($"Entity {entities[i]} Position: {transforms[i].position}"); transforms[i].position = new float3(1, 2, 3); - Console.WriteLine($"Updated Entity Position: {transforms[i].position}"); } } + + query.ForEach((e, ref t) => + { + Console.WriteLine($"Entity {e} Updated Position: {t.position}"); + }); } public void Cleanup() @@ -40,12 +45,12 @@ public partial class ArcEntityTest : ITest } } -public struct TransformData : IComponent +public struct Transform : IComponent { public float3 position; } -public struct MeshData : IComponent +public struct Mesh : IComponent { public int index; } diff --git a/Ghost.Entities.Test/Program.cs b/Ghost.Entities.Test/Program.cs index 67483f2..12c3a1b 100644 --- a/Ghost.Entities.Test/Program.cs +++ b/Ghost.Entities.Test/Program.cs @@ -1,4 +1,7 @@ using Ghost.Entities.Test; using Ghost.Test.Core; +using Misaki.HighPerformance.LowLevel.Buffer; -TestRunner.Run(); \ No newline at end of file +AllocationManager.EnableDebugLayer(); +TestRunner.Run(); +AllocationManager.Dispose(); \ No newline at end of file diff --git a/Ghost.Entities/Archetype.cs b/Ghost.Entities/Archetype.cs index a034fb3..2b2d68d 100644 --- a/Ghost.Entities/Archetype.cs +++ b/Ghost.Entities/Archetype.cs @@ -16,21 +16,21 @@ internal unsafe struct Chunk : IDisposable public int Count { - get => _count; + readonly get => _count; set => _count = value; } - public int Capacity => _capacity; + public readonly int Capacity => _capacity; public Chunk(int size, int capacity) { - _data = new UnsafeArray(size, Allocator.Persistent); + _data = new UnsafeArray(size, Allocator.Persistent, AllocationOption.Clear); _capacity = capacity; _count = 0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public byte* GetUnsafePtr() + public readonly byte* GetUnsafePtr() { return (byte*)_data.GetUnsafePtr(); } @@ -68,15 +68,16 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable private UnsafeList _edgesAdd; private UnsafeList _edgesRemove; - private int _hash; + private readonly int _hash; private int _entityCapacity; private int _maxComponentID; private int _entityIdsOffset; - public Identifier ID => _id; + public readonly Identifier ID => _id; - public int EntityCapacity => _entityCapacity; - public int ChunkCount => _chunks.Count; + public readonly int EntityCapacity => _entityCapacity; + public readonly int ChunkCount => _chunks.Count; + public readonly int EntityIDsOffset => _entityIdsOffset; public Archetype(Identifier id, Identifier worldID, ReadOnlySpan> componentIds) { @@ -204,7 +205,7 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable { for (var i = 0; i < _chunks.Count; i++) { - var chunk = _chunks[i]; + ref var chunk = ref _chunks[i]; if (chunk.Count < _entityCapacity) { rowIndex = chunk.Count; @@ -226,7 +227,7 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetEntity(int chunkIndex, int rowIndex, Entity entity) + public readonly void SetEntity(int chunkIndex, int rowIndex, Entity entity) { var chunk = _chunks[chunkIndex]; var chunkBase = chunk.GetUnsafePtr(); @@ -236,7 +237,7 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetComponentData(int chunkIndex, int rowIndex, Identifier componentID, void* pComponent) + public readonly void SetComponentData(int chunkIndex, int rowIndex, Identifier componentID, void* pComponent) { var offset = _componentIDToOffset[componentID]; var chunk = _chunks[chunkIndex]; @@ -249,7 +250,7 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void* GetComponentDataPtr(int chunkIndex, int rowIndex, Identifier componentID) + public readonly void* GetComponentDataPtr(int chunkIndex, int rowIndex, Identifier componentID) { var offset = _componentIDToOffset[componentID]; var chunk = _chunks[chunkIndex]; @@ -268,7 +269,7 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetOffset(int componentId) + public readonly int GetOffset(int componentId) { if (componentId >= _componentIDToOffset.Count) { @@ -326,7 +327,7 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool HasComponent(Identifier componentID) + public readonly bool HasComponent(Identifier componentID) { return _signature.IsSet(componentID); } @@ -342,7 +343,7 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Identifier GetEdgeAdd(Identifier componentID) + public readonly Identifier GetEdgeAdd(Identifier componentID) { for (var i = 0; i < _edgesAdd.Count; i++) { @@ -367,7 +368,7 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Identifier GetEdgeRemove(Identifier componentID) + public readonly Identifier GetEdgeRemove(Identifier componentID) { for (var i = 0; i < _edgesRemove.Count; i++) { @@ -382,7 +383,7 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span GetComponentArray(int chunkIndex) + public readonly Span GetComponentArray(int chunkIndex) where T : unmanaged, IComponent { var id = ComponentTypeID.value; @@ -401,7 +402,7 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable return new Span((T*)((byte*)chunk.GetUnsafePtr() + offset), chunk.Count); } - public override int GetHashCode() + public override readonly int GetHashCode() { return _hash; } diff --git a/Ghost.Entities/Component.cs b/Ghost.Entities/Component.cs index 7ff1dae..9b6a72c 100644 --- a/Ghost.Entities/Component.cs +++ b/Ghost.Entities/Component.cs @@ -16,7 +16,7 @@ public struct ComponentInfo public Identifier id; } -public static unsafe class ComponentTypeID +public static class ComponentTypeID where T : unmanaged, IComponent { public static readonly Identifier value = ComponentRegister.GetOrRegisterComponent(); diff --git a/Ghost.Entities/EntityManager.cs b/Ghost.Entities/EntityManager.cs index 09efbbc..f550f4c 100644 --- a/Ghost.Entities/EntityManager.cs +++ b/Ghost.Entities/EntityManager.cs @@ -15,13 +15,19 @@ public unsafe class EntityManager : IDisposable public int rowIndex; } - private World _world; + private readonly World _world; private UnsafeSlotMap _entityLocations; + private bool _disposed; internal EntityManager(World world, int initialCapacity) { _world = world; - _entityLocations = new UnsafeSlotMap(initialCapacity, Allocator.Persistent); + _entityLocations = new UnsafeSlotMap(initialCapacity, Allocator.Persistent, AllocationOption.Clear); + } + + ~EntityManager() + { + Dispose(); } internal ResultStatus UpdateEntityLocation(Entity entity, Identifier newArchetypeID, int newChunkIndex, int newRowIndex) @@ -115,8 +121,8 @@ public unsafe class EntityManager : IDisposable var layout = oldArch._layouts[i]; var src = oldArch._chunks[oldChunk].GetUnsafePtr() + layout.offset + (layout.size * oldRow); - var newOffset = newArch.GetOffset(layout.componentID); // O(1) Lookup - var dst = oldArch._chunks[oldChunk].GetUnsafePtr() + newOffset + (layout.size * newRow); + var newOffset = newArch.GetOffset(layout.componentID); // O(1) Looku + var dst = newArch._chunks[newChunk].GetUnsafePtr() + newOffset + (layout.size * newRow); MemoryUtility.MemCpy(src, dst, (nuint)layout.size); } @@ -217,10 +223,10 @@ public unsafe class EntityManager : IDisposable return ResultStatus.Success; } - public ResultStatus AddComponent(Entity entity, ref T component) + public ResultStatus AddComponent(Entity entity, T component) where T : unmanaged, IComponent { - return AddComponent(entity, ComponentTypeID.value, UnsafeUtility.AddressOf(ref component)); + return AddComponent(entity, ComponentTypeID.value, &component); } public ResultStatus SetComponentData(Entity entity, Identifier componentID, void* pComponent) @@ -236,10 +242,10 @@ public unsafe class EntityManager : IDisposable return ResultStatus.Success; } - public ResultStatus SetComponentData(Entity entity, ref T component) + public ResultStatus SetComponentData(Entity entity, T component) where T : unmanaged, IComponent { - return SetComponentData(entity, ComponentTypeID.value, UnsafeUtility.AddressOf(ref component)); + return SetComponentData(entity, ComponentTypeID.value, &component); } public bool HasComponent(Entity entity, Identifier componentID) @@ -261,6 +267,14 @@ public unsafe class EntityManager : IDisposable public void Dispose() { + if (_disposed) + { + return; + } + _entityLocations.Dispose(); + _disposed = true; + + GC.SuppressFinalize(this); } } diff --git a/Ghost.Entities/EntityQuery.cs b/Ghost.Entities/EntityQuery.cs index b284771..41f7a82 100644 --- a/Ghost.Entities/EntityQuery.cs +++ b/Ghost.Entities/EntityQuery.cs @@ -8,7 +8,7 @@ public unsafe class EntityQueryy struct ArchetypeCache { public Archetype Archetype; - public int Offset1; // Offset for T1 + public int Offset1; // Offset for T0 public int Offset2; // Offset for T2 } diff --git a/Ghost.Entities/Ghost.Entities.csproj b/Ghost.Entities/Ghost.Entities.csproj index f572c66..0f6ca75 100644 --- a/Ghost.Entities/Ghost.Entities.csproj +++ b/Ghost.Entities/Ghost.Entities.csproj @@ -7,8 +7,53 @@ True + + + + + + + True + True + EntityQuery.ForEach.tt + + + ForEach.tt + True + True + + + + + + TextTemplatingFileGenerator + EntityQuery.ForEach.gen.cs + + + ForEach.gen.cs + TextTemplatingFileGenerator + + + + + + + + + + True + True + EntityQuery.ForEach.tt + + + True + True + ForEach.tt + + + diff --git a/Ghost.Entities/Query.cs b/Ghost.Entities/Query.cs index 3f0f57f..8a4b9e2 100644 --- a/Ghost.Entities/Query.cs +++ b/Ghost.Entities/Query.cs @@ -1,6 +1,7 @@ using Ghost.Core; using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Collections; +using System.Runtime.CompilerServices; namespace Ghost.Entities; @@ -10,30 +11,15 @@ public struct EntityQueryMask : IDisposable public UnsafeBitSet any; public UnsafeBitSet absent; - public bool Matches(UnsafeBitSet archetypeSignature) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly 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; + return (!all.IsCreated || all.All(archetypeSignature)) + && (!absent.IsCreated || absent.None(archetypeSignature)) + && (!any.IsCreated || any.Count == 0 || any.Any(archetypeSignature)); } - public override int GetHashCode() + public readonly override int GetHashCode() { var hash = 17; if (all.IsCreated) hash = hash * 23 + all.GetHashCode(); @@ -51,90 +37,114 @@ public struct EntityQueryMask : IDisposable } } -public unsafe struct EntityQuery : IIdentifierType, IDisposable +public unsafe partial struct EntityQuery : IIdentifierType, IDisposable { - public readonly ref struct QueryItem + public readonly ref struct ChunkIterator { - private readonly ref Archetype _archetype; - private readonly ref Chunk _chunk; - - public readonly int Count => _chunk.Count; - - internal QueryItem(ref Archetype archetype, int chunkIndex) + public readonly ref struct ChunkView { - _archetype = ref archetype; - _chunk = ref archetype.GetChunkReference(chunkIndex); - } + private readonly ref Archetype _archetype; + private readonly ref Chunk _chunk; - public readonly Span GetComponentData() - where T : unmanaged, IComponent - { - var offset = _archetype.GetOffset(ComponentTypeID.value); - if (offset < 0) + public readonly int Count => _chunk.Count; + + internal ChunkView(ref Archetype archetype, int chunkIndex) { - throw new InvalidOperationException($"Archetype does not contain component of type {typeof(T)}"); + _archetype = ref archetype; + _chunk = ref archetype.GetChunkReference(chunkIndex); } - var ptr = (byte*)_chunk.GetUnsafePtr() + offset; - return new Span(ptr, _chunk.Count); + public readonly ReadOnlySpan GetEntities() + { + var ptr = _chunk.GetUnsafePtr(); + var pEntity = (Entity*)(ptr + _archetype.EntityIDsOffset); + return new ReadOnlySpan(pEntity, _chunk.Count); + } + + public readonly Span GetComponentData() + where T : unmanaged, IComponent + { + var offset = _archetype.GetOffset(ComponentTypeID.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(ptr, _chunk.Count); + } } - } - public ref struct ChunkEnumerator - { - private ReadOnlyUnsafeCollection> _matchingArchetypes; - private World _world; - private int _archetypeIndex; - private int _chunkIndex; + public ref struct Enumerator + { + private readonly ReadOnlyUnsafeCollection> _matchingArchetypes; + private readonly World _world; + private int _archetypeIndex; + private int _chunkIndex; - internal ChunkEnumerator(ReadOnlyUnsafeCollection> matchingArchetypes, World world) + internal Enumerator(ReadOnlyUnsafeCollection> matchingArchetypes, World world) + { + _matchingArchetypes = matchingArchetypes; + _world = world; + _archetypeIndex = 0; + _chunkIndex = -1; + } + + public readonly ChunkView Current + { + get + { + ref var archetype = ref _world.GetArchetypeReference(_matchingArchetypes[_archetypeIndex]); + return new ChunkView(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 readonly void Dispose() + { + } + } + + private readonly ReadOnlyUnsafeCollection> _matchingArchetypes; + private readonly World _world; + + internal ChunkIterator(ReadOnlyUnsafeCollection> 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() + public readonly Enumerator GetEnumerator() { + return new Enumerator(_matchingArchetypes, _world); } } - private Identifier _worldID; + private readonly Identifier _worldID; private EntityQueryMask _mask; private UnsafeList> _matchingArchetypes; @@ -145,7 +155,6 @@ public unsafe struct EntityQuery : IIdentifierType, IDisposable _matchingArchetypes = new UnsafeList>(8, Allocator.Persistent); } - // Called by World when a new archetype is created internal void AddArchetypeIfMatch(Archetype archetype) { if (_mask.Matches(archetype._signature)) @@ -154,10 +163,10 @@ public unsafe struct EntityQuery : IIdentifierType, IDisposable } } - public ChunkEnumerator GetEnumerator() + public readonly ChunkIterator GetChunkIterator() { var world = World.GetWorld(_worldID).Value; - return new ChunkEnumerator(_matchingArchetypes.AsReadOnly(), world); + return new ChunkIterator(_matchingArchetypes.AsReadOnly(), world); } public void Dispose() @@ -169,7 +178,7 @@ public unsafe struct EntityQuery : IIdentifierType, IDisposable public ref struct QueryBuilder { - private Stack.Scope _scope; + private readonly Stack.Scope _scope; private UnsafeList> _all; private UnsafeList> _any; @@ -244,7 +253,7 @@ public ref struct QueryBuilder return queryID; } - private void FindMax(UnsafeList> list, ref int max) + private static void FindMax(UnsafeList> list, ref int max) { foreach (var id in list) { diff --git a/Ghost.Entities/Templates/EntityQuery.ForEach.gen.cs b/Ghost.Entities/Templates/EntityQuery.ForEach.gen.cs new file mode 100644 index 0000000..6345423 --- /dev/null +++ b/Ghost.Entities/Templates/EntityQuery.ForEach.gen.cs @@ -0,0 +1,881 @@ + +using Ghost.Core; + +namespace Ghost.Entities; + +public unsafe partial struct EntityQuery +{ + public readonly void ForEach(ForEach action) + where T0 : unmanaged, IComponent + { + var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success); + + var compTypeIDs = stackalloc int[] { ComponentTypeID.value }; + var offsets = stackalloc int[1]; + var basePtrs = stackalloc byte*[1]; + + foreach (var archetypeID in _matchingArchetypes) + { + ref var archetype = ref world.GetArchetypeReference(archetypeID); + var hasAllComponents = true; + for (var index = 0; index < 1; index++) + { + offsets[index] = archetype.GetOffset(compTypeIDs[index]); + if (offsets[index] == -1) + { + hasAllComponents = false; + break; + } + } + + if (!hasAllComponents) + { + continue; + } + + for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++) + { + ref var chunk = ref archetype.GetChunkReference(chunkIndex); + var count = chunk.Count; + for (var index = 0; index < 1; index++) + { + basePtrs[index] = chunk.GetUnsafePtr() + offsets[index]; + } + + for (var entityIndex = 0; entityIndex < count; entityIndex++) + { + var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex)); + + action(ref *pComp0); + } + } + } + } + + public readonly void ForEach(ForEach action) + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + { + var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success); + + var compTypeIDs = stackalloc int[] { ComponentTypeID.value, ComponentTypeID.value }; + var offsets = stackalloc int[2]; + var basePtrs = stackalloc byte*[2]; + + foreach (var archetypeID in _matchingArchetypes) + { + ref var archetype = ref world.GetArchetypeReference(archetypeID); + var hasAllComponents = true; + for (var index = 0; index < 2; index++) + { + offsets[index] = archetype.GetOffset(compTypeIDs[index]); + if (offsets[index] == -1) + { + hasAllComponents = false; + break; + } + } + + if (!hasAllComponents) + { + continue; + } + + for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++) + { + ref var chunk = ref archetype.GetChunkReference(chunkIndex); + var count = chunk.Count; + for (var index = 0; index < 2; index++) + { + basePtrs[index] = chunk.GetUnsafePtr() + offsets[index]; + } + + for (var entityIndex = 0; entityIndex < count; entityIndex++) + { + var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex)); + var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex)); + + action(ref *pComp0,ref *pComp1); + } + } + } + } + + public readonly void ForEach(ForEach action) + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + { + var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success); + + var compTypeIDs = stackalloc int[] { ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value }; + var offsets = stackalloc int[3]; + var basePtrs = stackalloc byte*[3]; + + foreach (var archetypeID in _matchingArchetypes) + { + ref var archetype = ref world.GetArchetypeReference(archetypeID); + var hasAllComponents = true; + for (var index = 0; index < 3; index++) + { + offsets[index] = archetype.GetOffset(compTypeIDs[index]); + if (offsets[index] == -1) + { + hasAllComponents = false; + break; + } + } + + if (!hasAllComponents) + { + continue; + } + + for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++) + { + ref var chunk = ref archetype.GetChunkReference(chunkIndex); + var count = chunk.Count; + for (var index = 0; index < 3; index++) + { + basePtrs[index] = chunk.GetUnsafePtr() + offsets[index]; + } + + for (var entityIndex = 0; entityIndex < count; entityIndex++) + { + var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex)); + var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex)); + var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex)); + + action(ref *pComp0,ref *pComp1,ref *pComp2); + } + } + } + } + + public readonly void ForEach(ForEach action) + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + { + var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success); + + var compTypeIDs = stackalloc int[] { ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value }; + var offsets = stackalloc int[4]; + var basePtrs = stackalloc byte*[4]; + + foreach (var archetypeID in _matchingArchetypes) + { + ref var archetype = ref world.GetArchetypeReference(archetypeID); + var hasAllComponents = true; + for (var index = 0; index < 4; index++) + { + offsets[index] = archetype.GetOffset(compTypeIDs[index]); + if (offsets[index] == -1) + { + hasAllComponents = false; + break; + } + } + + if (!hasAllComponents) + { + continue; + } + + for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++) + { + ref var chunk = ref archetype.GetChunkReference(chunkIndex); + var count = chunk.Count; + for (var index = 0; index < 4; index++) + { + basePtrs[index] = chunk.GetUnsafePtr() + offsets[index]; + } + + for (var entityIndex = 0; entityIndex < count; entityIndex++) + { + var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex)); + var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex)); + var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex)); + var pComp3 = (T3*)(basePtrs[3] + (sizeof(T3) * entityIndex)); + + action(ref *pComp0,ref *pComp1,ref *pComp2,ref *pComp3); + } + } + } + } + + public readonly void ForEach(ForEach action) + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + where T4 : unmanaged, IComponent + { + var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success); + + var compTypeIDs = stackalloc int[] { ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value }; + var offsets = stackalloc int[5]; + var basePtrs = stackalloc byte*[5]; + + foreach (var archetypeID in _matchingArchetypes) + { + ref var archetype = ref world.GetArchetypeReference(archetypeID); + var hasAllComponents = true; + for (var index = 0; index < 5; index++) + { + offsets[index] = archetype.GetOffset(compTypeIDs[index]); + if (offsets[index] == -1) + { + hasAllComponents = false; + break; + } + } + + if (!hasAllComponents) + { + continue; + } + + for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++) + { + ref var chunk = ref archetype.GetChunkReference(chunkIndex); + var count = chunk.Count; + for (var index = 0; index < 5; index++) + { + basePtrs[index] = chunk.GetUnsafePtr() + offsets[index]; + } + + for (var entityIndex = 0; entityIndex < count; entityIndex++) + { + var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex)); + var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex)); + var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex)); + var pComp3 = (T3*)(basePtrs[3] + (sizeof(T3) * entityIndex)); + var pComp4 = (T4*)(basePtrs[4] + (sizeof(T4) * entityIndex)); + + action(ref *pComp0,ref *pComp1,ref *pComp2,ref *pComp3,ref *pComp4); + } + } + } + } + + public readonly void ForEach(ForEach action) + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + where T4 : unmanaged, IComponent + where T5 : unmanaged, IComponent + { + var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success); + + var compTypeIDs = stackalloc int[] { ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value }; + var offsets = stackalloc int[6]; + var basePtrs = stackalloc byte*[6]; + + foreach (var archetypeID in _matchingArchetypes) + { + ref var archetype = ref world.GetArchetypeReference(archetypeID); + var hasAllComponents = true; + for (var index = 0; index < 6; index++) + { + offsets[index] = archetype.GetOffset(compTypeIDs[index]); + if (offsets[index] == -1) + { + hasAllComponents = false; + break; + } + } + + if (!hasAllComponents) + { + continue; + } + + for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++) + { + ref var chunk = ref archetype.GetChunkReference(chunkIndex); + var count = chunk.Count; + for (var index = 0; index < 6; index++) + { + basePtrs[index] = chunk.GetUnsafePtr() + offsets[index]; + } + + for (var entityIndex = 0; entityIndex < count; entityIndex++) + { + var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex)); + var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex)); + var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex)); + var pComp3 = (T3*)(basePtrs[3] + (sizeof(T3) * entityIndex)); + var pComp4 = (T4*)(basePtrs[4] + (sizeof(T4) * entityIndex)); + var pComp5 = (T5*)(basePtrs[5] + (sizeof(T5) * entityIndex)); + + action(ref *pComp0,ref *pComp1,ref *pComp2,ref *pComp3,ref *pComp4,ref *pComp5); + } + } + } + } + + public readonly void ForEach(ForEach action) + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + where T4 : unmanaged, IComponent + where T5 : unmanaged, IComponent + where T6 : unmanaged, IComponent + { + var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success); + + var compTypeIDs = stackalloc int[] { ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value }; + var offsets = stackalloc int[7]; + var basePtrs = stackalloc byte*[7]; + + foreach (var archetypeID in _matchingArchetypes) + { + ref var archetype = ref world.GetArchetypeReference(archetypeID); + var hasAllComponents = true; + for (var index = 0; index < 7; index++) + { + offsets[index] = archetype.GetOffset(compTypeIDs[index]); + if (offsets[index] == -1) + { + hasAllComponents = false; + break; + } + } + + if (!hasAllComponents) + { + continue; + } + + for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++) + { + ref var chunk = ref archetype.GetChunkReference(chunkIndex); + var count = chunk.Count; + for (var index = 0; index < 7; index++) + { + basePtrs[index] = chunk.GetUnsafePtr() + offsets[index]; + } + + for (var entityIndex = 0; entityIndex < count; entityIndex++) + { + var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex)); + var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex)); + var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex)); + var pComp3 = (T3*)(basePtrs[3] + (sizeof(T3) * entityIndex)); + var pComp4 = (T4*)(basePtrs[4] + (sizeof(T4) * entityIndex)); + var pComp5 = (T5*)(basePtrs[5] + (sizeof(T5) * entityIndex)); + var pComp6 = (T6*)(basePtrs[6] + (sizeof(T6) * entityIndex)); + + action(ref *pComp0,ref *pComp1,ref *pComp2,ref *pComp3,ref *pComp4,ref *pComp5,ref *pComp6); + } + } + } + } + + public readonly void ForEach(ForEach action) + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + where T4 : unmanaged, IComponent + where T5 : unmanaged, IComponent + where T6 : unmanaged, IComponent + where T7 : unmanaged, IComponent + { + var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success); + + var compTypeIDs = stackalloc int[] { ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value }; + var offsets = stackalloc int[8]; + var basePtrs = stackalloc byte*[8]; + + foreach (var archetypeID in _matchingArchetypes) + { + ref var archetype = ref world.GetArchetypeReference(archetypeID); + var hasAllComponents = true; + for (var index = 0; index < 8; index++) + { + offsets[index] = archetype.GetOffset(compTypeIDs[index]); + if (offsets[index] == -1) + { + hasAllComponents = false; + break; + } + } + + if (!hasAllComponents) + { + continue; + } + + for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++) + { + ref var chunk = ref archetype.GetChunkReference(chunkIndex); + var count = chunk.Count; + for (var index = 0; index < 8; index++) + { + basePtrs[index] = chunk.GetUnsafePtr() + offsets[index]; + } + + for (var entityIndex = 0; entityIndex < count; entityIndex++) + { + var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex)); + var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex)); + var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex)); + var pComp3 = (T3*)(basePtrs[3] + (sizeof(T3) * entityIndex)); + var pComp4 = (T4*)(basePtrs[4] + (sizeof(T4) * entityIndex)); + var pComp5 = (T5*)(basePtrs[5] + (sizeof(T5) * entityIndex)); + var pComp6 = (T6*)(basePtrs[6] + (sizeof(T6) * entityIndex)); + var pComp7 = (T7*)(basePtrs[7] + (sizeof(T7) * entityIndex)); + + action(ref *pComp0,ref *pComp1,ref *pComp2,ref *pComp3,ref *pComp4,ref *pComp5,ref *pComp6,ref *pComp7); + } + } + } + } + + + public readonly void ForEach(ForEachWithEntity action) + where T0 : unmanaged, IComponent + { + var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success); + + var compTypeIDs = stackalloc int[] { ComponentTypeID.value }; + var offsets = stackalloc int[1]; + var basePtrs = stackalloc byte*[1]; + + foreach (var archetypeID in _matchingArchetypes) + { + ref var archetype = ref world.GetArchetypeReference(archetypeID); + var hasAllComponents = true; + for (var index = 0; index < 1; index++) + { + offsets[index] = archetype.GetOffset(compTypeIDs[index]); + if (offsets[index] == -1) + { + hasAllComponents = false; + break; + } + } + + if (!hasAllComponents) + { + continue; + } + + for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++) + { + ref var chunk = ref archetype.GetChunkReference(chunkIndex); + var count = chunk.Count; + for (var index = 0; index < 1; index++) + { + basePtrs[index] = chunk.GetUnsafePtr() + offsets[index]; + } + + for (var entityIndex = 0; entityIndex < count; entityIndex++) + { + var pEntity = (Entity*)(chunk.GetUnsafePtr() + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex)); + var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex)); + + action(*pEntity, ref *pComp0); + } + } + } + } + + public readonly void ForEach(ForEachWithEntity action) + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + { + var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success); + + var compTypeIDs = stackalloc int[] { ComponentTypeID.value, ComponentTypeID.value }; + var offsets = stackalloc int[2]; + var basePtrs = stackalloc byte*[2]; + + foreach (var archetypeID in _matchingArchetypes) + { + ref var archetype = ref world.GetArchetypeReference(archetypeID); + var hasAllComponents = true; + for (var index = 0; index < 2; index++) + { + offsets[index] = archetype.GetOffset(compTypeIDs[index]); + if (offsets[index] == -1) + { + hasAllComponents = false; + break; + } + } + + if (!hasAllComponents) + { + continue; + } + + for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++) + { + ref var chunk = ref archetype.GetChunkReference(chunkIndex); + var count = chunk.Count; + for (var index = 0; index < 2; index++) + { + basePtrs[index] = chunk.GetUnsafePtr() + offsets[index]; + } + + for (var entityIndex = 0; entityIndex < count; entityIndex++) + { + var pEntity = (Entity*)(chunk.GetUnsafePtr() + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex)); + var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex)); + var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex)); + + action(*pEntity, ref *pComp0,ref *pComp1); + } + } + } + } + + public readonly void ForEach(ForEachWithEntity action) + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + { + var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success); + + var compTypeIDs = stackalloc int[] { ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value }; + var offsets = stackalloc int[3]; + var basePtrs = stackalloc byte*[3]; + + foreach (var archetypeID in _matchingArchetypes) + { + ref var archetype = ref world.GetArchetypeReference(archetypeID); + var hasAllComponents = true; + for (var index = 0; index < 3; index++) + { + offsets[index] = archetype.GetOffset(compTypeIDs[index]); + if (offsets[index] == -1) + { + hasAllComponents = false; + break; + } + } + + if (!hasAllComponents) + { + continue; + } + + for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++) + { + ref var chunk = ref archetype.GetChunkReference(chunkIndex); + var count = chunk.Count; + for (var index = 0; index < 3; index++) + { + basePtrs[index] = chunk.GetUnsafePtr() + offsets[index]; + } + + for (var entityIndex = 0; entityIndex < count; entityIndex++) + { + var pEntity = (Entity*)(chunk.GetUnsafePtr() + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex)); + var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex)); + var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex)); + var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex)); + + action(*pEntity, ref *pComp0,ref *pComp1,ref *pComp2); + } + } + } + } + + public readonly void ForEach(ForEachWithEntity action) + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + { + var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success); + + var compTypeIDs = stackalloc int[] { ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value }; + var offsets = stackalloc int[4]; + var basePtrs = stackalloc byte*[4]; + + foreach (var archetypeID in _matchingArchetypes) + { + ref var archetype = ref world.GetArchetypeReference(archetypeID); + var hasAllComponents = true; + for (var index = 0; index < 4; index++) + { + offsets[index] = archetype.GetOffset(compTypeIDs[index]); + if (offsets[index] == -1) + { + hasAllComponents = false; + break; + } + } + + if (!hasAllComponents) + { + continue; + } + + for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++) + { + ref var chunk = ref archetype.GetChunkReference(chunkIndex); + var count = chunk.Count; + for (var index = 0; index < 4; index++) + { + basePtrs[index] = chunk.GetUnsafePtr() + offsets[index]; + } + + for (var entityIndex = 0; entityIndex < count; entityIndex++) + { + var pEntity = (Entity*)(chunk.GetUnsafePtr() + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex)); + var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex)); + var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex)); + var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex)); + var pComp3 = (T3*)(basePtrs[3] + (sizeof(T3) * entityIndex)); + + action(*pEntity, ref *pComp0,ref *pComp1,ref *pComp2,ref *pComp3); + } + } + } + } + + public readonly void ForEach(ForEachWithEntity action) + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + where T4 : unmanaged, IComponent + { + var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success); + + var compTypeIDs = stackalloc int[] { ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value }; + var offsets = stackalloc int[5]; + var basePtrs = stackalloc byte*[5]; + + foreach (var archetypeID in _matchingArchetypes) + { + ref var archetype = ref world.GetArchetypeReference(archetypeID); + var hasAllComponents = true; + for (var index = 0; index < 5; index++) + { + offsets[index] = archetype.GetOffset(compTypeIDs[index]); + if (offsets[index] == -1) + { + hasAllComponents = false; + break; + } + } + + if (!hasAllComponents) + { + continue; + } + + for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++) + { + ref var chunk = ref archetype.GetChunkReference(chunkIndex); + var count = chunk.Count; + for (var index = 0; index < 5; index++) + { + basePtrs[index] = chunk.GetUnsafePtr() + offsets[index]; + } + + for (var entityIndex = 0; entityIndex < count; entityIndex++) + { + var pEntity = (Entity*)(chunk.GetUnsafePtr() + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex)); + var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex)); + var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex)); + var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex)); + var pComp3 = (T3*)(basePtrs[3] + (sizeof(T3) * entityIndex)); + var pComp4 = (T4*)(basePtrs[4] + (sizeof(T4) * entityIndex)); + + action(*pEntity, ref *pComp0,ref *pComp1,ref *pComp2,ref *pComp3,ref *pComp4); + } + } + } + } + + public readonly void ForEach(ForEachWithEntity action) + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + where T4 : unmanaged, IComponent + where T5 : unmanaged, IComponent + { + var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success); + + var compTypeIDs = stackalloc int[] { ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value }; + var offsets = stackalloc int[6]; + var basePtrs = stackalloc byte*[6]; + + foreach (var archetypeID in _matchingArchetypes) + { + ref var archetype = ref world.GetArchetypeReference(archetypeID); + var hasAllComponents = true; + for (var index = 0; index < 6; index++) + { + offsets[index] = archetype.GetOffset(compTypeIDs[index]); + if (offsets[index] == -1) + { + hasAllComponents = false; + break; + } + } + + if (!hasAllComponents) + { + continue; + } + + for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++) + { + ref var chunk = ref archetype.GetChunkReference(chunkIndex); + var count = chunk.Count; + for (var index = 0; index < 6; index++) + { + basePtrs[index] = chunk.GetUnsafePtr() + offsets[index]; + } + + for (var entityIndex = 0; entityIndex < count; entityIndex++) + { + var pEntity = (Entity*)(chunk.GetUnsafePtr() + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex)); + var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex)); + var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex)); + var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex)); + var pComp3 = (T3*)(basePtrs[3] + (sizeof(T3) * entityIndex)); + var pComp4 = (T4*)(basePtrs[4] + (sizeof(T4) * entityIndex)); + var pComp5 = (T5*)(basePtrs[5] + (sizeof(T5) * entityIndex)); + + action(*pEntity, ref *pComp0,ref *pComp1,ref *pComp2,ref *pComp3,ref *pComp4,ref *pComp5); + } + } + } + } + + public readonly void ForEach(ForEachWithEntity action) + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + where T4 : unmanaged, IComponent + where T5 : unmanaged, IComponent + where T6 : unmanaged, IComponent + { + var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success); + + var compTypeIDs = stackalloc int[] { ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value }; + var offsets = stackalloc int[7]; + var basePtrs = stackalloc byte*[7]; + + foreach (var archetypeID in _matchingArchetypes) + { + ref var archetype = ref world.GetArchetypeReference(archetypeID); + var hasAllComponents = true; + for (var index = 0; index < 7; index++) + { + offsets[index] = archetype.GetOffset(compTypeIDs[index]); + if (offsets[index] == -1) + { + hasAllComponents = false; + break; + } + } + + if (!hasAllComponents) + { + continue; + } + + for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++) + { + ref var chunk = ref archetype.GetChunkReference(chunkIndex); + var count = chunk.Count; + for (var index = 0; index < 7; index++) + { + basePtrs[index] = chunk.GetUnsafePtr() + offsets[index]; + } + + for (var entityIndex = 0; entityIndex < count; entityIndex++) + { + var pEntity = (Entity*)(chunk.GetUnsafePtr() + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex)); + var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex)); + var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex)); + var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex)); + var pComp3 = (T3*)(basePtrs[3] + (sizeof(T3) * entityIndex)); + var pComp4 = (T4*)(basePtrs[4] + (sizeof(T4) * entityIndex)); + var pComp5 = (T5*)(basePtrs[5] + (sizeof(T5) * entityIndex)); + var pComp6 = (T6*)(basePtrs[6] + (sizeof(T6) * entityIndex)); + + action(*pEntity, ref *pComp0,ref *pComp1,ref *pComp2,ref *pComp3,ref *pComp4,ref *pComp5,ref *pComp6); + } + } + } + } + + public readonly void ForEach(ForEachWithEntity action) + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + where T4 : unmanaged, IComponent + where T5 : unmanaged, IComponent + where T6 : unmanaged, IComponent + where T7 : unmanaged, IComponent + { + var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success); + + var compTypeIDs = stackalloc int[] { ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value, ComponentTypeID.value }; + var offsets = stackalloc int[8]; + var basePtrs = stackalloc byte*[8]; + + foreach (var archetypeID in _matchingArchetypes) + { + ref var archetype = ref world.GetArchetypeReference(archetypeID); + var hasAllComponents = true; + for (var index = 0; index < 8; index++) + { + offsets[index] = archetype.GetOffset(compTypeIDs[index]); + if (offsets[index] == -1) + { + hasAllComponents = false; + break; + } + } + + if (!hasAllComponents) + { + continue; + } + + for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++) + { + ref var chunk = ref archetype.GetChunkReference(chunkIndex); + var count = chunk.Count; + for (var index = 0; index < 8; index++) + { + basePtrs[index] = chunk.GetUnsafePtr() + offsets[index]; + } + + for (var entityIndex = 0; entityIndex < count; entityIndex++) + { + var pEntity = (Entity*)(chunk.GetUnsafePtr() + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex)); + var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex)); + var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex)); + var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex)); + var pComp3 = (T3*)(basePtrs[3] + (sizeof(T3) * entityIndex)); + var pComp4 = (T4*)(basePtrs[4] + (sizeof(T4) * entityIndex)); + var pComp5 = (T5*)(basePtrs[5] + (sizeof(T5) * entityIndex)); + var pComp6 = (T6*)(basePtrs[6] + (sizeof(T6) * entityIndex)); + var pComp7 = (T7*)(basePtrs[7] + (sizeof(T7) * entityIndex)); + + action(*pEntity, ref *pComp0,ref *pComp1,ref *pComp2,ref *pComp3,ref *pComp4,ref *pComp5,ref *pComp6,ref *pComp7); + } + } + } + } + +} \ No newline at end of file diff --git a/Ghost.Entities/Templates/EntityQuery.ForEach.tt b/Ghost.Entities/Templates/EntityQuery.ForEach.tt new file mode 100644 index 0000000..5294b35 --- /dev/null +++ b/Ghost.Entities/Templates/EntityQuery.ForEach.tt @@ -0,0 +1,127 @@ +<#@ template language="C#" #> +<#@ output extension="gen.cs" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ include file="Helpers.ttinclude" #> +using Ghost.Core; + +namespace Ghost.Entities; + +public unsafe partial struct EntityQuery +{ +<# for (var i = 1; i <= Amount; i++) +{ + var generics = AppendGenerics(i); + var compGenerics = AppendGenericRefParameters(i); + var restrictions = AppendGenericRestrictionsMultiline(i, "unmanaged, IComponent", 2); +#> + public readonly void ForEach<<#= generics #>>(ForEach<<#= generics #>> action) +<#= restrictions #> + { + var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success); + + var compTypeIDs = stackalloc int[] { <#= AppendGenerics(i, "ComponentTypeID.value") #> }; + var offsets = stackalloc int[<#= i #>]; + var basePtrs = stackalloc byte*[<#= i #>]; + + foreach (var archetypeID in _matchingArchetypes) + { + ref var archetype = ref world.GetArchetypeReference(archetypeID); + var hasAllComponents = true; + for (var index = 0; index < <#= i #>; index++) + { + offsets[index] = archetype.GetOffset(compTypeIDs[index]); + if (offsets[index] == -1) + { + hasAllComponents = false; + break; + } + } + + if (!hasAllComponents) + { + continue; + } + + for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++) + { + ref var chunk = ref archetype.GetChunkReference(chunkIndex); + var count = chunk.Count; + for (var index = 0; index < <#= i #>; index++) + { + basePtrs[index] = chunk.GetUnsafePtr() + offsets[index]; + } + + for (var entityIndex = 0; entityIndex < count; entityIndex++) + { +<# for (var localIndex = 0; localIndex < i; localIndex++) { #> + var pComp<#= localIndex #> = (T<#= localIndex #>*)(basePtrs[<#= localIndex #>] + (sizeof(T<#= localIndex #>) * entityIndex)); +<# } #> + + action(<#= AppendRefParameters(i, "*pComp{0}") #>); + } + } + } + } + +<# } #> + +<# for (var i = 1; i <= Amount; i++) +{ + var generics = AppendGenerics(i); + var compGenerics = AppendGenericRefParameters(i); + var restrictions = AppendGenericRestrictionsMultiline(i, "unmanaged, IComponent", 2); +#> + public readonly void ForEach<<#= generics #>>(ForEachWithEntity<<#= generics #>> action) +<#= restrictions #> + { + var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success); + + var compTypeIDs = stackalloc int[] { <#= AppendGenerics(i, "ComponentTypeID.value") #> }; + var offsets = stackalloc int[<#= i #>]; + var basePtrs = stackalloc byte*[<#= i #>]; + + foreach (var archetypeID in _matchingArchetypes) + { + ref var archetype = ref world.GetArchetypeReference(archetypeID); + var hasAllComponents = true; + for (var index = 0; index < <#= i #>; index++) + { + offsets[index] = archetype.GetOffset(compTypeIDs[index]); + if (offsets[index] == -1) + { + hasAllComponents = false; + break; + } + } + + if (!hasAllComponents) + { + continue; + } + + for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++) + { + ref var chunk = ref archetype.GetChunkReference(chunkIndex); + var count = chunk.Count; + for (var index = 0; index < <#= i #>; index++) + { + basePtrs[index] = chunk.GetUnsafePtr() + offsets[index]; + } + + for (var entityIndex = 0; entityIndex < count; entityIndex++) + { + var pEntity = (Entity*)(chunk.GetUnsafePtr() + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex)); +<# for (var localIndex = 0; localIndex < i; localIndex++) { #> + var pComp<#= localIndex #> = (T<#= localIndex #>*)(basePtrs[<#= localIndex #>] + (sizeof(T<#= localIndex #>) * entityIndex)); +<# } #> + + action(*pEntity, <#= AppendRefParameters(i, "*pComp{0}") #>); + } + } + } + } + +<# } #> +} \ No newline at end of file diff --git a/Ghost.Entities/Templates/ForEach.gen.cs b/Ghost.Entities/Templates/ForEach.gen.cs new file mode 100644 index 0000000..6354032 --- /dev/null +++ b/Ghost.Entities/Templates/ForEach.gen.cs @@ -0,0 +1,21 @@ + + +namespace Ghost.Entities; + +public delegate void ForEach(ref T0 t0Component); +public delegate void ForEach(ref T0 t0Component,ref T1 t1Component); +public delegate void ForEach(ref T0 t0Component,ref T1 t1Component,ref T2 t2Component); +public delegate void ForEach(ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component); +public delegate void ForEach(ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component,ref T4 t4Component); +public delegate void ForEach(ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component,ref T4 t4Component,ref T5 t5Component); +public delegate void ForEach(ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component,ref T4 t4Component,ref T5 t5Component,ref T6 t6Component); +public delegate void ForEach(ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component,ref T4 t4Component,ref T5 t5Component,ref T6 t6Component,ref T7 t7Component); + +public delegate void ForEachWithEntity(Entity entity, ref T0 t0Component); +public delegate void ForEachWithEntity(Entity entity, ref T0 t0Component,ref T1 t1Component); +public delegate void ForEachWithEntity(Entity entity, ref T0 t0Component,ref T1 t1Component,ref T2 t2Component); +public delegate void ForEachWithEntity(Entity entity, ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component); +public delegate void ForEachWithEntity(Entity entity, ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component,ref T4 t4Component); +public delegate void ForEachWithEntity(Entity entity, ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component,ref T4 t4Component,ref T5 t5Component); +public delegate void ForEachWithEntity(Entity entity, ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component,ref T4 t4Component,ref T5 t5Component,ref T6 t6Component); +public delegate void ForEachWithEntity(Entity entity, ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component,ref T4 t4Component,ref T5 t5Component,ref T6 t6Component,ref T7 t7Component); diff --git a/Ghost.Entities/Templates/ForEach.tt b/Ghost.Entities/Templates/ForEach.tt new file mode 100644 index 0000000..7513fd2 --- /dev/null +++ b/Ghost.Entities/Templates/ForEach.tt @@ -0,0 +1,24 @@ +<#@ template language="C#" #> +<#@ output extension="gen.cs" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ include file="Helpers.ttinclude" #> + +namespace Ghost.Entities; + +<# for (var i = 1; i <= Amount; i++) +{ + var generics = AppendGenerics(i); + var compGenerics = AppendGenericRefParameters(i); +#> +public delegate void ForEach<<#= generics #>>(<#= compGenerics #>); +<# } #> + +<# for (var i = 1; i <= Amount; i++) +{ + var generics = AppendGenerics(i); + var compGenerics = AppendGenericRefParameters(i); +#> +public delegate void ForEachWithEntity<<#= generics #>>(Entity entity, <#= compGenerics #>); +<# } #> \ No newline at end of file diff --git a/Ghost.Entities/Templates/Helpers.ttinclude b/Ghost.Entities/Templates/Helpers.ttinclude new file mode 100644 index 0000000..1de7df1 --- /dev/null +++ b/Ghost.Entities/Templates/Helpers.ttinclude @@ -0,0 +1,149 @@ +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#+ + + public int Amount = 8; + public int ExtensionAmount = 3; + + public string Indent(StringBuilder sb, int spaces) + { + var indent = new string(' ', spaces); + return sb.ToString().Replace("\n", "\n" + indent); + } + + string AppendGenerics(int amount, string template) + { + var sb = new StringBuilder(); + for (var i = 0; i < amount; i++) + { + if (i > 0) sb.Append(", "); + sb.Append(string.Format(template, i)); + } + return sb.ToString(); + } + + string AppendGenerics(int amount) + { + return AppendGenerics(amount, "T{0}"); + } + + public StringBuilder AppendGenericRefParameters(int amount) + { + var sb = new StringBuilder(); + for (var localIndex = 0; localIndex < amount; localIndex++) + { + sb.Append($"ref T{localIndex} t{localIndex}Component,"); + } + + sb.Length--; + return sb; + } + + public StringBuilder AppendRefParameters(int amount, string template) + { + var sb = new StringBuilder(); + for (var localIndex = 0; localIndex < amount; localIndex++) + { + sb.Append($"ref {string.Format(template, localIndex)},"); + } + + sb.Length--; + return sb; + } + + public StringBuilder AppendGenericRestrictions(int amount, string Ttemplate, string template) + { + var sb = new StringBuilder(); + for (var localIndex = 0; localIndex < amount; localIndex++) + { + sb.Append($"where {Ttemplate}{localIndex} : {template}"); + if (localIndex < amount - 1) + { + sb.Append(' '); + } + } + return sb; + } + + public StringBuilder AppendGenericRestrictions(int amount, string template) + { + return AppendGenericRestrictions(amount, "T", template); + } + + public StringBuilder AppendGenericRestrictionsMultiline(int amount, string Ttemplate, string template, int indentation) + { + var sb = new StringBuilder(); + var spaces = new string(' ', indentation * 4); + + for (var localIndex = 0; localIndex < amount; localIndex++) + { + sb.Append($"{spaces}where {Ttemplate}{localIndex} : {template}"); + if (localIndex < amount - 1) + { + sb.AppendLine(); + } + } + return sb; + } + + public StringBuilder AppendGenericRestrictionsMultiline(int amount, string template, int indentation) + { + return AppendGenericRestrictionsMultiline(amount, "T", template, indentation); + } + + public StringBuilder TryGetComponentPools(int amount) + { + var sb = new StringBuilder(); + for (var localIndex = 0; localIndex < amount; localIndex++) + { + sb.Append($"_componentStorage.TryGetPool(out var pool{localIndex})"); + if (localIndex < amount - 1) + { + sb.Append(" && "); + } + } + return sb; + } + + public StringBuilder HasEntity(int amount) + { + var sb = new StringBuilder(); + for (var localIndex = 1; localIndex < amount; localIndex++) + { + sb.Append($"pool{localIndex}.Has(entity)"); + if (localIndex < amount - 1) + { + sb.Append(" && "); + } + } + return sb; + } + + public StringBuilder GetComponent(int amount) + { + var sb = new StringBuilder(); + for (var localIndex = 0; localIndex < amount; localIndex++) + { + sb.Append($"pool{localIndex}.GetRef(entity)"); + if (localIndex < amount - 1) + { + sb.Append(", "); + } + } + return sb; + } + + public StringBuilder GetComponentRef(int amount) + { + var sb = new StringBuilder(); + for (var localIndex = 0; localIndex < amount; localIndex++) + { + sb.Append($"ref pool{localIndex}.GetRef(entity)"); + if (localIndex < amount - 1) + { + sb.Append(", "); + } + } + return sb; + } +#> \ No newline at end of file diff --git a/Ghost.Entities/World.cs b/Ghost.Entities/World.cs index 822aaa5..a3ffa16 100644 --- a/Ghost.Entities/World.cs +++ b/Ghost.Entities/World.cs @@ -196,6 +196,16 @@ public partial class World : IIdentifierType, IDisposable, IEquatable return; } + foreach (var archetype in _archetypes) + { + archetype.Dispose(); + } + + foreach (var query in _entityQueries) + { + query.Dispose(); + } + _entityManager.Dispose(); _entityCommandBuffer.Dispose(); diff --git a/Ghost.SparseEntities/Template/Helpers.ttinclude b/Ghost.SparseEntities/Template/Helpers.ttinclude index 0759a28..568a02e 100644 --- a/Ghost.SparseEntities/Template/Helpers.ttinclude +++ b/Ghost.SparseEntities/Template/Helpers.ttinclude @@ -1,4 +1,4 @@ -<#@ import namespace="System.Text" #> +<#@ import namespace="System.Text" #> <#@ import namespace="System.Collections.Generic" #> <#+ @@ -17,14 +17,14 @@ for (var i = 0; i < amount; i++) { if (i > 0) sb.Append(", "); - sb.Append($"{template}{i}"); + sb.Append(string.Format(template, i)); } return sb.ToString(); } string AppendGenerics(int amount) { - return AppendGenerics(amount, "T"); + return AppendGenerics(amount, "T{0}"); } public StringBuilder AppendGenericRefParameters(int amount)