Per-component versioning and change tracking for ECS

Introduce per-component versioning in chunks and world for efficient change detection.
- Add version arrays to chunks and global version to world.
- Update queries and ForEach to mark written components as changed.
- Extend QueryBuilder with WithAllRW/WithPresentRW for write access.
- Expose change tracking API in ChunkView.
- Improve thread safety and debug code.
- Update tests and examples to demonstrate new features.
This commit is contained in:
2025-12-10 19:01:25 +09:00
parent 21e85e0c02
commit 856fa4f07d
11 changed files with 968 additions and 93 deletions

View File

@@ -39,7 +39,7 @@ public partial class EntityTest : ITest
var entity2 = _world.EntityManager.CreateEntity(ComponentTypeID<Transform>.value);
_world.EntityManager.SetComponent(entity2, new Transform { position = new float3(1, 2, 3) });
var queryID = new QueryBuilder().WithAll<Transform>().Build(_world);
var queryID = new QueryBuilder().WithAllRW<Transform>().WithAbsent<Mesh>().Build(_world);
ref var query = ref _world.GetEntityQueryReference(queryID);
// var testJob = new TestEntityQueryJob();
@@ -49,28 +49,32 @@ public partial class EntityTest : ITest
_world.EntityManager.AddScriptComponent<TestScriptComponent>(entity1);
_world.EntityManager.RemoveComponent<ManagedEntityRef>(entity1); // This should destory the managed entity and call OnDestroy
_world.AdvanceVersion();
query.ForEach<Transform>((e, ref t) =>
{
Console.WriteLine($"Entity {e} Has Position: {t.position}");
});
// foreach (var (entity, transform) in query.GetEntityComponentIterator<Transform>())
// {
// Console.WriteLine($"Entity {entity} Updated Position: {transform.Get().position}");
// }
//
// foreach (var chunk in query.GetChunkIterator())
// {
// var transforms = chunk.GetComponentData<Transform>();
// var entities = chunk.GetEntities();
// var bits = chunk.GetEnableBits<Transform>();
//
// var it = bits.GetIterator();
// while (it.Next(out var index) && index < chunk.Count)
// {
// Console.WriteLine($"Entity {entities[index]} Updated Position: {transforms[index].position}");
// }
// }
//foreach (var (entity, transform) in query.GetEntityComponentIterator<Transform>())
//{
// Console.WriteLine($"Entity {entity} Updated Position: {transform.Get().position}");
//}
foreach (var chunk in query.GetChunkIterator())
{
var transforms = chunk.GetComponentData<Transform>();
var entities = chunk.GetEntities();
var bits = chunk.GetEnableBits<Transform>();
var changed = chunk.HasChanged<Transform>(0);
var it = bits.GetIterator();
while (it.Next(out var index) && index < chunk.Count)
{
Console.WriteLine($"Entity {entities[index]} Updated Position: {transforms[index].position}");
}
}
_world.EntityManager.DestroyEntity(entity1);
_world.EntityManager.DestroyEntity(entity2);

View File

@@ -48,7 +48,7 @@ internal unsafe sealed class ChunkDebugView
{
get
{
#if DEBUG || GHOST_EDITOR
#if !(DEBUG || GHOST_EDITOR)
#else
if (count == 0)
#endif
@@ -56,9 +56,7 @@ internal unsafe sealed class ChunkDebugView
return [];
}
#pragma warning disable CS0162 // Unreachable code detected
var views = new List<object>();
#pragma warning restore CS0162 // Unreachable code detected
ref var archetype = ref World.GetWorld(worldID).GetValueOrThrow()
.GetArchetypeReference(archetypeID);
@@ -108,8 +106,8 @@ internal unsafe struct Chunk : IDisposable
public const int BIT_ALIGNMENT_MINUS_ONE = BIT_ALIGNMENT - 1;
private UnsafeArray<byte> _data;
private UnsafeArray<int> _versions;
internal int _version;
internal int _count;
internal readonly int _capacity;
@@ -119,11 +117,26 @@ internal unsafe struct Chunk : IDisposable
internal int _archetypeID;
#endif
public Chunk(int bufferSize, int capacity)
public Chunk(int bufferSize, int capacity, int componentCount, int globalVersion)
{
_data = new UnsafeArray<byte>(bufferSize, Allocator.Persistent, AllocationOption.Clear);
_versions = new UnsafeArray<int>(componentCount, Allocator.Persistent);
_capacity = capacity;
_count = 0;
_versions.AsSpan().Fill(globalVersion);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void MarkChanged(int componentTypeId, int globalVersion)
{
_versions[componentTypeId] = globalVersion;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly int GetVersion(int componentTypeId)
{
return _versions[componentTypeId];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -132,9 +145,16 @@ internal unsafe struct Chunk : IDisposable
return (byte*)_data.GetUnsafePtr();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly int* GetVersionUnsafePtr()
{
return (int*)_versions.GetUnsafePtr();
}
public void Dispose()
{
_data.Dispose();
_versions.Dispose();
}
}
@@ -324,8 +344,10 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
}
}
var world = World.GetWorldUncheck(_worldID);
// Need to allocate a new chunk
var newChunk = new Chunk(Chunk.CHUNK_BUFFER_SIZE, _entityCapacity);
var newChunk = new Chunk(Chunk.CHUNK_BUFFER_SIZE, _entityCapacity, _layouts.Count, world.Version);
#if DEBUG || GHOST_EDITOR
newChunk._worldID = _worldID;
newChunk._archetypeID = _id;
@@ -370,7 +392,7 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
}
var offset = r.Value.offset;
var chunk = _chunks[chunkIndex];
ref var chunk = ref _chunks[chunkIndex];
var chunkBase = chunk.GetUnsafePtr();
var size = ComponentRegister.GetComponentInfo(componentID).size;
@@ -378,6 +400,9 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
MemoryUtility.MemCpy(dst, pComponent, (nuint)size);
var world = World.GetWorldUncheck(_worldID);
chunk.MarkChanged(componentID, world.Version);
return ErrorStatus.None;
}
@@ -438,13 +463,8 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable
var pLastEntity = chunkBase + _entityIdsOffset + (sizeof(Entity) * lastIndex);
var pRowEntity = chunkBase + _entityIdsOffset + (sizeof(Entity) * rowIndex);
var wroldResult = World.GetWorld(_worldID);
if (wroldResult.Error != ErrorStatus.None)
{
return wroldResult.Error;
}
var result = wroldResult.Value.EntityManager.UpdateEntityLocation(*(Entity*)pLastEntity, _id, chunkIndex, rowIndex);
var wrold = World.GetWorldUncheck(_worldID);
var result = wrold.EntityManager.UpdateEntityLocation(*(Entity*)pLastEntity, _id, chunkIndex, rowIndex);
if (result != ErrorStatus.None)
{
return result;

View File

@@ -1,6 +1,7 @@
using Ghost.Core;
using Misaki.HighPerformance.LowLevel.Collections;
using Misaki.HighPerformance.LowLevel.Utilities;
using System.Runtime.CompilerServices;
namespace Ghost.Entities;
@@ -18,6 +19,7 @@ public struct ComponentInfo
public Identifier<IComponent> id;
public int size;
public int alignment;
public int lastWriteVersion;
public bool isEnableable;
}
@@ -77,6 +79,7 @@ internal static class ComponentRegister
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Identifier<IComponent> GetComponentID(Type type)
{
var typeHandle = type.TypeHandle.Value;
@@ -91,9 +94,24 @@ internal static class ComponentRegister
throw new KeyNotFoundException($"Component type {type} is not registered.");
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ComponentInfo GetComponentInfo(Identifier<IComponent> typeId)
{
return s_registeredComponents[typeId];
lock (s_registeredComponents)
{
return s_registeredComponents[typeId];
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SetComponentLastWrite(Identifier<IComponent> typeId, int version)
{
lock (s_registeredComponents)
{
var info = s_registeredComponents[typeId];
info.lastWriteVersion = version;
s_registeredComponents[typeId] = info;
}
}
public static int GetHashCode(params ReadOnlySpan<Identifier<IComponent>> componentTypeIDs)

View File

@@ -5,7 +5,7 @@ using System.Runtime.CompilerServices;
namespace Ghost.Entities;
public struct EntityQueryMask : IDisposable, IEquatable<EntityQueryMask>
internal struct EntityQueryMask : IDisposable, IEquatable<EntityQueryMask>
{
public UnsafeBitSet structuralAll;
public UnsafeBitSet structuralAny;
@@ -15,6 +15,8 @@ public struct EntityQueryMask : IDisposable, IEquatable<EntityQueryMask>
public UnsafeBitSet requireDisabled;
public UnsafeBitSet rejectIfEnabled;
public UnsafeBitSet writeAccess;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly bool Matches(ref readonly UnsafeBitSet archetypeSignature)
{
@@ -33,6 +35,7 @@ public struct EntityQueryMask : IDisposable, IEquatable<EntityQueryMask>
if (requireEnabled.IsCreated) hash = hash * 23 + requireEnabled.GetHashCode();
if (requireDisabled.IsCreated) hash = hash * 23 + requireDisabled.GetHashCode();
if (rejectIfEnabled.IsCreated) hash = hash * 23 + rejectIfEnabled.GetHashCode();
if (writeAccess.IsCreated) hash = hash * 23 + writeAccess.GetHashCode();
return hash;
}
@@ -44,7 +47,8 @@ public struct EntityQueryMask : IDisposable, IEquatable<EntityQueryMask>
&& structuralAbsent.Equals(other.structuralAbsent)
&& requireEnabled.Equals(other.requireEnabled)
&& requireDisabled.Equals(other.requireDisabled)
&& rejectIfEnabled.Equals(other.rejectIfEnabled);
&& rejectIfEnabled.Equals(other.rejectIfEnabled)
&& writeAccess.Equals(other.writeAccess);
}
public override readonly bool Equals(object? obj)
@@ -71,6 +75,8 @@ public struct EntityQueryMask : IDisposable, IEquatable<EntityQueryMask>
requireEnabled.Dispose();
requireDisabled.Dispose();
rejectIfEnabled.Dispose();
writeAccess.Dispose();
}
}
@@ -82,21 +88,11 @@ public readonly unsafe ref struct ChunkView
{
private readonly ReadOnlyUnsafeCollection<Archetype.ComponentMemoryLayout> _layouts;
private readonly byte* _pChunkData;
private readonly int* _pVersion;
private readonly int _entityOffset;
private readonly int _entityCount;
private readonly int _version;
public readonly int Count => _entityCount;
public readonly int Version => _version;
internal ChunkView(ReadOnlyUnsafeCollection<Archetype.ComponentMemoryLayout> layouts, byte* pChunkData, int entityOffset, int entityCount, int version)
{
_layouts = layouts;
_pChunkData = pChunkData;
_entityOffset = entityOffset;
_entityCount = entityCount;
_version = version;
}
internal ChunkView(ref readonly Archetype archetype, ref readonly Chunk chunk)
{
@@ -104,19 +100,63 @@ public readonly unsafe ref struct ChunkView
_pChunkData = chunk.GetUnsafePtr();
_entityOffset = archetype.EntityIDsOffset;
_entityCount = chunk._count;
_version = chunk._version;
_pVersion = chunk.GetVersionUnsafePtr();
}
// TODO: We do not have a proper versioning system yet.
public bool HasChanged(int version)
/// <summary>
/// Determines whether the specified component has changed since the given version.
/// </summary>
/// <param name="id">The identifier of the component to check for changes.</param>
/// <param name="version">The version number to compare against the component's current version. Must be greater than or equal to zero.</param>
/// <returns>true if the component's current version is less than or equal to the specified version; otherwise, false.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool HasChanged(Identifier<IComponent> id, int version)
{
return _version != version;
return version < _pVersion[id];
}
/// <summary>
/// Determines whether the specified version indicates that the component of type <typeparamref name="T"/> has
/// changed since the last recorded version.
/// </summary>
/// <typeparam name="T">The type of component to check for changes. Must be an unmanaged type that implements <see cref="IComponent"/>.</typeparam>
/// <param name="version">The version number to compare against the current version of the component.</param>
/// <returns>true if the component of type T has changed since the specified version; otherwise, false.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly bool HasChanged<T>(int version)
where T : unmanaged, IComponent
{
return version < _pVersion[ComponentTypeID<T>.value];
}
/// <summary>
/// Gets the current version number associated with the specified component identifier.
/// </summary>
/// <param name="id">The identifier of the component for which to retrieve the version number. Must reference a valid component.</param>
/// <returns>The version number of the specified component.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly int GetComponentVersion(Identifier<IComponent> id)
{
return _pVersion[id];
}
/// <summary>
/// Gets the current version number associated with the specified component type.
/// </summary>
/// <typeparam name="T">The component type for which to retrieve the version. Must be an unmanaged type that implements <see cref="IComponent"/>.</typeparam>
/// <returns>The version number of the component type <typeparamref name="T"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly int GetComponentVersion<T>()
where T : unmanaged, IComponent
{
return _pVersion[ComponentTypeID<T>.value];
}
/// <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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<Entity> GetEntities()
{
var pEntity = (Entity*)(_pChunkData + _entityOffset);
@@ -129,6 +169,7 @@ public readonly unsafe ref struct ChunkView
/// <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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<T> GetComponentData<T>()
where T : unmanaged, IComponent
{
@@ -144,6 +185,7 @@ public readonly unsafe ref struct ChunkView
/// <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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public SpanBitSet GetEnableBits<T>()
where T : unmanaged, IEnableableComponent
{
@@ -164,6 +206,7 @@ public readonly unsafe ref struct ChunkView
/// <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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsComponentEnabled<T>(int index)
where T : unmanaged, IEnableableComponent
{
@@ -373,6 +416,8 @@ public ref partial struct QueryBuilder
private UnsafeList<Identifier<IComponent>> _disabled;
private UnsafeList<Identifier<IComponent>> _present;
private UnsafeList<Identifier<IComponent>> _rw;
public QueryBuilder()
{
_scope = AllocationManager.CreateStackScope();
@@ -383,6 +428,8 @@ public ref partial struct QueryBuilder
_none = new UnsafeList<Identifier<IComponent>>(4, _scope.AllocationHandle);
_disabled = new UnsafeList<Identifier<IComponent>>(4, _scope.AllocationHandle);
_present = new UnsafeList<Identifier<IComponent>>(4, _scope.AllocationHandle);
_rw = new UnsafeList<Identifier<IComponent>>(4, _scope.AllocationHandle);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -394,7 +441,7 @@ public ref partial struct QueryBuilder
}
}
public Identifier<EntityQuery> Build(World world)
public Identifier<EntityQuery> Build(World world, Allocator allocator = Allocator.Persistent)
{
// 1. Calculate max component ID to size the BitSets
var maxID = 0;
@@ -408,12 +455,14 @@ public ref partial struct QueryBuilder
// 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),
structuralAll = new UnsafeBitSet(maxID + 1, allocator, AllocationOption.Clear),
structuralAny = new UnsafeBitSet(maxID + 1, allocator, AllocationOption.Clear),
structuralAbsent = new UnsafeBitSet(maxID + 1, allocator, AllocationOption.Clear),
requireEnabled = new UnsafeBitSet(maxID + 1, allocator, AllocationOption.Clear),
requireDisabled = new UnsafeBitSet(maxID + 1, allocator, AllocationOption.Clear),
rejectIfEnabled = new UnsafeBitSet(maxID + 1, allocator, AllocationOption.Clear),
writeAccess = new UnsafeBitSet(maxID + 1, allocator, AllocationOption.Clear),
};
// 3. Fill BitSets
@@ -456,6 +505,11 @@ public ref partial struct QueryBuilder
mask.structuralAny.SetBit(id);
}
foreach (var id in _rw)
{
mask.writeAccess.SetBit(id);
}
// 4. Ask World for the Query (Cached)
var maskHash = mask.GetHashCode();
var queryID = world.GetEntityQueryIDByMaskHash(maskHash);

View File

@@ -1,3 +1,4 @@
using Ghost.Core;
namespace Ghost.Entities;
@@ -7,12 +8,37 @@ public unsafe partial struct EntityQuery
public readonly void ForEach<T0>(ForEach<T0> action)
where T0 : unmanaged, IComponent
{
var world = World.GetWorld(_worldID).GetValueOrThrow();
var world = World.GetWorldUncheck(_worldID);
var globalVersion = world.Version;
var compTypeIDs = stackalloc int[] { ComponentTypeID<T0>.value };
var comp0TypeID = ComponentTypeID<T0>.value;
var compTypeIDs = stackalloc int[]
{
comp0TypeID.value,
};
var changedCompIDs = stackalloc int[1];
var offsets = stackalloc int[1];
var basePtrs = stackalloc byte*[1];
var changedCompCount = 0;
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 1; i++)
{
if (id == compTypeIDs[i])
{
ComponentRegister.SetComponentLastWrite(id, globalVersion);
changedCompIDs[changedCompCount] = id;
changedCompCount++;
break;
}
}
}
for (var i = 0; i < _matchingArchetypes.Count; i++)
{
ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
@@ -39,6 +65,11 @@ public unsafe partial struct EntityQuery
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
var pChunkData = chunk.GetUnsafePtr();
for (var j = 0; j < changedCompCount; j++)
{
chunk.MarkChanged(changedCompIDs[i], globalVersion);
}
for (var index = 0; index < 1; index++)
{
basePtrs[index] = pChunkData + offsets[index];
@@ -63,12 +94,39 @@ public unsafe partial struct EntityQuery
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
{
var world = World.GetWorld(_worldID).GetValueOrThrow();
var world = World.GetWorldUncheck(_worldID);
var globalVersion = world.Version;
var compTypeIDs = stackalloc int[] { ComponentTypeID<T0>.value, ComponentTypeID<T1>.value };
var comp0TypeID = ComponentTypeID<T0>.value;
var comp1TypeID = ComponentTypeID<T1>.value;
var compTypeIDs = stackalloc int[]
{
comp0TypeID.value,
comp1TypeID.value,
};
var changedCompIDs = stackalloc int[2];
var offsets = stackalloc int[2];
var basePtrs = stackalloc byte*[2];
var changedCompCount = 0;
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 2; i++)
{
if (id == compTypeIDs[i])
{
ComponentRegister.SetComponentLastWrite(id, globalVersion);
changedCompIDs[changedCompCount] = id;
changedCompCount++;
break;
}
}
}
for (var i = 0; i < _matchingArchetypes.Count; i++)
{
ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
@@ -95,6 +153,11 @@ public unsafe partial struct EntityQuery
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
var pChunkData = chunk.GetUnsafePtr();
for (var j = 0; j < changedCompCount; j++)
{
chunk.MarkChanged(changedCompIDs[i], globalVersion);
}
for (var index = 0; index < 2; index++)
{
basePtrs[index] = pChunkData + offsets[index];
@@ -121,12 +184,41 @@ public unsafe partial struct EntityQuery
where T1 : unmanaged, IComponent
where T2 : unmanaged, IComponent
{
var world = World.GetWorld(_worldID).GetValueOrThrow();
var world = World.GetWorldUncheck(_worldID);
var globalVersion = world.Version;
var compTypeIDs = stackalloc int[] { ComponentTypeID<T0>.value, ComponentTypeID<T1>.value, ComponentTypeID<T2>.value };
var comp0TypeID = ComponentTypeID<T0>.value;
var comp1TypeID = ComponentTypeID<T1>.value;
var comp2TypeID = ComponentTypeID<T2>.value;
var compTypeIDs = stackalloc int[]
{
comp0TypeID.value,
comp1TypeID.value,
comp2TypeID.value,
};
var changedCompIDs = stackalloc int[3];
var offsets = stackalloc int[3];
var basePtrs = stackalloc byte*[3];
var changedCompCount = 0;
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 3; i++)
{
if (id == compTypeIDs[i])
{
ComponentRegister.SetComponentLastWrite(id, globalVersion);
changedCompIDs[changedCompCount] = id;
changedCompCount++;
break;
}
}
}
for (var i = 0; i < _matchingArchetypes.Count; i++)
{
ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
@@ -153,6 +245,11 @@ public unsafe partial struct EntityQuery
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
var pChunkData = chunk.GetUnsafePtr();
for (var j = 0; j < changedCompCount; j++)
{
chunk.MarkChanged(changedCompIDs[i], globalVersion);
}
for (var index = 0; index < 3; index++)
{
basePtrs[index] = pChunkData + offsets[index];
@@ -181,12 +278,43 @@ public unsafe partial struct EntityQuery
where T2 : unmanaged, IComponent
where T3 : unmanaged, IComponent
{
var world = World.GetWorld(_worldID).GetValueOrThrow();
var world = World.GetWorldUncheck(_worldID);
var globalVersion = world.Version;
var compTypeIDs = stackalloc int[] { ComponentTypeID<T0>.value, ComponentTypeID<T1>.value, ComponentTypeID<T2>.value, ComponentTypeID<T3>.value };
var comp0TypeID = ComponentTypeID<T0>.value;
var comp1TypeID = ComponentTypeID<T1>.value;
var comp2TypeID = ComponentTypeID<T2>.value;
var comp3TypeID = ComponentTypeID<T3>.value;
var compTypeIDs = stackalloc int[]
{
comp0TypeID.value,
comp1TypeID.value,
comp2TypeID.value,
comp3TypeID.value,
};
var changedCompIDs = stackalloc int[4];
var offsets = stackalloc int[4];
var basePtrs = stackalloc byte*[4];
var changedCompCount = 0;
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 4; i++)
{
if (id == compTypeIDs[i])
{
ComponentRegister.SetComponentLastWrite(id, globalVersion);
changedCompIDs[changedCompCount] = id;
changedCompCount++;
break;
}
}
}
for (var i = 0; i < _matchingArchetypes.Count; i++)
{
ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
@@ -213,6 +341,11 @@ public unsafe partial struct EntityQuery
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
var pChunkData = chunk.GetUnsafePtr();
for (var j = 0; j < changedCompCount; j++)
{
chunk.MarkChanged(changedCompIDs[i], globalVersion);
}
for (var index = 0; index < 4; index++)
{
basePtrs[index] = pChunkData + offsets[index];
@@ -243,12 +376,45 @@ public unsafe partial struct EntityQuery
where T3 : unmanaged, IComponent
where T4 : unmanaged, IComponent
{
var world = World.GetWorld(_worldID).GetValueOrThrow();
var world = World.GetWorldUncheck(_worldID);
var globalVersion = world.Version;
var compTypeIDs = stackalloc int[] { ComponentTypeID<T0>.value, ComponentTypeID<T1>.value, ComponentTypeID<T2>.value, ComponentTypeID<T3>.value, ComponentTypeID<T4>.value };
var comp0TypeID = ComponentTypeID<T0>.value;
var comp1TypeID = ComponentTypeID<T1>.value;
var comp2TypeID = ComponentTypeID<T2>.value;
var comp3TypeID = ComponentTypeID<T3>.value;
var comp4TypeID = ComponentTypeID<T4>.value;
var compTypeIDs = stackalloc int[]
{
comp0TypeID.value,
comp1TypeID.value,
comp2TypeID.value,
comp3TypeID.value,
comp4TypeID.value,
};
var changedCompIDs = stackalloc int[5];
var offsets = stackalloc int[5];
var basePtrs = stackalloc byte*[5];
var changedCompCount = 0;
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 5; i++)
{
if (id == compTypeIDs[i])
{
ComponentRegister.SetComponentLastWrite(id, globalVersion);
changedCompIDs[changedCompCount] = id;
changedCompCount++;
break;
}
}
}
for (var i = 0; i < _matchingArchetypes.Count; i++)
{
ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
@@ -275,6 +441,11 @@ public unsafe partial struct EntityQuery
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
var pChunkData = chunk.GetUnsafePtr();
for (var j = 0; j < changedCompCount; j++)
{
chunk.MarkChanged(changedCompIDs[i], globalVersion);
}
for (var index = 0; index < 5; index++)
{
basePtrs[index] = pChunkData + offsets[index];
@@ -307,12 +478,47 @@ public unsafe partial struct EntityQuery
where T4 : unmanaged, IComponent
where T5 : unmanaged, IComponent
{
var world = World.GetWorld(_worldID).GetValueOrThrow();
var world = World.GetWorldUncheck(_worldID);
var globalVersion = world.Version;
var compTypeIDs = stackalloc int[] { ComponentTypeID<T0>.value, ComponentTypeID<T1>.value, ComponentTypeID<T2>.value, ComponentTypeID<T3>.value, ComponentTypeID<T4>.value, ComponentTypeID<T5>.value };
var comp0TypeID = ComponentTypeID<T0>.value;
var comp1TypeID = ComponentTypeID<T1>.value;
var comp2TypeID = ComponentTypeID<T2>.value;
var comp3TypeID = ComponentTypeID<T3>.value;
var comp4TypeID = ComponentTypeID<T4>.value;
var comp5TypeID = ComponentTypeID<T5>.value;
var compTypeIDs = stackalloc int[]
{
comp0TypeID.value,
comp1TypeID.value,
comp2TypeID.value,
comp3TypeID.value,
comp4TypeID.value,
comp5TypeID.value,
};
var changedCompIDs = stackalloc int[6];
var offsets = stackalloc int[6];
var basePtrs = stackalloc byte*[6];
var changedCompCount = 0;
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 6; i++)
{
if (id == compTypeIDs[i])
{
ComponentRegister.SetComponentLastWrite(id, globalVersion);
changedCompIDs[changedCompCount] = id;
changedCompCount++;
break;
}
}
}
for (var i = 0; i < _matchingArchetypes.Count; i++)
{
ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
@@ -339,6 +545,11 @@ public unsafe partial struct EntityQuery
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
var pChunkData = chunk.GetUnsafePtr();
for (var j = 0; j < changedCompCount; j++)
{
chunk.MarkChanged(changedCompIDs[i], globalVersion);
}
for (var index = 0; index < 6; index++)
{
basePtrs[index] = pChunkData + offsets[index];
@@ -373,12 +584,49 @@ public unsafe partial struct EntityQuery
where T5 : unmanaged, IComponent
where T6 : unmanaged, IComponent
{
var world = World.GetWorld(_worldID).GetValueOrThrow();
var world = World.GetWorldUncheck(_worldID);
var globalVersion = world.Version;
var compTypeIDs = stackalloc int[] { ComponentTypeID<T0>.value, ComponentTypeID<T1>.value, ComponentTypeID<T2>.value, ComponentTypeID<T3>.value, ComponentTypeID<T4>.value, ComponentTypeID<T5>.value, ComponentTypeID<T6>.value };
var comp0TypeID = ComponentTypeID<T0>.value;
var comp1TypeID = ComponentTypeID<T1>.value;
var comp2TypeID = ComponentTypeID<T2>.value;
var comp3TypeID = ComponentTypeID<T3>.value;
var comp4TypeID = ComponentTypeID<T4>.value;
var comp5TypeID = ComponentTypeID<T5>.value;
var comp6TypeID = ComponentTypeID<T6>.value;
var compTypeIDs = stackalloc int[]
{
comp0TypeID.value,
comp1TypeID.value,
comp2TypeID.value,
comp3TypeID.value,
comp4TypeID.value,
comp5TypeID.value,
comp6TypeID.value,
};
var changedCompIDs = stackalloc int[7];
var offsets = stackalloc int[7];
var basePtrs = stackalloc byte*[7];
var changedCompCount = 0;
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 7; i++)
{
if (id == compTypeIDs[i])
{
ComponentRegister.SetComponentLastWrite(id, globalVersion);
changedCompIDs[changedCompCount] = id;
changedCompCount++;
break;
}
}
}
for (var i = 0; i < _matchingArchetypes.Count; i++)
{
ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
@@ -405,6 +653,11 @@ public unsafe partial struct EntityQuery
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
var pChunkData = chunk.GetUnsafePtr();
for (var j = 0; j < changedCompCount; j++)
{
chunk.MarkChanged(changedCompIDs[i], globalVersion);
}
for (var index = 0; index < 7; index++)
{
basePtrs[index] = pChunkData + offsets[index];
@@ -441,12 +694,51 @@ public unsafe partial struct EntityQuery
where T6 : unmanaged, IComponent
where T7 : unmanaged, IComponent
{
var world = World.GetWorld(_worldID).GetValueOrThrow();
var world = World.GetWorldUncheck(_worldID);
var globalVersion = world.Version;
var compTypeIDs = stackalloc int[] { ComponentTypeID<T0>.value, ComponentTypeID<T1>.value, ComponentTypeID<T2>.value, ComponentTypeID<T3>.value, ComponentTypeID<T4>.value, ComponentTypeID<T5>.value, ComponentTypeID<T6>.value, ComponentTypeID<T7>.value };
var comp0TypeID = ComponentTypeID<T0>.value;
var comp1TypeID = ComponentTypeID<T1>.value;
var comp2TypeID = ComponentTypeID<T2>.value;
var comp3TypeID = ComponentTypeID<T3>.value;
var comp4TypeID = ComponentTypeID<T4>.value;
var comp5TypeID = ComponentTypeID<T5>.value;
var comp6TypeID = ComponentTypeID<T6>.value;
var comp7TypeID = ComponentTypeID<T7>.value;
var compTypeIDs = stackalloc int[]
{
comp0TypeID.value,
comp1TypeID.value,
comp2TypeID.value,
comp3TypeID.value,
comp4TypeID.value,
comp5TypeID.value,
comp6TypeID.value,
comp7TypeID.value,
};
var changedCompIDs = stackalloc int[8];
var offsets = stackalloc int[8];
var basePtrs = stackalloc byte*[8];
var changedCompCount = 0;
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 8; i++)
{
if (id == compTypeIDs[i])
{
ComponentRegister.SetComponentLastWrite(id, globalVersion);
changedCompIDs[changedCompCount] = id;
changedCompCount++;
break;
}
}
}
for (var i = 0; i < _matchingArchetypes.Count; i++)
{
ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
@@ -473,6 +765,11 @@ public unsafe partial struct EntityQuery
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
var pChunkData = chunk.GetUnsafePtr();
for (var j = 0; j < changedCompCount; j++)
{
chunk.MarkChanged(changedCompIDs[i], globalVersion);
}
for (var index = 0; index < 8; index++)
{
basePtrs[index] = pChunkData + offsets[index];
@@ -503,12 +800,37 @@ public unsafe partial struct EntityQuery
public readonly void ForEach<T0>(ForEachWithEntity<T0> action)
where T0 : unmanaged, IComponent
{
var world = World.GetWorld(_worldID).GetValueOrThrow();
var world = World.GetWorldUncheck(_worldID);
var globalVersion = world.Version;
var compTypeIDs = stackalloc int[] { ComponentTypeID<T0>.value };
var comp0TypeID = ComponentTypeID<T0>.value;
var compTypeIDs = stackalloc int[]
{
comp0TypeID.value,
};
var changedCompIDs = stackalloc int[1];
var offsets = stackalloc int[1];
var basePtrs = stackalloc byte*[1];
var changedCompCount = 0;
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 1; i++)
{
if (id == compTypeIDs[i])
{
ComponentRegister.SetComponentLastWrite(id, globalVersion);
changedCompIDs[changedCompCount] = id;
changedCompCount++;
break;
}
}
}
for (var i = 0; i < _matchingArchetypes.Count; i++)
{
ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
@@ -535,6 +857,11 @@ public unsafe partial struct EntityQuery
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
var pChunkData = chunk.GetUnsafePtr();
for (var j = 0; j < changedCompCount; j++)
{
chunk.MarkChanged(changedCompIDs[i], globalVersion);
}
for (var index = 0; index < 1; index++)
{
basePtrs[index] = pChunkData + offsets[index];
@@ -560,12 +887,39 @@ public unsafe partial struct EntityQuery
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
{
var world = World.GetWorld(_worldID).GetValueOrThrow();
var world = World.GetWorldUncheck(_worldID);
var globalVersion = world.Version;
var compTypeIDs = stackalloc int[] { ComponentTypeID<T0>.value, ComponentTypeID<T1>.value };
var comp0TypeID = ComponentTypeID<T0>.value;
var comp1TypeID = ComponentTypeID<T1>.value;
var compTypeIDs = stackalloc int[]
{
comp0TypeID.value,
comp1TypeID.value,
};
var changedCompIDs = stackalloc int[2];
var offsets = stackalloc int[2];
var basePtrs = stackalloc byte*[2];
var changedCompCount = 0;
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 2; i++)
{
if (id == compTypeIDs[i])
{
ComponentRegister.SetComponentLastWrite(id, globalVersion);
changedCompIDs[changedCompCount] = id;
changedCompCount++;
break;
}
}
}
for (var i = 0; i < _matchingArchetypes.Count; i++)
{
ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
@@ -592,6 +946,11 @@ public unsafe partial struct EntityQuery
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
var pChunkData = chunk.GetUnsafePtr();
for (var j = 0; j < changedCompCount; j++)
{
chunk.MarkChanged(changedCompIDs[i], globalVersion);
}
for (var index = 0; index < 2; index++)
{
basePtrs[index] = pChunkData + offsets[index];
@@ -619,12 +978,41 @@ public unsafe partial struct EntityQuery
where T1 : unmanaged, IComponent
where T2 : unmanaged, IComponent
{
var world = World.GetWorld(_worldID).GetValueOrThrow();
var world = World.GetWorldUncheck(_worldID);
var globalVersion = world.Version;
var compTypeIDs = stackalloc int[] { ComponentTypeID<T0>.value, ComponentTypeID<T1>.value, ComponentTypeID<T2>.value };
var comp0TypeID = ComponentTypeID<T0>.value;
var comp1TypeID = ComponentTypeID<T1>.value;
var comp2TypeID = ComponentTypeID<T2>.value;
var compTypeIDs = stackalloc int[]
{
comp0TypeID.value,
comp1TypeID.value,
comp2TypeID.value,
};
var changedCompIDs = stackalloc int[3];
var offsets = stackalloc int[3];
var basePtrs = stackalloc byte*[3];
var changedCompCount = 0;
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 3; i++)
{
if (id == compTypeIDs[i])
{
ComponentRegister.SetComponentLastWrite(id, globalVersion);
changedCompIDs[changedCompCount] = id;
changedCompCount++;
break;
}
}
}
for (var i = 0; i < _matchingArchetypes.Count; i++)
{
ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
@@ -651,6 +1039,11 @@ public unsafe partial struct EntityQuery
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
var pChunkData = chunk.GetUnsafePtr();
for (var j = 0; j < changedCompCount; j++)
{
chunk.MarkChanged(changedCompIDs[i], globalVersion);
}
for (var index = 0; index < 3; index++)
{
basePtrs[index] = pChunkData + offsets[index];
@@ -680,12 +1073,43 @@ public unsafe partial struct EntityQuery
where T2 : unmanaged, IComponent
where T3 : unmanaged, IComponent
{
var world = World.GetWorld(_worldID).GetValueOrThrow();
var world = World.GetWorldUncheck(_worldID);
var globalVersion = world.Version;
var compTypeIDs = stackalloc int[] { ComponentTypeID<T0>.value, ComponentTypeID<T1>.value, ComponentTypeID<T2>.value, ComponentTypeID<T3>.value };
var comp0TypeID = ComponentTypeID<T0>.value;
var comp1TypeID = ComponentTypeID<T1>.value;
var comp2TypeID = ComponentTypeID<T2>.value;
var comp3TypeID = ComponentTypeID<T3>.value;
var compTypeIDs = stackalloc int[]
{
comp0TypeID.value,
comp1TypeID.value,
comp2TypeID.value,
comp3TypeID.value,
};
var changedCompIDs = stackalloc int[4];
var offsets = stackalloc int[4];
var basePtrs = stackalloc byte*[4];
var changedCompCount = 0;
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 4; i++)
{
if (id == compTypeIDs[i])
{
ComponentRegister.SetComponentLastWrite(id, globalVersion);
changedCompIDs[changedCompCount] = id;
changedCompCount++;
break;
}
}
}
for (var i = 0; i < _matchingArchetypes.Count; i++)
{
ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
@@ -712,6 +1136,11 @@ public unsafe partial struct EntityQuery
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
var pChunkData = chunk.GetUnsafePtr();
for (var j = 0; j < changedCompCount; j++)
{
chunk.MarkChanged(changedCompIDs[i], globalVersion);
}
for (var index = 0; index < 4; index++)
{
basePtrs[index] = pChunkData + offsets[index];
@@ -743,12 +1172,45 @@ public unsafe partial struct EntityQuery
where T3 : unmanaged, IComponent
where T4 : unmanaged, IComponent
{
var world = World.GetWorld(_worldID).GetValueOrThrow();
var world = World.GetWorldUncheck(_worldID);
var globalVersion = world.Version;
var compTypeIDs = stackalloc int[] { ComponentTypeID<T0>.value, ComponentTypeID<T1>.value, ComponentTypeID<T2>.value, ComponentTypeID<T3>.value, ComponentTypeID<T4>.value };
var comp0TypeID = ComponentTypeID<T0>.value;
var comp1TypeID = ComponentTypeID<T1>.value;
var comp2TypeID = ComponentTypeID<T2>.value;
var comp3TypeID = ComponentTypeID<T3>.value;
var comp4TypeID = ComponentTypeID<T4>.value;
var compTypeIDs = stackalloc int[]
{
comp0TypeID.value,
comp1TypeID.value,
comp2TypeID.value,
comp3TypeID.value,
comp4TypeID.value,
};
var changedCompIDs = stackalloc int[5];
var offsets = stackalloc int[5];
var basePtrs = stackalloc byte*[5];
var changedCompCount = 0;
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 5; i++)
{
if (id == compTypeIDs[i])
{
ComponentRegister.SetComponentLastWrite(id, globalVersion);
changedCompIDs[changedCompCount] = id;
changedCompCount++;
break;
}
}
}
for (var i = 0; i < _matchingArchetypes.Count; i++)
{
ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
@@ -775,6 +1237,11 @@ public unsafe partial struct EntityQuery
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
var pChunkData = chunk.GetUnsafePtr();
for (var j = 0; j < changedCompCount; j++)
{
chunk.MarkChanged(changedCompIDs[i], globalVersion);
}
for (var index = 0; index < 5; index++)
{
basePtrs[index] = pChunkData + offsets[index];
@@ -808,12 +1275,47 @@ public unsafe partial struct EntityQuery
where T4 : unmanaged, IComponent
where T5 : unmanaged, IComponent
{
var world = World.GetWorld(_worldID).GetValueOrThrow();
var world = World.GetWorldUncheck(_worldID);
var globalVersion = world.Version;
var compTypeIDs = stackalloc int[] { ComponentTypeID<T0>.value, ComponentTypeID<T1>.value, ComponentTypeID<T2>.value, ComponentTypeID<T3>.value, ComponentTypeID<T4>.value, ComponentTypeID<T5>.value };
var comp0TypeID = ComponentTypeID<T0>.value;
var comp1TypeID = ComponentTypeID<T1>.value;
var comp2TypeID = ComponentTypeID<T2>.value;
var comp3TypeID = ComponentTypeID<T3>.value;
var comp4TypeID = ComponentTypeID<T4>.value;
var comp5TypeID = ComponentTypeID<T5>.value;
var compTypeIDs = stackalloc int[]
{
comp0TypeID.value,
comp1TypeID.value,
comp2TypeID.value,
comp3TypeID.value,
comp4TypeID.value,
comp5TypeID.value,
};
var changedCompIDs = stackalloc int[6];
var offsets = stackalloc int[6];
var basePtrs = stackalloc byte*[6];
var changedCompCount = 0;
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 6; i++)
{
if (id == compTypeIDs[i])
{
ComponentRegister.SetComponentLastWrite(id, globalVersion);
changedCompIDs[changedCompCount] = id;
changedCompCount++;
break;
}
}
}
for (var i = 0; i < _matchingArchetypes.Count; i++)
{
ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
@@ -840,6 +1342,11 @@ public unsafe partial struct EntityQuery
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
var pChunkData = chunk.GetUnsafePtr();
for (var j = 0; j < changedCompCount; j++)
{
chunk.MarkChanged(changedCompIDs[i], globalVersion);
}
for (var index = 0; index < 6; index++)
{
basePtrs[index] = pChunkData + offsets[index];
@@ -875,12 +1382,49 @@ public unsafe partial struct EntityQuery
where T5 : unmanaged, IComponent
where T6 : unmanaged, IComponent
{
var world = World.GetWorld(_worldID).GetValueOrThrow();
var world = World.GetWorldUncheck(_worldID);
var globalVersion = world.Version;
var compTypeIDs = stackalloc int[] { ComponentTypeID<T0>.value, ComponentTypeID<T1>.value, ComponentTypeID<T2>.value, ComponentTypeID<T3>.value, ComponentTypeID<T4>.value, ComponentTypeID<T5>.value, ComponentTypeID<T6>.value };
var comp0TypeID = ComponentTypeID<T0>.value;
var comp1TypeID = ComponentTypeID<T1>.value;
var comp2TypeID = ComponentTypeID<T2>.value;
var comp3TypeID = ComponentTypeID<T3>.value;
var comp4TypeID = ComponentTypeID<T4>.value;
var comp5TypeID = ComponentTypeID<T5>.value;
var comp6TypeID = ComponentTypeID<T6>.value;
var compTypeIDs = stackalloc int[]
{
comp0TypeID.value,
comp1TypeID.value,
comp2TypeID.value,
comp3TypeID.value,
comp4TypeID.value,
comp5TypeID.value,
comp6TypeID.value,
};
var changedCompIDs = stackalloc int[7];
var offsets = stackalloc int[7];
var basePtrs = stackalloc byte*[7];
var changedCompCount = 0;
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 7; i++)
{
if (id == compTypeIDs[i])
{
ComponentRegister.SetComponentLastWrite(id, globalVersion);
changedCompIDs[changedCompCount] = id;
changedCompCount++;
break;
}
}
}
for (var i = 0; i < _matchingArchetypes.Count; i++)
{
ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
@@ -907,6 +1451,11 @@ public unsafe partial struct EntityQuery
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
var pChunkData = chunk.GetUnsafePtr();
for (var j = 0; j < changedCompCount; j++)
{
chunk.MarkChanged(changedCompIDs[i], globalVersion);
}
for (var index = 0; index < 7; index++)
{
basePtrs[index] = pChunkData + offsets[index];
@@ -944,12 +1493,51 @@ public unsafe partial struct EntityQuery
where T6 : unmanaged, IComponent
where T7 : unmanaged, IComponent
{
var world = World.GetWorld(_worldID).GetValueOrThrow();
var world = World.GetWorldUncheck(_worldID);
var globalVersion = world.Version;
var compTypeIDs = stackalloc int[] { ComponentTypeID<T0>.value, ComponentTypeID<T1>.value, ComponentTypeID<T2>.value, ComponentTypeID<T3>.value, ComponentTypeID<T4>.value, ComponentTypeID<T5>.value, ComponentTypeID<T6>.value, ComponentTypeID<T7>.value };
var comp0TypeID = ComponentTypeID<T0>.value;
var comp1TypeID = ComponentTypeID<T1>.value;
var comp2TypeID = ComponentTypeID<T2>.value;
var comp3TypeID = ComponentTypeID<T3>.value;
var comp4TypeID = ComponentTypeID<T4>.value;
var comp5TypeID = ComponentTypeID<T5>.value;
var comp6TypeID = ComponentTypeID<T6>.value;
var comp7TypeID = ComponentTypeID<T7>.value;
var compTypeIDs = stackalloc int[]
{
comp0TypeID.value,
comp1TypeID.value,
comp2TypeID.value,
comp3TypeID.value,
comp4TypeID.value,
comp5TypeID.value,
comp6TypeID.value,
comp7TypeID.value,
};
var changedCompIDs = stackalloc int[8];
var offsets = stackalloc int[8];
var basePtrs = stackalloc byte*[8];
var changedCompCount = 0;
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < 8; i++)
{
if (id == compTypeIDs[i])
{
ComponentRegister.SetComponentLastWrite(id, globalVersion);
changedCompIDs[changedCompCount] = id;
changedCompCount++;
break;
}
}
}
for (var i = 0; i < _matchingArchetypes.Count; i++)
{
ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
@@ -976,6 +1564,11 @@ public unsafe partial struct EntityQuery
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
var pChunkData = chunk.GetUnsafePtr();
for (var j = 0; j < changedCompCount; j++)
{
chunk.MarkChanged(changedCompIDs[i], globalVersion);
}
for (var index = 0; index < 8; index++)
{
basePtrs[index] = pChunkData + offsets[index];

View File

@@ -24,12 +24,41 @@ public unsafe partial struct EntityQuery
public readonly void ForEach<<#= generics #>>(<#= delegateTupe #><<#= generics #>> action)
<#= restrictions #>
{
var world = World.GetWorld(_worldID).GetValueOrThrow();
var world = World.GetWorldUncheck(_worldID);
var globalVersion = world.Version;
var compTypeIDs = stackalloc int[] { <#= AppendGenerics(i, "ComponentTypeID<T{0}>.value") #> };
<# for (var localIndex = 0; localIndex < i; localIndex++) { #>
var comp<#= localIndex #>TypeID = ComponentTypeID<T<#= localIndex #>>.value;
<# } #>
var compTypeIDs = stackalloc int[]
{
<# for (var localIndex = 0; localIndex < i; localIndex++) { #>
comp<#= localIndex #>TypeID.value,
<# } #>
};
var changedCompIDs = stackalloc int[<#= i #>];
var offsets = stackalloc int[<#= i #>];
var basePtrs = stackalloc byte*[<#= i #>];
var changedCompCount = 0;
var it = _mask.writeAccess.GetIterator();
while (it.Next(out var id))
{
for (var i =0; i < <#= i #>; i++)
{
if (id == compTypeIDs[i])
{
ComponentRegister.SetComponentLastWrite(id, globalVersion);
changedCompIDs[changedCompCount] = id;
changedCompCount++;
break;
}
}
}
for (var i = 0; i < _matchingArchetypes.Count; i++)
{
ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]);
@@ -56,6 +85,11 @@ public unsafe partial struct EntityQuery
ref var chunk = ref archetype.GetChunkReference(chunkIndex);
var pChunkData = chunk.GetUnsafePtr();
for (var j = 0; j < changedCompCount; j++)
{
chunk.MarkChanged(changedCompIDs[i], globalVersion);
}
for (var index = 0; index < <#= i #>; index++)
{
basePtrs[index] = pChunkData + offsets[index];

View File

@@ -18,6 +18,20 @@ public ref partial struct QueryBuilder
return this;
}
/// <summary>
/// Adds the specified component type(s) to the 'All' filter of the query and requires read-write access.
/// Targets entities that have all of the specified component types and those component(s) must be enabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithAllRW<T0>()
where T0 : unmanaged, IComponent
{
_all.Add(ComponentTypeID<T0>.value);
_rw.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.
@@ -83,6 +97,20 @@ public ref partial struct QueryBuilder
return this;
}
/// <summary>
/// Adds the specified component type(s) to the 'Present' filter of the query and requires read-write access.
/// 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 WithPresentRW<T0>()
where T0 : unmanaged, IComponent
{
_present.Add(ComponentTypeID<T0>.value);
_rw.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.
@@ -98,6 +126,23 @@ public ref partial struct QueryBuilder
return this;
}
/// <summary>
/// Adds the specified component type(s) to the 'All' filter of the query and requires read-write access.
/// Targets entities that have all of the specified component types and those component(s) must be enabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithAllRW<T0, T1>()
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
{
_all.Add(ComponentTypeID<T0>.value);
_rw.Add(ComponentTypeID<T0>.value);
_all.Add(ComponentTypeID<T1>.value);
_rw.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.
@@ -173,6 +218,23 @@ public ref partial struct QueryBuilder
return this;
}
/// <summary>
/// Adds the specified component type(s) to the 'Present' filter of the query and requires read-write access.
/// 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 WithPresentRW<T0, T1>()
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
{
_present.Add(ComponentTypeID<T0>.value);
_rw.Add(ComponentTypeID<T0>.value);
_present.Add(ComponentTypeID<T1>.value);
_rw.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.
@@ -190,6 +252,26 @@ public ref partial struct QueryBuilder
return this;
}
/// <summary>
/// Adds the specified component type(s) to the 'All' filter of the query and requires read-write access.
/// Targets entities that have all of the specified component types and those component(s) must be enabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithAllRW<T0, T1, T2>()
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
where T2 : unmanaged, IComponent
{
_all.Add(ComponentTypeID<T0>.value);
_rw.Add(ComponentTypeID<T0>.value);
_all.Add(ComponentTypeID<T1>.value);
_rw.Add(ComponentTypeID<T1>.value);
_all.Add(ComponentTypeID<T2>.value);
_rw.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.
@@ -275,4 +357,24 @@ public ref partial struct QueryBuilder
return this;
}
/// <summary>
/// Adds the specified component type(s) to the 'Present' filter of the query and requires read-write access.
/// 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 WithPresentRW<T0, T1, T2>()
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
where T2 : unmanaged, IComponent
{
_present.Add(ComponentTypeID<T0>.value);
_rw.Add(ComponentTypeID<T0>.value);
_present.Add(ComponentTypeID<T1>.value);
_rw.Add(ComponentTypeID<T1>.value);
_present.Add(ComponentTypeID<T2>.value);
_rw.Add(ComponentTypeID<T2>.value);
return this;
}
}

View File

@@ -31,6 +31,22 @@ public ref partial struct QueryBuilder
return this;
}
/// <summary>
/// Adds the specified component type(s) to the 'All' filter of the query and requires read-write access.
/// Targets entities that have all of the specified component types and those component(s) must be enabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public QueryBuilder WithAllRW<<#= generics #>>()
<#= restrictions #>
{
<# for (var j = 0; j < i; j++) { #>
_all.Add(ComponentTypeID<T<#= j #>>.value);
_rw.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.
@@ -106,5 +122,21 @@ public ref partial struct QueryBuilder
return this;
}
/// <summary>
/// Adds the specified component type(s) to the 'Present' filter of the query and requires read-write access.
/// 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 WithPresentRW<<#= generics #>>()
<#= restrictions #>
{
<# for (var j = 0; j < i; j++) { #>
_present.Add(ComponentTypeID<T<#= j #>>.value);
_rw.Add(ComponentTypeID<T<#= j #>>.value);
<# } #>
return this;
}
<# } #>
}

View File

@@ -47,6 +47,12 @@ public partial class World
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static World GetWorldUncheck(Identifier<World> id)
{
return s_worlds[id.value]!;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Result<World, ErrorStatus> GetWorld(Identifier<World> id)
{
@@ -80,6 +86,7 @@ public partial class World : IIdentifierType, IDisposable, IEquatable<World>
private UnsafeHashMap<int, Identifier<Archetype>> _archetypeLookup; // Signature Hash to Archetype ID
private UnsafeHashMap<int, Identifier<EntityQuery>> _querieLookup; // Query Mask Hash to Query ID
private int _version;
private bool _disposed = false;
internal int ArchetypeCount => _archetypes.Count;
@@ -99,6 +106,11 @@ public partial class World : IIdentifierType, IDisposable, IEquatable<World>
/// </summary>
public EntityManager EntityManager => _entityManager;
/// <summary>
/// Gets the current version number of the world.
/// </summary>
public int Version => Interlocked.CompareExchange(ref _version, 0, 0);
/// <summary>
/// Gets the main entity command buffer for this world.
/// </summary>
@@ -196,6 +208,7 @@ public partial class World : IIdentifierType, IDisposable, IEquatable<World>
return Identifier<EntityQuery>.Invalid;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void PlaybackEntityCommandBuffers()
{
_entityCommandBuffer.Playback();
@@ -206,6 +219,12 @@ public partial class World : IIdentifierType, IDisposable, IEquatable<World>
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal int AdvanceVersion()
{
return Interlocked.Increment(ref _version);
}
/// <summary>
/// Gets a reference to the entity query with the specified identifier.
/// </summary>

View File

@@ -231,7 +231,7 @@ public unsafe static class MeshBuilder
public static void ComputeTangents(UnsafeList<Vertex> vertices, UnsafeList<uint> indices)
{
using var scope = AllocationManager.CreateStackScope();
using var bitangents = new UnsafeArray<float4>(vertices.Count, Allocator.Stack);
var bitangents = new UnsafeArray<float4>(vertices.Count, scope.AllocationHandle);
for (var i = 0; i < indices.Count; i += 3)
{

View File

@@ -20,7 +20,6 @@
<Project Path="Ghost.Zeux.MeshOptimizer/Ghost.Zeux.MeshOptimizer.csproj" />
</Folder>
<Folder Name="/Runtime/">
<Project Path="Ghost.ArcEntities/Ghost.ArcEntities.csproj" />
<Project Path="Ghost.Core/Ghost.Core.csproj" />
<Project Path="Ghost.Engine/Ghost.Engine.csproj" />
<Project Path="Ghost.Entities/Ghost.Entities.csproj" />