forked from Misaki/GhostEngine
Support enableable components and query enhancements
- Upgraded `Misaki.HighPerformance.LowLevel` to v1.2.8. - Added `IEquatable` to `Handle<T>` and `Identifier<T>`. - Improved `Result` extensions with `[CallerArgumentExpression]`. - Introduced `SetEnabled` in `EntityManager` to toggle components. - Refactored `Chunk` and `Archetype` for enableable components. - Added `EntityQueryMask` for filtering enabled/disabled components. - Enhanced `QueryBuilder` with new filtering methods (`WithAll`, etc.). - Improved `EntityQuery.ForEach` with entity validation.
This commit is contained in:
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Misaki.HighPerformance" Version="1.0.1" />
|
<PackageReference Include="Misaki.HighPerformance" Version="1.0.1" />
|
||||||
<PackageReference Include="Misaki.HighPerformance.LowLevel" Version="1.2.6" />
|
<PackageReference Include="Misaki.HighPerformance.LowLevel" Version="1.2.8" />
|
||||||
<PackageReference Include="Misaki.HighPerformance.Mathematics" Version="1.2.6" />
|
<PackageReference Include="Misaki.HighPerformance.Mathematics" Version="1.2.6" />
|
||||||
<PackageReference Include="System.IO.Hashing" Version="10.0.0" />
|
<PackageReference Include="System.IO.Hashing" Version="10.0.0" />
|
||||||
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.26100.5" />
|
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.26100.5" />
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ public interface IHandleType;
|
|||||||
public interface IIdentifierType;
|
public interface IIdentifierType;
|
||||||
public interface IKeyType;
|
public interface IKeyType;
|
||||||
|
|
||||||
public readonly struct Handle<T>
|
public readonly struct Handle<T> : IEquatable<Handle<T>>
|
||||||
where T : IHandleType
|
where T : IHandleType
|
||||||
{
|
{
|
||||||
public readonly int id;
|
public readonly int id;
|
||||||
@@ -57,7 +57,7 @@ public readonly struct Handle<T>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly struct Identifier<T>
|
public readonly struct Identifier<T> : IEquatable<Identifier<T>>
|
||||||
where T : IIdentifierType
|
where T : IIdentifierType
|
||||||
{
|
{
|
||||||
public readonly int value;
|
public readonly int value;
|
||||||
@@ -74,7 +74,7 @@ public readonly struct Identifier<T>
|
|||||||
|
|
||||||
public readonly override int GetHashCode()
|
public readonly override int GetHashCode()
|
||||||
{
|
{
|
||||||
return value.GetHashCode();
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly override bool Equals(object? obj)
|
public readonly override bool Equals(object? obj)
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using TerraFX.Interop.DirectX;
|
||||||
|
|
||||||
namespace Ghost.Core;
|
namespace Ghost.Core;
|
||||||
|
|
||||||
public readonly struct Result
|
public readonly struct Result
|
||||||
@@ -152,38 +155,38 @@ public readonly ref struct RefResult<T, S>
|
|||||||
|
|
||||||
public static class ResultExtensions
|
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)
|
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)
|
if (!result.IsSuccess)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"Operation failed: {result.Message}");
|
throw new InvalidOperationException($"{op} failed: {result.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T GetValueOrThrow<T>(this Result<T> result)
|
public static T GetValueOrThrow<T>(this Result<T> result, [CallerArgumentExpression(nameof(result))] string? op = null)
|
||||||
{
|
{
|
||||||
if (!result.IsSuccess)
|
if (!result.IsSuccess)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"Operation failed: {result.Message}");
|
throw new InvalidOperationException($"{op} failed: {result.Message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.Value;
|
return result.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T GetValueOrThrow<T, S>(this Result<T, S> result, S expect)
|
public static T GetValueOrThrow<T, S>(this Result<T, S> result, S expect, [CallerArgumentExpression(nameof(result))] string? op = null)
|
||||||
where S : Enum
|
where S : Enum
|
||||||
{
|
{
|
||||||
if (!EqualityComparer<S>.Default.Equals(result.Status, expect))
|
if (!EqualityComparer<S>.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;
|
return result.Value;
|
||||||
@@ -212,6 +215,19 @@ public static class ResultExtensions
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool TryGetValue<T, S>(this Result<T, S> result, S expect, out T value)
|
||||||
|
where S : Enum
|
||||||
|
{
|
||||||
|
if (EqualityComparer<S>.Default.Equals(result.Status, expect))
|
||||||
|
{
|
||||||
|
value = result.Value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = default!;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public static Result OnSuccess(this Result result, Action action)
|
public static Result OnSuccess(this Result result, Action action)
|
||||||
{
|
{
|
||||||
if (result.IsSuccess)
|
if (result.IsSuccess)
|
||||||
|
|||||||
@@ -15,28 +15,28 @@ public partial class ArcEntityTest : ITest
|
|||||||
public void Run()
|
public void Run()
|
||||||
{
|
{
|
||||||
var entity1 = _world.EntityManager.CreateEntity(ComponentTypeID<Transform>.value);
|
var entity1 = _world.EntityManager.CreateEntity(ComponentTypeID<Transform>.value);
|
||||||
Console.WriteLine(entity1);
|
_world.EntityManager.AddComponent(entity1, new Mesh { index = 1 });
|
||||||
_world.EntityManager.AddComponent<Mesh>(entity1, new Mesh { index = 1 });
|
|
||||||
|
|
||||||
var queryID = new QueryBuilder().WithAll<Transform>().Build(_world);
|
var queryID = new QueryBuilder().WithAll<Transform>().Build(_world);
|
||||||
ref var query = ref _world.GetEntityQueryReference(queryID);
|
ref var query = ref _world.GetEntityQueryReference(queryID);
|
||||||
|
|
||||||
|
query.ForEach<Transform>((ref t) =>
|
||||||
|
{
|
||||||
|
t.position = new float3(1, 2, 3);
|
||||||
|
});
|
||||||
|
|
||||||
foreach (var chunk in query.GetChunkIterator())
|
foreach (var chunk in query.GetChunkIterator())
|
||||||
{
|
{
|
||||||
var transforms = chunk.GetComponentData<Transform>();
|
var transforms = chunk.GetComponentData<Transform>();
|
||||||
var entities = chunk.GetEntities();
|
var entities = chunk.GetEntities();
|
||||||
|
var bits = chunk.GetEnableBits<Transform>();
|
||||||
for (var i = 0; i < chunk.Count; i++)
|
|
||||||
|
var it = bits.GetIterator();
|
||||||
|
while (it.Next(out var index) && index < chunk.Count)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Entity {entities[i]} Position: {transforms[i].position}");
|
Console.WriteLine($"Entity {entities[index]} Updated Position: {transforms[index].position}");
|
||||||
transforms[i].position = new float3(1, 2, 3);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
query.ForEach<Transform>((e, ref t) =>
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Entity {e} Updated Position: {t.position}");
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Cleanup()
|
public void Cleanup()
|
||||||
@@ -45,7 +45,7 @@ public partial class ArcEntityTest : ITest
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct Transform : IComponent
|
public struct Transform : IEnableableComponent
|
||||||
{
|
{
|
||||||
public float3 position;
|
public float3 position;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,13 @@ namespace Ghost.Entities;
|
|||||||
internal unsafe struct Chunk : IDisposable
|
internal unsafe struct Chunk : IDisposable
|
||||||
{
|
{
|
||||||
public const int CHUNK_SIZE = 16384; // 16 KB
|
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<byte> _data;
|
private UnsafeArray<byte> _data;
|
||||||
private int _count;
|
private int _count;
|
||||||
private int _capacity;
|
private readonly int _capacity;
|
||||||
|
|
||||||
public int Count
|
public int Count
|
||||||
{
|
{
|
||||||
@@ -41,21 +44,22 @@ internal unsafe struct Chunk : IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal struct Edge
|
|
||||||
{
|
|
||||||
public Identifier<IComponent> componentID;
|
|
||||||
public int targetArchetype; // can't use Identifier<Archetype> because cycle causer
|
|
||||||
}
|
|
||||||
|
|
||||||
internal struct ComponentMemoryLayout
|
|
||||||
{
|
|
||||||
public int offset;
|
|
||||||
public int size;
|
|
||||||
public Identifier<IComponent> componentID;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal unsafe struct Archetype : IIdentifierType, IDisposable
|
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<Archetype> because cycle causer
|
||||||
|
}
|
||||||
|
|
||||||
private readonly Identifier<Archetype> _id;
|
private readonly Identifier<Archetype> _id;
|
||||||
private readonly Identifier<World> _worldID;
|
private readonly Identifier<World> _worldID;
|
||||||
|
|
||||||
@@ -63,7 +67,7 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
|
|||||||
internal UnsafeList<Chunk> _chunks;
|
internal UnsafeList<Chunk> _chunks;
|
||||||
internal UnsafeArray<ComponentMemoryLayout> _layouts;
|
internal UnsafeArray<ComponentMemoryLayout> _layouts;
|
||||||
|
|
||||||
private UnsafeArray<int> _componentIDToOffset;
|
private UnsafeArray<int> _componentIDToLayoutIndex;
|
||||||
// TODO: Is hash map better?
|
// TODO: Is hash map better?
|
||||||
private UnsafeList<Edge> _edgesAdd;
|
private UnsafeList<Edge> _edgesAdd;
|
||||||
private UnsafeList<Edge> _edgesRemove;
|
private UnsafeList<Edge> _edgesRemove;
|
||||||
@@ -84,16 +88,16 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
|
|||||||
_id = id;
|
_id = id;
|
||||||
_worldID = worldID;
|
_worldID = worldID;
|
||||||
|
|
||||||
|
_chunks = new UnsafeList<Chunk>(4, Allocator.Persistent);
|
||||||
|
_edgesAdd = new UnsafeList<Edge>(4, Allocator.Persistent);
|
||||||
|
_edgesRemove = new UnsafeList<Edge>(4, Allocator.Persistent);
|
||||||
|
|
||||||
if (componentIds.IsEmpty)
|
if (componentIds.IsEmpty)
|
||||||
{
|
{
|
||||||
_signature = new UnsafeBitSet(1, Allocator.Persistent, AllocationOption.Clear);
|
_signature = new UnsafeBitSet(1, Allocator.Persistent, AllocationOption.Clear);
|
||||||
_chunks = new UnsafeList<Chunk>(4, Allocator.Persistent);
|
_hash = 0;
|
||||||
|
|
||||||
_edgesAdd = new UnsafeList<Edge>(4, Allocator.Persistent);
|
|
||||||
_edgesRemove = new UnsafeList<Edge>(4, Allocator.Persistent);
|
|
||||||
|
|
||||||
_signature.ClearAll();
|
_signature.ClearAll();
|
||||||
|
|
||||||
_entityCapacity = Chunk.CHUNK_SIZE / sizeof(Entity);
|
_entityCapacity = Chunk.CHUNK_SIZE / sizeof(Entity);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -109,28 +113,23 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
_signature = new UnsafeBitSet(highestComponentID + 1, Allocator.Persistent, AllocationOption.Clear);
|
_signature = new UnsafeBitSet(highestComponentID + 1, Allocator.Persistent, AllocationOption.Clear);
|
||||||
_chunks = new UnsafeList<Chunk>(4, Allocator.Persistent);
|
|
||||||
|
|
||||||
_edgesAdd = new UnsafeList<Edge>(4, Allocator.Persistent);
|
|
||||||
_edgesRemove = new UnsafeList<Edge>(4, Allocator.Persistent);
|
|
||||||
|
|
||||||
_hash = _signature.GetHashCode();
|
_hash = _signature.GetHashCode();
|
||||||
|
|
||||||
var pComponents = stackalloc ComponentInfo[componentIds.Length];
|
CalculateLayout(componentIds);
|
||||||
for (var i = 0; i < componentIds.Length; i++)
|
|
||||||
{
|
|
||||||
_signature.SetBit(componentIds[i]);
|
|
||||||
pComponents[i] = ComponentRegister.GetComponentInfo(componentIds[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
CalculateLayout(new Span<ComponentInfo>(pComponents, componentIds.Length));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CalculateLayout(Span<ComponentInfo> components)
|
private void CalculateLayout(ReadOnlySpan<Identifier<IComponent>> componentIds)
|
||||||
{
|
{
|
||||||
var entitySize = sizeof(Entity);
|
var entitySize = sizeof(Entity);
|
||||||
var entityAlign = (int)MemoryUtility.AlignOf<Entity>();
|
var entityAlign = (int)MemoryUtility.AlignOf<Entity>();
|
||||||
|
|
||||||
|
var components = (Span<ComponentInfo>)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
|
// Calculate total size per entity to get an initial capacity estimate
|
||||||
var bytesPerEntity = entitySize;
|
var bytesPerEntity = entitySize;
|
||||||
var maxComponentID = 0;
|
var maxComponentID = 0;
|
||||||
@@ -147,12 +146,13 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
|
|||||||
_maxComponentID = maxComponentID;
|
_maxComponentID = maxComponentID;
|
||||||
_entityCapacity = Chunk.CHUNK_SIZE / bytesPerEntity;
|
_entityCapacity = Chunk.CHUNK_SIZE / bytesPerEntity;
|
||||||
_layouts = new UnsafeArray<ComponentMemoryLayout>(components.Length, Allocator.Persistent);
|
_layouts = new UnsafeArray<ComponentMemoryLayout>(components.Length, Allocator.Persistent);
|
||||||
_componentIDToOffset = new UnsafeArray<int>(_maxComponentID + 1, Allocator.Persistent);
|
_componentIDToLayoutIndex = new UnsafeArray<int>(_maxComponentID + 1, Allocator.Persistent);
|
||||||
|
|
||||||
_componentIDToOffset.AsSpan().Fill(-1);
|
_componentIDToLayoutIndex.AsSpan().Fill(-1);
|
||||||
|
|
||||||
components.Sort((a, b) => b.alignment.CompareTo(a.alignment));
|
components.Sort((a, b) => b.alignment.CompareTo(a.alignment));
|
||||||
var tempOffsets = stackalloc int[components.Length];
|
var tempOffsets = stackalloc int[components.Length];
|
||||||
|
var tempBitmaskOffsets = stackalloc int[components.Length];
|
||||||
|
|
||||||
while (_entityCapacity > 0)
|
while (_entityCapacity > 0)
|
||||||
{
|
{
|
||||||
@@ -173,6 +173,19 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
|
|||||||
tempOffsets[i] = currentOffset;
|
tempOffsets[i] = currentOffset;
|
||||||
currentOffset += _entityCapacity * size;
|
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)
|
if (currentOffset > Chunk.CHUNK_SIZE)
|
||||||
{
|
{
|
||||||
fits = false;
|
fits = false;
|
||||||
@@ -188,10 +201,11 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
|
|||||||
{
|
{
|
||||||
offset = tempOffsets[i],
|
offset = tempOffsets[i],
|
||||||
size = components[i].size,
|
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;
|
return;
|
||||||
@@ -219,6 +233,18 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
|
|||||||
// Need to allocate a new chunk
|
// Need to allocate a new chunk
|
||||||
var newChunk = new Chunk(Chunk.CHUNK_SIZE, _entityCapacity);
|
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;
|
rowIndex = 0;
|
||||||
newChunk.Count++;
|
newChunk.Count++;
|
||||||
chunkIndex = _chunks.Count;
|
chunkIndex = _chunks.Count;
|
||||||
@@ -237,9 +263,15 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public readonly void SetComponentData(int chunkIndex, int rowIndex, Identifier<IComponent> componentID, void* pComponent)
|
public readonly ResultStatus SetComponentData(int chunkIndex, int rowIndex, Identifier<IComponent> 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 chunk = _chunks[chunkIndex];
|
||||||
|
|
||||||
var chunkBase = chunk.GetUnsafePtr();
|
var chunkBase = chunk.GetUnsafePtr();
|
||||||
@@ -247,19 +279,27 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
|
|||||||
var dst = chunkBase + offset + (size * rowIndex);
|
var dst = chunkBase + offset + (size * rowIndex);
|
||||||
|
|
||||||
MemoryUtility.MemCpy(pComponent, dst, (nuint)size);
|
MemoryUtility.MemCpy(pComponent, dst, (nuint)size);
|
||||||
|
|
||||||
|
return ResultStatus.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public readonly void* GetComponentDataPtr(int chunkIndex, int rowIndex, Identifier<IComponent> componentID)
|
public readonly ResultStatus GetComponentDataPtr(int chunkIndex, int rowIndex, Identifier<IComponent> 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 chunk = _chunks[chunkIndex];
|
||||||
|
|
||||||
var chunkBase = chunk.GetUnsafePtr();
|
var chunkBase = chunk.GetUnsafePtr();
|
||||||
var size = ComponentRegister.GetComponentInfo(componentID).size;
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@@ -269,14 +309,20 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public readonly int GetOffset(int componentId)
|
public readonly Result<ComponentMemoryLayout, ResultStatus> 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)
|
public ResultStatus RemoveEntity(int chunkIndex, int rowIndex)
|
||||||
@@ -382,26 +428,6 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
|
|||||||
return Identifier<Archetype>.Invalid;
|
return Identifier<Archetype>.Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public readonly Span<T> GetComponentArray<T>(int chunkIndex)
|
|
||||||
where T : unmanaged, IComponent
|
|
||||||
{
|
|
||||||
var id = ComponentTypeID<T>.value;
|
|
||||||
if (id >= _componentIDToOffset.Count)
|
|
||||||
{
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
|
|
||||||
var offset = _componentIDToOffset[id];
|
|
||||||
if (offset == -1)
|
|
||||||
{
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
|
|
||||||
var chunk = _chunks[chunkIndex];
|
|
||||||
return new Span<T>((T*)((byte*)chunk.GetUnsafePtr() + offset), chunk.Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override readonly int GetHashCode()
|
public override readonly int GetHashCode()
|
||||||
{
|
{
|
||||||
return _hash;
|
return _hash;
|
||||||
@@ -419,7 +445,7 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
|
|||||||
|
|
||||||
_signature.Dispose();
|
_signature.Dispose();
|
||||||
_chunks.Dispose();
|
_chunks.Dispose();
|
||||||
_componentIDToOffset.Dispose();
|
_componentIDToLayoutIndex.Dispose();
|
||||||
_layouts.Dispose();
|
_layouts.Dispose();
|
||||||
_edgesAdd.Dispose();
|
_edgesAdd.Dispose();
|
||||||
_edgesRemove.Dispose();
|
_edgesRemove.Dispose();
|
||||||
|
|||||||
@@ -8,12 +8,17 @@ public interface IComponent : IIdentifierType
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface IEnableableComponent : IComponent
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public struct ComponentInfo
|
public struct ComponentInfo
|
||||||
{
|
{
|
||||||
// public FixedText64 stableName; // Do we actually need this?
|
// public FixedText64 stableName; // Do we actually need this?
|
||||||
|
public Identifier<IComponent> id;
|
||||||
public int size;
|
public int size;
|
||||||
public int alignment;
|
public int alignment;
|
||||||
public Identifier<IComponent> id;
|
public bool isEnableable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ComponentTypeID<T>
|
public static class ComponentTypeID<T>
|
||||||
@@ -25,12 +30,12 @@ public static class ComponentTypeID<T>
|
|||||||
internal static class ComponentRegister
|
internal static class ComponentRegister
|
||||||
{
|
{
|
||||||
private static int s_nextComponentTypeID = 0;
|
private static int s_nextComponentTypeID = 0;
|
||||||
private static Dictionary<IntPtr, Identifier<IComponent>> s_typeHandleToID = new();
|
private static readonly Dictionary<IntPtr, Identifier<IComponent>> s_typeHandleToID = new();
|
||||||
|
|
||||||
private static List<ComponentInfo> s_registeredComponents = new();
|
private static readonly List<ComponentInfo> s_registeredComponents = new();
|
||||||
private static Dictionary<string, Identifier<IComponent>> s_nameToRuntimeID = new();
|
private static readonly Dictionary<string, Identifier<IComponent>> s_nameToRuntimeID = new();
|
||||||
|
|
||||||
public unsafe static Identifier<IComponent> GetOrRegisterComponent<T>()
|
public static unsafe Identifier<IComponent> GetOrRegisterComponent<T>()
|
||||||
where T : unmanaged, IComponent
|
where T : unmanaged, IComponent
|
||||||
{
|
{
|
||||||
var typeHandle = typeof(T).TypeHandle.Value;
|
var typeHandle = typeof(T).TypeHandle.Value;
|
||||||
@@ -50,9 +55,10 @@ internal static class ComponentRegister
|
|||||||
var info = new ComponentInfo
|
var info = new ComponentInfo
|
||||||
{
|
{
|
||||||
// stableName = new FixedText64(stableName),
|
// stableName = new FixedText64(stableName),
|
||||||
|
id = newID,
|
||||||
size = sizeof(T),
|
size = sizeof(T),
|
||||||
alignment = (int)MemoryUtility.AlignOf<T>(),
|
alignment = (int)MemoryUtility.AlignOf<T>(),
|
||||||
id = newID,
|
isEnableable = typeof(IEnableableComponent).IsAssignableFrom(typeof(T))
|
||||||
};
|
};
|
||||||
|
|
||||||
while (s_registeredComponents.Count <= newID.value) s_registeredComponents.Add(default);
|
while (s_registeredComponents.Count <= newID.value) s_registeredComponents.Add(default);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using Ghost.Core;
|
|||||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
using Misaki.HighPerformance.LowLevel.Collections;
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace Ghost.Entities;
|
namespace Ghost.Entities;
|
||||||
@@ -121,8 +122,14 @@ public unsafe class EntityManager : IDisposable
|
|||||||
var layout = oldArch._layouts[i];
|
var layout = oldArch._layouts[i];
|
||||||
|
|
||||||
var src = oldArch._chunks[oldChunk].GetUnsafePtr() + layout.offset + (layout.size * oldRow);
|
var src = oldArch._chunks[oldChunk].GetUnsafePtr() + layout.offset + (layout.size * oldRow);
|
||||||
var newOffset = newArch.GetOffset(layout.componentID); // O(1) Looku
|
var layoutResult = newArch.GetLayout(layout.componentID);
|
||||||
var dst = newArch._chunks[newChunk].GetUnsafePtr() + newOffset + (layout.size * newRow);
|
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);
|
MemoryUtility.MemCpy(src, dst, (nuint)layout.size);
|
||||||
}
|
}
|
||||||
@@ -210,10 +217,10 @@ public unsafe class EntityManager : IDisposable
|
|||||||
|
|
||||||
var r = oldArchetype.RemoveEntity(location.chunkIndex, location.rowIndex);
|
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.
|
Debug.Assert(r == ResultStatus.Success); // We assert it because the entity should exist if the whole system is consistent.
|
||||||
// if (r != ResultStatus.Success)
|
if (r != ResultStatus.Success)
|
||||||
// {
|
{
|
||||||
// return r;
|
return r;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// Update location
|
// Update location
|
||||||
location.archetypeID = newArcID;
|
location.archetypeID = newArcID;
|
||||||
@@ -265,6 +272,43 @@ public unsafe class EntityManager : IDisposable
|
|||||||
return HasComponent(entity, ComponentTypeID<T>.value);
|
return HasComponent(entity, ComponentTypeID<T>.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ResultStatus SetEnabled<T>(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<T>.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()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (_disposed)
|
if (_disposed)
|
||||||
|
|||||||
@@ -1,60 +0,0 @@
|
|||||||
namespace Ghost.Entities;
|
|
||||||
|
|
||||||
public unsafe class EntityQueryy<T1, T2>
|
|
||||||
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<ArchetypeCache> _cache = new();
|
|
||||||
|
|
||||||
internal void AddMatchingArchetype(Archetype archetype)
|
|
||||||
{
|
|
||||||
// We look up the offsets ONCE when the archetype is registered
|
|
||||||
int off1 = archetype.GetOffset(ComponentTypeID<T1>.value);
|
|
||||||
int off2 = archetype.GetOffset(ComponentTypeID<T2>.value);
|
|
||||||
|
|
||||||
_cache.Add(new ArchetypeCache
|
|
||||||
{
|
|
||||||
Archetype = archetype,
|
|
||||||
Offset1 = off1,
|
|
||||||
Offset2 = off2
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// The Optimized Iteration Loop
|
|
||||||
public void ForEach(delegate*<ref T1, ref T2, void> 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]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
5
Ghost.Entities/Exceptions.cs
Normal file
5
Ghost.Entities/Exceptions.cs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
namespace Ghost.Entities;
|
||||||
|
|
||||||
|
public class EntityNotFoundException : Exception
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -7,6 +7,16 @@
|
|||||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<IsAotCompatible>True</IsAotCompatible>
|
||||||
|
<IsTrimmable>True</IsTrimmable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<IsAotCompatible>True</IsAotCompatible>
|
||||||
|
<IsTrimmable>True</IsTrimmable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Remove="Templates\ForEach.cs" />
|
<Compile Remove="Templates\ForEach.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -22,6 +32,11 @@
|
|||||||
<DesignTime>True</DesignTime>
|
<DesignTime>True</DesignTime>
|
||||||
<AutoGen>True</AutoGen>
|
<AutoGen>True</AutoGen>
|
||||||
</None>
|
</None>
|
||||||
|
<None Include="Templates\QueryBuilder.With.gen.cs">
|
||||||
|
<DesignTime>True</DesignTime>
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DependentUpon>QueryBuilder.With.tt</DependentUpon>
|
||||||
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -37,6 +52,10 @@
|
|||||||
<LastGenOutput>ForEach.gen.cs</LastGenOutput>
|
<LastGenOutput>ForEach.gen.cs</LastGenOutput>
|
||||||
<Generator>TextTemplatingFileGenerator</Generator>
|
<Generator>TextTemplatingFileGenerator</Generator>
|
||||||
</None>
|
</None>
|
||||||
|
<None Update="Templates\QueryBuilder.With.tt">
|
||||||
|
<Generator>TextTemplatingFileGenerator</Generator>
|
||||||
|
<LastGenOutput>QueryBuilder.With.gen.cs</LastGenOutput>
|
||||||
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -54,6 +73,11 @@
|
|||||||
<AutoGen>True</AutoGen>
|
<AutoGen>True</AutoGen>
|
||||||
<DependentUpon>ForEach.tt</DependentUpon>
|
<DependentUpon>ForEach.tt</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Update="Templates\QueryBuilder.With.gen.cs">
|
||||||
|
<DesignTime>True</DesignTime>
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DependentUpon>QueryBuilder.With.tt</DependentUpon>
|
||||||
|
</Compile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -5,42 +5,86 @@ using System.Runtime.CompilerServices;
|
|||||||
|
|
||||||
namespace Ghost.Entities;
|
namespace Ghost.Entities;
|
||||||
|
|
||||||
public struct EntityQueryMask : IDisposable
|
public struct EntityQueryMask : IDisposable, IEquatable<EntityQueryMask>
|
||||||
{
|
{
|
||||||
public UnsafeBitSet all;
|
public UnsafeBitSet structuralAll;
|
||||||
public UnsafeBitSet any;
|
public UnsafeBitSet structuralAny;
|
||||||
public UnsafeBitSet absent;
|
public UnsafeBitSet structuralAbsent;
|
||||||
|
|
||||||
|
public UnsafeBitSet requireEnabled;
|
||||||
|
public UnsafeBitSet requireDisabled;
|
||||||
|
public UnsafeBitSet rejectIfEnabled;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public readonly bool Matches(UnsafeBitSet archetypeSignature)
|
public readonly bool Matches(ref readonly UnsafeBitSet archetypeSignature)
|
||||||
{
|
{
|
||||||
return (!all.IsCreated || all.All(archetypeSignature))
|
return (!structuralAll.IsCreated || structuralAll.All(archetypeSignature))
|
||||||
&& (!absent.IsCreated || absent.None(archetypeSignature))
|
&& (!structuralAbsent.IsCreated || structuralAbsent.None(archetypeSignature))
|
||||||
&& (!any.IsCreated || any.Count == 0 || any.Any(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;
|
var hash = 17;
|
||||||
if (all.IsCreated) hash = hash * 23 + all.GetHashCode();
|
if (structuralAll.IsCreated) hash = hash * 23 + structuralAll.GetHashCode();
|
||||||
if (absent.IsCreated) hash = hash * 23 + absent.GetHashCode();
|
if (structuralAbsent.IsCreated) hash = hash * 23 + structuralAbsent.GetHashCode();
|
||||||
if (any.IsCreated) hash = hash * 23 + any.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;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
all.Dispose();
|
structuralAll.Dispose();
|
||||||
any.Dispose();
|
structuralAny.Dispose();
|
||||||
absent.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
|
public unsafe partial struct EntityQuery : IIdentifierType, IDisposable
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides an enumerator for iterating over chunks of entities and their component data that match a set of archetypes within a world.
|
||||||
|
/// </summary>
|
||||||
public readonly ref struct ChunkIterator
|
public readonly ref struct ChunkIterator
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides a read-only view over a chunk of entities and their component data within an archetype.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>This does not filter disabled/enabled components. You must handle that manually.</remarks>
|
||||||
public readonly ref struct ChunkView
|
public readonly ref struct ChunkView
|
||||||
{
|
{
|
||||||
private readonly ref Archetype _archetype;
|
private readonly ref Archetype _archetype;
|
||||||
@@ -54,6 +98,10 @@ public unsafe partial struct EntityQuery : IIdentifierType, IDisposable
|
|||||||
_chunk = ref archetype.GetChunkReference(chunkIndex);
|
_chunk = ref archetype.GetChunkReference(chunkIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a read-only span containing structuralAll entities stored in the current chunk.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A read-only span of <see cref="Entity"/> values representing the entities in the chunk.</returns>
|
||||||
public readonly ReadOnlySpan<Entity> GetEntities()
|
public readonly ReadOnlySpan<Entity> GetEntities()
|
||||||
{
|
{
|
||||||
var ptr = _chunk.GetUnsafePtr();
|
var ptr = _chunk.GetUnsafePtr();
|
||||||
@@ -61,31 +109,70 @@ public unsafe partial struct EntityQuery : IIdentifierType, IDisposable
|
|||||||
return new ReadOnlySpan<Entity>(pEntity, _chunk.Count);
|
return new ReadOnlySpan<Entity>(pEntity, _chunk.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a span providing direct access to the component data of type T0 for structuralAll entities in the chunk.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of component to access. Must be an unmanaged type that implements <see cref="Component"/>.</typeparam>
|
||||||
|
/// <returns>A span of type <see cref="{T}"/> containing the component data for each entity in the chunk.</returns>
|
||||||
|
/// <exception cref="InvalidOperationException">Thrown if the specified component type is not present in the archetype.</exception>
|
||||||
public readonly Span<T> GetComponentData<T>()
|
public readonly Span<T> GetComponentData<T>()
|
||||||
where T : unmanaged, IComponent
|
where T : unmanaged, IComponent
|
||||||
{
|
{
|
||||||
var offset = _archetype.GetOffset(ComponentTypeID<T>.value);
|
var layout = _archetype.GetLayout(ComponentTypeID<T>.value).GetValueOrThrow(ResultStatus.Success);
|
||||||
if (offset < 0)
|
var ptr = _chunk.GetUnsafePtr() + layout.offset;
|
||||||
|
return new Span<T>(ptr, _chunk.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a bit set representing the enabled state of each instance of the specified enableable component
|
||||||
|
/// type within the current chunk.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The component type for which to retrieve enablement bits. Must be unmanaged and implement <see cref="IEnableableComponent"/>.</typeparam>
|
||||||
|
/// <returns>A <see cref="SpanBitSet"/> that provides access to the enablement bits for all instances of the specified component type in the chunk.</returns>
|
||||||
|
/// <exception cref="InvalidOperationException">Thrown if the specified component type does not support enablement.</exception>
|
||||||
|
public SpanBitSet GetEnableBits<T>()
|
||||||
|
where T : unmanaged, IEnableableComponent
|
||||||
|
{
|
||||||
|
var layout = _archetype.GetLayout(ComponentTypeID<T>.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;
|
var maskBase = _chunk.GetUnsafePtr() + layout.enableBitsOffset;
|
||||||
return new Span<T>(ptr, _chunk.Count);
|
return new SpanBitSet(new Span<uint>(maskBase, (_chunk.Count + 31) / 32));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified component of type <typeparamref name="T"/> at the given index is currently enabled.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the component to check. Must be an unmanaged type that implements <see cref="IEnableableComponent"/>.</typeparam>
|
||||||
|
/// <param name="index">The zero-based index of the component instance to check within the chunk.</param>
|
||||||
|
/// <returns>true if the component at the specified index is enabled; otherwise, false.</returns>
|
||||||
|
/// <exception cref="InvalidOperationException">Thrown if the specified component type <typeparamref name="T"/> does not support enable/disable functionality.</exception>
|
||||||
|
public readonly bool IsComponentEnabled<T>(int index)
|
||||||
|
where T : unmanaged, IEnableableComponent
|
||||||
|
{
|
||||||
|
var layout = _archetype.GetLayout(ComponentTypeID<T>.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
|
public ref struct Enumerator
|
||||||
{
|
{
|
||||||
private readonly ReadOnlyUnsafeCollection<Identifier<Archetype>> _matchingArchetypes;
|
private readonly ChunkIterator _iterator;
|
||||||
private readonly World _world;
|
|
||||||
private int _archetypeIndex;
|
private int _archetypeIndex;
|
||||||
private int _chunkIndex;
|
private int _chunkIndex;
|
||||||
|
|
||||||
internal Enumerator(ReadOnlyUnsafeCollection<Identifier<Archetype>> matchingArchetypes, World world)
|
internal Enumerator(ChunkIterator iterator)
|
||||||
{
|
{
|
||||||
_matchingArchetypes = matchingArchetypes;
|
_iterator = iterator;
|
||||||
_world = world;
|
|
||||||
_archetypeIndex = 0;
|
_archetypeIndex = 0;
|
||||||
_chunkIndex = -1;
|
_chunkIndex = -1;
|
||||||
}
|
}
|
||||||
@@ -94,7 +181,7 @@ public unsafe partial struct EntityQuery : IIdentifierType, IDisposable
|
|||||||
{
|
{
|
||||||
get
|
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);
|
return new ChunkView(ref archetype, _chunkIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,9 +190,9 @@ public unsafe partial struct EntityQuery : IIdentifierType, IDisposable
|
|||||||
{
|
{
|
||||||
_chunkIndex++;
|
_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)
|
if (_chunkIndex < archetype.ChunkCount)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@@ -140,14 +227,15 @@ public unsafe partial struct EntityQuery : IIdentifierType, IDisposable
|
|||||||
|
|
||||||
public readonly Enumerator GetEnumerator()
|
public readonly Enumerator GetEnumerator()
|
||||||
{
|
{
|
||||||
return new Enumerator(_matchingArchetypes, _world);
|
return new Enumerator(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly Identifier<World> _worldID;
|
private readonly Identifier<World> _worldID;
|
||||||
private EntityQueryMask _mask;
|
|
||||||
private UnsafeList<Identifier<Archetype>> _matchingArchetypes;
|
private UnsafeList<Identifier<Archetype>> _matchingArchetypes;
|
||||||
|
|
||||||
|
internal EntityQueryMask _mask;
|
||||||
|
|
||||||
internal EntityQuery(Identifier<World> worldID, EntityQueryMask mask)
|
internal EntityQuery(Identifier<World> worldID, EntityQueryMask mask)
|
||||||
{
|
{
|
||||||
_worldID = worldID;
|
_worldID = worldID;
|
||||||
@@ -155,14 +243,96 @@ public unsafe partial struct EntityQuery : IIdentifierType, IDisposable
|
|||||||
_matchingArchetypes = new UnsafeList<Identifier<Archetype>>(8, Allocator.Persistent);
|
_matchingArchetypes = new UnsafeList<Identifier<Archetype>>(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);
|
_matchingArchetypes.Add(archetype.ID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public readonly ChunkIterator GetChunkIterator()
|
public readonly ChunkIterator GetChunkIterator()
|
||||||
{
|
{
|
||||||
var world = World.GetWorld(_worldID).Value;
|
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 readonly Stack.Scope _scope;
|
||||||
|
|
||||||
private UnsafeList<Identifier<IComponent>> _all;
|
private UnsafeList<Identifier<IComponent>> _all;
|
||||||
private UnsafeList<Identifier<IComponent>> _any;
|
private UnsafeList<Identifier<IComponent>> _any;
|
||||||
private UnsafeList<Identifier<IComponent>> _absent;
|
private UnsafeList<Identifier<IComponent>> _absent;
|
||||||
|
private UnsafeList<Identifier<IComponent>> _none;
|
||||||
|
private UnsafeList<Identifier<IComponent>> _disabled;
|
||||||
|
private UnsafeList<Identifier<IComponent>> _present;
|
||||||
|
|
||||||
public QueryBuilder()
|
public QueryBuilder()
|
||||||
{
|
{
|
||||||
@@ -191,68 +364,12 @@ public ref struct QueryBuilder
|
|||||||
_all = new UnsafeList<Identifier<IComponent>>(4, Allocator.Stack);
|
_all = new UnsafeList<Identifier<IComponent>>(4, Allocator.Stack);
|
||||||
_any = new UnsafeList<Identifier<IComponent>>(4, Allocator.Stack);
|
_any = new UnsafeList<Identifier<IComponent>>(4, Allocator.Stack);
|
||||||
_absent = new UnsafeList<Identifier<IComponent>>(4, Allocator.Stack);
|
_absent = new UnsafeList<Identifier<IComponent>>(4, Allocator.Stack);
|
||||||
|
_none = new UnsafeList<Identifier<IComponent>>(4, Allocator.Stack);
|
||||||
|
_disabled = new UnsafeList<Identifier<IComponent>>(4, Allocator.Stack);
|
||||||
|
_present = new UnsafeList<Identifier<IComponent>>(4, Allocator.Stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
public QueryBuilder WithAll<T>()
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
where T : unmanaged, IComponent
|
|
||||||
{
|
|
||||||
_all.Add(ComponentTypeID<T>.value);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public QueryBuilder WithAny<T>()
|
|
||||||
where T : unmanaged, IComponent
|
|
||||||
{
|
|
||||||
_any.Add(ComponentTypeID<T>.value);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public QueryBuilder WithNone<T>()
|
|
||||||
where T : unmanaged, IComponent
|
|
||||||
{
|
|
||||||
_absent.Add(ComponentTypeID<T>.value);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Identifier<EntityQuery> Build(World world)
|
|
||||||
{
|
|
||||||
// 1. Calculate max component ID to size the BitSets
|
|
||||||
int maxID = 0;
|
|
||||||
FindMax(_all, ref maxID);
|
|
||||||
FindMax(_any, ref maxID);
|
|
||||||
FindMax(_absent, ref maxID);
|
|
||||||
|
|
||||||
// 2. Create the Mask
|
|
||||||
using var mask = new EntityQueryMask
|
|
||||||
{
|
|
||||||
all = new UnsafeBitSet(maxID + 1, Allocator.Stack),
|
|
||||||
any = new UnsafeBitSet(maxID + 1, Allocator.Stack),
|
|
||||||
absent = new UnsafeBitSet(maxID + 1, Allocator.Stack)
|
|
||||||
};
|
|
||||||
|
|
||||||
// 3. Fill BitSets
|
|
||||||
foreach (var id in _all) mask.all.SetBit(id);
|
|
||||||
foreach (var id in _any) mask.any.SetBit(id);
|
|
||||||
foreach (var id in _absent) mask.absent.SetBit(id);
|
|
||||||
|
|
||||||
// 4. Ask World for the Query (Cached)
|
|
||||||
var queryID = world.GetEntityQueryIDByMaskHash(mask.GetHashCode());
|
|
||||||
if (queryID.IsNotValid)
|
|
||||||
{
|
|
||||||
queryID = world.CreateEntityQuery(mask);
|
|
||||||
ref var query = ref world.GetEntityQueryReference(queryID);
|
|
||||||
for (var i = 0; i < world.ArchetypeCount; i++)
|
|
||||||
{
|
|
||||||
ref var archetype = ref world.GetArchetypeReference(i);
|
|
||||||
query.AddArchetypeIfMatch(archetype);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Dispose();
|
|
||||||
|
|
||||||
return queryID;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void FindMax(UnsafeList<Identifier<IComponent>> list, ref int max)
|
private static void FindMax(UnsafeList<Identifier<IComponent>> list, ref int max)
|
||||||
{
|
{
|
||||||
foreach (var id in list)
|
foreach (var id in list)
|
||||||
@@ -261,11 +378,98 @@ public ref struct QueryBuilder
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Identifier<EntityQuery> 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()
|
private void Dispose()
|
||||||
{
|
{
|
||||||
_all.Dispose();
|
_all.Dispose();
|
||||||
_any.Dispose();
|
_any.Dispose();
|
||||||
_absent.Dispose();
|
_absent.Dispose();
|
||||||
|
_none.Dispose();
|
||||||
|
_disabled.Dispose();
|
||||||
|
_present.Dispose();
|
||||||
|
|
||||||
_scope.Dispose();
|
_scope.Dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,18 +14,20 @@ public unsafe partial struct EntityQuery
|
|||||||
var offsets = stackalloc int[1];
|
var offsets = stackalloc int[1];
|
||||||
var basePtrs = stackalloc byte*[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;
|
var hasAllComponents = true;
|
||||||
for (var index = 0; index < 1; index++)
|
for (var index = 0; index < 1; index++)
|
||||||
{
|
{
|
||||||
offsets[index] = archetype.GetOffset(compTypeIDs[index]);
|
var layoutResult = archetype.GetLayout(compTypeIDs[index]);
|
||||||
if (offsets[index] == -1)
|
if (layoutResult.Status != ResultStatus.Success)
|
||||||
{
|
{
|
||||||
hasAllComponents = false;
|
hasAllComponents = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offsets[index] = layoutResult.Value.offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasAllComponents)
|
if (!hasAllComponents)
|
||||||
@@ -36,14 +38,20 @@ public unsafe partial struct EntityQuery
|
|||||||
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
||||||
{
|
{
|
||||||
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
||||||
var count = chunk.Count;
|
var pChunkData = chunk.GetUnsafePtr();
|
||||||
|
|
||||||
for (var index = 0; index < 1; index++)
|
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));
|
var pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex));
|
||||||
|
|
||||||
action(ref *pComp0);
|
action(ref *pComp0);
|
||||||
@@ -62,18 +70,20 @@ public unsafe partial struct EntityQuery
|
|||||||
var offsets = stackalloc int[2];
|
var offsets = stackalloc int[2];
|
||||||
var basePtrs = stackalloc byte*[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;
|
var hasAllComponents = true;
|
||||||
for (var index = 0; index < 2; index++)
|
for (var index = 0; index < 2; index++)
|
||||||
{
|
{
|
||||||
offsets[index] = archetype.GetOffset(compTypeIDs[index]);
|
var layoutResult = archetype.GetLayout(compTypeIDs[index]);
|
||||||
if (offsets[index] == -1)
|
if (layoutResult.Status != ResultStatus.Success)
|
||||||
{
|
{
|
||||||
hasAllComponents = false;
|
hasAllComponents = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offsets[index] = layoutResult.Value.offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasAllComponents)
|
if (!hasAllComponents)
|
||||||
@@ -84,14 +94,20 @@ public unsafe partial struct EntityQuery
|
|||||||
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
||||||
{
|
{
|
||||||
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
||||||
var count = chunk.Count;
|
var pChunkData = chunk.GetUnsafePtr();
|
||||||
|
|
||||||
for (var index = 0; index < 2; index++)
|
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 pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex));
|
||||||
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
|
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
|
||||||
|
|
||||||
@@ -112,18 +128,20 @@ public unsafe partial struct EntityQuery
|
|||||||
var offsets = stackalloc int[3];
|
var offsets = stackalloc int[3];
|
||||||
var basePtrs = stackalloc byte*[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;
|
var hasAllComponents = true;
|
||||||
for (var index = 0; index < 3; index++)
|
for (var index = 0; index < 3; index++)
|
||||||
{
|
{
|
||||||
offsets[index] = archetype.GetOffset(compTypeIDs[index]);
|
var layoutResult = archetype.GetLayout(compTypeIDs[index]);
|
||||||
if (offsets[index] == -1)
|
if (layoutResult.Status != ResultStatus.Success)
|
||||||
{
|
{
|
||||||
hasAllComponents = false;
|
hasAllComponents = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offsets[index] = layoutResult.Value.offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasAllComponents)
|
if (!hasAllComponents)
|
||||||
@@ -134,14 +152,20 @@ public unsafe partial struct EntityQuery
|
|||||||
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
||||||
{
|
{
|
||||||
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
||||||
var count = chunk.Count;
|
var pChunkData = chunk.GetUnsafePtr();
|
||||||
|
|
||||||
for (var index = 0; index < 3; index++)
|
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 pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex));
|
||||||
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
|
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
|
||||||
var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex));
|
var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex));
|
||||||
@@ -164,18 +188,20 @@ public unsafe partial struct EntityQuery
|
|||||||
var offsets = stackalloc int[4];
|
var offsets = stackalloc int[4];
|
||||||
var basePtrs = stackalloc byte*[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;
|
var hasAllComponents = true;
|
||||||
for (var index = 0; index < 4; index++)
|
for (var index = 0; index < 4; index++)
|
||||||
{
|
{
|
||||||
offsets[index] = archetype.GetOffset(compTypeIDs[index]);
|
var layoutResult = archetype.GetLayout(compTypeIDs[index]);
|
||||||
if (offsets[index] == -1)
|
if (layoutResult.Status != ResultStatus.Success)
|
||||||
{
|
{
|
||||||
hasAllComponents = false;
|
hasAllComponents = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offsets[index] = layoutResult.Value.offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasAllComponents)
|
if (!hasAllComponents)
|
||||||
@@ -186,14 +212,20 @@ public unsafe partial struct EntityQuery
|
|||||||
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
||||||
{
|
{
|
||||||
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
||||||
var count = chunk.Count;
|
var pChunkData = chunk.GetUnsafePtr();
|
||||||
|
|
||||||
for (var index = 0; index < 4; index++)
|
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 pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex));
|
||||||
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
|
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
|
||||||
var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex));
|
var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex));
|
||||||
@@ -218,18 +250,20 @@ public unsafe partial struct EntityQuery
|
|||||||
var offsets = stackalloc int[5];
|
var offsets = stackalloc int[5];
|
||||||
var basePtrs = stackalloc byte*[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;
|
var hasAllComponents = true;
|
||||||
for (var index = 0; index < 5; index++)
|
for (var index = 0; index < 5; index++)
|
||||||
{
|
{
|
||||||
offsets[index] = archetype.GetOffset(compTypeIDs[index]);
|
var layoutResult = archetype.GetLayout(compTypeIDs[index]);
|
||||||
if (offsets[index] == -1)
|
if (layoutResult.Status != ResultStatus.Success)
|
||||||
{
|
{
|
||||||
hasAllComponents = false;
|
hasAllComponents = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offsets[index] = layoutResult.Value.offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasAllComponents)
|
if (!hasAllComponents)
|
||||||
@@ -240,14 +274,20 @@ public unsafe partial struct EntityQuery
|
|||||||
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
||||||
{
|
{
|
||||||
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
||||||
var count = chunk.Count;
|
var pChunkData = chunk.GetUnsafePtr();
|
||||||
|
|
||||||
for (var index = 0; index < 5; index++)
|
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 pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex));
|
||||||
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
|
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
|
||||||
var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex));
|
var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex));
|
||||||
@@ -274,18 +314,20 @@ public unsafe partial struct EntityQuery
|
|||||||
var offsets = stackalloc int[6];
|
var offsets = stackalloc int[6];
|
||||||
var basePtrs = stackalloc byte*[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;
|
var hasAllComponents = true;
|
||||||
for (var index = 0; index < 6; index++)
|
for (var index = 0; index < 6; index++)
|
||||||
{
|
{
|
||||||
offsets[index] = archetype.GetOffset(compTypeIDs[index]);
|
var layoutResult = archetype.GetLayout(compTypeIDs[index]);
|
||||||
if (offsets[index] == -1)
|
if (layoutResult.Status != ResultStatus.Success)
|
||||||
{
|
{
|
||||||
hasAllComponents = false;
|
hasAllComponents = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offsets[index] = layoutResult.Value.offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasAllComponents)
|
if (!hasAllComponents)
|
||||||
@@ -296,14 +338,20 @@ public unsafe partial struct EntityQuery
|
|||||||
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
||||||
{
|
{
|
||||||
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
||||||
var count = chunk.Count;
|
var pChunkData = chunk.GetUnsafePtr();
|
||||||
|
|
||||||
for (var index = 0; index < 6; index++)
|
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 pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex));
|
||||||
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
|
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
|
||||||
var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex));
|
var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex));
|
||||||
@@ -332,18 +380,20 @@ public unsafe partial struct EntityQuery
|
|||||||
var offsets = stackalloc int[7];
|
var offsets = stackalloc int[7];
|
||||||
var basePtrs = stackalloc byte*[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;
|
var hasAllComponents = true;
|
||||||
for (var index = 0; index < 7; index++)
|
for (var index = 0; index < 7; index++)
|
||||||
{
|
{
|
||||||
offsets[index] = archetype.GetOffset(compTypeIDs[index]);
|
var layoutResult = archetype.GetLayout(compTypeIDs[index]);
|
||||||
if (offsets[index] == -1)
|
if (layoutResult.Status != ResultStatus.Success)
|
||||||
{
|
{
|
||||||
hasAllComponents = false;
|
hasAllComponents = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offsets[index] = layoutResult.Value.offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasAllComponents)
|
if (!hasAllComponents)
|
||||||
@@ -354,14 +404,20 @@ public unsafe partial struct EntityQuery
|
|||||||
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
||||||
{
|
{
|
||||||
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
||||||
var count = chunk.Count;
|
var pChunkData = chunk.GetUnsafePtr();
|
||||||
|
|
||||||
for (var index = 0; index < 7; index++)
|
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 pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex));
|
||||||
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
|
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
|
||||||
var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex));
|
var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex));
|
||||||
@@ -392,18 +448,20 @@ public unsafe partial struct EntityQuery
|
|||||||
var offsets = stackalloc int[8];
|
var offsets = stackalloc int[8];
|
||||||
var basePtrs = stackalloc byte*[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;
|
var hasAllComponents = true;
|
||||||
for (var index = 0; index < 8; index++)
|
for (var index = 0; index < 8; index++)
|
||||||
{
|
{
|
||||||
offsets[index] = archetype.GetOffset(compTypeIDs[index]);
|
var layoutResult = archetype.GetLayout(compTypeIDs[index]);
|
||||||
if (offsets[index] == -1)
|
if (layoutResult.Status != ResultStatus.Success)
|
||||||
{
|
{
|
||||||
hasAllComponents = false;
|
hasAllComponents = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offsets[index] = layoutResult.Value.offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasAllComponents)
|
if (!hasAllComponents)
|
||||||
@@ -414,14 +472,20 @@ public unsafe partial struct EntityQuery
|
|||||||
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
||||||
{
|
{
|
||||||
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
||||||
var count = chunk.Count;
|
var pChunkData = chunk.GetUnsafePtr();
|
||||||
|
|
||||||
for (var index = 0; index < 8; index++)
|
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 pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex));
|
||||||
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
|
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
|
||||||
var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex));
|
var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex));
|
||||||
@@ -437,7 +501,6 @@ public unsafe partial struct EntityQuery
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public readonly void ForEach<T0>(ForEachWithEntity<T0> action)
|
public readonly void ForEach<T0>(ForEachWithEntity<T0> action)
|
||||||
where T0 : unmanaged, IComponent
|
where T0 : unmanaged, IComponent
|
||||||
{
|
{
|
||||||
@@ -447,18 +510,20 @@ public unsafe partial struct EntityQuery
|
|||||||
var offsets = stackalloc int[1];
|
var offsets = stackalloc int[1];
|
||||||
var basePtrs = stackalloc byte*[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;
|
var hasAllComponents = true;
|
||||||
for (var index = 0; index < 1; index++)
|
for (var index = 0; index < 1; index++)
|
||||||
{
|
{
|
||||||
offsets[index] = archetype.GetOffset(compTypeIDs[index]);
|
var layoutResult = archetype.GetLayout(compTypeIDs[index]);
|
||||||
if (offsets[index] == -1)
|
if (layoutResult.Status != ResultStatus.Success)
|
||||||
{
|
{
|
||||||
hasAllComponents = false;
|
hasAllComponents = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offsets[index] = layoutResult.Value.offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasAllComponents)
|
if (!hasAllComponents)
|
||||||
@@ -469,17 +534,23 @@ public unsafe partial struct EntityQuery
|
|||||||
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
||||||
{
|
{
|
||||||
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
||||||
var count = chunk.Count;
|
var pChunkData = chunk.GetUnsafePtr();
|
||||||
|
|
||||||
for (var index = 0; index < 1; index++)
|
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 pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex));
|
||||||
|
|
||||||
|
var pEntity = (Entity*)(pChunkData + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex));
|
||||||
action(*pEntity, ref *pComp0);
|
action(*pEntity, ref *pComp0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -496,18 +567,20 @@ public unsafe partial struct EntityQuery
|
|||||||
var offsets = stackalloc int[2];
|
var offsets = stackalloc int[2];
|
||||||
var basePtrs = stackalloc byte*[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;
|
var hasAllComponents = true;
|
||||||
for (var index = 0; index < 2; index++)
|
for (var index = 0; index < 2; index++)
|
||||||
{
|
{
|
||||||
offsets[index] = archetype.GetOffset(compTypeIDs[index]);
|
var layoutResult = archetype.GetLayout(compTypeIDs[index]);
|
||||||
if (offsets[index] == -1)
|
if (layoutResult.Status != ResultStatus.Success)
|
||||||
{
|
{
|
||||||
hasAllComponents = false;
|
hasAllComponents = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offsets[index] = layoutResult.Value.offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasAllComponents)
|
if (!hasAllComponents)
|
||||||
@@ -518,18 +591,24 @@ public unsafe partial struct EntityQuery
|
|||||||
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
||||||
{
|
{
|
||||||
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
||||||
var count = chunk.Count;
|
var pChunkData = chunk.GetUnsafePtr();
|
||||||
|
|
||||||
for (var index = 0; index < 2; index++)
|
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 pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex));
|
||||||
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -547,18 +626,20 @@ public unsafe partial struct EntityQuery
|
|||||||
var offsets = stackalloc int[3];
|
var offsets = stackalloc int[3];
|
||||||
var basePtrs = stackalloc byte*[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;
|
var hasAllComponents = true;
|
||||||
for (var index = 0; index < 3; index++)
|
for (var index = 0; index < 3; index++)
|
||||||
{
|
{
|
||||||
offsets[index] = archetype.GetOffset(compTypeIDs[index]);
|
var layoutResult = archetype.GetLayout(compTypeIDs[index]);
|
||||||
if (offsets[index] == -1)
|
if (layoutResult.Status != ResultStatus.Success)
|
||||||
{
|
{
|
||||||
hasAllComponents = false;
|
hasAllComponents = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offsets[index] = layoutResult.Value.offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasAllComponents)
|
if (!hasAllComponents)
|
||||||
@@ -569,19 +650,25 @@ public unsafe partial struct EntityQuery
|
|||||||
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
||||||
{
|
{
|
||||||
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
||||||
var count = chunk.Count;
|
var pChunkData = chunk.GetUnsafePtr();
|
||||||
|
|
||||||
for (var index = 0; index < 3; index++)
|
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 pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex));
|
||||||
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
|
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
|
||||||
var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * 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);
|
action(*pEntity, ref *pComp0,ref *pComp1,ref *pComp2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -600,18 +687,20 @@ public unsafe partial struct EntityQuery
|
|||||||
var offsets = stackalloc int[4];
|
var offsets = stackalloc int[4];
|
||||||
var basePtrs = stackalloc byte*[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;
|
var hasAllComponents = true;
|
||||||
for (var index = 0; index < 4; index++)
|
for (var index = 0; index < 4; index++)
|
||||||
{
|
{
|
||||||
offsets[index] = archetype.GetOffset(compTypeIDs[index]);
|
var layoutResult = archetype.GetLayout(compTypeIDs[index]);
|
||||||
if (offsets[index] == -1)
|
if (layoutResult.Status != ResultStatus.Success)
|
||||||
{
|
{
|
||||||
hasAllComponents = false;
|
hasAllComponents = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offsets[index] = layoutResult.Value.offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasAllComponents)
|
if (!hasAllComponents)
|
||||||
@@ -622,20 +711,26 @@ public unsafe partial struct EntityQuery
|
|||||||
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
||||||
{
|
{
|
||||||
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
||||||
var count = chunk.Count;
|
var pChunkData = chunk.GetUnsafePtr();
|
||||||
|
|
||||||
for (var index = 0; index < 4; index++)
|
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 pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex));
|
||||||
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
|
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
|
||||||
var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex));
|
var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex));
|
||||||
var pComp3 = (T3*)(basePtrs[3] + (sizeof(T3) * 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);
|
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 offsets = stackalloc int[5];
|
||||||
var basePtrs = stackalloc byte*[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;
|
var hasAllComponents = true;
|
||||||
for (var index = 0; index < 5; index++)
|
for (var index = 0; index < 5; index++)
|
||||||
{
|
{
|
||||||
offsets[index] = archetype.GetOffset(compTypeIDs[index]);
|
var layoutResult = archetype.GetLayout(compTypeIDs[index]);
|
||||||
if (offsets[index] == -1)
|
if (layoutResult.Status != ResultStatus.Success)
|
||||||
{
|
{
|
||||||
hasAllComponents = false;
|
hasAllComponents = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offsets[index] = layoutResult.Value.offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasAllComponents)
|
if (!hasAllComponents)
|
||||||
@@ -677,21 +774,27 @@ public unsafe partial struct EntityQuery
|
|||||||
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
||||||
{
|
{
|
||||||
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
||||||
var count = chunk.Count;
|
var pChunkData = chunk.GetUnsafePtr();
|
||||||
|
|
||||||
for (var index = 0; index < 5; index++)
|
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 pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex));
|
||||||
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
|
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
|
||||||
var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex));
|
var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * entityIndex));
|
||||||
var pComp3 = (T3*)(basePtrs[3] + (sizeof(T3) * entityIndex));
|
var pComp3 = (T3*)(basePtrs[3] + (sizeof(T3) * entityIndex));
|
||||||
var pComp4 = (T4*)(basePtrs[4] + (sizeof(T4) * 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);
|
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 offsets = stackalloc int[6];
|
||||||
var basePtrs = stackalloc byte*[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;
|
var hasAllComponents = true;
|
||||||
for (var index = 0; index < 6; index++)
|
for (var index = 0; index < 6; index++)
|
||||||
{
|
{
|
||||||
offsets[index] = archetype.GetOffset(compTypeIDs[index]);
|
var layoutResult = archetype.GetLayout(compTypeIDs[index]);
|
||||||
if (offsets[index] == -1)
|
if (layoutResult.Status != ResultStatus.Success)
|
||||||
{
|
{
|
||||||
hasAllComponents = false;
|
hasAllComponents = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offsets[index] = layoutResult.Value.offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasAllComponents)
|
if (!hasAllComponents)
|
||||||
@@ -734,15 +839,20 @@ public unsafe partial struct EntityQuery
|
|||||||
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
||||||
{
|
{
|
||||||
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
||||||
var count = chunk.Count;
|
var pChunkData = chunk.GetUnsafePtr();
|
||||||
|
|
||||||
for (var index = 0; index < 6; index++)
|
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 pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex));
|
||||||
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
|
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
|
||||||
var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * 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 pComp4 = (T4*)(basePtrs[4] + (sizeof(T4) * entityIndex));
|
||||||
var pComp5 = (T5*)(basePtrs[5] + (sizeof(T5) * 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);
|
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 offsets = stackalloc int[7];
|
||||||
var basePtrs = stackalloc byte*[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;
|
var hasAllComponents = true;
|
||||||
for (var index = 0; index < 7; index++)
|
for (var index = 0; index < 7; index++)
|
||||||
{
|
{
|
||||||
offsets[index] = archetype.GetOffset(compTypeIDs[index]);
|
var layoutResult = archetype.GetLayout(compTypeIDs[index]);
|
||||||
if (offsets[index] == -1)
|
if (layoutResult.Status != ResultStatus.Success)
|
||||||
{
|
{
|
||||||
hasAllComponents = false;
|
hasAllComponents = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offsets[index] = layoutResult.Value.offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasAllComponents)
|
if (!hasAllComponents)
|
||||||
@@ -793,15 +906,20 @@ public unsafe partial struct EntityQuery
|
|||||||
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
||||||
{
|
{
|
||||||
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
||||||
var count = chunk.Count;
|
var pChunkData = chunk.GetUnsafePtr();
|
||||||
|
|
||||||
for (var index = 0; index < 7; index++)
|
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 pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex));
|
||||||
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
|
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
|
||||||
var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * 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 pComp5 = (T5*)(basePtrs[5] + (sizeof(T5) * entityIndex));
|
||||||
var pComp6 = (T6*)(basePtrs[6] + (sizeof(T6) * 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);
|
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 offsets = stackalloc int[8];
|
||||||
var basePtrs = stackalloc byte*[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;
|
var hasAllComponents = true;
|
||||||
for (var index = 0; index < 8; index++)
|
for (var index = 0; index < 8; index++)
|
||||||
{
|
{
|
||||||
offsets[index] = archetype.GetOffset(compTypeIDs[index]);
|
var layoutResult = archetype.GetLayout(compTypeIDs[index]);
|
||||||
if (offsets[index] == -1)
|
if (layoutResult.Status != ResultStatus.Success)
|
||||||
{
|
{
|
||||||
hasAllComponents = false;
|
hasAllComponents = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offsets[index] = layoutResult.Value.offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasAllComponents)
|
if (!hasAllComponents)
|
||||||
@@ -854,15 +975,20 @@ public unsafe partial struct EntityQuery
|
|||||||
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
||||||
{
|
{
|
||||||
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
||||||
var count = chunk.Count;
|
var pChunkData = chunk.GetUnsafePtr();
|
||||||
|
|
||||||
for (var index = 0; index < 8; index++)
|
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 pComp0 = (T0*)(basePtrs[0] + (sizeof(T0) * entityIndex));
|
||||||
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
|
var pComp1 = (T1*)(basePtrs[1] + (sizeof(T1) * entityIndex));
|
||||||
var pComp2 = (T2*)(basePtrs[2] + (sizeof(T2) * 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 pComp6 = (T6*)(basePtrs[6] + (sizeof(T6) * entityIndex));
|
||||||
var pComp7 = (T7*)(basePtrs[7] + (sizeof(T7) * 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);
|
action(*pEntity, ref *pComp0,ref *pComp1,ref *pComp2,ref *pComp3,ref *pComp4,ref *pComp5,ref *pComp6,ref *pComp7);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,13 +10,19 @@ namespace Ghost.Entities;
|
|||||||
|
|
||||||
public unsafe partial struct EntityQuery
|
public unsafe partial struct EntityQuery
|
||||||
{
|
{
|
||||||
|
<# for (var f = 0; f < 2; f++)
|
||||||
|
{
|
||||||
|
var isForEachWithEntity = f != 0;
|
||||||
|
#>
|
||||||
<# for (var i = 1; i <= Amount; i++)
|
<# for (var i = 1; i <= Amount; i++)
|
||||||
{
|
{
|
||||||
var generics = AppendGenerics(i);
|
var generics = AppendGenerics(i);
|
||||||
var compGenerics = AppendGenericRefParameters(i);
|
var compGenerics = AppendGenericRefParameters(i);
|
||||||
var restrictions = AppendGenericRestrictionsMultiline(i, "unmanaged, IComponent", 2);
|
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 #>
|
<#= restrictions #>
|
||||||
{
|
{
|
||||||
var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success);
|
var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success);
|
||||||
@@ -25,18 +31,20 @@ public unsafe partial struct EntityQuery
|
|||||||
var offsets = stackalloc int[<#= i #>];
|
var offsets = stackalloc int[<#= i #>];
|
||||||
var basePtrs = stackalloc byte*[<#= 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;
|
var hasAllComponents = true;
|
||||||
for (var index = 0; index < <#= i #>; index++)
|
for (var index = 0; index < <#= i #>; index++)
|
||||||
{
|
{
|
||||||
offsets[index] = archetype.GetOffset(compTypeIDs[index]);
|
var layoutResult = archetype.GetLayout(compTypeIDs[index]);
|
||||||
if (offsets[index] == -1)
|
if (layoutResult.Status != ResultStatus.Success)
|
||||||
{
|
{
|
||||||
hasAllComponents = false;
|
hasAllComponents = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offsets[index] = layoutResult.Value.offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasAllComponents)
|
if (!hasAllComponents)
|
||||||
@@ -47,81 +55,35 @@ public unsafe partial struct EntityQuery
|
|||||||
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
||||||
{
|
{
|
||||||
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
||||||
var count = chunk.Count;
|
var pChunkData = chunk.GetUnsafePtr();
|
||||||
|
|
||||||
for (var index = 0; index < <#= i #>; index++)
|
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++) { #>
|
<# for (var localIndex = 0; localIndex < i; localIndex++) { #>
|
||||||
var pComp<#= localIndex #> = (T<#= localIndex #>*)(basePtrs[<#= localIndex #>] + (sizeof(T<#= localIndex #>) * entityIndex));
|
var pComp<#= localIndex #> = (T<#= localIndex #>*)(basePtrs[<#= localIndex #>] + (sizeof(T<#= localIndex #>) * entityIndex));
|
||||||
<# } #>
|
<# } #>
|
||||||
|
|
||||||
action(<#= AppendRefParameters(i, "*pComp{0}") #>);
|
<# if (isForEachWithEntity) { #>
|
||||||
}
|
var pEntity = (Entity*)(pChunkData + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex));
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
<# } #>
|
|
||||||
|
|
||||||
<# 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<T{0}>.value") #> };
|
|
||||||
var offsets = stackalloc int[<#= i #>];
|
|
||||||
var basePtrs = stackalloc byte*[<#= i #>];
|
|
||||||
|
|
||||||
foreach (var archetypeID in _matchingArchetypes)
|
|
||||||
{
|
|
||||||
ref var archetype = ref world.GetArchetypeReference(archetypeID);
|
|
||||||
var hasAllComponents = true;
|
|
||||||
for (var index = 0; index < <#= i #>; index++)
|
|
||||||
{
|
|
||||||
offsets[index] = archetype.GetOffset(compTypeIDs[index]);
|
|
||||||
if (offsets[index] == -1)
|
|
||||||
{
|
|
||||||
hasAllComponents = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasAllComponents)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var chunkIndex = 0; chunkIndex < archetype.ChunkCount; chunkIndex++)
|
|
||||||
{
|
|
||||||
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
|
|
||||||
var count = chunk.Count;
|
|
||||||
for (var index = 0; index < <#= i #>; index++)
|
|
||||||
{
|
|
||||||
basePtrs[index] = chunk.GetUnsafePtr() + offsets[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var entityIndex = 0; entityIndex < count; entityIndex++)
|
|
||||||
{
|
|
||||||
var pEntity = (Entity*)(chunk.GetUnsafePtr() + archetype.EntityIDsOffset + (sizeof(Entity) * entityIndex));
|
|
||||||
<# for (var localIndex = 0; localIndex < i; localIndex++) { #>
|
|
||||||
var pComp<#= localIndex #> = (T<#= localIndex #>*)(basePtrs[<#= localIndex #>] + (sizeof(T<#= localIndex #>) * entityIndex));
|
|
||||||
<# } #>
|
|
||||||
|
|
||||||
action(*pEntity, <#= AppendRefParameters(i, "*pComp{0}") #>);
|
action(*pEntity, <#= AppendRefParameters(i, "*pComp{0}") #>);
|
||||||
|
<# } else { #>
|
||||||
|
action(<#= AppendRefParameters(i, "*pComp{0}") #>);
|
||||||
|
<# } #>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<# } #>
|
||||||
<# } #>
|
<# } #>
|
||||||
}
|
}
|
||||||
278
Ghost.Entities/Templates/QueryBuilder.With.gen.cs
Normal file
278
Ghost.Entities/Templates/QueryBuilder.With.gen.cs
Normal file
@@ -0,0 +1,278 @@
|
|||||||
|
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Ghost.Entities;
|
||||||
|
|
||||||
|
public ref partial struct QueryBuilder
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public QueryBuilder WithAll<T0>()
|
||||||
|
where T0 : unmanaged, IComponent
|
||||||
|
{
|
||||||
|
_all.Add(ComponentTypeID<T0>.value);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public QueryBuilder WithAny<T0>()
|
||||||
|
where T0 : unmanaged, IComponent
|
||||||
|
{
|
||||||
|
_any.Add(ComponentTypeID<T0>.value);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public QueryBuilder WithAbsent<T0>()
|
||||||
|
where T0 : unmanaged, IComponent
|
||||||
|
{
|
||||||
|
_absent.Add(ComponentTypeID<T0>.value);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public QueryBuilder WithNone<T0>()
|
||||||
|
where T0 : unmanaged, IComponent
|
||||||
|
{
|
||||||
|
_none.Add(ComponentTypeID<T0>.value);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public QueryBuilder WithDisabled<T0>()
|
||||||
|
where T0 : unmanaged, IEnableableComponent
|
||||||
|
{
|
||||||
|
_disabled.Add(ComponentTypeID<T0>.value);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public QueryBuilder WithPresent<T0>()
|
||||||
|
where T0 : unmanaged, IComponent
|
||||||
|
{
|
||||||
|
_present.Add(ComponentTypeID<T0>.value);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public QueryBuilder WithAll<T0, T1>()
|
||||||
|
where T0 : unmanaged, IComponent
|
||||||
|
where T1 : unmanaged, IComponent
|
||||||
|
{
|
||||||
|
_all.Add(ComponentTypeID<T0>.value);
|
||||||
|
_all.Add(ComponentTypeID<T1>.value);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public QueryBuilder WithAny<T0, T1>()
|
||||||
|
where T0 : unmanaged, IComponent
|
||||||
|
where T1 : unmanaged, IComponent
|
||||||
|
{
|
||||||
|
_any.Add(ComponentTypeID<T0>.value);
|
||||||
|
_any.Add(ComponentTypeID<T1>.value);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public QueryBuilder WithAbsent<T0, T1>()
|
||||||
|
where T0 : unmanaged, IComponent
|
||||||
|
where T1 : unmanaged, IComponent
|
||||||
|
{
|
||||||
|
_absent.Add(ComponentTypeID<T0>.value);
|
||||||
|
_absent.Add(ComponentTypeID<T1>.value);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public QueryBuilder WithNone<T0, T1>()
|
||||||
|
where T0 : unmanaged, IComponent
|
||||||
|
where T1 : unmanaged, IComponent
|
||||||
|
{
|
||||||
|
_none.Add(ComponentTypeID<T0>.value);
|
||||||
|
_none.Add(ComponentTypeID<T1>.value);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public QueryBuilder WithDisabled<T0, T1>()
|
||||||
|
where T0 : unmanaged, IEnableableComponent
|
||||||
|
where T1 : unmanaged, IEnableableComponent
|
||||||
|
{
|
||||||
|
_disabled.Add(ComponentTypeID<T0>.value);
|
||||||
|
_disabled.Add(ComponentTypeID<T1>.value);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public QueryBuilder WithPresent<T0, T1>()
|
||||||
|
where T0 : unmanaged, IComponent
|
||||||
|
where T1 : unmanaged, IComponent
|
||||||
|
{
|
||||||
|
_present.Add(ComponentTypeID<T0>.value);
|
||||||
|
_present.Add(ComponentTypeID<T1>.value);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public QueryBuilder WithAll<T0, T1, T2>()
|
||||||
|
where T0 : unmanaged, IComponent
|
||||||
|
where T1 : unmanaged, IComponent
|
||||||
|
where T2 : unmanaged, IComponent
|
||||||
|
{
|
||||||
|
_all.Add(ComponentTypeID<T0>.value);
|
||||||
|
_all.Add(ComponentTypeID<T1>.value);
|
||||||
|
_all.Add(ComponentTypeID<T2>.value);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public QueryBuilder WithAny<T0, T1, T2>()
|
||||||
|
where T0 : unmanaged, IComponent
|
||||||
|
where T1 : unmanaged, IComponent
|
||||||
|
where T2 : unmanaged, IComponent
|
||||||
|
{
|
||||||
|
_any.Add(ComponentTypeID<T0>.value);
|
||||||
|
_any.Add(ComponentTypeID<T1>.value);
|
||||||
|
_any.Add(ComponentTypeID<T2>.value);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public QueryBuilder WithAbsent<T0, T1, T2>()
|
||||||
|
where T0 : unmanaged, IComponent
|
||||||
|
where T1 : unmanaged, IComponent
|
||||||
|
where T2 : unmanaged, IComponent
|
||||||
|
{
|
||||||
|
_absent.Add(ComponentTypeID<T0>.value);
|
||||||
|
_absent.Add(ComponentTypeID<T1>.value);
|
||||||
|
_absent.Add(ComponentTypeID<T2>.value);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public QueryBuilder WithNone<T0, T1, T2>()
|
||||||
|
where T0 : unmanaged, IComponent
|
||||||
|
where T1 : unmanaged, IComponent
|
||||||
|
where T2 : unmanaged, IComponent
|
||||||
|
{
|
||||||
|
_none.Add(ComponentTypeID<T0>.value);
|
||||||
|
_none.Add(ComponentTypeID<T1>.value);
|
||||||
|
_none.Add(ComponentTypeID<T2>.value);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public QueryBuilder WithDisabled<T0, T1, T2>()
|
||||||
|
where T0 : unmanaged, IEnableableComponent
|
||||||
|
where T1 : unmanaged, IEnableableComponent
|
||||||
|
where T2 : unmanaged, IEnableableComponent
|
||||||
|
{
|
||||||
|
_disabled.Add(ComponentTypeID<T0>.value);
|
||||||
|
_disabled.Add(ComponentTypeID<T1>.value);
|
||||||
|
_disabled.Add(ComponentTypeID<T2>.value);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public QueryBuilder WithPresent<T0, T1, T2>()
|
||||||
|
where T0 : unmanaged, IComponent
|
||||||
|
where T1 : unmanaged, IComponent
|
||||||
|
where T2 : unmanaged, IComponent
|
||||||
|
{
|
||||||
|
_present.Add(ComponentTypeID<T0>.value);
|
||||||
|
_present.Add(ComponentTypeID<T1>.value);
|
||||||
|
_present.Add(ComponentTypeID<T2>.value);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
110
Ghost.Entities/Templates/QueryBuilder.With.tt
Normal file
110
Ghost.Entities/Templates/QueryBuilder.With.tt
Normal file
@@ -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);
|
||||||
|
#>
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public QueryBuilder WithAll<<#= generics #>>()
|
||||||
|
<#= restrictions #>
|
||||||
|
{
|
||||||
|
<# for (var j = 0; j < i; j++) { #>
|
||||||
|
_all.Add(ComponentTypeID<T<#= j #>>.value);
|
||||||
|
<# } #>
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public QueryBuilder WithAny<<#= generics #>>()
|
||||||
|
<#= restrictions #>
|
||||||
|
{
|
||||||
|
<# for (var j = 0; j < i; j++) { #>
|
||||||
|
_any.Add(ComponentTypeID<T<#= j #>>.value);
|
||||||
|
<# } #>
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public QueryBuilder WithAbsent<<#= generics #>>()
|
||||||
|
<#= restrictions #>
|
||||||
|
{
|
||||||
|
<# for (var j = 0; j < i; j++) { #>
|
||||||
|
_absent.Add(ComponentTypeID<T<#= j #>>.value);
|
||||||
|
<# } #>
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public QueryBuilder WithNone<<#= generics #>>()
|
||||||
|
<#= restrictions #>
|
||||||
|
{
|
||||||
|
<# for (var j = 0; j < i; j++) { #>
|
||||||
|
_none.Add(ComponentTypeID<T<#= j #>>.value);
|
||||||
|
<# } #>
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public QueryBuilder WithDisabled<<#= generics #>>()
|
||||||
|
<#= enableRestrictions #>
|
||||||
|
{
|
||||||
|
<# for (var j = 0; j < i; j++) { #>
|
||||||
|
_disabled.Add(ComponentTypeID<T<#= j #>>.value);
|
||||||
|
<# } #>
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public QueryBuilder WithPresent<<#= generics #>>()
|
||||||
|
<#= restrictions #>
|
||||||
|
{
|
||||||
|
<# for (var j = 0; j < i; j++) { #>
|
||||||
|
_present.Add(ComponentTypeID<T<#= j #>>.value);
|
||||||
|
<# } #>
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
<# } #>
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ using Ghost.Core;
|
|||||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
using Misaki.HighPerformance.LowLevel.Collections;
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using TerraFX.Interop.Windows;
|
||||||
|
|
||||||
namespace Ghost.Entities;
|
namespace Ghost.Entities;
|
||||||
|
|
||||||
@@ -110,6 +111,7 @@ public partial class World : IIdentifierType, IDisposable, IEquatable<World>
|
|||||||
Dispose();
|
Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
internal Identifier<Archetype> CreateArchetype(ReadOnlySpan<Identifier<IComponent>> componentTypeIDs, int signatureHash)
|
internal Identifier<Archetype> CreateArchetype(ReadOnlySpan<Identifier<IComponent>> componentTypeIDs, int signatureHash)
|
||||||
{
|
{
|
||||||
var arcID = new Identifier<Archetype>(_archetypes.Count);
|
var arcID = new Identifier<Archetype>(_archetypes.Count);
|
||||||
@@ -119,12 +121,13 @@ public partial class World : IIdentifierType, IDisposable, IEquatable<World>
|
|||||||
for (int i = 0; i < _entityQueries.Count; i++)
|
for (int i = 0; i < _entityQueries.Count; i++)
|
||||||
{
|
{
|
||||||
ref var query = ref _entityQueries[i];
|
ref var query = ref _entityQueries[i];
|
||||||
query.AddArchetypeIfMatch(_archetypes[arcID.value]);
|
query.AddArchetypeIfMatch(in _archetypes[arcID.value]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return arcID;
|
return arcID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
internal Identifier<Archetype> GetArchetypeIDBySignatureHash(int signatureHash)
|
internal Identifier<Archetype> GetArchetypeIDBySignatureHash(int signatureHash)
|
||||||
{
|
{
|
||||||
if (_archetypeLookup.TryGetValue(signatureHash, out var arcID))
|
if (_archetypeLookup.TryGetValue(signatureHash, out var arcID))
|
||||||
@@ -135,20 +138,29 @@ public partial class World : IIdentifierType, IDisposable, IEquatable<World>
|
|||||||
return Identifier<Archetype>.Invalid;
|
return Identifier<Archetype>.Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
internal ref Archetype GetArchetypeReference(Identifier<Archetype> id)
|
internal ref Archetype GetArchetypeReference(Identifier<Archetype> id)
|
||||||
{
|
{
|
||||||
return ref _archetypes[id.value];
|
return ref _archetypes[id.value];
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Identifier<EntityQuery> CreateEntityQuery(EntityQueryMask mask)
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal Identifier<EntityQuery> CreateEntityQuery(EntityQueryMask mask, int maskHash)
|
||||||
{
|
{
|
||||||
var queryID = new Identifier<EntityQuery>(_entityQueries.Count);
|
var queryID = new Identifier<EntityQuery>(_entityQueries.Count);
|
||||||
_entityQueries.Add(new EntityQuery(_id, mask));
|
_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;
|
return queryID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
internal Identifier<EntityQuery> GetEntityQueryIDByMaskHash(int maskHash)
|
internal Identifier<EntityQuery> GetEntityQueryIDByMaskHash(int maskHash)
|
||||||
{
|
{
|
||||||
if (_querieLookup.TryGetValue(maskHash, out var queryID))
|
if (_querieLookup.TryGetValue(maskHash, out var queryID))
|
||||||
@@ -159,6 +171,7 @@ public partial class World : IIdentifierType, IDisposable, IEquatable<World>
|
|||||||
return Identifier<EntityQuery>.Invalid;
|
return Identifier<EntityQuery>.Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public ref EntityQuery GetEntityQueryReference(Identifier<EntityQuery> id)
|
public ref EntityQuery GetEntityQueryReference(Identifier<EntityQuery> id)
|
||||||
{
|
{
|
||||||
return ref _entityQueries[id.value];
|
return ref _entityQueries[id.value];
|
||||||
|
|||||||
Reference in New Issue
Block a user