diff --git a/Ghost.Core/Ghost.Core.csproj b/Ghost.Core/Ghost.Core.csproj
index eae157a..68c0063 100644
--- a/Ghost.Core/Ghost.Core.csproj
+++ b/Ghost.Core/Ghost.Core.csproj
@@ -21,7 +21,7 @@
-
+
diff --git a/Ghost.Core/Handle.cs b/Ghost.Core/Handle.cs
index 7307554..ddd0071 100644
--- a/Ghost.Core/Handle.cs
+++ b/Ghost.Core/Handle.cs
@@ -4,7 +4,7 @@ public interface IHandleType;
public interface IIdentifierType;
public interface IKeyType;
-public readonly struct Handle
+public readonly struct Handle : IEquatable>
where T : IHandleType
{
public readonly int id;
@@ -57,7 +57,7 @@ public readonly struct Handle
}
}
-public readonly struct Identifier
+public readonly struct Identifier : IEquatable>
where T : IIdentifierType
{
public readonly int value;
@@ -74,7 +74,7 @@ public readonly struct Identifier
public readonly override int GetHashCode()
{
- return value.GetHashCode();
+ return value;
}
public readonly override bool Equals(object? obj)
diff --git a/Ghost.Core/Result.cs b/Ghost.Core/Result.cs
index 79e2ab0..6b26cc3 100644
--- a/Ghost.Core/Result.cs
+++ b/Ghost.Core/Result.cs
@@ -1,3 +1,6 @@
+using System.Runtime.CompilerServices;
+using TerraFX.Interop.DirectX;
+
namespace Ghost.Core;
public readonly struct Result
@@ -152,38 +155,38 @@ public readonly ref struct RefResult
public static class ResultExtensions
{
- public static void ThrowIfFailed(this ResultStatus result)
+ public static void ThrowIfFailed(this ResultStatus result, [CallerArgumentExpression(nameof(result))] string? op = null)
{
if (result != ResultStatus.Success)
{
- throw new InvalidOperationException($"Operation failed: {result}");
+ throw new InvalidOperationException($"{op} failed: {result}");
}
}
- public static void ThrowIfFailed(this Result result)
+ public static void ThrowIfFailed(this Result result, [CallerArgumentExpression(nameof(result))] string? op = null)
{
if (!result.IsSuccess)
{
- throw new InvalidOperationException($"Operation failed: {result.Message}");
+ throw new InvalidOperationException($"{op} failed: {result.Message}");
}
}
- public static T GetValueOrThrow(this Result result)
+ public static T GetValueOrThrow(this Result result, [CallerArgumentExpression(nameof(result))] string? op = null)
{
if (!result.IsSuccess)
{
- throw new InvalidOperationException($"Operation failed: {result.Message}");
+ throw new InvalidOperationException($"{op} failed: {result.Message}");
}
return result.Value;
}
- public static T GetValueOrThrow(this Result result, S expect)
+ public static T GetValueOrThrow(this Result result, S expect, [CallerArgumentExpression(nameof(result))] string? op = null)
where S : Enum
{
if (!EqualityComparer.Default.Equals(result.Status, expect))
{
- throw new InvalidOperationException($"Operation failed: expected status {expect}, but got {result.Status}");
+ throw new InvalidOperationException($"{op} failed: expected status {expect}, but got {result.Status}");
}
return result.Value;
@@ -212,6 +215,19 @@ public static class ResultExtensions
return false;
}
+ public static bool TryGetValue(this Result result, S expect, out T value)
+ where S : Enum
+ {
+ if (EqualityComparer.Default.Equals(result.Status, expect))
+ {
+ value = result.Value;
+ return true;
+ }
+
+ value = default!;
+ return false;
+ }
+
public static Result OnSuccess(this Result result, Action action)
{
if (result.IsSuccess)
diff --git a/Ghost.Entities.Test/ArcEntityTest.cs b/Ghost.Entities.Test/ArcEntityTest.cs
index 387d0c6..a86197d 100644
--- a/Ghost.Entities.Test/ArcEntityTest.cs
+++ b/Ghost.Entities.Test/ArcEntityTest.cs
@@ -15,28 +15,28 @@ public partial class ArcEntityTest : ITest
public void Run()
{
var entity1 = _world.EntityManager.CreateEntity(ComponentTypeID.value);
- Console.WriteLine(entity1);
- _world.EntityManager.AddComponent(entity1, new Mesh { index = 1 });
+ _world.EntityManager.AddComponent(entity1, new Mesh { index = 1 });
var queryID = new QueryBuilder().WithAll().Build(_world);
ref var query = ref _world.GetEntityQueryReference(queryID);
+ query.ForEach((ref t) =>
+ {
+ t.position = new float3(1, 2, 3);
+ });
+
foreach (var chunk in query.GetChunkIterator())
{
var transforms = chunk.GetComponentData();
var entities = chunk.GetEntities();
-
- for (var i = 0; i < chunk.Count; i++)
+ var bits = chunk.GetEnableBits();
+
+ var it = bits.GetIterator();
+ while (it.Next(out var index) && index < chunk.Count)
{
- Console.WriteLine($"Entity {entities[i]} Position: {transforms[i].position}");
- transforms[i].position = new float3(1, 2, 3);
+ Console.WriteLine($"Entity {entities[index]} Updated Position: {transforms[index].position}");
}
}
-
- query.ForEach((e, ref t) =>
- {
- Console.WriteLine($"Entity {e} Updated Position: {t.position}");
- });
}
public void Cleanup()
@@ -45,7 +45,7 @@ public partial class ArcEntityTest : ITest
}
}
-public struct Transform : IComponent
+public struct Transform : IEnableableComponent
{
public float3 position;
}
diff --git a/Ghost.Entities/Archetype.cs b/Ghost.Entities/Archetype.cs
index 2b2d68d..10807ee 100644
--- a/Ghost.Entities/Archetype.cs
+++ b/Ghost.Entities/Archetype.cs
@@ -9,10 +9,13 @@ namespace Ghost.Entities;
internal unsafe struct Chunk : IDisposable
{
public const int CHUNK_SIZE = 16384; // 16 KB
+ public const int BIT_ALIGNMENT = 8;
+ public const int BIT_SHIFT = 3; // log2(BIT_ALIGNMENT)
+ public const int BIT_ALIGNMENT_MINUS_ONE = BIT_ALIGNMENT - 1;
private UnsafeArray _data;
private int _count;
- private int _capacity;
+ private readonly int _capacity;
public int Count
{
@@ -41,21 +44,22 @@ internal unsafe struct Chunk : IDisposable
}
}
-internal struct Edge
-{
- public Identifier componentID;
- public int targetArchetype; // can't use Identifier because cycle causer
-}
-
-internal struct ComponentMemoryLayout
-{
- public int offset;
- public int size;
- public Identifier componentID;
-}
-
internal unsafe struct Archetype : IIdentifierType, IDisposable
{
+ internal struct ComponentMemoryLayout
+ {
+ public int componentID;
+ public int size;
+ public int offset;
+ public int enableBitsOffset; // TODO: Support enableable component
+ }
+
+ private struct Edge
+ {
+ public int componentID;
+ public int targetArchetype; // can't use Identifier because cycle causer
+ }
+
private readonly Identifier _id;
private readonly Identifier _worldID;
@@ -63,7 +67,7 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
internal UnsafeList _chunks;
internal UnsafeArray _layouts;
- private UnsafeArray _componentIDToOffset;
+ private UnsafeArray _componentIDToLayoutIndex;
// TODO: Is hash map better?
private UnsafeList _edgesAdd;
private UnsafeList _edgesRemove;
@@ -84,16 +88,16 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
_id = id;
_worldID = worldID;
+ _chunks = new UnsafeList(4, Allocator.Persistent);
+ _edgesAdd = new UnsafeList(4, Allocator.Persistent);
+ _edgesRemove = new UnsafeList(4, Allocator.Persistent);
+
if (componentIds.IsEmpty)
{
_signature = new UnsafeBitSet(1, Allocator.Persistent, AllocationOption.Clear);
- _chunks = new UnsafeList(4, Allocator.Persistent);
-
- _edgesAdd = new UnsafeList(4, Allocator.Persistent);
- _edgesRemove = new UnsafeList(4, Allocator.Persistent);
+ _hash = 0;
_signature.ClearAll();
-
_entityCapacity = Chunk.CHUNK_SIZE / sizeof(Entity);
return;
@@ -109,28 +113,23 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
}
_signature = new UnsafeBitSet(highestComponentID + 1, Allocator.Persistent, AllocationOption.Clear);
- _chunks = new UnsafeList(4, Allocator.Persistent);
-
- _edgesAdd = new UnsafeList(4, Allocator.Persistent);
- _edgesRemove = new UnsafeList(4, Allocator.Persistent);
-
_hash = _signature.GetHashCode();
- var pComponents = stackalloc ComponentInfo[componentIds.Length];
- for (var i = 0; i < componentIds.Length; i++)
- {
- _signature.SetBit(componentIds[i]);
- pComponents[i] = ComponentRegister.GetComponentInfo(componentIds[i]);
- }
-
- CalculateLayout(new Span(pComponents, componentIds.Length));
+ CalculateLayout(componentIds);
}
- private void CalculateLayout(Span components)
+ private void CalculateLayout(ReadOnlySpan> componentIds)
{
var entitySize = sizeof(Entity);
var entityAlign = (int)MemoryUtility.AlignOf();
+ var components = (Span)stackalloc ComponentInfo[componentIds.Length];
+ for (var i = 0; i < componentIds.Length; i++)
+ {
+ _signature.SetBit(componentIds[i]);
+ components[i] = ComponentRegister.GetComponentInfo(componentIds[i]);
+ }
+
// Calculate total size per entity to get an initial capacity estimate
var bytesPerEntity = entitySize;
var maxComponentID = 0;
@@ -147,12 +146,13 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
_maxComponentID = maxComponentID;
_entityCapacity = Chunk.CHUNK_SIZE / bytesPerEntity;
_layouts = new UnsafeArray(components.Length, Allocator.Persistent);
- _componentIDToOffset = new UnsafeArray(_maxComponentID + 1, Allocator.Persistent);
+ _componentIDToLayoutIndex = new UnsafeArray(_maxComponentID + 1, Allocator.Persistent);
- _componentIDToOffset.AsSpan().Fill(-1);
+ _componentIDToLayoutIndex.AsSpan().Fill(-1);
components.Sort((a, b) => b.alignment.CompareTo(a.alignment));
var tempOffsets = stackalloc int[components.Length];
+ var tempBitmaskOffsets = stackalloc int[components.Length];
while (_entityCapacity > 0)
{
@@ -173,6 +173,19 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
tempOffsets[i] = currentOffset;
currentOffset += _entityCapacity * size;
+ var bitmaskOffset = -1;
+ if (components[i].isEnableable)
+ {
+ var bitmaskSize = (_entityCapacity + Chunk.BIT_ALIGNMENT_MINUS_ONE) / Chunk.BIT_ALIGNMENT;
+ // Reserve space for the bitmask (1 bit per entity)
+
+ currentOffset = (currentOffset + Chunk.BIT_ALIGNMENT_MINUS_ONE) & ~Chunk.BIT_ALIGNMENT_MINUS_ONE; // Align
+ bitmaskOffset = currentOffset;
+ currentOffset += bitmaskSize;
+ }
+
+ tempBitmaskOffsets[i] = bitmaskOffset;
+
if (currentOffset > Chunk.CHUNK_SIZE)
{
fits = false;
@@ -188,10 +201,11 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
{
offset = tempOffsets[i],
size = components[i].size,
- componentID = components[i].id
+ componentID = components[i].id,
+ enableBitsOffset = tempBitmaskOffsets[i],
};
- _componentIDToOffset[components[i].id] = tempOffsets[i];
+ _componentIDToLayoutIndex[components[i].id] = i;
}
return;
@@ -219,6 +233,18 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
// Need to allocate a new chunk
var newChunk = new Chunk(Chunk.CHUNK_SIZE, _entityCapacity);
+ // Set all enable to true by default for enableable components
+ for (var i = 0; i < _layouts.Count; i++)
+ {
+ var layout = _layouts[i];
+ if (layout.enableBitsOffset != -1)
+ {
+ var pChunk = newChunk.GetUnsafePtr();
+ var pBits = pChunk + layout.enableBitsOffset;
+ MemoryUtility.MemSet(pBits, 0xFF, (nuint)((_entityCapacity + Chunk.BIT_ALIGNMENT_MINUS_ONE) / Chunk.BIT_ALIGNMENT));
+ }
+ }
+
rowIndex = 0;
newChunk.Count++;
chunkIndex = _chunks.Count;
@@ -237,9 +263,15 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public readonly void SetComponentData(int chunkIndex, int rowIndex, Identifier componentID, void* pComponent)
+ public readonly ResultStatus SetComponentData(int chunkIndex, int rowIndex, Identifier componentID, void* pComponent)
{
- var offset = _componentIDToOffset[componentID];
+ var r = GetLayout(componentID);
+ if (r.Status != ResultStatus.Success)
+ {
+ return r.Status;
+ }
+
+ var offset = r.Value.offset;
var chunk = _chunks[chunkIndex];
var chunkBase = chunk.GetUnsafePtr();
@@ -247,19 +279,27 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
var dst = chunkBase + offset + (size * rowIndex);
MemoryUtility.MemCpy(pComponent, dst, (nuint)size);
+
+ return ResultStatus.Success;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public readonly void* GetComponentDataPtr(int chunkIndex, int rowIndex, Identifier componentID)
+ public readonly ResultStatus GetComponentDataPtr(int chunkIndex, int rowIndex, Identifier componentID, void** ppv)
{
- var offset = _componentIDToOffset[componentID];
+ var r = GetLayout(componentID);
+ if (r.Status != ResultStatus.Success)
+ {
+ return r.Status;
+ }
+
+ var offset = r.Value.offset;
var chunk = _chunks[chunkIndex];
var chunkBase = chunk.GetUnsafePtr();
var size = ComponentRegister.GetComponentInfo(componentID).size;
- var dst = chunkBase + offset + (size * rowIndex);
+ *ppv = chunkBase + offset + (size * rowIndex);
- return dst;
+ return ResultStatus.Success;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -269,14 +309,20 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public readonly int GetOffset(int componentId)
+ public readonly Result GetLayout(int componentID)
{
- if (componentId >= _componentIDToOffset.Count)
+ if (componentID >= _componentIDToLayoutIndex.Count)
{
- return -1;
+ return Result.Create(default(ComponentMemoryLayout), ResultStatus.InvalidArgument);
}
- return _componentIDToOffset[componentId];
+ var layoutIndex = _componentIDToLayoutIndex[componentID];
+ if (layoutIndex == -1)
+ {
+ return Result.Create(default(ComponentMemoryLayout), ResultStatus.NotFound);
+ }
+
+ return Result.Create(_layouts[layoutIndex], ResultStatus.Success);
}
public ResultStatus RemoveEntity(int chunkIndex, int rowIndex)
@@ -382,26 +428,6 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
return Identifier.Invalid;
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public readonly Span GetComponentArray(int chunkIndex)
- where T : unmanaged, IComponent
- {
- var id = ComponentTypeID.value;
- if (id >= _componentIDToOffset.Count)
- {
- return default;
- }
-
- var offset = _componentIDToOffset[id];
- if (offset == -1)
- {
- return default;
- }
-
- var chunk = _chunks[chunkIndex];
- return new Span((T*)((byte*)chunk.GetUnsafePtr() + offset), chunk.Count);
- }
-
public override readonly int GetHashCode()
{
return _hash;
@@ -419,7 +445,7 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
_signature.Dispose();
_chunks.Dispose();
- _componentIDToOffset.Dispose();
+ _componentIDToLayoutIndex.Dispose();
_layouts.Dispose();
_edgesAdd.Dispose();
_edgesRemove.Dispose();
diff --git a/Ghost.Entities/Component.cs b/Ghost.Entities/Component.cs
index 9b6a72c..1945ce2 100644
--- a/Ghost.Entities/Component.cs
+++ b/Ghost.Entities/Component.cs
@@ -8,12 +8,17 @@ public interface IComponent : IIdentifierType
{
}
+public interface IEnableableComponent : IComponent
+{
+}
+
public struct ComponentInfo
{
// public FixedText64 stableName; // Do we actually need this?
+ public Identifier id;
public int size;
public int alignment;
- public Identifier id;
+ public bool isEnableable;
}
public static class ComponentTypeID
@@ -25,12 +30,12 @@ public static class ComponentTypeID
internal static class ComponentRegister
{
private static int s_nextComponentTypeID = 0;
- private static Dictionary> s_typeHandleToID = new();
+ private static readonly Dictionary> s_typeHandleToID = new();
- private static List s_registeredComponents = new();
- private static Dictionary> s_nameToRuntimeID = new();
+ private static readonly List s_registeredComponents = new();
+ private static readonly Dictionary> s_nameToRuntimeID = new();
- public unsafe static Identifier GetOrRegisterComponent()
+ public static unsafe Identifier GetOrRegisterComponent()
where T : unmanaged, IComponent
{
var typeHandle = typeof(T).TypeHandle.Value;
@@ -50,9 +55,10 @@ internal static class ComponentRegister
var info = new ComponentInfo
{
// stableName = new FixedText64(stableName),
+ id = newID,
size = sizeof(T),
alignment = (int)MemoryUtility.AlignOf(),
- id = newID,
+ isEnableable = typeof(IEnableableComponent).IsAssignableFrom(typeof(T))
};
while (s_registeredComponents.Count <= newID.value) s_registeredComponents.Add(default);
diff --git a/Ghost.Entities/EntityManager.cs b/Ghost.Entities/EntityManager.cs
index f550f4c..26f4d9f 100644
--- a/Ghost.Entities/EntityManager.cs
+++ b/Ghost.Entities/EntityManager.cs
@@ -2,6 +2,7 @@ using Ghost.Core;
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
using Misaki.HighPerformance.LowLevel.Utilities;
+using System;
using System.Diagnostics;
namespace Ghost.Entities;
@@ -121,8 +122,14 @@ 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) Looku
- var dst = newArch._chunks[newChunk].GetUnsafePtr() + newOffset + (layout.size * newRow);
+ var layoutResult = newArch.GetLayout(layout.componentID);
+ Debug.Assert(layoutResult.Status == ResultStatus.Success); // This should always be true if the system is consistent.
+ if (layoutResult.Status != ResultStatus.Success)
+ {
+ continue;
+ }
+
+ var dst = newArch._chunks[newChunk].GetUnsafePtr() + layoutResult.Value.offset + (layout.size * newRow);
MemoryUtility.MemCpy(src, dst, (nuint)layout.size);
}
@@ -210,10 +217,10 @@ public unsafe class EntityManager : IDisposable
var r = oldArchetype.RemoveEntity(location.chunkIndex, location.rowIndex);
Debug.Assert(r == ResultStatus.Success); // We assert it because the entity should exist if the whole system is consistent.
- // if (r != ResultStatus.Success)
- // {
- // return r;
- // }
+ if (r != ResultStatus.Success)
+ {
+ return r;
+ }
// Update location
location.archetypeID = newArcID;
@@ -265,6 +272,43 @@ public unsafe class EntityManager : IDisposable
return HasComponent(entity, ComponentTypeID.value);
}
+ public ResultStatus SetEnabled(Entity entity, bool enabled)
+ where T : unmanaged, IEnableableComponent
+ {
+ if (!_entityLocations.TryGetElementAt(entity.ID, entity.Generation, out var location))
+ {
+ return ResultStatus.NotFound;
+ }
+
+ ref var archetype = ref _world.GetArchetypeReference(location.archetypeID);
+ var chunkIndex = location.chunkIndex;
+ var rowIndex = location.rowIndex;
+
+ var layoutResult = archetype.GetLayout(ComponentTypeID.value);
+ if (layoutResult.Status != ResultStatus.Success)
+ {
+ return layoutResult.Status;
+ }
+
+ ref var chunk = ref archetype.GetChunkReference(chunkIndex);
+ var chunkBase = chunk.GetUnsafePtr();
+ var maskBase = chunkBase + layoutResult.Value.enableBitsOffset;
+
+ var byteIndex = rowIndex >> Chunk.BIT_SHIFT;
+ var bitIndex = rowIndex & Chunk.BIT_ALIGNMENT_MINUS_ONE;
+
+ if (enabled)
+ {
+ maskBase[byteIndex] |= (byte)(1 << bitIndex);
+ }
+ else
+ {
+ maskBase[byteIndex] &= (byte)~(1 << bitIndex);
+ }
+
+ return ResultStatus.Success;
+ }
+
public void Dispose()
{
if (_disposed)
diff --git a/Ghost.Entities/EntityQuery.cs b/Ghost.Entities/EntityQuery.cs
deleted file mode 100644
index 41f7a82..0000000
--- a/Ghost.Entities/EntityQuery.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-namespace Ghost.Entities;
-
-public unsafe class EntityQueryy
- where T1 : unmanaged, IComponent
- where T2 : unmanaged, IComponent
-{
- // The Cache Struct
- struct ArchetypeCache
- {
- public Archetype Archetype;
- public int Offset1; // Offset for T0
- public int Offset2; // Offset for T2
- }
-
- private List _cache = new();
-
- internal void AddMatchingArchetype(Archetype archetype)
- {
- // We look up the offsets ONCE when the archetype is registered
- int off1 = archetype.GetOffset(ComponentTypeID.value);
- int off2 = archetype.GetOffset(ComponentTypeID.value);
-
- _cache.Add(new ArchetypeCache
- {
- Archetype = archetype,
- Offset1 = off1,
- Offset2 = off2
- });
- }
-
- // The Optimized Iteration Loop
- public void ForEach(delegate*[ action)
- {
- foreach (var cache in _cache)
- {
- var archetype = cache.Archetype;
- var offset1 = cache.Offset1;
- var offset2 = cache.Offset2;
-
- // Iterate Chunks
- for (int i = 0; i < archetype.ChunkCount; i++)
- {
- var chunk = archetype.GetChunkReference(i);
- var chunkPtr = chunk.GetUnsafePtr();
- var count = chunk.Count;
-
- // POINTER MATH ONLY - NO LOOKUPS
- // We use the pre-calculated integer offsets
- T1* ptr1 = (T1*)(chunkPtr + offset1);
- T2* ptr2 = (T2*)(chunkPtr + offset2);
-
- // The hot loop
- for (int k = 0; k < count; k++)
- {
- action(ref ptr1[k], ref ptr2[k]);
- }
- }
- }
- }
-}
diff --git a/Ghost.Entities/Exceptions.cs b/Ghost.Entities/Exceptions.cs
new file mode 100644
index 0000000..b33d616
--- /dev/null
+++ b/Ghost.Entities/Exceptions.cs
@@ -0,0 +1,5 @@
+namespace Ghost.Entities;
+
+public class EntityNotFoundException : Exception
+{
+}
\ No newline at end of file
diff --git a/Ghost.Entities/Ghost.Entities.csproj b/Ghost.Entities/Ghost.Entities.csproj
index 0f6ca75..ca01b7d 100644
--- a/Ghost.Entities/Ghost.Entities.csproj
+++ b/Ghost.Entities/Ghost.Entities.csproj
@@ -7,6 +7,16 @@
True
+
+ True
+ True
+
+
+
+ True
+ True
+
+
@@ -22,6 +32,11 @@
True
True
+
+ True
+ True
+ QueryBuilder.With.tt
+
]
@@ -37,6 +52,10 @@
ForEach.gen.cs
TextTemplatingFileGenerator
+
+ TextTemplatingFileGenerator
+ QueryBuilder.With.gen.cs
+
@@ -54,6 +73,11 @@
True
ForEach.tt
+
+ True
+ True
+ QueryBuilder.With.tt
+
diff --git a/Ghost.Entities/Query.cs b/Ghost.Entities/Query.cs
index 8a4b9e2..e053778 100644
--- a/Ghost.Entities/Query.cs
+++ b/Ghost.Entities/Query.cs
@@ -5,42 +5,86 @@ using System.Runtime.CompilerServices;
namespace Ghost.Entities;
-public struct EntityQueryMask : IDisposable
+public struct EntityQueryMask : IDisposable, IEquatable
{
- public UnsafeBitSet all;
- public UnsafeBitSet any;
- public UnsafeBitSet absent;
+ public UnsafeBitSet structuralAll;
+ public UnsafeBitSet structuralAny;
+ public UnsafeBitSet structuralAbsent;
+
+ public UnsafeBitSet requireEnabled;
+ public UnsafeBitSet requireDisabled;
+ public UnsafeBitSet rejectIfEnabled;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public readonly bool Matches(UnsafeBitSet archetypeSignature)
+ public readonly bool Matches(ref readonly UnsafeBitSet archetypeSignature)
{
- return (!all.IsCreated || all.All(archetypeSignature))
- && (!absent.IsCreated || absent.None(archetypeSignature))
- && (!any.IsCreated || any.Count == 0 || any.Any(archetypeSignature));
+ return (!structuralAll.IsCreated || structuralAll.All(archetypeSignature))
+ && (!structuralAbsent.IsCreated || structuralAbsent.None(archetypeSignature))
+ && (!structuralAny.IsCreated || structuralAny.Count == 0 || structuralAny.Any(archetypeSignature));
}
- public readonly override int GetHashCode()
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override readonly int GetHashCode()
{
var hash = 17;
- if (all.IsCreated) hash = hash * 23 + all.GetHashCode();
- if (absent.IsCreated) hash = hash * 23 + absent.GetHashCode();
- if (any.IsCreated) hash = hash * 23 + any.GetHashCode();
+ if (structuralAll.IsCreated) hash = hash * 23 + structuralAll.GetHashCode();
+ if (structuralAbsent.IsCreated) hash = hash * 23 + structuralAbsent.GetHashCode();
+ if (structuralAny.IsCreated) hash = hash * 23 + structuralAny.GetHashCode();
+ if (requireEnabled.IsCreated) hash = hash * 23 + requireEnabled.GetHashCode();
+ if (requireDisabled.IsCreated) hash = hash * 23 + requireDisabled.GetHashCode();
+ if (rejectIfEnabled.IsCreated) hash = hash * 23 + rejectIfEnabled.GetHashCode();
return hash;
}
public void Dispose()
{
- all.Dispose();
- any.Dispose();
- absent.Dispose();
+ structuralAll.Dispose();
+ structuralAny.Dispose();
+ structuralAbsent.Dispose();
+
+ requireEnabled.Dispose();
+ requireDisabled.Dispose();
+ rejectIfEnabled.Dispose();
+ }
+
+ public readonly bool Equals(EntityQueryMask other)
+ {
+ return structuralAll.Equals(other.structuralAll)
+ && structuralAny.Equals(other.structuralAny)
+ && structuralAbsent.Equals(other.structuralAbsent)
+ && requireEnabled.Equals(other.requireEnabled)
+ && requireDisabled.Equals(other.requireDisabled)
+ && rejectIfEnabled.Equals(other.rejectIfEnabled);
+ }
+
+ public override readonly bool Equals(object? obj)
+ {
+ return obj is EntityQueryMask mask && Equals(mask);
+ }
+
+ public static bool operator ==(EntityQueryMask left, EntityQueryMask right)
+ {
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(EntityQueryMask left, EntityQueryMask right)
+ {
+ return !(left == right);
}
}
public unsafe partial struct EntityQuery : IIdentifierType, IDisposable
{
+ ///
+ /// Provides an enumerator for iterating over chunks of entities and their component data that match a set of archetypes within a world.
+ ///
public readonly ref struct ChunkIterator
{
+ ///
+ /// Provides a read-only view over a chunk of entities and their component data within an archetype.
+ ///
+ /// This does not filter disabled/enabled components. You must handle that manually.
public readonly ref struct ChunkView
{
private readonly ref Archetype _archetype;
@@ -54,6 +98,10 @@ public unsafe partial struct EntityQuery : IIdentifierType, IDisposable
_chunk = ref archetype.GetChunkReference(chunkIndex);
}
+ ///
+ /// Returns a read-only span containing structuralAll entities stored in the current chunk.
+ ///
+ /// A read-only span of values representing the entities in the chunk.
public readonly ReadOnlySpan GetEntities()
{
var ptr = _chunk.GetUnsafePtr();
@@ -61,31 +109,70 @@ public unsafe partial struct EntityQuery : IIdentifierType, IDisposable
return new ReadOnlySpan(pEntity, _chunk.Count);
}
+ ///
+ /// Gets a span providing direct access to the component data of type T0 for structuralAll entities in the chunk.
+ ///
+ /// The type of component to access. Must be an unmanaged type that implements .
+ /// A span of type containing the component data for each entity in the chunk.
+ /// Thrown if the specified component type is not present in the archetype.
public readonly Span GetComponentData()
where T : unmanaged, IComponent
{
- var offset = _archetype.GetOffset(ComponentTypeID.value);
- if (offset < 0)
+ var layout = _archetype.GetLayout(ComponentTypeID.value).GetValueOrThrow(ResultStatus.Success);
+ var ptr = _chunk.GetUnsafePtr() + layout.offset;
+ return new Span(ptr, _chunk.Count);
+ }
+
+ ///
+ /// Gets a bit set representing the enabled state of each instance of the specified enableable component
+ /// type within the current chunk.
+ ///
+ /// The component type for which to retrieve enablement bits. Must be unmanaged and implement .
+ /// A that provides access to the enablement bits for all instances of the specified component type in the chunk.
+ /// Thrown if the specified component type does not support enablement.
+ public SpanBitSet GetEnableBits()
+ where T : unmanaged, IEnableableComponent
+ {
+ var layout = _archetype.GetLayout(ComponentTypeID.value).GetValueOrThrow(ResultStatus.Success);
+ if (layout.enableBitsOffset == -1)
{
- throw new InvalidOperationException($"Archetype does not contain component of type {typeof(T)}");
+ throw new InvalidOperationException($"Component {typeof(T).FullName} is not enableable.");
}
- var ptr = (byte*)_chunk.GetUnsafePtr() + offset;
- return new Span(ptr, _chunk.Count);
+ var maskBase = _chunk.GetUnsafePtr() + layout.enableBitsOffset;
+ return new SpanBitSet(new Span(maskBase, (_chunk.Count + 31) / 32));
+ }
+
+ ///
+ /// Determines whether the specified component of type at the given index is currently enabled.
+ ///
+ /// The type of the component to check. Must be an unmanaged type that implements .
+ /// The zero-based index of the component instance to check within the chunk.
+ /// true if the component at the specified index is enabled; otherwise, false.
+ /// Thrown if the specified component type does not support enable/disable functionality.
+ public readonly bool IsComponentEnabled(int index)
+ where T : unmanaged, IEnableableComponent
+ {
+ var layout = _archetype.GetLayout(ComponentTypeID.value).GetValueOrThrow(ResultStatus.Success);
+ if (layout.enableBitsOffset == -1)
+ {
+ throw new InvalidOperationException($"Component {typeof(T).FullName} is not enableable.");
+ }
+
+ var maskBase = _chunk.GetUnsafePtr() + layout.enableBitsOffset;
+ return CheckBit(maskBase, index);
}
}
public ref struct Enumerator
{
- private readonly ReadOnlyUnsafeCollection> _matchingArchetypes;
- private readonly World _world;
+ private readonly ChunkIterator _iterator;
private int _archetypeIndex;
private int _chunkIndex;
- internal Enumerator(ReadOnlyUnsafeCollection> matchingArchetypes, World world)
+ internal Enumerator(ChunkIterator iterator)
{
- _matchingArchetypes = matchingArchetypes;
- _world = world;
+ _iterator = iterator;
_archetypeIndex = 0;
_chunkIndex = -1;
}
@@ -94,7 +181,7 @@ public unsafe partial struct EntityQuery : IIdentifierType, IDisposable
{
get
{
- ref var archetype = ref _world.GetArchetypeReference(_matchingArchetypes[_archetypeIndex]);
+ ref var archetype = ref _iterator._world.GetArchetypeReference(_iterator._matchingArchetypes[_archetypeIndex]);
return new ChunkView(ref archetype, _chunkIndex);
}
}
@@ -103,9 +190,9 @@ public unsafe partial struct EntityQuery : IIdentifierType, IDisposable
{
_chunkIndex++;
- while (_archetypeIndex < _matchingArchetypes.Count)
+ while (_archetypeIndex < _iterator._matchingArchetypes.Count)
{
- ref var archetype = ref _world.GetArchetypeReference(_matchingArchetypes[_archetypeIndex]);
+ ref var archetype = ref _iterator._world.GetArchetypeReference(_iterator._matchingArchetypes[_archetypeIndex]);
if (_chunkIndex < archetype.ChunkCount)
{
return true;
@@ -140,14 +227,15 @@ public unsafe partial struct EntityQuery : IIdentifierType, IDisposable
public readonly Enumerator GetEnumerator()
{
- return new Enumerator(_matchingArchetypes, _world);
+ return new Enumerator(this);
}
}
private readonly Identifier _worldID;
- private EntityQueryMask _mask;
private UnsafeList> _matchingArchetypes;
+ internal EntityQueryMask _mask;
+
internal EntityQuery(Identifier worldID, EntityQueryMask mask)
{
_worldID = worldID;
@@ -155,14 +243,96 @@ public unsafe partial struct EntityQuery : IIdentifierType, IDisposable
_matchingArchetypes = new UnsafeList>(8, Allocator.Persistent);
}
- internal void AddArchetypeIfMatch(Archetype archetype)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static bool IsEntityValid(byte* chunkBase, int entityIndex, ref readonly Archetype archetype, ref readonly EntityQueryMask mask)
{
- if (_mask.Matches(archetype._signature))
+ // 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)
+ {
+ // Not enableable, always true
+ continue;
+ }
+
+ // Check bit
+ if (!CheckBit(chunkBase + layout.enableBitsOffset, entityIndex))
+ {
+ return false;
+ }
+ }
+
+ // 2. Check "Require Disabled" (WithDisabled)
+ it = mask.requireDisabled.GetIterator();
+ while (it.Next(out var id))
+ {
+ var layout = archetype.GetLayout(id).Value;
+
+ // 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))
+ {
+ return false;
+ }
+ }
+
+ // 3. Check "Reject if Enabled" (The "Soft WithNone")
+ it = mask.rejectIfEnabled.GetIterator();
+ while (it.Next(out var id))
+ {
+ 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))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static bool CheckBit(byte* maskBase, int index)
+ {
+ var byteIndex = index >> Chunk.BIT_SHIFT;
+ var bitIndex = index & Chunk.BIT_ALIGNMENT_MINUS_ONE;
+ return (maskBase[byteIndex] & (1 << bitIndex)) != 0;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal void AddArchetypeIfMatch(ref readonly Archetype archetype)
+ {
+ if (_mask.Matches(in archetype._signature))
{
_matchingArchetypes.Add(archetype.ID);
}
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly ChunkIterator GetChunkIterator()
{
var world = World.GetWorld(_worldID).Value;
@@ -176,13 +346,16 @@ public unsafe partial struct EntityQuery : IIdentifierType, IDisposable
}
}
-public ref struct QueryBuilder
+public ref partial struct QueryBuilder
{
private readonly Stack.Scope _scope;
private UnsafeList> _all;
private UnsafeList> _any;
private UnsafeList> _absent;
+ private UnsafeList> _none;
+ private UnsafeList> _disabled;
+ private UnsafeList> _present;
public QueryBuilder()
{
@@ -191,68 +364,12 @@ public ref struct QueryBuilder
_all = new UnsafeList>(4, Allocator.Stack);
_any = new UnsafeList>(4, Allocator.Stack);
_absent = new UnsafeList>(4, Allocator.Stack);
+ _none = new UnsafeList>(4, Allocator.Stack);
+ _disabled = new UnsafeList>(4, Allocator.Stack);
+ _present = new UnsafeList>(4, Allocator.Stack);
}
- public QueryBuilder WithAll()
- where T : unmanaged, IComponent
- {
- _all.Add(ComponentTypeID.value);
- return this;
- }
-
- public QueryBuilder WithAny()
- where T : unmanaged, IComponent
- {
- _any.Add(ComponentTypeID.value);
- return this;
- }
-
- public QueryBuilder WithNone()
- where T : unmanaged, IComponent
- {
- _absent.Add(ComponentTypeID.value);
- return this;
- }
-
- public Identifier Build(World world)
- {
- // 1. Calculate max component ID to size the BitSets
- int maxID = 0;
- FindMax(_all, ref maxID);
- FindMax(_any, ref maxID);
- FindMax(_absent, ref maxID);
-
- // 2. Create the Mask
- using var mask = new EntityQueryMask
- {
- all = new UnsafeBitSet(maxID + 1, Allocator.Stack),
- any = new UnsafeBitSet(maxID + 1, Allocator.Stack),
- absent = new UnsafeBitSet(maxID + 1, Allocator.Stack)
- };
-
- // 3. Fill BitSets
- foreach (var id in _all) mask.all.SetBit(id);
- foreach (var id in _any) mask.any.SetBit(id);
- foreach (var id in _absent) mask.absent.SetBit(id);
-
- // 4. Ask World for the Query (Cached)
- var queryID = world.GetEntityQueryIDByMaskHash(mask.GetHashCode());
- if (queryID.IsNotValid)
- {
- queryID = world.CreateEntityQuery(mask);
- ref var query = ref world.GetEntityQueryReference(queryID);
- for (var i = 0; i < world.ArchetypeCount; i++)
- {
- ref var archetype = ref world.GetArchetypeReference(i);
- query.AddArchetypeIfMatch(archetype);
- }
- }
-
- Dispose();
-
- return queryID;
- }
-
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void FindMax(UnsafeList> list, ref int max)
{
foreach (var id in list)
@@ -261,11 +378,98 @@ public ref struct QueryBuilder
}
}
+ public Identifier Build(World world)
+ {
+ // 1. Calculate max component ID to size the BitSets
+ var maxID = 0;
+ FindMax(_all, ref maxID);
+ FindMax(_any, ref maxID);
+ FindMax(_absent, ref maxID);
+ FindMax(_none, ref maxID);
+ FindMax(_disabled, ref maxID);
+ FindMax(_present, ref maxID);
+
+ // 2. Create the Mask
+ var mask = new EntityQueryMask
+ {
+ structuralAll = new UnsafeBitSet(maxID + 1, Allocator.Persistent, AllocationOption.Clear),
+ structuralAny = new UnsafeBitSet(maxID + 1, Allocator.Persistent, AllocationOption.Clear),
+ structuralAbsent = new UnsafeBitSet(maxID + 1, Allocator.Persistent, AllocationOption.Clear),
+ requireEnabled = new UnsafeBitSet(maxID + 1, Allocator.Persistent, AllocationOption.Clear),
+ requireDisabled = new UnsafeBitSet(maxID + 1, Allocator.Persistent, AllocationOption.Clear),
+ rejectIfEnabled = new UnsafeBitSet(maxID + 1, Allocator.Persistent, AllocationOption.Clear),
+ };
+
+ // 3. Fill BitSets
+ foreach (var id in _all)
+ {
+ mask.structuralAll.SetBit(id); // Structure: Must Exist
+ mask.requireEnabled.SetBit(id); // Filter: Must be Enabled
+ }
+
+ foreach (var id in _disabled)
+ {
+ mask.structuralAll.SetBit(id); // Structure: Must Exist
+ mask.requireDisabled.SetBit(id); // Filter: Must be Disabled
+ }
+
+ foreach (var id in _none)
+ {
+ if (ComponentRegister.GetComponentInfo(id).isEnableable)
+ {
+ mask.rejectIfEnabled.SetBit(id); // Filter: Must Not be Enabled (Can be Absent or Disabled)
+ }
+ else
+ {
+ mask.structuralAbsent.SetBit(id); // Structure: Must Not Exist
+ }
+ }
+
+ foreach (var id in _present)
+ {
+ mask.structuralAll.SetBit(id);
+ }
+
+ foreach (var id in _absent)
+ {
+ mask.structuralAbsent.SetBit(id);
+ }
+
+ foreach (var id in _any)
+ {
+ mask.structuralAny.SetBit(id);
+ }
+
+ // 4. Ask World for the Query (Cached)
+ var maskHash = mask.GetHashCode();
+ var queryID = world.GetEntityQueryIDByMaskHash(maskHash);
+ if (queryID.IsValid)
+ {
+ // Check if the masks are actually equal (Hash collision?).
+ // Really worth it? It's unlikely to have collisions here.
+ if (world.GetEntityQueryReference(queryID)._mask.Equals(mask))
+ {
+ mask.Dispose();
+ goto Return;
+ }
+ }
+
+ // NOTE: We do not dispose the mask here, as it is now owned by the EntityQuery.
+ queryID = world.CreateEntityQuery(mask, maskHash);
+
+ Return:
+ Dispose();
+ return queryID;
+ }
+
private void Dispose()
{
_all.Dispose();
_any.Dispose();
_absent.Dispose();
+ _none.Dispose();
+ _disabled.Dispose();
+ _present.Dispose();
_scope.Dispose();
}
diff --git a/Ghost.Entities/Templates/EntityQuery.ForEach.gen.cs b/Ghost.Entities/Templates/EntityQuery.ForEach.gen.cs
index 6345423..9b3af5b 100644
--- a/Ghost.Entities/Templates/EntityQuery.ForEach.gen.cs
+++ b/Ghost.Entities/Templates/EntityQuery.ForEach.gen.cs
@@ -14,18 +14,20 @@ public unsafe partial struct EntityQuery
var offsets = stackalloc int[1];
var basePtrs = stackalloc byte*[1];
- foreach (var archetypeID in _matchingArchetypes)
+ for (var i = 0; i < _matchingArchetypes.Count; i++)
{
- ref var archetype = ref world.GetArchetypeReference(archetypeID);
+ ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
var hasAllComponents = true;
for (var index = 0; index < 1; index++)
{
- offsets[index] = archetype.GetOffset(compTypeIDs[index]);
- if (offsets[index] == -1)
+ var layoutResult = archetype.GetLayout(compTypeIDs[index]);
+ if (layoutResult.Status != ResultStatus.Success)
{
hasAllComponents = false;
break;
}
+
+ offsets[index] = layoutResult.Value.offset;
}
if (!hasAllComponents)
@@ -36,14 +38,20 @@ public unsafe partial struct EntityQuery
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
{
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
- var count = chunk.Count;
+ var pChunkData = chunk.GetUnsafePtr();
+
for (var index = 0; index < 1; index++)
{
- basePtrs[index] = chunk.GetUnsafePtr() + offsets[index];
+ basePtrs[index] = pChunkData + offsets[index];
}
- for (var entityIndex = 0; entityIndex < count; entityIndex++)
+ for (var entityIndex = 0; entityIndex < chunk.Count; entityIndex++)
{
+ if (!IsEntityValid(pChunkData, entityIndex, in archetype, in _mask))
+ {
+ continue;
+ }
+
var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex));
action(ref *pComp0);
@@ -62,18 +70,20 @@ public unsafe partial struct EntityQuery
var offsets = stackalloc int[2];
var basePtrs = stackalloc byte*[2];
- foreach (var archetypeID in _matchingArchetypes)
+ for (var i = 0; i < _matchingArchetypes.Count; i++)
{
- ref var archetype = ref world.GetArchetypeReference(archetypeID);
+ ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
var hasAllComponents = true;
for (var index = 0; index < 2; index++)
{
- offsets[index] = archetype.GetOffset(compTypeIDs[index]);
- if (offsets[index] == -1)
+ var layoutResult = archetype.GetLayout(compTypeIDs[index]);
+ if (layoutResult.Status != ResultStatus.Success)
{
hasAllComponents = false;
break;
}
+
+ offsets[index] = layoutResult.Value.offset;
}
if (!hasAllComponents)
@@ -84,14 +94,20 @@ public unsafe partial struct EntityQuery
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
{
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
- var count = chunk.Count;
+ var pChunkData = chunk.GetUnsafePtr();
+
for (var index = 0; index < 2; index++)
{
- basePtrs[index] = chunk.GetUnsafePtr() + offsets[index];
+ basePtrs[index] = pChunkData + offsets[index];
}
- for (var entityIndex = 0; entityIndex < count; entityIndex++)
+ for (var entityIndex = 0; entityIndex < chunk.Count; entityIndex++)
{
+ if (!IsEntityValid(pChunkData, entityIndex, in archetype, in _mask))
+ {
+ continue;
+ }
+
var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex));
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
@@ -112,18 +128,20 @@ public unsafe partial struct EntityQuery
var offsets = stackalloc int[3];
var basePtrs = stackalloc byte*[3];
- foreach (var archetypeID in _matchingArchetypes)
+ for (var i = 0; i < _matchingArchetypes.Count; i++)
{
- ref var archetype = ref world.GetArchetypeReference(archetypeID);
+ ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
var hasAllComponents = true;
for (var index = 0; index < 3; index++)
{
- offsets[index] = archetype.GetOffset(compTypeIDs[index]);
- if (offsets[index] == -1)
+ var layoutResult = archetype.GetLayout(compTypeIDs[index]);
+ if (layoutResult.Status != ResultStatus.Success)
{
hasAllComponents = false;
break;
}
+
+ offsets[index] = layoutResult.Value.offset;
}
if (!hasAllComponents)
@@ -134,14 +152,20 @@ public unsafe partial struct EntityQuery
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
{
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
- var count = chunk.Count;
+ var pChunkData = chunk.GetUnsafePtr();
+
for (var index = 0; index < 3; index++)
{
- basePtrs[index] = chunk.GetUnsafePtr() + offsets[index];
+ basePtrs[index] = pChunkData + offsets[index];
}
- for (var entityIndex = 0; entityIndex < count; entityIndex++)
+ for (var entityIndex = 0; entityIndex < chunk.Count; entityIndex++)
{
+ if (!IsEntityValid(pChunkData, entityIndex, in archetype, in _mask))
+ {
+ continue;
+ }
+
var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex));
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex));
@@ -164,18 +188,20 @@ public unsafe partial struct EntityQuery
var offsets = stackalloc int[4];
var basePtrs = stackalloc byte*[4];
- foreach (var archetypeID in _matchingArchetypes)
+ for (var i = 0; i < _matchingArchetypes.Count; i++)
{
- ref var archetype = ref world.GetArchetypeReference(archetypeID);
+ ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
var hasAllComponents = true;
for (var index = 0; index < 4; index++)
{
- offsets[index] = archetype.GetOffset(compTypeIDs[index]);
- if (offsets[index] == -1)
+ var layoutResult = archetype.GetLayout(compTypeIDs[index]);
+ if (layoutResult.Status != ResultStatus.Success)
{
hasAllComponents = false;
break;
}
+
+ offsets[index] = layoutResult.Value.offset;
}
if (!hasAllComponents)
@@ -186,14 +212,20 @@ public unsafe partial struct EntityQuery
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
{
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
- var count = chunk.Count;
+ var pChunkData = chunk.GetUnsafePtr();
+
for (var index = 0; index < 4; index++)
{
- basePtrs[index] = chunk.GetUnsafePtr() + offsets[index];
+ basePtrs[index] = pChunkData + offsets[index];
}
- for (var entityIndex = 0; entityIndex < count; entityIndex++)
+ for (var entityIndex = 0; entityIndex < chunk.Count; entityIndex++)
{
+ if (!IsEntityValid(pChunkData, entityIndex, in archetype, in _mask))
+ {
+ continue;
+ }
+
var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex));
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex));
@@ -218,18 +250,20 @@ public unsafe partial struct EntityQuery
var offsets = stackalloc int[5];
var basePtrs = stackalloc byte*[5];
- foreach (var archetypeID in _matchingArchetypes)
+ for (var i = 0; i < _matchingArchetypes.Count; i++)
{
- ref var archetype = ref world.GetArchetypeReference(archetypeID);
+ ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
var hasAllComponents = true;
for (var index = 0; index < 5; index++)
{
- offsets[index] = archetype.GetOffset(compTypeIDs[index]);
- if (offsets[index] == -1)
+ var layoutResult = archetype.GetLayout(compTypeIDs[index]);
+ if (layoutResult.Status != ResultStatus.Success)
{
hasAllComponents = false;
break;
}
+
+ offsets[index] = layoutResult.Value.offset;
}
if (!hasAllComponents)
@@ -240,14 +274,20 @@ public unsafe partial struct EntityQuery
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
{
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
- var count = chunk.Count;
+ var pChunkData = chunk.GetUnsafePtr();
+
for (var index = 0; index < 5; index++)
{
- basePtrs[index] = chunk.GetUnsafePtr() + offsets[index];
+ basePtrs[index] = pChunkData + offsets[index];
}
- for (var entityIndex = 0; entityIndex < count; entityIndex++)
+ for (var entityIndex = 0; entityIndex < chunk.Count; entityIndex++)
{
+ if (!IsEntityValid(pChunkData, entityIndex, in archetype, in _mask))
+ {
+ continue;
+ }
+
var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex));
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex));
@@ -274,18 +314,20 @@ public unsafe partial struct EntityQuery
var offsets = stackalloc int[6];
var basePtrs = stackalloc byte*[6];
- foreach (var archetypeID in _matchingArchetypes)
+ for (var i = 0; i < _matchingArchetypes.Count; i++)
{
- ref var archetype = ref world.GetArchetypeReference(archetypeID);
+ ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
var hasAllComponents = true;
for (var index = 0; index < 6; index++)
{
- offsets[index] = archetype.GetOffset(compTypeIDs[index]);
- if (offsets[index] == -1)
+ var layoutResult = archetype.GetLayout(compTypeIDs[index]);
+ if (layoutResult.Status != ResultStatus.Success)
{
hasAllComponents = false;
break;
}
+
+ offsets[index] = layoutResult.Value.offset;
}
if (!hasAllComponents)
@@ -296,14 +338,20 @@ public unsafe partial struct EntityQuery
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
{
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
- var count = chunk.Count;
+ var pChunkData = chunk.GetUnsafePtr();
+
for (var index = 0; index < 6; index++)
{
- basePtrs[index] = chunk.GetUnsafePtr() + offsets[index];
+ basePtrs[index] = pChunkData + offsets[index];
}
- for (var entityIndex = 0; entityIndex < count; entityIndex++)
+ for (var entityIndex = 0; entityIndex < chunk.Count; entityIndex++)
{
+ if (!IsEntityValid(pChunkData, entityIndex, in archetype, in _mask))
+ {
+ continue;
+ }
+
var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex));
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex));
@@ -332,18 +380,20 @@ public unsafe partial struct EntityQuery
var offsets = stackalloc int[7];
var basePtrs = stackalloc byte*[7];
- foreach (var archetypeID in _matchingArchetypes)
+ for (var i = 0; i < _matchingArchetypes.Count; i++)
{
- ref var archetype = ref world.GetArchetypeReference(archetypeID);
+ ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
var hasAllComponents = true;
for (var index = 0; index < 7; index++)
{
- offsets[index] = archetype.GetOffset(compTypeIDs[index]);
- if (offsets[index] == -1)
+ var layoutResult = archetype.GetLayout(compTypeIDs[index]);
+ if (layoutResult.Status != ResultStatus.Success)
{
hasAllComponents = false;
break;
}
+
+ offsets[index] = layoutResult.Value.offset;
}
if (!hasAllComponents)
@@ -354,14 +404,20 @@ public unsafe partial struct EntityQuery
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
{
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
- var count = chunk.Count;
+ var pChunkData = chunk.GetUnsafePtr();
+
for (var index = 0; index < 7; index++)
{
- basePtrs[index] = chunk.GetUnsafePtr() + offsets[index];
+ basePtrs[index] = pChunkData + offsets[index];
}
- for (var entityIndex = 0; entityIndex < count; entityIndex++)
+ for (var entityIndex = 0; entityIndex < chunk.Count; entityIndex++)
{
+ if (!IsEntityValid(pChunkData, entityIndex, in archetype, in _mask))
+ {
+ continue;
+ }
+
var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex));
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex));
@@ -392,18 +448,20 @@ public unsafe partial struct EntityQuery
var offsets = stackalloc int[8];
var basePtrs = stackalloc byte*[8];
- foreach (var archetypeID in _matchingArchetypes)
+ for (var i = 0; i < _matchingArchetypes.Count; i++)
{
- ref var archetype = ref world.GetArchetypeReference(archetypeID);
+ ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
var hasAllComponents = true;
for (var index = 0; index < 8; index++)
{
- offsets[index] = archetype.GetOffset(compTypeIDs[index]);
- if (offsets[index] == -1)
+ var layoutResult = archetype.GetLayout(compTypeIDs[index]);
+ if (layoutResult.Status != ResultStatus.Success)
{
hasAllComponents = false;
break;
}
+
+ offsets[index] = layoutResult.Value.offset;
}
if (!hasAllComponents)
@@ -414,14 +472,20 @@ public unsafe partial struct EntityQuery
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
{
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
- var count = chunk.Count;
+ var pChunkData = chunk.GetUnsafePtr();
+
for (var index = 0; index < 8; index++)
{
- basePtrs[index] = chunk.GetUnsafePtr() + offsets[index];
+ basePtrs[index] = pChunkData + offsets[index];
}
- for (var entityIndex = 0; entityIndex < count; entityIndex++)
+ for (var entityIndex = 0; entityIndex < chunk.Count; entityIndex++)
{
+ if (!IsEntityValid(pChunkData, entityIndex, in archetype, in _mask))
+ {
+ continue;
+ }
+
var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex));
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex));
@@ -437,7 +501,6 @@ public unsafe partial struct EntityQuery
}
}
-
public readonly void ForEach(ForEachWithEntity action)
where T0 : unmanaged, IComponent
{
@@ -447,18 +510,20 @@ public unsafe partial struct EntityQuery
var offsets = stackalloc int[1];
var basePtrs = stackalloc byte*[1];
- foreach (var archetypeID in _matchingArchetypes)
+ for (var i = 0; i < _matchingArchetypes.Count; i++)
{
- ref var archetype = ref world.GetArchetypeReference(archetypeID);
+ ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
var hasAllComponents = true;
for (var index = 0; index < 1; index++)
{
- offsets[index] = archetype.GetOffset(compTypeIDs[index]);
- if (offsets[index] == -1)
+ var layoutResult = archetype.GetLayout(compTypeIDs[index]);
+ if (layoutResult.Status != ResultStatus.Success)
{
hasAllComponents = false;
break;
}
+
+ offsets[index] = layoutResult.Value.offset;
}
if (!hasAllComponents)
@@ -469,17 +534,23 @@ public unsafe partial struct EntityQuery
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
{
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
- var count = chunk.Count;
+ var pChunkData = chunk.GetUnsafePtr();
+
for (var index = 0; index < 1; index++)
{
- basePtrs[index] = chunk.GetUnsafePtr() + offsets[index];
+ basePtrs[index] = pChunkData + offsets[index];
}
- for (var entityIndex = 0; entityIndex < count; entityIndex++)
+ for (var entityIndex = 0; entityIndex < chunk.Count; entityIndex++)
{
- var pEntity = (Entity*)(chunk.GetUnsafePtr() + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex));
+ if (!IsEntityValid(pChunkData, entityIndex, in archetype, in _mask))
+ {
+ continue;
+ }
+
var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex));
+ var pEntity = (Entity*)(pChunkData + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex));
action(*pEntity, ref *pComp0);
}
}
@@ -496,18 +567,20 @@ public unsafe partial struct EntityQuery
var offsets = stackalloc int[2];
var basePtrs = stackalloc byte*[2];
- foreach (var archetypeID in _matchingArchetypes)
+ for (var i = 0; i < _matchingArchetypes.Count; i++)
{
- ref var archetype = ref world.GetArchetypeReference(archetypeID);
+ ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
var hasAllComponents = true;
for (var index = 0; index < 2; index++)
{
- offsets[index] = archetype.GetOffset(compTypeIDs[index]);
- if (offsets[index] == -1)
+ var layoutResult = archetype.GetLayout(compTypeIDs[index]);
+ if (layoutResult.Status != ResultStatus.Success)
{
hasAllComponents = false;
break;
}
+
+ offsets[index] = layoutResult.Value.offset;
}
if (!hasAllComponents)
@@ -518,18 +591,24 @@ public unsafe partial struct EntityQuery
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
{
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
- var count = chunk.Count;
+ var pChunkData = chunk.GetUnsafePtr();
+
for (var index = 0; index < 2; index++)
{
- basePtrs[index] = chunk.GetUnsafePtr() + offsets[index];
+ basePtrs[index] = pChunkData + offsets[index];
}
- for (var entityIndex = 0; entityIndex < count; entityIndex++)
+ for (var entityIndex = 0; entityIndex < chunk.Count; entityIndex++)
{
- var pEntity = (Entity*)(chunk.GetUnsafePtr() + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex));
+ if (!IsEntityValid(pChunkData, entityIndex, in archetype, in _mask))
+ {
+ continue;
+ }
+
var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex));
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
+ var pEntity = (Entity*)(pChunkData + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex));
action(*pEntity, ref *pComp0,ref *pComp1);
}
}
@@ -547,18 +626,20 @@ public unsafe partial struct EntityQuery
var offsets = stackalloc int[3];
var basePtrs = stackalloc byte*[3];
- foreach (var archetypeID in _matchingArchetypes)
+ for (var i = 0; i < _matchingArchetypes.Count; i++)
{
- ref var archetype = ref world.GetArchetypeReference(archetypeID);
+ ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
var hasAllComponents = true;
for (var index = 0; index < 3; index++)
{
- offsets[index] = archetype.GetOffset(compTypeIDs[index]);
- if (offsets[index] == -1)
+ var layoutResult = archetype.GetLayout(compTypeIDs[index]);
+ if (layoutResult.Status != ResultStatus.Success)
{
hasAllComponents = false;
break;
}
+
+ offsets[index] = layoutResult.Value.offset;
}
if (!hasAllComponents)
@@ -569,19 +650,25 @@ public unsafe partial struct EntityQuery
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
{
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
- var count = chunk.Count;
+ var pChunkData = chunk.GetUnsafePtr();
+
for (var index = 0; index < 3; index++)
{
- basePtrs[index] = chunk.GetUnsafePtr() + offsets[index];
+ basePtrs[index] = pChunkData + offsets[index];
}
- for (var entityIndex = 0; entityIndex < count; entityIndex++)
+ for (var entityIndex = 0; entityIndex < chunk.Count; entityIndex++)
{
- var pEntity = (Entity*)(chunk.GetUnsafePtr() + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex));
+ if (!IsEntityValid(pChunkData, entityIndex, in archetype, in _mask))
+ {
+ continue;
+ }
+
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 pEntity = (Entity*)(pChunkData + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex));
action(*pEntity, ref *pComp0,ref *pComp1,ref *pComp2);
}
}
@@ -600,18 +687,20 @@ public unsafe partial struct EntityQuery
var offsets = stackalloc int[4];
var basePtrs = stackalloc byte*[4];
- foreach (var archetypeID in _matchingArchetypes)
+ for (var i = 0; i < _matchingArchetypes.Count; i++)
{
- ref var archetype = ref world.GetArchetypeReference(archetypeID);
+ ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
var hasAllComponents = true;
for (var index = 0; index < 4; index++)
{
- offsets[index] = archetype.GetOffset(compTypeIDs[index]);
- if (offsets[index] == -1)
+ var layoutResult = archetype.GetLayout(compTypeIDs[index]);
+ if (layoutResult.Status != ResultStatus.Success)
{
hasAllComponents = false;
break;
}
+
+ offsets[index] = layoutResult.Value.offset;
}
if (!hasAllComponents)
@@ -622,20 +711,26 @@ public unsafe partial struct EntityQuery
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
{
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
- var count = chunk.Count;
+ var pChunkData = chunk.GetUnsafePtr();
+
for (var index = 0; index < 4; index++)
{
- basePtrs[index] = chunk.GetUnsafePtr() + offsets[index];
+ basePtrs[index] = pChunkData + offsets[index];
}
- for (var entityIndex = 0; entityIndex < count; entityIndex++)
+ for (var entityIndex = 0; entityIndex < chunk.Count; entityIndex++)
{
- var pEntity = (Entity*)(chunk.GetUnsafePtr() + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex));
+ if (!IsEntityValid(pChunkData, entityIndex, in archetype, in _mask))
+ {
+ continue;
+ }
+
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 pEntity = (Entity*)(pChunkData + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex));
action(*pEntity, ref *pComp0,ref *pComp1,ref *pComp2,ref *pComp3);
}
}
@@ -655,18 +750,20 @@ public unsafe partial struct EntityQuery
var offsets = stackalloc int[5];
var basePtrs = stackalloc byte*[5];
- foreach (var archetypeID in _matchingArchetypes)
+ for (var i = 0; i < _matchingArchetypes.Count; i++)
{
- ref var archetype = ref world.GetArchetypeReference(archetypeID);
+ ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
var hasAllComponents = true;
for (var index = 0; index < 5; index++)
{
- offsets[index] = archetype.GetOffset(compTypeIDs[index]);
- if (offsets[index] == -1)
+ var layoutResult = archetype.GetLayout(compTypeIDs[index]);
+ if (layoutResult.Status != ResultStatus.Success)
{
hasAllComponents = false;
break;
}
+
+ offsets[index] = layoutResult.Value.offset;
}
if (!hasAllComponents)
@@ -677,21 +774,27 @@ public unsafe partial struct EntityQuery
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
{
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
- var count = chunk.Count;
+ var pChunkData = chunk.GetUnsafePtr();
+
for (var index = 0; index < 5; index++)
{
- basePtrs[index] = chunk.GetUnsafePtr() + offsets[index];
+ basePtrs[index] = pChunkData + offsets[index];
}
- for (var entityIndex = 0; entityIndex < count; entityIndex++)
+ for (var entityIndex = 0; entityIndex < chunk.Count; entityIndex++)
{
- var pEntity = (Entity*)(chunk.GetUnsafePtr() + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex));
+ if (!IsEntityValid(pChunkData, entityIndex, in archetype, in _mask))
+ {
+ continue;
+ }
+
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 pEntity = (Entity*)(pChunkData + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex));
action(*pEntity, ref *pComp0,ref *pComp1,ref *pComp2,ref *pComp3,ref *pComp4);
}
}
@@ -712,18 +815,20 @@ public unsafe partial struct EntityQuery
var offsets = stackalloc int[6];
var basePtrs = stackalloc byte*[6];
- foreach (var archetypeID in _matchingArchetypes)
+ for (var i = 0; i < _matchingArchetypes.Count; i++)
{
- ref var archetype = ref world.GetArchetypeReference(archetypeID);
+ ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
var hasAllComponents = true;
for (var index = 0; index < 6; index++)
{
- offsets[index] = archetype.GetOffset(compTypeIDs[index]);
- if (offsets[index] == -1)
+ var layoutResult = archetype.GetLayout(compTypeIDs[index]);
+ if (layoutResult.Status != ResultStatus.Success)
{
hasAllComponents = false;
break;
}
+
+ offsets[index] = layoutResult.Value.offset;
}
if (!hasAllComponents)
@@ -734,15 +839,20 @@ public unsafe partial struct EntityQuery
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
{
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
- var count = chunk.Count;
+ var pChunkData = chunk.GetUnsafePtr();
+
for (var index = 0; index < 6; index++)
{
- basePtrs[index] = chunk.GetUnsafePtr() + offsets[index];
+ basePtrs[index] = pChunkData + offsets[index];
}
- for (var entityIndex = 0; entityIndex < count; entityIndex++)
+ for (var entityIndex = 0; entityIndex < chunk.Count; entityIndex++)
{
- var pEntity = (Entity*)(chunk.GetUnsafePtr() + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex));
+ if (!IsEntityValid(pChunkData, entityIndex, in archetype, in _mask))
+ {
+ continue;
+ }
+
var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex));
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex));
@@ -750,6 +860,7 @@ public unsafe partial struct EntityQuery
var pComp4 = (T4*)(basePtrs[4] + (sizeof(T4) * entityIndex));
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);
}
}
@@ -771,18 +882,20 @@ public unsafe partial struct EntityQuery
var offsets = stackalloc int[7];
var basePtrs = stackalloc byte*[7];
- foreach (var archetypeID in _matchingArchetypes)
+ for (var i = 0; i < _matchingArchetypes.Count; i++)
{
- ref var archetype = ref world.GetArchetypeReference(archetypeID);
+ ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
var hasAllComponents = true;
for (var index = 0; index < 7; index++)
{
- offsets[index] = archetype.GetOffset(compTypeIDs[index]);
- if (offsets[index] == -1)
+ var layoutResult = archetype.GetLayout(compTypeIDs[index]);
+ if (layoutResult.Status != ResultStatus.Success)
{
hasAllComponents = false;
break;
}
+
+ offsets[index] = layoutResult.Value.offset;
}
if (!hasAllComponents)
@@ -793,15 +906,20 @@ public unsafe partial struct EntityQuery
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
{
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
- var count = chunk.Count;
+ var pChunkData = chunk.GetUnsafePtr();
+
for (var index = 0; index < 7; index++)
{
- basePtrs[index] = chunk.GetUnsafePtr() + offsets[index];
+ basePtrs[index] = pChunkData + offsets[index];
}
- for (var entityIndex = 0; entityIndex < count; entityIndex++)
+ for (var entityIndex = 0; entityIndex < chunk.Count; entityIndex++)
{
- var pEntity = (Entity*)(chunk.GetUnsafePtr() + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex));
+ if (!IsEntityValid(pChunkData, entityIndex, in archetype, in _mask))
+ {
+ continue;
+ }
+
var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex));
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex));
@@ -810,6 +928,7 @@ public unsafe partial struct EntityQuery
var pComp5 = (T5*)(basePtrs[5] + (sizeof(T5) * entityIndex));
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);
}
}
@@ -832,18 +951,20 @@ public unsafe partial struct EntityQuery
var offsets = stackalloc int[8];
var basePtrs = stackalloc byte*[8];
- foreach (var archetypeID in _matchingArchetypes)
+ for (var i = 0; i < _matchingArchetypes.Count; i++)
{
- ref var archetype = ref world.GetArchetypeReference(archetypeID);
+ ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
var hasAllComponents = true;
for (var index = 0; index < 8; index++)
{
- offsets[index] = archetype.GetOffset(compTypeIDs[index]);
- if (offsets[index] == -1)
+ var layoutResult = archetype.GetLayout(compTypeIDs[index]);
+ if (layoutResult.Status != ResultStatus.Success)
{
hasAllComponents = false;
break;
}
+
+ offsets[index] = layoutResult.Value.offset;
}
if (!hasAllComponents)
@@ -854,15 +975,20 @@ public unsafe partial struct EntityQuery
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
{
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
- var count = chunk.Count;
+ var pChunkData = chunk.GetUnsafePtr();
+
for (var index = 0; index < 8; index++)
{
- basePtrs[index] = chunk.GetUnsafePtr() + offsets[index];
+ basePtrs[index] = pChunkData + offsets[index];
}
- for (var entityIndex = 0; entityIndex < count; entityIndex++)
+ for (var entityIndex = 0; entityIndex < chunk.Count; entityIndex++)
{
- var pEntity = (Entity*)(chunk.GetUnsafePtr() + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex));
+ if (!IsEntityValid(pChunkData, entityIndex, in archetype, in _mask))
+ {
+ continue;
+ }
+
var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex));
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex));
@@ -872,6 +998,7 @@ public unsafe partial struct EntityQuery
var pComp6 = (T6*)(basePtrs[6] + (sizeof(T6) * entityIndex));
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);
}
}
diff --git a/Ghost.Entities/Templates/EntityQuery.ForEach.tt b/Ghost.Entities/Templates/EntityQuery.ForEach.tt
index 5294b35..88ed12a 100644
--- a/Ghost.Entities/Templates/EntityQuery.ForEach.tt
+++ b/Ghost.Entities/Templates/EntityQuery.ForEach.tt
@@ -10,13 +10,19 @@ namespace Ghost.Entities;
public unsafe partial struct EntityQuery
{
+<# for (var f = 0; f < 2; f++)
+{
+ var isForEachWithEntity = f != 0;
+#>
<# for (var i = 1; i <= Amount; i++)
{
var generics = AppendGenerics(i);
var compGenerics = AppendGenericRefParameters(i);
var restrictions = AppendGenericRestrictionsMultiline(i, "unmanaged, IComponent", 2);
+
+ var delegateTupe = isForEachWithEntity ? "ForEachWithEntity" : "ForEach";
#>
- public readonly void ForEach<<#= generics #>>(ForEach<<#= generics #>> action)
+ public readonly void ForEach<<#= generics #>>(<#= delegateTupe #><<#= generics #>> action)
<#= restrictions #>
{
var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success);
@@ -25,18 +31,20 @@ public unsafe partial struct EntityQuery
var offsets = stackalloc int[<#= i #>];
var basePtrs = stackalloc byte*[<#= i #>];
- foreach (var archetypeID in _matchingArchetypes)
+ for (var i = 0; i < _matchingArchetypes.Count; i++)
{
- ref var archetype = ref world.GetArchetypeReference(archetypeID);
+ ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
var hasAllComponents = true;
for (var index = 0; index < <#= i #>; index++)
{
- offsets[index] = archetype.GetOffset(compTypeIDs[index]);
- if (offsets[index] == -1)
+ var layoutResult = archetype.GetLayout(compTypeIDs[index]);
+ if (layoutResult.Status != ResultStatus.Success)
{
hasAllComponents = false;
break;
}
+
+ offsets[index] = layoutResult.Value.offset;
}
if (!hasAllComponents)
@@ -47,81 +55,35 @@ public unsafe partial struct EntityQuery
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
{
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
- var count = chunk.Count;
+ var pChunkData = chunk.GetUnsafePtr();
+
for (var index = 0; index < <#= i #>; index++)
{
- basePtrs[index] = chunk.GetUnsafePtr() + offsets[index];
+ basePtrs[index] = pChunkData + offsets[index];
}
- for (var entityIndex = 0; entityIndex < count; entityIndex++)
+ for (var entityIndex = 0; entityIndex < chunk.Count; entityIndex++)
{
+ if (!IsEntityValid(pChunkData, entityIndex, in archetype, in _mask))
+ {
+ continue;
+ }
+
<# 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));
-<# } #>
-
+<# if (isForEachWithEntity) { #>
+ var pEntity = (Entity*)(pChunkData + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex));
action(*pEntity, <#= AppendRefParameters(i, "*pComp{0}") #>);
+<# } else { #>
+ action(<#= AppendRefParameters(i, "*pComp{0}") #>);
+<# } #>
}
}
}
}
+<# } #>
<# } #>
}
\ No newline at end of file
diff --git a/Ghost.Entities/Templates/QueryBuilder.With.gen.cs b/Ghost.Entities/Templates/QueryBuilder.With.gen.cs
new file mode 100644
index 0000000..2c79ab8
--- /dev/null
+++ b/Ghost.Entities/Templates/QueryBuilder.With.gen.cs
@@ -0,0 +1,278 @@
+
+using System.Runtime.CompilerServices;
+
+namespace Ghost.Entities;
+
+public ref partial struct QueryBuilder
+{
+ ///
+ /// Adds the specified component type(s) to the 'All' filter of the query.
+ /// Targets entities that have all of the specified component types and those component(s) must be enabled.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public QueryBuilder WithAll()
+ where T0 : unmanaged, IComponent
+ {
+ _all.Add(ComponentTypeID.value);
+
+ return this;
+ }
+
+ ///
+ /// Adds the specified component type(s) to the 'Any' filter of the query.
+ /// Targets entities that have at least one of the specified component types and those component(s) must be enabled.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public QueryBuilder WithAny()
+ where T0 : unmanaged, IComponent
+ {
+ _any.Add(ComponentTypeID.value);
+
+ return this;
+ }
+
+ ///
+ /// Adds the specified component type(s) to the 'Absent' filter of the query.
+ /// Targets entities that do not have any of the specified component types.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public QueryBuilder WithAbsent()
+ where T0 : unmanaged, IComponent
+ {
+ _absent.Add(ComponentTypeID.value);
+
+ return this;
+ }
+
+ ///
+ /// Adds the specified component type(s) to the 'None' filter of the query.
+ /// Targets entities that do not have any of the specified component types, or those component(s) are disabled.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public QueryBuilder WithNone()
+ where T0 : unmanaged, IComponent
+ {
+ _none.Add(ComponentTypeID.value);
+
+ return this;
+ }
+
+ ///
+ /// Adds the specified component type(s) to the 'Disabled' filter of the query.
+ /// Targets entities that have all of the specified component types and those component(s) are disabled.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public QueryBuilder WithDisabled()
+ where T0 : unmanaged, IEnableableComponent
+ {
+ _disabled.Add(ComponentTypeID.value);
+
+ return this;
+ }
+
+ ///
+ /// Adds the specified component type(s) to the 'Present' filter of the query.
+ /// Targets entities that have all of the specified component types, regardless of whether those component(s) are enabled or disabled.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public QueryBuilder WithPresent()
+ where T0 : unmanaged, IComponent
+ {
+ _present.Add(ComponentTypeID.value);
+
+ return this;
+ }
+
+ ///
+ /// Adds the specified component type(s) to the 'All' filter of the query.
+ /// Targets entities that have all of the specified component types and those component(s) must be enabled.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public QueryBuilder WithAll()
+ where T0 : unmanaged, IComponent
+ where T1 : unmanaged, IComponent
+ {
+ _all.Add(ComponentTypeID.value);
+ _all.Add(ComponentTypeID.value);
+
+ return this;
+ }
+
+ ///
+ /// Adds the specified component type(s) to the 'Any' filter of the query.
+ /// Targets entities that have at least one of the specified component types and those component(s) must be enabled.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public QueryBuilder WithAny()
+ where T0 : unmanaged, IComponent
+ where T1 : unmanaged, IComponent
+ {
+ _any.Add(ComponentTypeID.value);
+ _any.Add(ComponentTypeID.value);
+
+ return this;
+ }
+
+ ///
+ /// Adds the specified component type(s) to the 'Absent' filter of the query.
+ /// Targets entities that do not have any of the specified component types.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public QueryBuilder WithAbsent()
+ where T0 : unmanaged, IComponent
+ where T1 : unmanaged, IComponent
+ {
+ _absent.Add(ComponentTypeID.value);
+ _absent.Add(ComponentTypeID.value);
+
+ return this;
+ }
+
+ ///
+ /// Adds the specified component type(s) to the 'None' filter of the query.
+ /// Targets entities that do not have any of the specified component types, or those component(s) are disabled.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public QueryBuilder WithNone()
+ where T0 : unmanaged, IComponent
+ where T1 : unmanaged, IComponent
+ {
+ _none.Add(ComponentTypeID.value);
+ _none.Add(ComponentTypeID.value);
+
+ return this;
+ }
+
+ ///
+ /// Adds the specified component type(s) to the 'Disabled' filter of the query.
+ /// Targets entities that have all of the specified component types and those component(s) are disabled.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public QueryBuilder WithDisabled()
+ where T0 : unmanaged, IEnableableComponent
+ where T1 : unmanaged, IEnableableComponent
+ {
+ _disabled.Add(ComponentTypeID.value);
+ _disabled.Add(ComponentTypeID.value);
+
+ return this;
+ }
+
+ ///
+ /// Adds the specified component type(s) to the 'Present' filter of the query.
+ /// Targets entities that have all of the specified component types, regardless of whether those component(s) are enabled or disabled.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public QueryBuilder WithPresent()
+ where T0 : unmanaged, IComponent
+ where T1 : unmanaged, IComponent
+ {
+ _present.Add(ComponentTypeID.value);
+ _present.Add(ComponentTypeID.value);
+
+ return this;
+ }
+
+ ///
+ /// Adds the specified component type(s) to the 'All' filter of the query.
+ /// Targets entities that have all of the specified component types and those component(s) must be enabled.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public QueryBuilder WithAll()
+ where T0 : unmanaged, IComponent
+ where T1 : unmanaged, IComponent
+ where T2 : unmanaged, IComponent
+ {
+ _all.Add(ComponentTypeID.value);
+ _all.Add(ComponentTypeID.value);
+ _all.Add(ComponentTypeID.value);
+
+ return this;
+ }
+
+ ///
+ /// Adds the specified component type(s) to the 'Any' filter of the query.
+ /// Targets entities that have at least one of the specified component types and those component(s) must be enabled.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public QueryBuilder WithAny()
+ where T0 : unmanaged, IComponent
+ where T1 : unmanaged, IComponent
+ where T2 : unmanaged, IComponent
+ {
+ _any.Add(ComponentTypeID.value);
+ _any.Add(ComponentTypeID.value);
+ _any.Add(ComponentTypeID.value);
+
+ return this;
+ }
+
+ ///
+ /// Adds the specified component type(s) to the 'Absent' filter of the query.
+ /// Targets entities that do not have any of the specified component types.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public QueryBuilder WithAbsent()
+ where T0 : unmanaged, IComponent
+ where T1 : unmanaged, IComponent
+ where T2 : unmanaged, IComponent
+ {
+ _absent.Add(ComponentTypeID.value);
+ _absent.Add(ComponentTypeID.value);
+ _absent.Add(ComponentTypeID.value);
+
+ return this;
+ }
+
+ ///
+ /// Adds the specified component type(s) to the 'None' filter of the query.
+ /// Targets entities that do not have any of the specified component types, or those component(s) are disabled.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public QueryBuilder WithNone()
+ where T0 : unmanaged, IComponent
+ where T1 : unmanaged, IComponent
+ where T2 : unmanaged, IComponent
+ {
+ _none.Add(ComponentTypeID.value);
+ _none.Add(ComponentTypeID.value);
+ _none.Add(ComponentTypeID.value);
+
+ return this;
+ }
+
+ ///
+ /// Adds the specified component type(s) to the 'Disabled' filter of the query.
+ /// Targets entities that have all of the specified component types and those component(s) are disabled.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public QueryBuilder WithDisabled()
+ where T0 : unmanaged, IEnableableComponent
+ where T1 : unmanaged, IEnableableComponent
+ where T2 : unmanaged, IEnableableComponent
+ {
+ _disabled.Add(ComponentTypeID.value);
+ _disabled.Add(ComponentTypeID.value);
+ _disabled.Add(ComponentTypeID.value);
+
+ return this;
+ }
+
+ ///
+ /// Adds the specified component type(s) to the 'Present' filter of the query.
+ /// Targets entities that have all of the specified component types, regardless of whether those component(s) are enabled or disabled.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public QueryBuilder WithPresent()
+ where T0 : unmanaged, IComponent
+ where T1 : unmanaged, IComponent
+ where T2 : unmanaged, IComponent
+ {
+ _present.Add(ComponentTypeID.value);
+ _present.Add(ComponentTypeID.value);
+ _present.Add(ComponentTypeID.value);
+
+ return this;
+ }
+
+}
\ No newline at end of file
diff --git a/Ghost.Entities/Templates/QueryBuilder.With.tt b/Ghost.Entities/Templates/QueryBuilder.With.tt
new file mode 100644
index 0000000..dbed074
--- /dev/null
+++ b/Ghost.Entities/Templates/QueryBuilder.With.tt
@@ -0,0 +1,110 @@
+<#@ template language="C#" #>
+<#@ output extension="gen.cs" #>
+<#@ assembly name="System.Core" #>
+<#@ import namespace="System.Linq" #>
+<#@ import namespace="System.Text" #>
+<#@ include file="Helpers.ttinclude" #>
+using System.Runtime.CompilerServices;
+
+namespace Ghost.Entities;
+
+public ref partial struct QueryBuilder
+{
+<# for (var i = 1; i <= 3; i++)
+{
+ var generics = AppendGenerics(i);
+ var restrictions = AppendGenericRestrictionsMultiline(i, "unmanaged, IComponent", 2);
+ var enableRestrictions = AppendGenericRestrictionsMultiline(i, "unmanaged, IEnableableComponent", 2);
+#>
+ ///
+ /// Adds the specified component type(s) to the 'All' filter of the query.
+ /// Targets entities that have all of the specified component types and those component(s) must be enabled.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public QueryBuilder WithAll<<#= generics #>>()
+<#= restrictions #>
+ {
+<# for (var j = 0; j < i; j++) { #>
+ _all.Add(ComponentTypeID>.value);
+<# } #>
+
+ return this;
+ }
+
+ ///
+ /// Adds the specified component type(s) to the 'Any' filter of the query.
+ /// Targets entities that have at least one of the specified component types and those component(s) must be enabled.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public QueryBuilder WithAny<<#= generics #>>()
+<#= restrictions #>
+ {
+<# for (var j = 0; j < i; j++) { #>
+ _any.Add(ComponentTypeID>.value);
+<# } #>
+
+ return this;
+ }
+
+ ///
+ /// Adds the specified component type(s) to the 'Absent' filter of the query.
+ /// Targets entities that do not have any of the specified component types.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public QueryBuilder WithAbsent<<#= generics #>>()
+<#= restrictions #>
+ {
+<# for (var j = 0; j < i; j++) { #>
+ _absent.Add(ComponentTypeID>.value);
+<# } #>
+
+ return this;
+ }
+
+ ///
+ /// Adds the specified component type(s) to the 'None' filter of the query.
+ /// Targets entities that do not have any of the specified component types, or those component(s) are disabled.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public QueryBuilder WithNone<<#= generics #>>()
+<#= restrictions #>
+ {
+<# for (var j = 0; j < i; j++) { #>
+ _none.Add(ComponentTypeID>.value);
+<# } #>
+
+ return this;
+ }
+
+ ///
+ /// Adds the specified component type(s) to the 'Disabled' filter of the query.
+ /// Targets entities that have all of the specified component types and those component(s) are disabled.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public QueryBuilder WithDisabled<<#= generics #>>()
+<#= enableRestrictions #>
+ {
+<# for (var j = 0; j < i; j++) { #>
+ _disabled.Add(ComponentTypeID>.value);
+<# } #>
+
+ return this;
+ }
+
+ ///
+ /// Adds the specified component type(s) to the 'Present' filter of the query.
+ /// Targets entities that have all of the specified component types, regardless of whether those component(s) are enabled or disabled.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public QueryBuilder WithPresent<<#= generics #>>()
+<#= restrictions #>
+ {
+<# for (var j = 0; j < i; j++) { #>
+ _present.Add(ComponentTypeID>.value);
+<# } #>
+
+ return this;
+ }
+
+<# } #>
+}
\ No newline at end of file
diff --git a/Ghost.Entities/World.cs b/Ghost.Entities/World.cs
index a3ffa16..e62d3da 100644
--- a/Ghost.Entities/World.cs
+++ b/Ghost.Entities/World.cs
@@ -2,6 +2,7 @@ using Ghost.Core;
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
using System.Runtime.CompilerServices;
+using TerraFX.Interop.Windows;
namespace Ghost.Entities;
@@ -110,6 +111,7 @@ public partial class World : IIdentifierType, IDisposable, IEquatable
Dispose();
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Identifier CreateArchetype(ReadOnlySpan> componentTypeIDs, int signatureHash)
{
var arcID = new Identifier(_archetypes.Count);
@@ -119,12 +121,13 @@ public partial class World : IIdentifierType, IDisposable, IEquatable
for (int i = 0; i < _entityQueries.Count; i++)
{
ref var query = ref _entityQueries[i];
- query.AddArchetypeIfMatch(_archetypes[arcID.value]);
+ query.AddArchetypeIfMatch(in _archetypes[arcID.value]);
}
return arcID;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Identifier GetArchetypeIDBySignatureHash(int signatureHash)
{
if (_archetypeLookup.TryGetValue(signatureHash, out var arcID))
@@ -135,20 +138,29 @@ public partial class World : IIdentifierType, IDisposable, IEquatable
return Identifier.Invalid;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ref Archetype GetArchetypeReference(Identifier id)
{
return ref _archetypes[id.value];
}
- internal Identifier CreateEntityQuery(EntityQueryMask mask)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal Identifier CreateEntityQuery(EntityQueryMask mask, int maskHash)
{
var queryID = new Identifier(_entityQueries.Count);
_entityQueries.Add(new EntityQuery(_id, mask));
- _querieLookup.Add(mask.GetHashCode(), queryID);
+ _querieLookup.Add(maskHash, queryID);
+
+ ref var query = ref _entityQueries[queryID.value];
+ for (var i = 0; i < _archetypes.Count; i++)
+ {
+ query.AddArchetypeIfMatch(in _archetypes[i]);
+ }
return queryID;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Identifier GetEntityQueryIDByMaskHash(int maskHash)
{
if (_querieLookup.TryGetValue(maskHash, out var queryID))
@@ -159,6 +171,7 @@ public partial class World : IIdentifierType, IDisposable, IEquatable
return Identifier.Invalid;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref EntityQuery GetEntityQueryReference(Identifier id)
{
return ref _entityQueries[id.value];