From 224b2b2dd57ce69b1503d106a2581e90cd810223 Mon Sep 17 00:00:00 2001 From: Misaki Date: Fri, 5 Dec 2025 00:29:12 +0900 Subject: [PATCH] Refactor ECS framework and improve performance Refactored `ArcEntityTest` to use updated `Transform` and `Mesh` components, improving query logic with `GetChunkIterator` and introducing `ForEach` methods for better modularity. Enhanced `Chunk` and `Archetype` structs with `readonly` properties and memory optimizations. Fixed bugs in memory copy logic and entity relocation. Improved `EntityManager` with proper disposal handling, added a destructor, and fixed pointer usage in `AddComponent` and `SetComponentData`. Refactored `EntityQuery` to use `ChunkIterator` and `ChunkView` for better abstraction. Simplified `EntityQueryMask` logic for performance. Introduced templated `ForEach` methods and `ForEachWithEntity` methods, dynamically generated using T4 templates for scalability. Added disposal logic for archetypes and queries in `World`. Updated `Program.cs` to include memory debugging setup. Integrated T4 templates for dynamic code generation and added helper functions for template generation. Updated project file to include templates and generated outputs. General improvements include enforcing immutability, optimizing memory management, and adding debugging/logging for better traceability. --- Ghost.Entities.Test/ArcEntityTest.cs | 29 +- Ghost.Entities.Test/Program.cs | 5 +- Ghost.Entities/Archetype.cs | 37 +- Ghost.Entities/Component.cs | 2 +- Ghost.Entities/EntityManager.cs | 30 +- Ghost.Entities/EntityQuery.cs | 2 +- Ghost.Entities/Ghost.Entities.csproj | 45 + Ghost.Entities/Query.cs | 191 ++-- .../Templates/EntityQuery.ForEach.gen.cs | 881 ++++++++++++++++++ .../Templates/EntityQuery.ForEach.tt | 127 +++ Ghost.Entities/Templates/ForEach.gen.cs | 21 + Ghost.Entities/Templates/ForEach.tt | 24 + Ghost.Entities/Templates/Helpers.ttinclude | 149 +++ Ghost.Entities/World.cs | 10 + .../Template/Helpers.ttinclude | 6 +- 15 files changed, 1424 insertions(+), 135 deletions(-) create mode 100644 Ghost.Entities/Templates/EntityQuery.ForEach.gen.cs create mode 100644 Ghost.Entities/Templates/EntityQuery.ForEach.tt create mode 100644 Ghost.Entities/Templates/ForEach.gen.cs create mode 100644 Ghost.Entities/Templates/ForEach.tt create mode 100644 Ghost.Entities/Templates/Helpers.ttinclude 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)