diff --git a/Ghost.Core/Ghost.Core.csproj b/Ghost.Core/Ghost.Core.csproj index 68c0063..ad3eaf5 100644 --- a/Ghost.Core/Ghost.Core.csproj +++ b/Ghost.Core/Ghost.Core.csproj @@ -21,6 +21,7 @@ + diff --git a/Ghost.Entities.Test/ArcEntityTest.cs b/Ghost.Entities.Test/ArcEntityTest.cs index a86197d..6107043 100644 --- a/Ghost.Entities.Test/ArcEntityTest.cs +++ b/Ghost.Entities.Test/ArcEntityTest.cs @@ -1,15 +1,28 @@ using Ghost.Test.Core; +using Misaki.HighPerformance.Jobs; +using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.Mathematics; +using System; namespace Ghost.Entities.Test; +internal struct TestEntityQueryJob : IJobEntityParallel +{ + public readonly void Execute(Entity entity, ref Transform transform) + { + transform.position += new float3(10, 10, 10); + } +} + public partial class ArcEntityTest : ITest { private World _world = null!; + private JobScheduler _jobScheduler = null!; public void Setup() { _world = World.Create(); + _jobScheduler = new JobScheduler(4); } public void Run() @@ -20,23 +33,27 @@ public partial class ArcEntityTest : ITest var queryID = new QueryBuilder().WithAll().Build(_world); ref var query = ref _world.GetEntityQueryReference(queryID); - query.ForEach((ref t) => + var testJob = new TestEntityQueryJob(); + var handle = query.ScheduleEntityParallel(_jobScheduler, testJob, Allocator.Temp, 64, JobHandle.Invalid); + _jobScheduler.WaitComplete(handle); + + query.ForEach((e, ref t) => { - t.position = new float3(1, 2, 3); + Console.WriteLine($"Entity {e} Has Position: {t.position}"); }); - foreach (var chunk in query.GetChunkIterator()) - { - var transforms = chunk.GetComponentData(); - var entities = chunk.GetEntities(); - var bits = chunk.GetEnableBits(); - - var it = bits.GetIterator(); - while (it.Next(out var index) && index < chunk.Count) - { - Console.WriteLine($"Entity {entities[index]} Updated Position: {transforms[index].position}"); - } - } + //foreach (var chunk in query.GetChunkIterator()) + //{ + // var transforms = chunk.GetComponentData(); + // var entities = chunk.GetEntities(); + // var bits = chunk.GetEnableBits(); + + // var it = bits.GetIterator(); + // while (it.Next(out var index) && index < chunk.Count) + // { + // Console.WriteLine($"Entity {entities[index]} Updated Position: {transforms[index].position}"); + // } + //} } public void Cleanup() diff --git a/Ghost.Entities/Ghost.Entities.csproj b/Ghost.Entities/Ghost.Entities.csproj index ca01b7d..a4429fe 100644 --- a/Ghost.Entities/Ghost.Entities.csproj +++ b/Ghost.Entities/Ghost.Entities.csproj @@ -52,6 +52,10 @@ ForEach.gen.cs TextTemplatingFileGenerator + + TextTemplatingFileGenerator + EntityQuery.JobEntityParallel.gen.cs + TextTemplatingFileGenerator QueryBuilder.With.gen.cs @@ -68,6 +72,11 @@ True EntityQuery.ForEach.tt + + True + True + EntityQuery.JobEntityParallel.tt + True True diff --git a/Ghost.Entities/Query.cs b/Ghost.Entities/Query.cs index e053778..d699ce8 100644 --- a/Ghost.Entities/Query.cs +++ b/Ghost.Entities/Query.cs @@ -231,37 +231,37 @@ public unsafe partial struct EntityQuery : IIdentifierType, IDisposable } } - private readonly Identifier _worldID; - private UnsafeList> _matchingArchetypes; - internal EntityQueryMask _mask; - internal EntityQuery(Identifier worldID, EntityQueryMask mask) + private UnsafeList> _matchingArchetypes; + private readonly Identifier _id; + private readonly Identifier _worldID; + + internal EntityQuery(Identifier id, Identifier worldID, EntityQueryMask mask) { + _id = id; _worldID = worldID; _mask = mask; _matchingArchetypes = new UnsafeList>(8, Allocator.Persistent); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool IsEntityValid(byte* chunkBase, int entityIndex, ref readonly Archetype archetype, ref readonly EntityQueryMask mask) { // 1. Check "Require Enabled" (WithAll) - // We iterate over the bits set in 'requireEnabled' var it = mask.requireEnabled.GetIterator(); - while (it.Next(out var id)) { // Get the EnableBitmask for this component in this chunk - var layout = archetype.GetLayout(id).Value; - if (layout.enableBitsOffset == -1) - { + var layoutResult = archetype.GetLayout(id); + if (layoutResult.Status != ResultStatus.Success // Not enableable, always true + || layoutResult.Value.enableBitsOffset == -1) + { continue; } // Check bit - if (!CheckBit(chunkBase + layout.enableBitsOffset, entityIndex)) + if (!CheckBit(chunkBase + layoutResult.Value.enableBitsOffset, entityIndex)) { return false; } @@ -271,17 +271,18 @@ public unsafe partial struct EntityQuery : IIdentifierType, IDisposable it = mask.requireDisabled.GetIterator(); while (it.Next(out var id)) { - var layout = archetype.GetLayout(id).Value; + var layoutResult = archetype.GetLayout(id); + if (layoutResult.Status != ResultStatus.Success) + { + continue; + } // If component is not enableable, it is technically "Always Enabled", // so it cannot satisfy "WithDisabled". - if (layout.enableBitsOffset == -1) - { - return false; - } // Check bit (Must be 0) - if (CheckBit(chunkBase + layout.enableBitsOffset, entityIndex)) + if (layoutResult.Value.enableBitsOffset == -1 + || CheckBit(chunkBase + layoutResult.Value.enableBitsOffset, entityIndex)) { return false; } @@ -294,19 +295,15 @@ public unsafe partial struct EntityQuery : IIdentifierType, IDisposable var layoutResult = archetype.GetLayout(id); if (layoutResult.Status != ResultStatus.Success) { - // Component is absent, so it is not enabled. continue; } // If component is not enableable, it is technically "Always Enabled", // so it cannot satisfy "Reject if Enabled". - if (layoutResult.Value.enableBitsOffset == -1) - { - return false; - } // Check bit (Must be 0) - if (CheckBit(chunkBase + layoutResult.Value.enableBitsOffset, entityIndex)) + if (layoutResult.Value.enableBitsOffset == -1 + || CheckBit(chunkBase + layoutResult.Value.enableBitsOffset, entityIndex)) { return false; } @@ -316,7 +313,7 @@ public unsafe partial struct EntityQuery : IIdentifierType, IDisposable } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool CheckBit(byte* maskBase, int index) + internal static bool CheckBit(byte* maskBase, int index) { var byteIndex = index >> Chunk.BIT_SHIFT; var bitIndex = index & Chunk.BIT_ALIGNMENT_MINUS_ONE; diff --git a/Ghost.Entities/Templates/EntityQuery.ForEach.gen.cs b/Ghost.Entities/Templates/EntityQuery.ForEach.gen.cs index 9b3af5b..74d85ca 100644 --- a/Ghost.Entities/Templates/EntityQuery.ForEach.gen.cs +++ b/Ghost.Entities/Templates/EntityQuery.ForEach.gen.cs @@ -111,7 +111,7 @@ public unsafe partial struct EntityQuery var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex)); var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex)); - action(ref *pComp0,ref *pComp1); + action(ref *pComp0, ref *pComp1); } } } @@ -170,7 +170,7 @@ public unsafe partial struct EntityQuery var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex)); var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex)); - action(ref *pComp0,ref *pComp1,ref *pComp2); + action(ref *pComp0, ref *pComp1, ref *pComp2); } } } @@ -231,7 +231,7 @@ public unsafe partial struct EntityQuery 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); + action(ref *pComp0, ref *pComp1, ref *pComp2, ref *pComp3); } } } @@ -294,7 +294,7 @@ public unsafe partial struct EntityQuery 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); + action(ref *pComp0, ref *pComp1, ref *pComp2, ref *pComp3, ref *pComp4); } } } @@ -359,7 +359,7 @@ public unsafe partial struct EntityQuery 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); + action(ref *pComp0, ref *pComp1, ref *pComp2, ref *pComp3, ref *pComp4, ref *pComp5); } } } @@ -426,7 +426,7 @@ public unsafe partial struct EntityQuery 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); + action(ref *pComp0, ref *pComp1, ref *pComp2, ref *pComp3, ref *pComp4, ref *pComp5, ref *pComp6); } } } @@ -495,7 +495,7 @@ public unsafe partial struct EntityQuery 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); + action(ref *pComp0, ref *pComp1, ref *pComp2, ref *pComp3, ref *pComp4, ref *pComp5, ref *pComp6, ref *pComp7); } } } @@ -609,7 +609,7 @@ public unsafe partial struct EntityQuery var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex)); var pEntity = (Entity*)(pChunkData + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex)); - action(*pEntity, ref *pComp0,ref *pComp1); + action(*pEntity, ref *pComp0, ref *pComp1); } } } @@ -669,7 +669,7 @@ public unsafe partial struct EntityQuery var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex)); var pEntity = (Entity*)(pChunkData + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex)); - action(*pEntity, ref *pComp0,ref *pComp1,ref *pComp2); + action(*pEntity, ref *pComp0, ref *pComp1, ref *pComp2); } } } @@ -731,7 +731,7 @@ public unsafe partial struct EntityQuery var pComp3 = (T3*)(basePtrs[3] + (sizeof(T3) * entityIndex)); var pEntity = (Entity*)(pChunkData + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex)); - action(*pEntity, ref *pComp0,ref *pComp1,ref *pComp2,ref *pComp3); + action(*pEntity, ref *pComp0, ref *pComp1, ref *pComp2, ref *pComp3); } } } @@ -795,7 +795,7 @@ public unsafe partial struct EntityQuery var pComp4 = (T4*)(basePtrs[4] + (sizeof(T4) * entityIndex)); var pEntity = (Entity*)(pChunkData + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex)); - action(*pEntity, ref *pComp0,ref *pComp1,ref *pComp2,ref *pComp3,ref *pComp4); + action(*pEntity, ref *pComp0, ref *pComp1, ref *pComp2, ref *pComp3, ref *pComp4); } } } @@ -861,7 +861,7 @@ public unsafe partial struct EntityQuery var pComp5 = (T5*)(basePtrs[5] + (sizeof(T5) * entityIndex)); var pEntity = (Entity*)(pChunkData + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex)); - action(*pEntity, ref *pComp0,ref *pComp1,ref *pComp2,ref *pComp3,ref *pComp4,ref *pComp5); + action(*pEntity, ref *pComp0, ref *pComp1, ref *pComp2, ref *pComp3, ref *pComp4, ref *pComp5); } } } @@ -929,7 +929,7 @@ public unsafe partial struct EntityQuery var pComp6 = (T6*)(basePtrs[6] + (sizeof(T6) * entityIndex)); var pEntity = (Entity*)(pChunkData + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex)); - action(*pEntity, ref *pComp0,ref *pComp1,ref *pComp2,ref *pComp3,ref *pComp4,ref *pComp5,ref *pComp6); + action(*pEntity, ref *pComp0, ref *pComp1, ref *pComp2, ref *pComp3, ref *pComp4, ref *pComp5, ref *pComp6); } } } @@ -999,7 +999,7 @@ public unsafe partial struct EntityQuery var pComp7 = (T7*)(basePtrs[7] + (sizeof(T7) * entityIndex)); var pEntity = (Entity*)(pChunkData + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex)); - action(*pEntity, ref *pComp0,ref *pComp1,ref *pComp2,ref *pComp3,ref *pComp4,ref *pComp5,ref *pComp6,ref *pComp7); + action(*pEntity, ref *pComp0, ref *pComp1, ref *pComp2, ref *pComp3, ref *pComp4, ref *pComp5, ref *pComp6, ref *pComp7); } } } diff --git a/Ghost.Entities/Templates/EntityQuery.JobEntityParallel.gen.cs b/Ghost.Entities/Templates/EntityQuery.JobEntityParallel.gen.cs new file mode 100644 index 0000000..a1ea9a8 --- /dev/null +++ b/Ghost.Entities/Templates/EntityQuery.JobEntityParallel.gen.cs @@ -0,0 +1,2103 @@ + +using Ghost.Core; +using Misaki.HighPerformance.Jobs; +using Misaki.HighPerformance.LowLevel.Buffer; +using Misaki.HighPerformance.LowLevel.Collections; + +namespace Ghost.Entities; + +public interface IJobEntityParallel + where T0 : unmanaged, IComponent +{ + void Execute(Entity entity, ref T0 component0); +} + +internal unsafe struct JobEntityBatch : IJobParallelFor + where TJob : unmanaged, IJobEntityParallel + where T0 : unmanaged, IComponent +{ + public TJob userJob; + + public UnsafeList chunks; + public UnsafeList chunkCount; + public UnsafeList entityOffset; + + public UnsafeList offsets0; + public UnsafeList bitsOffsets0; + + public void Execute(int loopIndex, int threadIndex) + { + // 1. Get the specific pChunk for this thread + var pChunk = (byte*)chunks[loopIndex]; + var count = chunkCount[loopIndex]; + + var off0 = offsets0[loopIndex]; + var enableOff0 = bitsOffsets0[loopIndex]; + + var pEntity = (Entity*)(pChunk + entityOffset[loopIndex]); + var ptr0 = (T0*)(pChunk + off0); + + for (var i = 0; i < count; i++) + { + if (enableOff0 != -1 && !EntityQuery.CheckBit(pChunk + enableOff0, i)) + { + continue; + } + + userJob.Execute(pEntity[i], ref ptr0[i]); + } + } +} + +public interface IJobEntityParallel + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent +{ + void Execute(Entity entity, ref T0 component0, ref T1 component1); +} + +internal unsafe struct JobEntityBatch : IJobParallelFor + where TJob : unmanaged, IJobEntityParallel + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent +{ + public TJob userJob; + + public UnsafeList chunks; + public UnsafeList chunkCount; + public UnsafeList entityOffset; + + public UnsafeList offsets0; + public UnsafeList bitsOffsets0; + + public UnsafeList offsets1; + public UnsafeList bitsOffsets1; + + public void Execute(int loopIndex, int threadIndex) + { + // 1. Get the specific pChunk for this thread + var pChunk = (byte*)chunks[loopIndex]; + var count = chunkCount[loopIndex]; + + var off0 = offsets0[loopIndex]; + var enableOff0 = bitsOffsets0[loopIndex]; + + var off1 = offsets1[loopIndex]; + var enableOff1 = bitsOffsets1[loopIndex]; + + var pEntity = (Entity*)(pChunk + entityOffset[loopIndex]); + var ptr0 = (T0*)(pChunk + off0); + var ptr1 = (T1*)(pChunk + off1); + + for (var i = 0; i < count; i++) + { + if (enableOff0 != -1 && !EntityQuery.CheckBit(pChunk + enableOff0, i)) + { + continue; + } + + if (enableOff1 != -1 && !EntityQuery.CheckBit(pChunk + enableOff1, i)) + { + continue; + } + + userJob.Execute(pEntity[i], ref ptr0[i], ref ptr1[i]); + } + } +} + +public interface IJobEntityParallel + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent +{ + void Execute(Entity entity, ref T0 component0, ref T1 component1, ref T2 component2); +} + +internal unsafe struct JobEntityBatch : IJobParallelFor + where TJob : unmanaged, IJobEntityParallel + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent +{ + public TJob userJob; + + public UnsafeList chunks; + public UnsafeList chunkCount; + public UnsafeList entityOffset; + + public UnsafeList offsets0; + public UnsafeList bitsOffsets0; + + public UnsafeList offsets1; + public UnsafeList bitsOffsets1; + + public UnsafeList offsets2; + public UnsafeList bitsOffsets2; + + public void Execute(int loopIndex, int threadIndex) + { + // 1. Get the specific pChunk for this thread + var pChunk = (byte*)chunks[loopIndex]; + var count = chunkCount[loopIndex]; + + var off0 = offsets0[loopIndex]; + var enableOff0 = bitsOffsets0[loopIndex]; + + var off1 = offsets1[loopIndex]; + var enableOff1 = bitsOffsets1[loopIndex]; + + var off2 = offsets2[loopIndex]; + var enableOff2 = bitsOffsets2[loopIndex]; + + var pEntity = (Entity*)(pChunk + entityOffset[loopIndex]); + var ptr0 = (T0*)(pChunk + off0); + var ptr1 = (T1*)(pChunk + off1); + var ptr2 = (T2*)(pChunk + off2); + + for (var i = 0; i < count; i++) + { + if (enableOff0 != -1 && !EntityQuery.CheckBit(pChunk + enableOff0, i)) + { + continue; + } + + if (enableOff1 != -1 && !EntityQuery.CheckBit(pChunk + enableOff1, i)) + { + continue; + } + + if (enableOff2 != -1 && !EntityQuery.CheckBit(pChunk + enableOff2, i)) + { + continue; + } + + userJob.Execute(pEntity[i], ref ptr0[i], ref ptr1[i], ref ptr2[i]); + } + } +} + +public interface IJobEntityParallel + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent +{ + void Execute(Entity entity, ref T0 component0, ref T1 component1, ref T2 component2, ref T3 component3); +} + +internal unsafe struct JobEntityBatch : IJobParallelFor + where TJob : unmanaged, IJobEntityParallel + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent +{ + public TJob userJob; + + public UnsafeList chunks; + public UnsafeList chunkCount; + public UnsafeList entityOffset; + + public UnsafeList offsets0; + public UnsafeList bitsOffsets0; + + public UnsafeList offsets1; + public UnsafeList bitsOffsets1; + + public UnsafeList offsets2; + public UnsafeList bitsOffsets2; + + public UnsafeList offsets3; + public UnsafeList bitsOffsets3; + + public void Execute(int loopIndex, int threadIndex) + { + // 1. Get the specific pChunk for this thread + var pChunk = (byte*)chunks[loopIndex]; + var count = chunkCount[loopIndex]; + + var off0 = offsets0[loopIndex]; + var enableOff0 = bitsOffsets0[loopIndex]; + + var off1 = offsets1[loopIndex]; + var enableOff1 = bitsOffsets1[loopIndex]; + + var off2 = offsets2[loopIndex]; + var enableOff2 = bitsOffsets2[loopIndex]; + + var off3 = offsets3[loopIndex]; + var enableOff3 = bitsOffsets3[loopIndex]; + + var pEntity = (Entity*)(pChunk + entityOffset[loopIndex]); + var ptr0 = (T0*)(pChunk + off0); + var ptr1 = (T1*)(pChunk + off1); + var ptr2 = (T2*)(pChunk + off2); + var ptr3 = (T3*)(pChunk + off3); + + for (var i = 0; i < count; i++) + { + if (enableOff0 != -1 && !EntityQuery.CheckBit(pChunk + enableOff0, i)) + { + continue; + } + + if (enableOff1 != -1 && !EntityQuery.CheckBit(pChunk + enableOff1, i)) + { + continue; + } + + if (enableOff2 != -1 && !EntityQuery.CheckBit(pChunk + enableOff2, i)) + { + continue; + } + + if (enableOff3 != -1 && !EntityQuery.CheckBit(pChunk + enableOff3, i)) + { + continue; + } + + userJob.Execute(pEntity[i], ref ptr0[i], ref ptr1[i], ref ptr2[i], ref ptr3[i]); + } + } +} + +public interface IJobEntityParallel + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + where T4 : unmanaged, IComponent +{ + void Execute(Entity entity, ref T0 component0, ref T1 component1, ref T2 component2, ref T3 component3, ref T4 component4); +} + +internal unsafe struct JobEntityBatch : IJobParallelFor + where TJob : unmanaged, IJobEntityParallel + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + where T4 : unmanaged, IComponent +{ + public TJob userJob; + + public UnsafeList chunks; + public UnsafeList chunkCount; + public UnsafeList entityOffset; + + public UnsafeList offsets0; + public UnsafeList bitsOffsets0; + + public UnsafeList offsets1; + public UnsafeList bitsOffsets1; + + public UnsafeList offsets2; + public UnsafeList bitsOffsets2; + + public UnsafeList offsets3; + public UnsafeList bitsOffsets3; + + public UnsafeList offsets4; + public UnsafeList bitsOffsets4; + + public void Execute(int loopIndex, int threadIndex) + { + // 1. Get the specific pChunk for this thread + var pChunk = (byte*)chunks[loopIndex]; + var count = chunkCount[loopIndex]; + + var off0 = offsets0[loopIndex]; + var enableOff0 = bitsOffsets0[loopIndex]; + + var off1 = offsets1[loopIndex]; + var enableOff1 = bitsOffsets1[loopIndex]; + + var off2 = offsets2[loopIndex]; + var enableOff2 = bitsOffsets2[loopIndex]; + + var off3 = offsets3[loopIndex]; + var enableOff3 = bitsOffsets3[loopIndex]; + + var off4 = offsets4[loopIndex]; + var enableOff4 = bitsOffsets4[loopIndex]; + + var pEntity = (Entity*)(pChunk + entityOffset[loopIndex]); + var ptr0 = (T0*)(pChunk + off0); + var ptr1 = (T1*)(pChunk + off1); + var ptr2 = (T2*)(pChunk + off2); + var ptr3 = (T3*)(pChunk + off3); + var ptr4 = (T4*)(pChunk + off4); + + for (var i = 0; i < count; i++) + { + if (enableOff0 != -1 && !EntityQuery.CheckBit(pChunk + enableOff0, i)) + { + continue; + } + + if (enableOff1 != -1 && !EntityQuery.CheckBit(pChunk + enableOff1, i)) + { + continue; + } + + if (enableOff2 != -1 && !EntityQuery.CheckBit(pChunk + enableOff2, i)) + { + continue; + } + + if (enableOff3 != -1 && !EntityQuery.CheckBit(pChunk + enableOff3, i)) + { + continue; + } + + if (enableOff4 != -1 && !EntityQuery.CheckBit(pChunk + enableOff4, i)) + { + continue; + } + + userJob.Execute(pEntity[i], ref ptr0[i], ref ptr1[i], ref ptr2[i], ref ptr3[i], ref ptr4[i]); + } + } +} + +public interface IJobEntityParallel + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + where T4 : unmanaged, IComponent + where T5 : unmanaged, IComponent +{ + void Execute(Entity entity, ref T0 component0, ref T1 component1, ref T2 component2, ref T3 component3, ref T4 component4, ref T5 component5); +} + +internal unsafe struct JobEntityBatch : IJobParallelFor + where TJob : unmanaged, IJobEntityParallel + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + where T4 : unmanaged, IComponent + where T5 : unmanaged, IComponent +{ + public TJob userJob; + + public UnsafeList chunks; + public UnsafeList chunkCount; + public UnsafeList entityOffset; + + public UnsafeList offsets0; + public UnsafeList bitsOffsets0; + + public UnsafeList offsets1; + public UnsafeList bitsOffsets1; + + public UnsafeList offsets2; + public UnsafeList bitsOffsets2; + + public UnsafeList offsets3; + public UnsafeList bitsOffsets3; + + public UnsafeList offsets4; + public UnsafeList bitsOffsets4; + + public UnsafeList offsets5; + public UnsafeList bitsOffsets5; + + public void Execute(int loopIndex, int threadIndex) + { + // 1. Get the specific pChunk for this thread + var pChunk = (byte*)chunks[loopIndex]; + var count = chunkCount[loopIndex]; + + var off0 = offsets0[loopIndex]; + var enableOff0 = bitsOffsets0[loopIndex]; + + var off1 = offsets1[loopIndex]; + var enableOff1 = bitsOffsets1[loopIndex]; + + var off2 = offsets2[loopIndex]; + var enableOff2 = bitsOffsets2[loopIndex]; + + var off3 = offsets3[loopIndex]; + var enableOff3 = bitsOffsets3[loopIndex]; + + var off4 = offsets4[loopIndex]; + var enableOff4 = bitsOffsets4[loopIndex]; + + var off5 = offsets5[loopIndex]; + var enableOff5 = bitsOffsets5[loopIndex]; + + var pEntity = (Entity*)(pChunk + entityOffset[loopIndex]); + var ptr0 = (T0*)(pChunk + off0); + var ptr1 = (T1*)(pChunk + off1); + var ptr2 = (T2*)(pChunk + off2); + var ptr3 = (T3*)(pChunk + off3); + var ptr4 = (T4*)(pChunk + off4); + var ptr5 = (T5*)(pChunk + off5); + + for (var i = 0; i < count; i++) + { + if (enableOff0 != -1 && !EntityQuery.CheckBit(pChunk + enableOff0, i)) + { + continue; + } + + if (enableOff1 != -1 && !EntityQuery.CheckBit(pChunk + enableOff1, i)) + { + continue; + } + + if (enableOff2 != -1 && !EntityQuery.CheckBit(pChunk + enableOff2, i)) + { + continue; + } + + if (enableOff3 != -1 && !EntityQuery.CheckBit(pChunk + enableOff3, i)) + { + continue; + } + + if (enableOff4 != -1 && !EntityQuery.CheckBit(pChunk + enableOff4, i)) + { + continue; + } + + if (enableOff5 != -1 && !EntityQuery.CheckBit(pChunk + enableOff5, i)) + { + continue; + } + + userJob.Execute(pEntity[i], ref ptr0[i], ref ptr1[i], ref ptr2[i], ref ptr3[i], ref ptr4[i], ref ptr5[i]); + } + } +} + +public interface IJobEntityParallel + 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 +{ + void Execute(Entity entity, ref T0 component0, ref T1 component1, ref T2 component2, ref T3 component3, ref T4 component4, ref T5 component5, ref T6 component6); +} + +internal unsafe struct JobEntityBatch : IJobParallelFor + where TJob : unmanaged, IJobEntityParallel + 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 +{ + public TJob userJob; + + public UnsafeList chunks; + public UnsafeList chunkCount; + public UnsafeList entityOffset; + + public UnsafeList offsets0; + public UnsafeList bitsOffsets0; + + public UnsafeList offsets1; + public UnsafeList bitsOffsets1; + + public UnsafeList offsets2; + public UnsafeList bitsOffsets2; + + public UnsafeList offsets3; + public UnsafeList bitsOffsets3; + + public UnsafeList offsets4; + public UnsafeList bitsOffsets4; + + public UnsafeList offsets5; + public UnsafeList bitsOffsets5; + + public UnsafeList offsets6; + public UnsafeList bitsOffsets6; + + public void Execute(int loopIndex, int threadIndex) + { + // 1. Get the specific pChunk for this thread + var pChunk = (byte*)chunks[loopIndex]; + var count = chunkCount[loopIndex]; + + var off0 = offsets0[loopIndex]; + var enableOff0 = bitsOffsets0[loopIndex]; + + var off1 = offsets1[loopIndex]; + var enableOff1 = bitsOffsets1[loopIndex]; + + var off2 = offsets2[loopIndex]; + var enableOff2 = bitsOffsets2[loopIndex]; + + var off3 = offsets3[loopIndex]; + var enableOff3 = bitsOffsets3[loopIndex]; + + var off4 = offsets4[loopIndex]; + var enableOff4 = bitsOffsets4[loopIndex]; + + var off5 = offsets5[loopIndex]; + var enableOff5 = bitsOffsets5[loopIndex]; + + var off6 = offsets6[loopIndex]; + var enableOff6 = bitsOffsets6[loopIndex]; + + var pEntity = (Entity*)(pChunk + entityOffset[loopIndex]); + var ptr0 = (T0*)(pChunk + off0); + var ptr1 = (T1*)(pChunk + off1); + var ptr2 = (T2*)(pChunk + off2); + var ptr3 = (T3*)(pChunk + off3); + var ptr4 = (T4*)(pChunk + off4); + var ptr5 = (T5*)(pChunk + off5); + var ptr6 = (T6*)(pChunk + off6); + + for (var i = 0; i < count; i++) + { + if (enableOff0 != -1 && !EntityQuery.CheckBit(pChunk + enableOff0, i)) + { + continue; + } + + if (enableOff1 != -1 && !EntityQuery.CheckBit(pChunk + enableOff1, i)) + { + continue; + } + + if (enableOff2 != -1 && !EntityQuery.CheckBit(pChunk + enableOff2, i)) + { + continue; + } + + if (enableOff3 != -1 && !EntityQuery.CheckBit(pChunk + enableOff3, i)) + { + continue; + } + + if (enableOff4 != -1 && !EntityQuery.CheckBit(pChunk + enableOff4, i)) + { + continue; + } + + if (enableOff5 != -1 && !EntityQuery.CheckBit(pChunk + enableOff5, i)) + { + continue; + } + + if (enableOff6 != -1 && !EntityQuery.CheckBit(pChunk + enableOff6, i)) + { + continue; + } + + userJob.Execute(pEntity[i], ref ptr0[i], ref ptr1[i], ref ptr2[i], ref ptr3[i], ref ptr4[i], ref ptr5[i], ref ptr6[i]); + } + } +} + +public interface IJobEntityParallel + 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 +{ + void Execute(Entity entity, ref T0 component0, ref T1 component1, ref T2 component2, ref T3 component3, ref T4 component4, ref T5 component5, ref T6 component6, ref T7 component7); +} + +internal unsafe struct JobEntityBatch : IJobParallelFor + where TJob : unmanaged, IJobEntityParallel + 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 +{ + public TJob userJob; + + public UnsafeList chunks; + public UnsafeList chunkCount; + public UnsafeList entityOffset; + + public UnsafeList offsets0; + public UnsafeList bitsOffsets0; + + public UnsafeList offsets1; + public UnsafeList bitsOffsets1; + + public UnsafeList offsets2; + public UnsafeList bitsOffsets2; + + public UnsafeList offsets3; + public UnsafeList bitsOffsets3; + + public UnsafeList offsets4; + public UnsafeList bitsOffsets4; + + public UnsafeList offsets5; + public UnsafeList bitsOffsets5; + + public UnsafeList offsets6; + public UnsafeList bitsOffsets6; + + public UnsafeList offsets7; + public UnsafeList bitsOffsets7; + + public void Execute(int loopIndex, int threadIndex) + { + // 1. Get the specific pChunk for this thread + var pChunk = (byte*)chunks[loopIndex]; + var count = chunkCount[loopIndex]; + + var off0 = offsets0[loopIndex]; + var enableOff0 = bitsOffsets0[loopIndex]; + + var off1 = offsets1[loopIndex]; + var enableOff1 = bitsOffsets1[loopIndex]; + + var off2 = offsets2[loopIndex]; + var enableOff2 = bitsOffsets2[loopIndex]; + + var off3 = offsets3[loopIndex]; + var enableOff3 = bitsOffsets3[loopIndex]; + + var off4 = offsets4[loopIndex]; + var enableOff4 = bitsOffsets4[loopIndex]; + + var off5 = offsets5[loopIndex]; + var enableOff5 = bitsOffsets5[loopIndex]; + + var off6 = offsets6[loopIndex]; + var enableOff6 = bitsOffsets6[loopIndex]; + + var off7 = offsets7[loopIndex]; + var enableOff7 = bitsOffsets7[loopIndex]; + + var pEntity = (Entity*)(pChunk + entityOffset[loopIndex]); + var ptr0 = (T0*)(pChunk + off0); + var ptr1 = (T1*)(pChunk + off1); + var ptr2 = (T2*)(pChunk + off2); + var ptr3 = (T3*)(pChunk + off3); + var ptr4 = (T4*)(pChunk + off4); + var ptr5 = (T5*)(pChunk + off5); + var ptr6 = (T6*)(pChunk + off6); + var ptr7 = (T7*)(pChunk + off7); + + for (var i = 0; i < count; i++) + { + if (enableOff0 != -1 && !EntityQuery.CheckBit(pChunk + enableOff0, i)) + { + continue; + } + + if (enableOff1 != -1 && !EntityQuery.CheckBit(pChunk + enableOff1, i)) + { + continue; + } + + if (enableOff2 != -1 && !EntityQuery.CheckBit(pChunk + enableOff2, i)) + { + continue; + } + + if (enableOff3 != -1 && !EntityQuery.CheckBit(pChunk + enableOff3, i)) + { + continue; + } + + if (enableOff4 != -1 && !EntityQuery.CheckBit(pChunk + enableOff4, i)) + { + continue; + } + + if (enableOff5 != -1 && !EntityQuery.CheckBit(pChunk + enableOff5, i)) + { + continue; + } + + if (enableOff6 != -1 && !EntityQuery.CheckBit(pChunk + enableOff6, i)) + { + continue; + } + + if (enableOff7 != -1 && !EntityQuery.CheckBit(pChunk + enableOff7, i)) + { + continue; + } + + userJob.Execute(pEntity[i], ref ptr0[i], ref ptr1[i], ref ptr2[i], ref ptr3[i], ref ptr4[i], ref ptr5[i], ref ptr6[i], ref ptr7[i]); + } + } +} + +public unsafe partial struct EntityQuery +{ + private struct DisposeJobEntity1 : IJob + { + public UnsafeList chunkList; + public UnsafeList chunkEntityCounts; + public UnsafeList entityOffsets; + + public UnsafeList offsets0; + public UnsafeList bitsOffsets0; + + public void Execute(int threadIndex) + { + chunkList.Dispose(); + chunkEntityCounts.Dispose(); + entityOffsets.Dispose(); + + offsets0.Dispose(); + bitsOffsets0.Dispose(); + + } + } + + public JobHandle ScheduleEntityParallel(JobScheduler scheduler, TJob jobData, Allocator allocator, int batchSize, JobHandle dependency) + where TJob : unmanaged, IJobEntityParallel + where T0 : unmanaged, IComponent + { + // 1. Flatten the World + var chunkList = new UnsafeList(128, allocator); + var chunkEntityCounts = new UnsafeList(128, allocator); + var entityOffsets = new UnsafeList(128, allocator); + + var offsets0 = new UnsafeList(128, allocator); + var bitsOffsets0 = new UnsafeList(128, allocator); + + // Iterate the Query's matching archetypes + foreach (var archID in _matchingArchetypes) + { + ref var arch = ref World.GetWorld(_worldID) + .GetValueOrThrow(ResultStatus.Success) + .GetArchetypeReference(archID); + + if (arch.ChunkCount == 0) + { + continue; + } + + // Get offsets ONCE per archetype + var layout0 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + + // Add all chunks from this archetype + for (var i = 0; i < arch.ChunkCount; i++) + { + ref var chunkRef = ref arch.GetChunkReference(i); + + chunkList.Add((IntPtr)chunkRef.GetUnsafePtr()); + chunkEntityCounts.Add(chunkRef.Count); + entityOffsets.Add(arch.EntityIDsOffset); + + offsets0.Add(layout0.offset); + bitsOffsets0.Add(layout0.enableBitsOffset); + + } + } + + // 2. Create the Runner + var runner = new JobEntityBatch + { + userJob = jobData, + chunks = chunkList, + chunkCount = chunkEntityCounts, + entityOffset = entityOffsets, + + offsets0 = offsets0, + bitsOffsets0 = bitsOffsets0, + + }; + + var jobHandle = scheduler.ScheduleParallel(ref runner, chunkList.Count, batchSize, dependency); + + // 3. Dispose the temp lists + var disposeJob = new DisposeJobEntity1 + { + chunkList = chunkList, + chunkEntityCounts = chunkEntityCounts, + entityOffsets = entityOffsets, + + offsets0 = offsets0, + bitsOffsets0 = bitsOffsets0, + + }; + + scheduler.Schedule(ref disposeJob, jobHandle); + + return jobHandle; + } + + private struct DisposeJobEntity2 : IJob + { + public UnsafeList chunkList; + public UnsafeList chunkEntityCounts; + public UnsafeList entityOffsets; + + public UnsafeList offsets0; + public UnsafeList bitsOffsets0; + + public UnsafeList offsets1; + public UnsafeList bitsOffsets1; + + public void Execute(int threadIndex) + { + chunkList.Dispose(); + chunkEntityCounts.Dispose(); + entityOffsets.Dispose(); + + offsets0.Dispose(); + bitsOffsets0.Dispose(); + + offsets1.Dispose(); + bitsOffsets1.Dispose(); + + } + } + + public JobHandle ScheduleEntityParallel(JobScheduler scheduler, TJob jobData, Allocator allocator, int batchSize, JobHandle dependency) + where TJob : unmanaged, IJobEntityParallel + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + { + // 1. Flatten the World + var chunkList = new UnsafeList(128, allocator); + var chunkEntityCounts = new UnsafeList(128, allocator); + var entityOffsets = new UnsafeList(128, allocator); + + var offsets0 = new UnsafeList(128, allocator); + var bitsOffsets0 = new UnsafeList(128, allocator); + + var offsets1 = new UnsafeList(128, allocator); + var bitsOffsets1 = new UnsafeList(128, allocator); + + // Iterate the Query's matching archetypes + foreach (var archID in _matchingArchetypes) + { + ref var arch = ref World.GetWorld(_worldID) + .GetValueOrThrow(ResultStatus.Success) + .GetArchetypeReference(archID); + + if (arch.ChunkCount == 0) + { + continue; + } + + // Get offsets ONCE per archetype + var layout0 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + var layout1 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + + // Add all chunks from this archetype + for (var i = 0; i < arch.ChunkCount; i++) + { + ref var chunkRef = ref arch.GetChunkReference(i); + + chunkList.Add((IntPtr)chunkRef.GetUnsafePtr()); + chunkEntityCounts.Add(chunkRef.Count); + entityOffsets.Add(arch.EntityIDsOffset); + + offsets0.Add(layout0.offset); + bitsOffsets0.Add(layout0.enableBitsOffset); + + offsets1.Add(layout1.offset); + bitsOffsets1.Add(layout1.enableBitsOffset); + + } + } + + // 2. Create the Runner + var runner = new JobEntityBatch + { + userJob = jobData, + chunks = chunkList, + chunkCount = chunkEntityCounts, + entityOffset = entityOffsets, + + offsets0 = offsets0, + bitsOffsets0 = bitsOffsets0, + + offsets1 = offsets1, + bitsOffsets1 = bitsOffsets1, + + }; + + var jobHandle = scheduler.ScheduleParallel(ref runner, chunkList.Count, batchSize, dependency); + + // 3. Dispose the temp lists + var disposeJob = new DisposeJobEntity2 + { + chunkList = chunkList, + chunkEntityCounts = chunkEntityCounts, + entityOffsets = entityOffsets, + + offsets0 = offsets0, + bitsOffsets0 = bitsOffsets0, + + offsets1 = offsets1, + bitsOffsets1 = bitsOffsets1, + + }; + + scheduler.Schedule(ref disposeJob, jobHandle); + + return jobHandle; + } + + private struct DisposeJobEntity3 : IJob + { + public UnsafeList chunkList; + public UnsafeList chunkEntityCounts; + public UnsafeList entityOffsets; + + public UnsafeList offsets0; + public UnsafeList bitsOffsets0; + + public UnsafeList offsets1; + public UnsafeList bitsOffsets1; + + public UnsafeList offsets2; + public UnsafeList bitsOffsets2; + + public void Execute(int threadIndex) + { + chunkList.Dispose(); + chunkEntityCounts.Dispose(); + entityOffsets.Dispose(); + + offsets0.Dispose(); + bitsOffsets0.Dispose(); + + offsets1.Dispose(); + bitsOffsets1.Dispose(); + + offsets2.Dispose(); + bitsOffsets2.Dispose(); + + } + } + + public JobHandle ScheduleEntityParallel(JobScheduler scheduler, TJob jobData, Allocator allocator, int batchSize, JobHandle dependency) + where TJob : unmanaged, IJobEntityParallel + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + { + // 1. Flatten the World + var chunkList = new UnsafeList(128, allocator); + var chunkEntityCounts = new UnsafeList(128, allocator); + var entityOffsets = new UnsafeList(128, allocator); + + var offsets0 = new UnsafeList(128, allocator); + var bitsOffsets0 = new UnsafeList(128, allocator); + + var offsets1 = new UnsafeList(128, allocator); + var bitsOffsets1 = new UnsafeList(128, allocator); + + var offsets2 = new UnsafeList(128, allocator); + var bitsOffsets2 = new UnsafeList(128, allocator); + + // Iterate the Query's matching archetypes + foreach (var archID in _matchingArchetypes) + { + ref var arch = ref World.GetWorld(_worldID) + .GetValueOrThrow(ResultStatus.Success) + .GetArchetypeReference(archID); + + if (arch.ChunkCount == 0) + { + continue; + } + + // Get offsets ONCE per archetype + var layout0 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + var layout1 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + var layout2 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + + // Add all chunks from this archetype + for (var i = 0; i < arch.ChunkCount; i++) + { + ref var chunkRef = ref arch.GetChunkReference(i); + + chunkList.Add((IntPtr)chunkRef.GetUnsafePtr()); + chunkEntityCounts.Add(chunkRef.Count); + entityOffsets.Add(arch.EntityIDsOffset); + + offsets0.Add(layout0.offset); + bitsOffsets0.Add(layout0.enableBitsOffset); + + offsets1.Add(layout1.offset); + bitsOffsets1.Add(layout1.enableBitsOffset); + + offsets2.Add(layout2.offset); + bitsOffsets2.Add(layout2.enableBitsOffset); + + } + } + + // 2. Create the Runner + var runner = new JobEntityBatch + { + userJob = jobData, + chunks = chunkList, + chunkCount = chunkEntityCounts, + entityOffset = entityOffsets, + + offsets0 = offsets0, + bitsOffsets0 = bitsOffsets0, + + offsets1 = offsets1, + bitsOffsets1 = bitsOffsets1, + + offsets2 = offsets2, + bitsOffsets2 = bitsOffsets2, + + }; + + var jobHandle = scheduler.ScheduleParallel(ref runner, chunkList.Count, batchSize, dependency); + + // 3. Dispose the temp lists + var disposeJob = new DisposeJobEntity3 + { + chunkList = chunkList, + chunkEntityCounts = chunkEntityCounts, + entityOffsets = entityOffsets, + + offsets0 = offsets0, + bitsOffsets0 = bitsOffsets0, + + offsets1 = offsets1, + bitsOffsets1 = bitsOffsets1, + + offsets2 = offsets2, + bitsOffsets2 = bitsOffsets2, + + }; + + scheduler.Schedule(ref disposeJob, jobHandle); + + return jobHandle; + } + + private struct DisposeJobEntity4 : IJob + { + public UnsafeList chunkList; + public UnsafeList chunkEntityCounts; + public UnsafeList entityOffsets; + + public UnsafeList offsets0; + public UnsafeList bitsOffsets0; + + public UnsafeList offsets1; + public UnsafeList bitsOffsets1; + + public UnsafeList offsets2; + public UnsafeList bitsOffsets2; + + public UnsafeList offsets3; + public UnsafeList bitsOffsets3; + + public void Execute(int threadIndex) + { + chunkList.Dispose(); + chunkEntityCounts.Dispose(); + entityOffsets.Dispose(); + + offsets0.Dispose(); + bitsOffsets0.Dispose(); + + offsets1.Dispose(); + bitsOffsets1.Dispose(); + + offsets2.Dispose(); + bitsOffsets2.Dispose(); + + offsets3.Dispose(); + bitsOffsets3.Dispose(); + + } + } + + public JobHandle ScheduleEntityParallel(JobScheduler scheduler, TJob jobData, Allocator allocator, int batchSize, JobHandle dependency) + where TJob : unmanaged, IJobEntityParallel + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + { + // 1. Flatten the World + var chunkList = new UnsafeList(128, allocator); + var chunkEntityCounts = new UnsafeList(128, allocator); + var entityOffsets = new UnsafeList(128, allocator); + + var offsets0 = new UnsafeList(128, allocator); + var bitsOffsets0 = new UnsafeList(128, allocator); + + var offsets1 = new UnsafeList(128, allocator); + var bitsOffsets1 = new UnsafeList(128, allocator); + + var offsets2 = new UnsafeList(128, allocator); + var bitsOffsets2 = new UnsafeList(128, allocator); + + var offsets3 = new UnsafeList(128, allocator); + var bitsOffsets3 = new UnsafeList(128, allocator); + + // Iterate the Query's matching archetypes + foreach (var archID in _matchingArchetypes) + { + ref var arch = ref World.GetWorld(_worldID) + .GetValueOrThrow(ResultStatus.Success) + .GetArchetypeReference(archID); + + if (arch.ChunkCount == 0) + { + continue; + } + + // Get offsets ONCE per archetype + var layout0 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + var layout1 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + var layout2 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + var layout3 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + + // Add all chunks from this archetype + for (var i = 0; i < arch.ChunkCount; i++) + { + ref var chunkRef = ref arch.GetChunkReference(i); + + chunkList.Add((IntPtr)chunkRef.GetUnsafePtr()); + chunkEntityCounts.Add(chunkRef.Count); + entityOffsets.Add(arch.EntityIDsOffset); + + offsets0.Add(layout0.offset); + bitsOffsets0.Add(layout0.enableBitsOffset); + + offsets1.Add(layout1.offset); + bitsOffsets1.Add(layout1.enableBitsOffset); + + offsets2.Add(layout2.offset); + bitsOffsets2.Add(layout2.enableBitsOffset); + + offsets3.Add(layout3.offset); + bitsOffsets3.Add(layout3.enableBitsOffset); + + } + } + + // 2. Create the Runner + var runner = new JobEntityBatch + { + userJob = jobData, + chunks = chunkList, + chunkCount = chunkEntityCounts, + entityOffset = entityOffsets, + + offsets0 = offsets0, + bitsOffsets0 = bitsOffsets0, + + offsets1 = offsets1, + bitsOffsets1 = bitsOffsets1, + + offsets2 = offsets2, + bitsOffsets2 = bitsOffsets2, + + offsets3 = offsets3, + bitsOffsets3 = bitsOffsets3, + + }; + + var jobHandle = scheduler.ScheduleParallel(ref runner, chunkList.Count, batchSize, dependency); + + // 3. Dispose the temp lists + var disposeJob = new DisposeJobEntity4 + { + chunkList = chunkList, + chunkEntityCounts = chunkEntityCounts, + entityOffsets = entityOffsets, + + offsets0 = offsets0, + bitsOffsets0 = bitsOffsets0, + + offsets1 = offsets1, + bitsOffsets1 = bitsOffsets1, + + offsets2 = offsets2, + bitsOffsets2 = bitsOffsets2, + + offsets3 = offsets3, + bitsOffsets3 = bitsOffsets3, + + }; + + scheduler.Schedule(ref disposeJob, jobHandle); + + return jobHandle; + } + + private struct DisposeJobEntity5 : IJob + { + public UnsafeList chunkList; + public UnsafeList chunkEntityCounts; + public UnsafeList entityOffsets; + + public UnsafeList offsets0; + public UnsafeList bitsOffsets0; + + public UnsafeList offsets1; + public UnsafeList bitsOffsets1; + + public UnsafeList offsets2; + public UnsafeList bitsOffsets2; + + public UnsafeList offsets3; + public UnsafeList bitsOffsets3; + + public UnsafeList offsets4; + public UnsafeList bitsOffsets4; + + public void Execute(int threadIndex) + { + chunkList.Dispose(); + chunkEntityCounts.Dispose(); + entityOffsets.Dispose(); + + offsets0.Dispose(); + bitsOffsets0.Dispose(); + + offsets1.Dispose(); + bitsOffsets1.Dispose(); + + offsets2.Dispose(); + bitsOffsets2.Dispose(); + + offsets3.Dispose(); + bitsOffsets3.Dispose(); + + offsets4.Dispose(); + bitsOffsets4.Dispose(); + + } + } + + public JobHandle ScheduleEntityParallel(JobScheduler scheduler, TJob jobData, Allocator allocator, int batchSize, JobHandle dependency) + where TJob : unmanaged, IJobEntityParallel + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + where T4 : unmanaged, IComponent + { + // 1. Flatten the World + var chunkList = new UnsafeList(128, allocator); + var chunkEntityCounts = new UnsafeList(128, allocator); + var entityOffsets = new UnsafeList(128, allocator); + + var offsets0 = new UnsafeList(128, allocator); + var bitsOffsets0 = new UnsafeList(128, allocator); + + var offsets1 = new UnsafeList(128, allocator); + var bitsOffsets1 = new UnsafeList(128, allocator); + + var offsets2 = new UnsafeList(128, allocator); + var bitsOffsets2 = new UnsafeList(128, allocator); + + var offsets3 = new UnsafeList(128, allocator); + var bitsOffsets3 = new UnsafeList(128, allocator); + + var offsets4 = new UnsafeList(128, allocator); + var bitsOffsets4 = new UnsafeList(128, allocator); + + // Iterate the Query's matching archetypes + foreach (var archID in _matchingArchetypes) + { + ref var arch = ref World.GetWorld(_worldID) + .GetValueOrThrow(ResultStatus.Success) + .GetArchetypeReference(archID); + + if (arch.ChunkCount == 0) + { + continue; + } + + // Get offsets ONCE per archetype + var layout0 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + var layout1 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + var layout2 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + var layout3 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + var layout4 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + + // Add all chunks from this archetype + for (var i = 0; i < arch.ChunkCount; i++) + { + ref var chunkRef = ref arch.GetChunkReference(i); + + chunkList.Add((IntPtr)chunkRef.GetUnsafePtr()); + chunkEntityCounts.Add(chunkRef.Count); + entityOffsets.Add(arch.EntityIDsOffset); + + offsets0.Add(layout0.offset); + bitsOffsets0.Add(layout0.enableBitsOffset); + + offsets1.Add(layout1.offset); + bitsOffsets1.Add(layout1.enableBitsOffset); + + offsets2.Add(layout2.offset); + bitsOffsets2.Add(layout2.enableBitsOffset); + + offsets3.Add(layout3.offset); + bitsOffsets3.Add(layout3.enableBitsOffset); + + offsets4.Add(layout4.offset); + bitsOffsets4.Add(layout4.enableBitsOffset); + + } + } + + // 2. Create the Runner + var runner = new JobEntityBatch + { + userJob = jobData, + chunks = chunkList, + chunkCount = chunkEntityCounts, + entityOffset = entityOffsets, + + offsets0 = offsets0, + bitsOffsets0 = bitsOffsets0, + + offsets1 = offsets1, + bitsOffsets1 = bitsOffsets1, + + offsets2 = offsets2, + bitsOffsets2 = bitsOffsets2, + + offsets3 = offsets3, + bitsOffsets3 = bitsOffsets3, + + offsets4 = offsets4, + bitsOffsets4 = bitsOffsets4, + + }; + + var jobHandle = scheduler.ScheduleParallel(ref runner, chunkList.Count, batchSize, dependency); + + // 3. Dispose the temp lists + var disposeJob = new DisposeJobEntity5 + { + chunkList = chunkList, + chunkEntityCounts = chunkEntityCounts, + entityOffsets = entityOffsets, + + offsets0 = offsets0, + bitsOffsets0 = bitsOffsets0, + + offsets1 = offsets1, + bitsOffsets1 = bitsOffsets1, + + offsets2 = offsets2, + bitsOffsets2 = bitsOffsets2, + + offsets3 = offsets3, + bitsOffsets3 = bitsOffsets3, + + offsets4 = offsets4, + bitsOffsets4 = bitsOffsets4, + + }; + + scheduler.Schedule(ref disposeJob, jobHandle); + + return jobHandle; + } + + private struct DisposeJobEntity6 : IJob + { + public UnsafeList chunkList; + public UnsafeList chunkEntityCounts; + public UnsafeList entityOffsets; + + public UnsafeList offsets0; + public UnsafeList bitsOffsets0; + + public UnsafeList offsets1; + public UnsafeList bitsOffsets1; + + public UnsafeList offsets2; + public UnsafeList bitsOffsets2; + + public UnsafeList offsets3; + public UnsafeList bitsOffsets3; + + public UnsafeList offsets4; + public UnsafeList bitsOffsets4; + + public UnsafeList offsets5; + public UnsafeList bitsOffsets5; + + public void Execute(int threadIndex) + { + chunkList.Dispose(); + chunkEntityCounts.Dispose(); + entityOffsets.Dispose(); + + offsets0.Dispose(); + bitsOffsets0.Dispose(); + + offsets1.Dispose(); + bitsOffsets1.Dispose(); + + offsets2.Dispose(); + bitsOffsets2.Dispose(); + + offsets3.Dispose(); + bitsOffsets3.Dispose(); + + offsets4.Dispose(); + bitsOffsets4.Dispose(); + + offsets5.Dispose(); + bitsOffsets5.Dispose(); + + } + } + + public JobHandle ScheduleEntityParallel(JobScheduler scheduler, TJob jobData, Allocator allocator, int batchSize, JobHandle dependency) + where TJob : unmanaged, IJobEntityParallel + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + where T4 : unmanaged, IComponent + where T5 : unmanaged, IComponent + { + // 1. Flatten the World + var chunkList = new UnsafeList(128, allocator); + var chunkEntityCounts = new UnsafeList(128, allocator); + var entityOffsets = new UnsafeList(128, allocator); + + var offsets0 = new UnsafeList(128, allocator); + var bitsOffsets0 = new UnsafeList(128, allocator); + + var offsets1 = new UnsafeList(128, allocator); + var bitsOffsets1 = new UnsafeList(128, allocator); + + var offsets2 = new UnsafeList(128, allocator); + var bitsOffsets2 = new UnsafeList(128, allocator); + + var offsets3 = new UnsafeList(128, allocator); + var bitsOffsets3 = new UnsafeList(128, allocator); + + var offsets4 = new UnsafeList(128, allocator); + var bitsOffsets4 = new UnsafeList(128, allocator); + + var offsets5 = new UnsafeList(128, allocator); + var bitsOffsets5 = new UnsafeList(128, allocator); + + // Iterate the Query's matching archetypes + foreach (var archID in _matchingArchetypes) + { + ref var arch = ref World.GetWorld(_worldID) + .GetValueOrThrow(ResultStatus.Success) + .GetArchetypeReference(archID); + + if (arch.ChunkCount == 0) + { + continue; + } + + // Get offsets ONCE per archetype + var layout0 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + var layout1 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + var layout2 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + var layout3 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + var layout4 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + var layout5 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + + // Add all chunks from this archetype + for (var i = 0; i < arch.ChunkCount; i++) + { + ref var chunkRef = ref arch.GetChunkReference(i); + + chunkList.Add((IntPtr)chunkRef.GetUnsafePtr()); + chunkEntityCounts.Add(chunkRef.Count); + entityOffsets.Add(arch.EntityIDsOffset); + + offsets0.Add(layout0.offset); + bitsOffsets0.Add(layout0.enableBitsOffset); + + offsets1.Add(layout1.offset); + bitsOffsets1.Add(layout1.enableBitsOffset); + + offsets2.Add(layout2.offset); + bitsOffsets2.Add(layout2.enableBitsOffset); + + offsets3.Add(layout3.offset); + bitsOffsets3.Add(layout3.enableBitsOffset); + + offsets4.Add(layout4.offset); + bitsOffsets4.Add(layout4.enableBitsOffset); + + offsets5.Add(layout5.offset); + bitsOffsets5.Add(layout5.enableBitsOffset); + + } + } + + // 2. Create the Runner + var runner = new JobEntityBatch + { + userJob = jobData, + chunks = chunkList, + chunkCount = chunkEntityCounts, + entityOffset = entityOffsets, + + offsets0 = offsets0, + bitsOffsets0 = bitsOffsets0, + + offsets1 = offsets1, + bitsOffsets1 = bitsOffsets1, + + offsets2 = offsets2, + bitsOffsets2 = bitsOffsets2, + + offsets3 = offsets3, + bitsOffsets3 = bitsOffsets3, + + offsets4 = offsets4, + bitsOffsets4 = bitsOffsets4, + + offsets5 = offsets5, + bitsOffsets5 = bitsOffsets5, + + }; + + var jobHandle = scheduler.ScheduleParallel(ref runner, chunkList.Count, batchSize, dependency); + + // 3. Dispose the temp lists + var disposeJob = new DisposeJobEntity6 + { + chunkList = chunkList, + chunkEntityCounts = chunkEntityCounts, + entityOffsets = entityOffsets, + + offsets0 = offsets0, + bitsOffsets0 = bitsOffsets0, + + offsets1 = offsets1, + bitsOffsets1 = bitsOffsets1, + + offsets2 = offsets2, + bitsOffsets2 = bitsOffsets2, + + offsets3 = offsets3, + bitsOffsets3 = bitsOffsets3, + + offsets4 = offsets4, + bitsOffsets4 = bitsOffsets4, + + offsets5 = offsets5, + bitsOffsets5 = bitsOffsets5, + + }; + + scheduler.Schedule(ref disposeJob, jobHandle); + + return jobHandle; + } + + private struct DisposeJobEntity7 : IJob + { + public UnsafeList chunkList; + public UnsafeList chunkEntityCounts; + public UnsafeList entityOffsets; + + public UnsafeList offsets0; + public UnsafeList bitsOffsets0; + + public UnsafeList offsets1; + public UnsafeList bitsOffsets1; + + public UnsafeList offsets2; + public UnsafeList bitsOffsets2; + + public UnsafeList offsets3; + public UnsafeList bitsOffsets3; + + public UnsafeList offsets4; + public UnsafeList bitsOffsets4; + + public UnsafeList offsets5; + public UnsafeList bitsOffsets5; + + public UnsafeList offsets6; + public UnsafeList bitsOffsets6; + + public void Execute(int threadIndex) + { + chunkList.Dispose(); + chunkEntityCounts.Dispose(); + entityOffsets.Dispose(); + + offsets0.Dispose(); + bitsOffsets0.Dispose(); + + offsets1.Dispose(); + bitsOffsets1.Dispose(); + + offsets2.Dispose(); + bitsOffsets2.Dispose(); + + offsets3.Dispose(); + bitsOffsets3.Dispose(); + + offsets4.Dispose(); + bitsOffsets4.Dispose(); + + offsets5.Dispose(); + bitsOffsets5.Dispose(); + + offsets6.Dispose(); + bitsOffsets6.Dispose(); + + } + } + + public JobHandle ScheduleEntityParallel(JobScheduler scheduler, TJob jobData, Allocator allocator, int batchSize, JobHandle dependency) + where TJob : unmanaged, IJobEntityParallel + 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 + { + // 1. Flatten the World + var chunkList = new UnsafeList(128, allocator); + var chunkEntityCounts = new UnsafeList(128, allocator); + var entityOffsets = new UnsafeList(128, allocator); + + var offsets0 = new UnsafeList(128, allocator); + var bitsOffsets0 = new UnsafeList(128, allocator); + + var offsets1 = new UnsafeList(128, allocator); + var bitsOffsets1 = new UnsafeList(128, allocator); + + var offsets2 = new UnsafeList(128, allocator); + var bitsOffsets2 = new UnsafeList(128, allocator); + + var offsets3 = new UnsafeList(128, allocator); + var bitsOffsets3 = new UnsafeList(128, allocator); + + var offsets4 = new UnsafeList(128, allocator); + var bitsOffsets4 = new UnsafeList(128, allocator); + + var offsets5 = new UnsafeList(128, allocator); + var bitsOffsets5 = new UnsafeList(128, allocator); + + var offsets6 = new UnsafeList(128, allocator); + var bitsOffsets6 = new UnsafeList(128, allocator); + + // Iterate the Query's matching archetypes + foreach (var archID in _matchingArchetypes) + { + ref var arch = ref World.GetWorld(_worldID) + .GetValueOrThrow(ResultStatus.Success) + .GetArchetypeReference(archID); + + if (arch.ChunkCount == 0) + { + continue; + } + + // Get offsets ONCE per archetype + var layout0 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + var layout1 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + var layout2 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + var layout3 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + var layout4 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + var layout5 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + var layout6 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + + // Add all chunks from this archetype + for (var i = 0; i < arch.ChunkCount; i++) + { + ref var chunkRef = ref arch.GetChunkReference(i); + + chunkList.Add((IntPtr)chunkRef.GetUnsafePtr()); + chunkEntityCounts.Add(chunkRef.Count); + entityOffsets.Add(arch.EntityIDsOffset); + + offsets0.Add(layout0.offset); + bitsOffsets0.Add(layout0.enableBitsOffset); + + offsets1.Add(layout1.offset); + bitsOffsets1.Add(layout1.enableBitsOffset); + + offsets2.Add(layout2.offset); + bitsOffsets2.Add(layout2.enableBitsOffset); + + offsets3.Add(layout3.offset); + bitsOffsets3.Add(layout3.enableBitsOffset); + + offsets4.Add(layout4.offset); + bitsOffsets4.Add(layout4.enableBitsOffset); + + offsets5.Add(layout5.offset); + bitsOffsets5.Add(layout5.enableBitsOffset); + + offsets6.Add(layout6.offset); + bitsOffsets6.Add(layout6.enableBitsOffset); + + } + } + + // 2. Create the Runner + var runner = new JobEntityBatch + { + userJob = jobData, + chunks = chunkList, + chunkCount = chunkEntityCounts, + entityOffset = entityOffsets, + + offsets0 = offsets0, + bitsOffsets0 = bitsOffsets0, + + offsets1 = offsets1, + bitsOffsets1 = bitsOffsets1, + + offsets2 = offsets2, + bitsOffsets2 = bitsOffsets2, + + offsets3 = offsets3, + bitsOffsets3 = bitsOffsets3, + + offsets4 = offsets4, + bitsOffsets4 = bitsOffsets4, + + offsets5 = offsets5, + bitsOffsets5 = bitsOffsets5, + + offsets6 = offsets6, + bitsOffsets6 = bitsOffsets6, + + }; + + var jobHandle = scheduler.ScheduleParallel(ref runner, chunkList.Count, batchSize, dependency); + + // 3. Dispose the temp lists + var disposeJob = new DisposeJobEntity7 + { + chunkList = chunkList, + chunkEntityCounts = chunkEntityCounts, + entityOffsets = entityOffsets, + + offsets0 = offsets0, + bitsOffsets0 = bitsOffsets0, + + offsets1 = offsets1, + bitsOffsets1 = bitsOffsets1, + + offsets2 = offsets2, + bitsOffsets2 = bitsOffsets2, + + offsets3 = offsets3, + bitsOffsets3 = bitsOffsets3, + + offsets4 = offsets4, + bitsOffsets4 = bitsOffsets4, + + offsets5 = offsets5, + bitsOffsets5 = bitsOffsets5, + + offsets6 = offsets6, + bitsOffsets6 = bitsOffsets6, + + }; + + scheduler.Schedule(ref disposeJob, jobHandle); + + return jobHandle; + } + + private struct DisposeJobEntity8 : IJob + { + public UnsafeList chunkList; + public UnsafeList chunkEntityCounts; + public UnsafeList entityOffsets; + + public UnsafeList offsets0; + public UnsafeList bitsOffsets0; + + public UnsafeList offsets1; + public UnsafeList bitsOffsets1; + + public UnsafeList offsets2; + public UnsafeList bitsOffsets2; + + public UnsafeList offsets3; + public UnsafeList bitsOffsets3; + + public UnsafeList offsets4; + public UnsafeList bitsOffsets4; + + public UnsafeList offsets5; + public UnsafeList bitsOffsets5; + + public UnsafeList offsets6; + public UnsafeList bitsOffsets6; + + public UnsafeList offsets7; + public UnsafeList bitsOffsets7; + + public void Execute(int threadIndex) + { + chunkList.Dispose(); + chunkEntityCounts.Dispose(); + entityOffsets.Dispose(); + + offsets0.Dispose(); + bitsOffsets0.Dispose(); + + offsets1.Dispose(); + bitsOffsets1.Dispose(); + + offsets2.Dispose(); + bitsOffsets2.Dispose(); + + offsets3.Dispose(); + bitsOffsets3.Dispose(); + + offsets4.Dispose(); + bitsOffsets4.Dispose(); + + offsets5.Dispose(); + bitsOffsets5.Dispose(); + + offsets6.Dispose(); + bitsOffsets6.Dispose(); + + offsets7.Dispose(); + bitsOffsets7.Dispose(); + + } + } + + public JobHandle ScheduleEntityParallel(JobScheduler scheduler, TJob jobData, Allocator allocator, int batchSize, JobHandle dependency) + where TJob : unmanaged, IJobEntityParallel + 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 + { + // 1. Flatten the World + var chunkList = new UnsafeList(128, allocator); + var chunkEntityCounts = new UnsafeList(128, allocator); + var entityOffsets = new UnsafeList(128, allocator); + + var offsets0 = new UnsafeList(128, allocator); + var bitsOffsets0 = new UnsafeList(128, allocator); + + var offsets1 = new UnsafeList(128, allocator); + var bitsOffsets1 = new UnsafeList(128, allocator); + + var offsets2 = new UnsafeList(128, allocator); + var bitsOffsets2 = new UnsafeList(128, allocator); + + var offsets3 = new UnsafeList(128, allocator); + var bitsOffsets3 = new UnsafeList(128, allocator); + + var offsets4 = new UnsafeList(128, allocator); + var bitsOffsets4 = new UnsafeList(128, allocator); + + var offsets5 = new UnsafeList(128, allocator); + var bitsOffsets5 = new UnsafeList(128, allocator); + + var offsets6 = new UnsafeList(128, allocator); + var bitsOffsets6 = new UnsafeList(128, allocator); + + var offsets7 = new UnsafeList(128, allocator); + var bitsOffsets7 = new UnsafeList(128, allocator); + + // Iterate the Query's matching archetypes + foreach (var archID in _matchingArchetypes) + { + ref var arch = ref World.GetWorld(_worldID) + .GetValueOrThrow(ResultStatus.Success) + .GetArchetypeReference(archID); + + if (arch.ChunkCount == 0) + { + continue; + } + + // Get offsets ONCE per archetype + var layout0 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + var layout1 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + var layout2 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + var layout3 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + var layout4 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + var layout5 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + var layout6 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + var layout7 = arch.GetLayout(ComponentTypeID.value) + .GetValueOrThrow(ResultStatus.Success); + + // Add all chunks from this archetype + for (var i = 0; i < arch.ChunkCount; i++) + { + ref var chunkRef = ref arch.GetChunkReference(i); + + chunkList.Add((IntPtr)chunkRef.GetUnsafePtr()); + chunkEntityCounts.Add(chunkRef.Count); + entityOffsets.Add(arch.EntityIDsOffset); + + offsets0.Add(layout0.offset); + bitsOffsets0.Add(layout0.enableBitsOffset); + + offsets1.Add(layout1.offset); + bitsOffsets1.Add(layout1.enableBitsOffset); + + offsets2.Add(layout2.offset); + bitsOffsets2.Add(layout2.enableBitsOffset); + + offsets3.Add(layout3.offset); + bitsOffsets3.Add(layout3.enableBitsOffset); + + offsets4.Add(layout4.offset); + bitsOffsets4.Add(layout4.enableBitsOffset); + + offsets5.Add(layout5.offset); + bitsOffsets5.Add(layout5.enableBitsOffset); + + offsets6.Add(layout6.offset); + bitsOffsets6.Add(layout6.enableBitsOffset); + + offsets7.Add(layout7.offset); + bitsOffsets7.Add(layout7.enableBitsOffset); + + } + } + + // 2. Create the Runner + var runner = new JobEntityBatch + { + userJob = jobData, + chunks = chunkList, + chunkCount = chunkEntityCounts, + entityOffset = entityOffsets, + + offsets0 = offsets0, + bitsOffsets0 = bitsOffsets0, + + offsets1 = offsets1, + bitsOffsets1 = bitsOffsets1, + + offsets2 = offsets2, + bitsOffsets2 = bitsOffsets2, + + offsets3 = offsets3, + bitsOffsets3 = bitsOffsets3, + + offsets4 = offsets4, + bitsOffsets4 = bitsOffsets4, + + offsets5 = offsets5, + bitsOffsets5 = bitsOffsets5, + + offsets6 = offsets6, + bitsOffsets6 = bitsOffsets6, + + offsets7 = offsets7, + bitsOffsets7 = bitsOffsets7, + + }; + + var jobHandle = scheduler.ScheduleParallel(ref runner, chunkList.Count, batchSize, dependency); + + // 3. Dispose the temp lists + var disposeJob = new DisposeJobEntity8 + { + chunkList = chunkList, + chunkEntityCounts = chunkEntityCounts, + entityOffsets = entityOffsets, + + offsets0 = offsets0, + bitsOffsets0 = bitsOffsets0, + + offsets1 = offsets1, + bitsOffsets1 = bitsOffsets1, + + offsets2 = offsets2, + bitsOffsets2 = bitsOffsets2, + + offsets3 = offsets3, + bitsOffsets3 = bitsOffsets3, + + offsets4 = offsets4, + bitsOffsets4 = bitsOffsets4, + + offsets5 = offsets5, + bitsOffsets5 = bitsOffsets5, + + offsets6 = offsets6, + bitsOffsets6 = bitsOffsets6, + + offsets7 = offsets7, + bitsOffsets7 = bitsOffsets7, + + }; + + scheduler.Schedule(ref disposeJob, jobHandle); + + return jobHandle; + } + +} \ No newline at end of file diff --git a/Ghost.Entities/Templates/EntityQuery.JobEntityParallel.tt b/Ghost.Entities/Templates/EntityQuery.JobEntityParallel.tt new file mode 100644 index 0000000..8ebecd1 --- /dev/null +++ b/Ghost.Entities/Templates/EntityQuery.JobEntityParallel.tt @@ -0,0 +1,189 @@ +<#@ 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; +using Misaki.HighPerformance.Jobs; +using Misaki.HighPerformance.LowLevel.Buffer; +using Misaki.HighPerformance.LowLevel.Collections; + +namespace Ghost.Entities; + +<# for (var i = 1; i <= Amount; i++) +{ + var generics = AppendGenerics(i); + var restrictions = AppendGenericRestrictionsMultiline(i, "unmanaged, IComponent", 1); +#> +public interface IJobEntityParallel<<#= generics #>> +<#= restrictions #> +{ + void Execute(Entity entity, <#= AppendParameters(i, "ref T{0} component{0}") #>); +} + +internal unsafe struct JobEntityBatch> : IJobParallelFor + where TJob : unmanaged, IJobEntityParallel<<#= generics #>> +<#= restrictions #> +{ + public TJob userJob; + + public UnsafeList chunks; + public UnsafeList chunkCount; + public UnsafeList entityOffset; + +<# for (var j = 0; j < i; j++){ #> + public UnsafeList offsets<#= j #>; + public UnsafeList bitsOffsets<#= j #>; + +<# } #> + public void Execute(int loopIndex, int threadIndex) + { + // 1. Get the specific pChunk for this thread + var pChunk = (byte*)chunks[loopIndex]; + var count = chunkCount[loopIndex]; + +<# for (var j = 0; j < i; j++){ #> + var off<#= j #> = offsets<#= j #>[loopIndex]; + var enableOff<#= j #> = bitsOffsets<#= j #>[loopIndex]; + +<# } #> + var pEntity = (Entity*)(pChunk + entityOffset[loopIndex]); +<# for (var j = 0; j < i; j++){ #> + var ptr<#= j #> = (<#= "T" + j #>*)(pChunk + off<#= j #>); +<# } #> + + for (var i = 0; i < count; i++) + { +<# for (var j = 0; j < i; j++){ #> + if (enableOff<#= j #> != -1 && !EntityQuery.CheckBit(pChunk + enableOff<#= j #>, i)) + { + continue; + } + +<# } #> + userJob.Execute(pEntity[i], <#= AppendParameters(i, "ref ptr{0}[i]") #>); + } + } +} + +<# } #> +public unsafe partial struct EntityQuery +{ +<# for (var i = 1; i <= Amount; i++) +{ + var generics = AppendGenerics(i); + var restrictions = AppendGenericRestrictionsMultiline(i, "unmanaged, IComponent", 2); +#> + private struct DisposeJobEntity<#= i #> : IJob + { + public UnsafeList chunkList; + public UnsafeList chunkEntityCounts; + public UnsafeList entityOffsets; + +<# for (var j = 0; j < i; j++){ #> + public UnsafeList offsets<#= j #>; + public UnsafeList bitsOffsets<#= j #>; + +<# } #> + public void Execute(int threadIndex) + { + chunkList.Dispose(); + chunkEntityCounts.Dispose(); + entityOffsets.Dispose(); + +<# for (var j = 0; j < i; j++){ #> + offsets<#= j #>.Dispose(); + bitsOffsets<#= j #>.Dispose(); + +<# } #> + } + } + + public JobHandle ScheduleEntityParallel>(JobScheduler scheduler, TJob jobData, Allocator allocator, int batchSize, JobHandle dependency) + where TJob : unmanaged, IJobEntityParallel<<#= generics #>> +<#= restrictions #> + { + // 1. Flatten the World + var chunkList = new UnsafeList(128, allocator); + var chunkEntityCounts = new UnsafeList(128, allocator); + var entityOffsets = new UnsafeList(128, allocator); + +<# for (var j = 0; j < i; j++){ #> + var offsets<#= j #> = new UnsafeList(128, allocator); + var bitsOffsets<#= j #> = new UnsafeList(128, allocator); + +<# } #> + // Iterate the Query's matching archetypes + foreach (var archID in _matchingArchetypes) + { + ref var arch = ref World.GetWorld(_worldID) + .GetValueOrThrow(ResultStatus.Success) + .GetArchetypeReference(archID); + + if (arch.ChunkCount == 0) + { + continue; + } + + // Get offsets ONCE per archetype +<# for (var j = 0; j < i; j++){ #> + var layout<#= j #> = arch.GetLayout(ComponentTypeID>.value) + .GetValueOrThrow(ResultStatus.Success); +<# } #> + + // Add all chunks from this archetype + for (var i = 0; i < arch.ChunkCount; i++) + { + ref var chunkRef = ref arch.GetChunkReference(i); + + chunkList.Add((IntPtr)chunkRef.GetUnsafePtr()); + chunkEntityCounts.Add(chunkRef.Count); + entityOffsets.Add(arch.EntityIDsOffset); + +<# for (var j = 0; j < i; j++){ #> + offsets<#= j #>.Add(layout<#= j #>.offset); + bitsOffsets<#= j #>.Add(layout<#= j #>.enableBitsOffset); + +<# } #> + } + } + + // 2. Create the Runner + var runner = new JobEntityBatch> + { + userJob = jobData, + chunks = chunkList, + chunkCount = chunkEntityCounts, + entityOffset = entityOffsets, + +<# for (var j = 0; j < i; j++){ #> + offsets<#= j #> = offsets<#= j #>, + bitsOffsets<#= j #> = bitsOffsets<#= j #>, + +<# } #> + }; + + var jobHandle = scheduler.ScheduleParallel(ref runner, chunkList.Count, batchSize, dependency); + + // 3. Dispose the temp lists + var disposeJob = new DisposeJobEntity<#= i #> + { + chunkList = chunkList, + chunkEntityCounts = chunkEntityCounts, + entityOffsets = entityOffsets, + +<# for (var j = 0; j < i; j++){ #> + offsets<#= j #> = offsets<#= j #>, + bitsOffsets<#= j #> = bitsOffsets<#= j #>, + +<# } #> + }; + + scheduler.Schedule(ref disposeJob, jobHandle); + + return jobHandle; + } + +<# } #> +} \ No newline at end of file diff --git a/Ghost.Entities/Templates/Helpers.ttinclude b/Ghost.Entities/Templates/Helpers.ttinclude index 1de7df1..8dba87f 100644 --- a/Ghost.Entities/Templates/Helpers.ttinclude +++ b/Ghost.Entities/Templates/Helpers.ttinclude @@ -19,6 +19,7 @@ if (i > 0) sb.Append(", "); sb.Append(string.Format(template, i)); } + return sb.ToString(); } @@ -27,27 +28,18 @@ return AppendGenerics(amount, "T{0}"); } - public StringBuilder AppendGenericRefParameters(int amount) + public StringBuilder AppendParameters(int amount, string template) { var sb = new StringBuilder(); for (var localIndex = 0; localIndex < amount; localIndex++) { - sb.Append($"ref T{localIndex} t{localIndex}Component,"); + sb.Append(string.Format(template, localIndex)); + if (localIndex < amount - 1) + { + sb.Append(", "); + } } - 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; } @@ -62,6 +54,7 @@ sb.Append(' '); } } + return sb; } @@ -83,6 +76,7 @@ sb.AppendLine(); } } + return sb; } @@ -102,6 +96,7 @@ sb.Append(" && "); } } + return sb; } @@ -116,6 +111,7 @@ sb.Append(" && "); } } + return sb; } @@ -130,6 +126,7 @@ sb.Append(", "); } } + return sb; } @@ -144,6 +141,7 @@ sb.Append(", "); } } + return sb; } #> \ No newline at end of file diff --git a/Ghost.Entities/World.cs b/Ghost.Entities/World.cs index e62d3da..36bf077 100644 --- a/Ghost.Entities/World.cs +++ b/Ghost.Entities/World.cs @@ -148,7 +148,7 @@ public partial class World : IIdentifierType, IDisposable, IEquatable internal Identifier CreateEntityQuery(EntityQueryMask mask, int maskHash) { var queryID = new Identifier(_entityQueries.Count); - _entityQueries.Add(new EntityQuery(_id, mask)); + _entityQueries.Add(new EntityQuery(queryID, _id, mask)); _querieLookup.Add(maskHash, queryID); ref var query = ref _entityQueries[queryID.value]; diff --git a/Ghost.Graphics/Ghost.Graphics.csproj b/Ghost.Graphics/Ghost.Graphics.csproj index 64b8d0b..ce2e3b9 100644 --- a/Ghost.Graphics/Ghost.Graphics.csproj +++ b/Ghost.Graphics/Ghost.Graphics.csproj @@ -16,6 +16,12 @@ True + + + + + + @@ -48,8 +54,4 @@ - - - -