diff --git a/Ghost.ArcEntities/Archetype.cs b/Ghost.ArcEntities/Archetype.cs deleted file mode 100644 index cb62460..0000000 --- a/Ghost.ArcEntities/Archetype.cs +++ /dev/null @@ -1,416 +0,0 @@ -using Ghost.Core; -using Misaki.HighPerformance.LowLevel.Buffer; -using Misaki.HighPerformance.LowLevel.Collections; -using Misaki.HighPerformance.LowLevel.Utilities; -using System.Runtime.CompilerServices; - -namespace Ghost.ArcEntities; - -internal unsafe struct Chuck : IDisposable -{ - public const int CHUNK_SIZE = 16384; // 16 KB - - private UnsafeArray _data; - private int _count; - private int _capacity; - - public int Count - { - get => _count; - set => _count = value; - } - - public int Capacity => _capacity; - - public Chuck(int size, int capacity) - { - _data = new UnsafeArray(size, Allocator.Persistent); - _capacity = capacity; - _count = 0; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public byte* GetUnsafePtr() - { - return (byte*)_data.GetUnsafePtr(); - } - - public void Dispose() - { - _data.Dispose(); - } -} - -internal struct Edge -{ - public Identifier componentID; - public Identifier targetArchetype; -} - -internal struct ComponentMemoryLayout -{ - public int offset; - public int size; - public Identifier componentID; -} - -internal unsafe struct Archetype : IIdentifierType, IDisposable -{ - private readonly Identifier _id; - private readonly Identifier _worldID; - - private UnsafeBitSet _signature; - private UnsafeList _chunks; - private UnsafeArray _layouts; - private UnsafeArray _componentIDToOffset; - - // TODO: Is hash map better? - private UnsafeList _edgesAdd; - private UnsafeList _edgesRemove; - - private int _hash; - private int _entityCapacity; - private int _maxComponentID; - private int _entityIdsOffset; - - public Identifier ID => _id; - - public UnsafeBitSet Signature => _signature; - public UnsafeList Chunks => _chunks; - public UnsafeArray Layouts => _layouts; - - public int EntityCapacity => _entityCapacity; - public int ChunkCount => _chunks.Count; - - public Archetype(Identifier id, Identifier worldID, ReadOnlySpan> componentIds) - { - _id = id; - _worldID = worldID; - - if (componentIds.IsEmpty) - { - _signature = new UnsafeBitSet(1, Allocator.Persistent, AllocationOption.Clear); - _chunks = new UnsafeList(4, Allocator.Persistent); - - _edgesAdd = new UnsafeList(4, Allocator.Persistent); - _edgesRemove = new UnsafeList(4, Allocator.Persistent); - - _signature.ClearAll(); - - _entityCapacity = Chuck.CHUNK_SIZE / sizeof(Entity); - - return; - } - - var highestComponentID = 0; - for (var i = 0; i < componentIds.Length; i++) - { - if (componentIds[i] > highestComponentID) - { - highestComponentID = componentIds[i]; - } - } - - _signature = new UnsafeBitSet(highestComponentID, Allocator.Persistent, AllocationOption.Clear); - _chunks = new UnsafeList(4, Allocator.Persistent); - - _edgesAdd = new UnsafeList(4, Allocator.Persistent); - _edgesRemove = new UnsafeList(4, Allocator.Persistent); - - _hash = _signature.GetHashCode(); - - var pComponents = stackalloc ComponentInfo[componentIds.Length]; - for (var i = 0; i < componentIds.Length; i++) - { - _signature.SetBit(componentIds[i]); - pComponents[i] = ComponentRegister.GetComponentInfo(componentIds[i]); - } - - CalculateLayout(new Span(pComponents, componentIds.Length)); - } - - private void CalculateLayout(Span components) - { - var entitySize = sizeof(Entity); - var entityAlign = (int)MemoryUtility.AlignOf(); - - // Calculate total size per entity to get an initial capacity estimate - var bytesPerEntity = entitySize; - var maxComponentID = 0; - for (var i = 0; i < components.Length; i++) - { - var comp = components[i]; - bytesPerEntity += comp.size; - if (comp.id > maxComponentID) - { - maxComponentID = comp.id; - } - } - - _maxComponentID = maxComponentID; - _entityCapacity = Chuck.CHUNK_SIZE / bytesPerEntity; - _layouts = new UnsafeArray(components.Length, Allocator.Persistent); - _componentIDToOffset = new UnsafeArray(_maxComponentID + 1, Allocator.Persistent); - - _componentIDToOffset.AsSpan().Fill(-1); - - components.Sort((a, b) => b.alignment.CompareTo(a.alignment)); - var tempOffsets = stackalloc int[components.Length]; - - while (_entityCapacity > 0) - { - var currentOffset = 0; - var fits = true; - - currentOffset = (currentOffset + entityAlign - 1) & ~(entityAlign - 1); - - _entityIdsOffset = currentOffset; - currentOffset += _entityCapacity * entitySize; - - for (var i = 0; i < components.Length; i++) - { - var size = components[i].size; - var align = components[i].alignment; - - currentOffset = (currentOffset + align - 1) & ~(align - 1); - tempOffsets[i] = currentOffset; - currentOffset += _entityCapacity * size; - - if (currentOffset > Chuck.CHUNK_SIZE) - { - fits = false; - break; - } - } - - if (fits) - { - for (var i = 0; i < components.Length; i++) - { - _layouts[i] = new ComponentMemoryLayout - { - offset = tempOffsets[i], - size = components[i].size, - componentID = components[i].id - }; - - _componentIDToOffset[components[i].id] = tempOffsets[i]; - } - - return; - } - - _entityCapacity--; - } - } - - public void AllocateEntity(out int chunkIndex, out int rowIndex) - { - for (var i = 0; i < _chunks.Count; i++) - { - var chunk = _chunks[i]; - if (chunk.Count < _entityCapacity) - { - rowIndex = chunk.Count; - chunk.Count++; - chunkIndex = i; - - return; - } - } - - // Need to allocate a new chunk - var newChunk = new Chuck(Chuck.CHUNK_SIZE, _entityCapacity); - _chunks.Add(newChunk); - - rowIndex = 0; - newChunk.Count++; - chunkIndex = _chunks.Count - 1; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetEntity(int chunkIndex, int rowIndex, Entity entity) - { - var chunk = _chunks[chunkIndex]; - var chunkBase = chunk.GetUnsafePtr(); - var pEntity = chunkBase + _entityIdsOffset + (sizeof(Entity) * rowIndex); - - MemoryUtility.MemCpy(&entity, pEntity, (nuint)sizeof(Entity)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetComponentData(int chunkIndex, int rowIndex, Identifier componentID, void* pComponent) - { - var offset = _componentIDToOffset[componentID]; - var chunk = _chunks[chunkIndex]; - - var chunkBase = chunk.GetUnsafePtr(); - var size = ComponentRegister.GetComponentInfo(componentID).size; - var dst = chunkBase + offset + (size * rowIndex); - - MemoryUtility.MemCpy(pComponent, dst, (nuint)size); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref Chuck GetChunkReference(int index) - { - return ref _chunks[index]; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetOffset(int componentId) - { - if (componentId >= _componentIDToOffset.Count) - { - return -1; - } - - return _componentIDToOffset[componentId]; - } - - public ResultStatus RemoveEntity(int chunkIndex, int rowIndex) - { - if (chunkIndex < 0 || chunkIndex >= _chunks.Count) - { - return ResultStatus.InvalidArgument; - } - - ref var chunk = ref _chunks[chunkIndex]; - int lastIndex = chunk.Count - 1; - - // If we are NOT removing the very last entity, we must swap. - if (rowIndex != lastIndex) - { - var chunkBase = chunk.GetUnsafePtr(); - var pLastEntity = chunkBase + _entityIdsOffset + (sizeof(Entity) * lastIndex); - var pRowEntity = chunkBase + _entityIdsOffset + (sizeof(Entity) * rowIndex); - - var wroldResult = World.GetWorld(_worldID); - if (wroldResult.Status != ResultStatus.Success) - { - return wroldResult.Status; - } - - var result = wroldResult.Value.EntityManager.UpdateEntityLocation(*(Entity*)pLastEntity, _id, chunkIndex, rowIndex); - if (result != ResultStatus.Success) - { - return result; - } - - // Only operate the swap back after the update is succeed. - MemoryUtility.MemCpy(pLastEntity, pRowEntity, (nuint)sizeof(Entity)); - - for (var i = 0; i <= _layouts.Count; i++) - { - var layout = _layouts[i]; - - var pRow = chunk.GetUnsafePtr() + layout.offset + (layout.size * rowIndex); - var pLast = chunk.GetUnsafePtr() + layout.offset + (layout.size * lastIndex); - - MemoryUtility.MemCpy(pLast, pRow, (nuint)layout.size); - } - } - - chunk.Count--; - return ResultStatus.Success; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool HasComponent(Identifier componentID) - { - return _signature.IsSet(componentID); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddEdgeAdd(Identifier componentID, Identifier targetArchetype) - { - _edgesAdd.Add(new Edge - { - componentID = componentID, - targetArchetype = targetArchetype - }); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Identifier GetEdgeAdd(Identifier componentID) - { - for (var i = 0; i < _edgesAdd.Count; i++) - { - var edge = _edgesAdd[i]; - if (edge.componentID == componentID) - { - return edge.targetArchetype; - } - } - - return Identifier.Invalid; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddEdgeRemove(Identifier componentID, Identifier targetArchetype) - { - _edgesRemove.Add(new Edge - { - componentID = componentID, - targetArchetype = targetArchetype - }); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Identifier GetEdgeRemove(Identifier componentID) - { - for (var i = 0; i < _edgesRemove.Count; i++) - { - var edge = _edgesRemove[i]; - if (edge.componentID == componentID) - { - return edge.targetArchetype; - } - } - - return Identifier.Invalid; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span GetComponentArray(int chunkIndex) - where T : unmanaged, IComponent - { - var id = ComponentTypeID.value; - if (id >= _componentIDToOffset.Count) - { - return default; - } - - var offset = _componentIDToOffset[id]; - if (offset == -1) - { - return default; - } - - var chunk = _chunks[chunkIndex]; - return new Span((T*)((byte*)chunk.GetUnsafePtr() + offset), chunk.Count); - } - - public override int GetHashCode() - { - return _hash; - } - - public void Dispose() - { - if (_chunks.IsCreated) - { - foreach (ref var chunk in _chunks) - { - chunk.Dispose(); - } - } - - _signature.Dispose(); - _chunks.Dispose(); - _componentIDToOffset.Dispose(); - _layouts.Dispose(); - _edgesAdd.Dispose(); - _edgesRemove.Dispose(); - } -} diff --git a/Ghost.ArcEntities/AssemblyInfo.cs b/Ghost.ArcEntities/AssemblyInfo.cs deleted file mode 100644 index 6d2c349..0000000 --- a/Ghost.ArcEntities/AssemblyInfo.cs +++ /dev/null @@ -1,3 +0,0 @@ -global using EntityID = System.Int32; -global using GenerationID = System.Int32; - diff --git a/Ghost.ArcEntities/Component.cs b/Ghost.ArcEntities/Component.cs deleted file mode 100644 index dfa3cc6..0000000 --- a/Ghost.ArcEntities/Component.cs +++ /dev/null @@ -1,96 +0,0 @@ -using Ghost.Core; -using Misaki.HighPerformance.LowLevel.Collections; -using Misaki.HighPerformance.LowLevel.Utilities; - -namespace Ghost.ArcEntities; - -public interface IComponent : IIdentifierType -{ -} - -public struct ComponentInfo -{ - // public FixedText64 stableName; // Do we actually need this? - public int size; - public int alignment; - public Identifier id; -} - -public static unsafe class ComponentTypeID - where T : unmanaged, IComponent -{ - public static readonly Identifier value = ComponentRegister.GetOrRegisterComponent(); -} - -internal static class ComponentRegister -{ - private static int s_nextComponentTypeID = 0; - private static Dictionary> s_typeHandleToID = new(); - - private static List s_registeredComponents = new(); - private static Dictionary> s_nameToRuntimeID = new(); - - public unsafe static Identifier GetOrRegisterComponent() - where T : unmanaged, IComponent - { - var typeHandle = typeof(T).TypeHandle.Value; - - lock (s_registeredComponents) - { - if (s_typeHandleToID.TryGetValue(typeHandle, out var existingID)) - { - return existingID; - } - - var newID = new Identifier(s_nextComponentTypeID); - s_nextComponentTypeID++; - - var stableName = typeof(T).FullName ?? typeof(T).Name; - - var info = new ComponentInfo - { - // stableName = new FixedText64(stableName), - size = sizeof(T), - alignment = (int)MemoryUtility.AlignOf(), - id = newID, - }; - - while (s_registeredComponents.Count <= newID.value) s_registeredComponents.Add(default); - s_registeredComponents[newID.value] = info; - - s_typeHandleToID[typeHandle] = newID; - s_nameToRuntimeID[stableName] = newID; - - return newID; - } - } - - public static ComponentInfo GetComponentInfo(Identifier typeId) - { - return s_registeredComponents[typeId]; - } - - public static int GetHashCode(ReadOnlySpan> componentTypeIDs) - { - var largestID = 0; - foreach (var id in componentTypeIDs) - { - if (id.value > largestID) - { - largestID = id.value; - } - } - - var length = UnsafeBitSet.RequiredLength(largestID + 1); - var bits = (Span)stackalloc uint[length]; - bits.Clear(); - - var bitSet = new SpanBitSet(bits); - foreach (var id in componentTypeIDs) - { - bitSet.SetBit(id.value); - } - - return bitSet.GetHashCode(); - } -} diff --git a/Ghost.ArcEntities/Entity.cs b/Ghost.ArcEntities/Entity.cs deleted file mode 100644 index 081f763..0000000 --- a/Ghost.ArcEntities/Entity.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace Ghost.ArcEntities; - -[StructLayout(LayoutKind.Sequential, Size = 8)] -public readonly struct Entity : IEquatable, IComparable -{ - public const EntityID INVALID_ID = -1; - - private readonly EntityID _id; - private readonly GenerationID _generation; - - public EntityID ID - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _id; - } - - public GenerationID Generation - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _generation; - } - - public bool IsValid - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ID != INVALID_ID; - } - - public static Entity Invalid - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => new(INVALID_ID, GenerationID.MaxValue); - } - - internal Entity(EntityID id, GenerationID generation) - { - _id = id; - _generation = generation; - } - - public bool Equals(Entity other) - { - return _id == other._id && _generation == other._generation; - } - - public int CompareTo(Entity other) - { - return _id.CompareTo(other._id); - } - - public override bool Equals(object? obj) - { - return obj is Entity other && Equals(other); - } - - public override int GetHashCode() - { - return _id ^ _generation << 16; - } - - public static bool operator ==(Entity left, Entity right) - { - return left.Equals(right); - } - - public static bool operator !=(Entity left, Entity right) - { - return !(left == right); - } - - public override string ToString() - { - return $"Entity {{ Index: {ID}, Generation: {Generation} }}"; - } -} diff --git a/Ghost.ArcEntities/EntityCommandBuffer.cs b/Ghost.ArcEntities/EntityCommandBuffer.cs deleted file mode 100644 index 10645bb..0000000 --- a/Ghost.ArcEntities/EntityCommandBuffer.cs +++ /dev/null @@ -1,110 +0,0 @@ -using Misaki.HighPerformance.LowLevel.Collections; -using Misaki.HighPerformance.LowLevel.Utilities; - -namespace Ghost.ArcEntities; - -public unsafe class EntityCommandBuffer : IDisposable -{ - private enum CommandType - { - CreateEntity, - DestroyEntity, - AddComponent, - RemoveComponent, - SetComponent, - } - - private struct Command - { - public UnsafeArray data; - public CommandType type; - public Entity entity; - public int componentTypeID; - } - - private readonly EntityManager _entityManager; - private UnsafeList _commands; // TODO: Maybe use UnsafeArray directly? - - public EntityCommandBuffer(EntityManager entityManager) - { - _entityManager = entityManager; - _commands = new UnsafeList(32, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent); - } - - public void CreateEntity() - { - var command = new Command - { - type = CommandType.CreateEntity, - data = default, - entity = default, - componentTypeID = -1 - }; - - _commands.Add(command); - } - - public void DestroyEntity(Entity entity) - { - var command = new Command - { - type = CommandType.DestroyEntity, - data = default, - entity = entity, - componentTypeID = -1 - }; - - _commands.Add(command); - } - - public void AddComponent(Entity entity, T component) - where T : unmanaged, IComponent - { - var data = new UnsafeArray(sizeof(T), Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent); - MemoryUtility.MemCpy(&component, data.GetUnsafePtr(), (nuint)sizeof(T)); - - var command = new Command - { - type = CommandType.AddComponent, - data = data, - entity = entity, - componentTypeID = ComponentTypeID.value - }; - - _commands.Add(command); - } - - public void RemoveComponent(Entity entity) - where T : unmanaged, IComponent - { - var command = new Command - { - type = CommandType.RemoveComponent, - data = default, - entity = entity, - componentTypeID = ComponentTypeID.value - }; - - _commands.Add(command); - } - - public void Reset() - { - foreach (ref var command in _commands) - { - command.data.Dispose(); - } - - _commands.Clear(); - } - - public void Dispose() - { - foreach (ref var command in _commands) - { - command.data.Dispose(); - } - - _commands.Dispose(); - } -} diff --git a/Ghost.ArcEntities/EntityManager.cs b/Ghost.ArcEntities/EntityManager.cs deleted file mode 100644 index d4ce56c..0000000 --- a/Ghost.ArcEntities/EntityManager.cs +++ /dev/null @@ -1,266 +0,0 @@ -using Ghost.Core; -using Misaki.HighPerformance.LowLevel.Buffer; -using Misaki.HighPerformance.LowLevel.Collections; -using Misaki.HighPerformance.LowLevel.Utilities; -using System.Diagnostics; - -namespace Ghost.ArcEntities; - -public unsafe class EntityManager : IDisposable -{ - private struct EntityLocation - { - public Identifier archetypeID; - public int chunkIndex; - public int rowIndex; - } - - private World _world; - private UnsafeSlotMap _entityLocations; - - internal EntityManager(World world, int initialCapacity) - { - _world = world; - _entityLocations = new UnsafeSlotMap(initialCapacity, Allocator.Persistent); - } - - internal ResultStatus UpdateEntityLocation(Entity entity, Identifier newArchetypeID, int newChunkIndex, int newRowIndex) - { - ref var location = ref _entityLocations.GetElementReferenceAt(entity.ID, entity.Generation, out var exist); - if (!exist) - { - return ResultStatus.NotFound; - } - - location.archetypeID = newArchetypeID; - location.chunkIndex = newChunkIndex; - location.rowIndex = newRowIndex; - - return ResultStatus.Success; - } - - public Entity CreateEntity(params ReadOnlySpan> componentTypeIDs) - { - var signatureHash = ComponentRegister.GetHashCode(componentTypeIDs); - var arcID = _world.GetArchetypeIDBySignatureHash(signatureHash); - - if (arcID.IsNotValid) - { - arcID = _world.CreateArchetype(componentTypeIDs, signatureHash); - } - - ref var archetype = ref _world.GetArchetypeReference(arcID); - archetype.AllocateEntity(out var chunkIndex, out var rowIndex); - - var id = _entityLocations.Add(new EntityLocation - { - archetypeID = arcID, - chunkIndex = chunkIndex, - rowIndex = rowIndex - }, out var generation); - - var entity = new Entity(id, generation); - archetype.SetEntity(chunkIndex, rowIndex, entity); - - return entity; - } - - public Entity CreateEntity() - { - // Put into empty archetype - ref var emptyArchetype = ref _world.GetArchetypeReference(World.EmptyArchetypeID); - emptyArchetype.AllocateEntity(out var chunkIndex, out var rowIndex); - - var id = _entityLocations.Add(new EntityLocation - { - archetypeID = World.EmptyArchetypeID, - chunkIndex = chunkIndex, - rowIndex = rowIndex - }, out var generation); - - var entity = new Entity(id, generation); - emptyArchetype.SetEntity(chunkIndex, rowIndex, entity); - - return entity; - } - - public ResultStatus DestoryEntity(Entity entity) - { - if (!_entityLocations.TryGetElementAt(entity.ID, entity.Generation, out var location)) - { - return ResultStatus.NotFound; - } - - ref var archetype = ref _world.GetArchetypeReference(location.archetypeID); - var r = archetype.RemoveEntity(location.chunkIndex, location.rowIndex); - if (r != ResultStatus.Success) - { - return r; - } - - if (!_entityLocations.Remove(entity.ID, entity.Generation)) - { - return ResultStatus.NotFound; - } - - return ResultStatus.Success; - } - - private static void CopyData(ref Archetype oldArch, int oldChunk, int oldRow, - ref Archetype newArch, int newChunk, int newRow) - { - // Iterate every component type in the OLD archetype - for (int i = 0; i < oldArch.Layouts.Count; i++) - { - var layout = oldArch.Layouts[i]; - - var src = oldArch.Chunks[oldChunk].GetUnsafePtr() + layout.offset + (layout.size * oldRow); - var newOffset = newArch.GetOffset(layout.componentID); // O(1) Lookup - var dst = oldArch.Chunks[oldChunk].GetUnsafePtr() + newOffset + (layout.size * newRow); - - MemoryUtility.MemCpy(src, dst, (nuint)layout.size); - } - } - - public ResultStatus AddComponent(Entity entity, Identifier componentID, void* component) - { - // Find current location - ref var location = ref _entityLocations.GetElementReferenceAt(entity.ID, entity.Generation, out var exist); - if (!exist) - { - return ResultStatus.NotFound; - } - - // Build new archetype signature - ref var oldArchetype = ref _world.GetArchetypeReference(location.archetypeID); - var oldSignature = oldArchetype.Signature; - - // TODO: Check edge cache first. - var newArcID = oldArchetype.GetEdgeAdd(componentID); - if (newArcID.IsNotValid) - { - var largestComponentID = Math.Max(oldSignature.Count, componentID); - var length = UnsafeBitSet.RequiredLength(largestComponentID + 1); - - Span bits = stackalloc uint[length]; - bits.Clear(); - - var newSignature = new SpanBitSet(bits); - - var iterator = 0; - var compCount = 0; - while (true) - { - int bit = oldSignature.NextSetBit(iterator); - if (bit == -1) - { - break; - } - - newSignature.SetBit(bit); - iterator = bit + 1; - compCount++; - } - - compCount++; - newSignature.SetBit(componentID); - - // Find or create new archetype - var newSignatureHash = newSignature.GetHashCode(); - newArcID = _world.GetArchetypeIDBySignatureHash(newSignatureHash); - if (newArcID.IsNotValid) - { - // Create new archetype - Span> componentTypeIDs = stackalloc Identifier[compCount]; - componentTypeIDs[0] = componentID; - - iterator = 0; - while (true) - { - int bit = oldSignature.NextSetBit(iterator); - if (bit == -1) - { - break; - } - - componentTypeIDs[--compCount] = bit; - iterator = bit + 1; - } - - _world.CreateArchetype(componentTypeIDs, newSignatureHash); - } - - oldArchetype.AddEdgeAdd(componentID, newArcID); - } - - // Move entity data - ref var newArchetype = ref _world.GetArchetypeReference(newArcID); - newArchetype.AllocateEntity(out var newChunkIndex, out var newRowIndex); - CopyData(ref oldArchetype, location.chunkIndex, location.rowIndex, - ref newArchetype, newChunkIndex, newRowIndex); - - newArchetype.SetEntity(newChunkIndex, newRowIndex, entity); - newArchetype.SetComponentData(newChunkIndex, newRowIndex, componentID, component); - - var r = oldArchetype.RemoveEntity(location.chunkIndex, location.rowIndex); - Debug.Assert(r == ResultStatus.Success); // We assert it because the entity should exist if the whole system is consistent. - // if (r != ResultStatus.Success) - // { - // return r; - // } - - // Update location - location.archetypeID = newArcID; - location.chunkIndex = newChunkIndex; - location.rowIndex = newRowIndex; - - return ResultStatus.Success; - } - - public ResultStatus AddComponent(Entity entity, ref T component) - where T : unmanaged, IComponent - { - return AddComponent(entity, ComponentTypeID.value, UnsafeUtility.AddressOf(ref component)); - } - - public ResultStatus SetComponentData(Entity entity, Identifier componentID, void* pComponent) - { - if (!_entityLocations.TryGetElementAt(entity.ID, entity.Generation, out var location)) - { - return ResultStatus.NotFound; - } - - ref var archetype = ref _world.GetArchetypeReference(location.archetypeID); - archetype.SetComponentData(location.chunkIndex, location.rowIndex, componentID, pComponent); - - return ResultStatus.Success; - } - - public ResultStatus SetComponentData(Entity entity, ref T component) - where T : unmanaged, IComponent - { - return SetComponentData(entity, ComponentTypeID.value, UnsafeUtility.AddressOf(ref component)); - } - - public bool HasComponent(Entity entity, Identifier componentID) - { - if (!_entityLocations.TryGetElementAt(entity.ID, entity.Generation, out var location)) - { - return false; - } - - ref var archetype = ref _world.GetArchetypeReference(location.archetypeID); - return archetype.HasComponent(componentID); - } - - public bool HasComponent(Entity entity) - where T : unmanaged, IComponent - { - return HasComponent(entity, ComponentTypeID.value); - } - - public void Dispose() - { - _entityLocations.Dispose(); - } -} diff --git a/Ghost.ArcEntities/EntityQuery.cs b/Ghost.ArcEntities/EntityQuery.cs deleted file mode 100644 index f1e36bf..0000000 --- a/Ghost.ArcEntities/EntityQuery.cs +++ /dev/null @@ -1,60 +0,0 @@ -namespace Ghost.ArcEntities; - -public unsafe class EntityQuery - where T1 : unmanaged, IComponent - where T2 : unmanaged, IComponent -{ - // The Cache Struct - struct ArchetypeCache - { - public Archetype Archetype; - public int Offset1; // Offset for T1 - public int Offset2; // Offset for T2 - } - - private List _cache = new(); - - internal void AddMatchingArchetype(Archetype archetype) - { - // We look up the offsets ONCE when the archetype is registered - int off1 = archetype.GetOffset(ComponentTypeID.value); - int off2 = archetype.GetOffset(ComponentTypeID.value); - - _cache.Add(new ArchetypeCache - { - Archetype = archetype, - Offset1 = off1, - Offset2 = off2 - }); - } - - // The Optimized Iteration Loop - public void ForEach(delegate* action) - { - foreach (var cache in _cache) - { - var archetype = cache.Archetype; - var offset1 = cache.Offset1; - var offset2 = cache.Offset2; - - // Iterate Chunks - for (int i = 0; i < archetype.ChunkCount; i++) - { - var chunk = archetype.GetChunkReference(i); - var chunkPtr = (byte*)chunk.GetUnsafePtr(); - var count = chunk.Count; - - // POINTER MATH ONLY - NO LOOKUPS - // We use the pre-calculated integer offsets - T1* ptr1 = (T1*)(chunkPtr + offset1); - T2* ptr2 = (T2*)(chunkPtr + offset2); - - // The hot loop - for (int k = 0; k < count; k++) - { - action(ref ptr1[k], ref ptr2[k]); - } - } - } - } -} diff --git a/Ghost.ArcEntities/Ghost.ArcEntities.csproj b/Ghost.ArcEntities/Ghost.ArcEntities.csproj deleted file mode 100644 index 7fa3d2d..0000000 --- a/Ghost.ArcEntities/Ghost.ArcEntities.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - net10.0 - enable - enable - True - - - - True - True - - - - True - True - - - - - - - diff --git a/Ghost.ArcEntities/Utility.cs b/Ghost.ArcEntities/Utility.cs deleted file mode 100644 index 5aefd8b..0000000 --- a/Ghost.ArcEntities/Utility.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Ghost.ArcEntities; - -public static class Utility -{ -} diff --git a/Ghost.ArcEntities/World.cs b/Ghost.ArcEntities/World.cs deleted file mode 100644 index afdbcdc..0000000 --- a/Ghost.ArcEntities/World.cs +++ /dev/null @@ -1,155 +0,0 @@ -using Ghost.Core; -using Misaki.HighPerformance.LowLevel.Buffer; -using Misaki.HighPerformance.LowLevel.Collections; -using System.Runtime.CompilerServices; - -namespace Ghost.ArcEntities; - -public partial class World -{ - private static List s_worlds = new(4); - private static Queue> s_freeWorldSlots = new(); - - internal static Identifier EmptyArchetypeID => new Identifier(0); - - public static int WorldCount => s_worlds.Count - s_freeWorldSlots.Count; - - public static World Create(int entityCapacity = 16) - { - lock (s_worlds) - { - if (s_freeWorldSlots.TryDequeue(out var index)) - { - s_worlds[index.value] = new World(index, entityCapacity); - } - else - { - index = new Identifier(s_worlds.Count); - s_worlds.Add(new World(index, entityCapacity)); - } - - return s_worlds[index.value]!; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Result GetWorld(Identifier id) - { - if (id.value < 0 || id.value >= s_worlds.Count) - { - return Result.Create(default(World)!, ResultStatus.NotFound); - } - - var world = s_worlds[id.value]; - if (world is null) - { - return Result.Create(default(World)!, ResultStatus.NotFound); - } - - return Result.Create(world, ResultStatus.Success); - } -} - -public partial class World : IIdentifierType, IDisposable, IEquatable -{ - private readonly Identifier _id; - - private UnsafeList _archetypes; - private UnsafeHashMap> _archetypeLookup; // Signature Hash to Archetype ID - private EntityManager _entityManager; - private EntityCommandBuffer _entityCommandBuffer; - - private bool _disposed = false; - - public Identifier ID => _id; - public EntityManager EntityManager => _entityManager; - public EntityCommandBuffer EntityCommandBuffer => _entityCommandBuffer; - - private World(Identifier id, int entityCapacity) - { - _id = id; - - _archetypes = new UnsafeList(16, Allocator.Persistent); - _archetypeLookup = new UnsafeHashMap>(16, Allocator.Persistent); - - _entityManager = new EntityManager(this, entityCapacity); - _entityCommandBuffer = new EntityCommandBuffer(_entityManager); - - // Create the empty archetype - CreateArchetype(ReadOnlySpan>.Empty, 0); - } - - ~World() - { - Dispose(); - } - - internal Identifier CreateArchetype(ReadOnlySpan> componentTypeIDs, int signatureHash) - { - var arcID = new Identifier(_archetypes.Count); - _archetypes.Add(new Archetype(arcID, _id, componentTypeIDs)); - _archetypeLookup.Add(signatureHash, arcID); - - return arcID; - } - - internal ref Archetype GetArchetypeReference(Identifier id) - { - return ref _archetypes[id.value]; - } - - internal Identifier GetArchetypeIDBySignatureHash(int signatureHash) - { - if (_archetypeLookup.TryGetValue(signatureHash, out var arcID)) - { - return arcID; - } - - return Identifier.Invalid; - } - - public bool Equals(World? other) - { - return other is not null && _id == other._id; - } - - public override int GetHashCode() - { - return _id.GetHashCode(); - } - - public override bool Equals(object? obj) - { - return obj is World other && Equals(other); - } - - public static bool operator ==(World? left, World? right) - { - return left?.Equals(right) ?? right is null; - } - - public static bool operator !=(World? left, World? right) - { - return !(left == right); - } - - public void Dispose() - { - if (_disposed) - { - return; - } - - _entityManager.Dispose(); - - _archetypes.Dispose(); - _archetypeLookup.Dispose(); - - s_freeWorldSlots.Enqueue(_id); - - _disposed = true; - - GC.SuppressFinalize(this); - } -} - diff --git a/Ghost.Core/Ghost.Core.csproj b/Ghost.Core/Ghost.Core.csproj index ad3eaf5..a7dfc8f 100644 --- a/Ghost.Core/Ghost.Core.csproj +++ b/Ghost.Core/Ghost.Core.csproj @@ -21,8 +21,8 @@ - - + + diff --git a/Ghost.Core/Result.cs b/Ghost.Core/Result.cs index 6b26cc3..b6ac891 100644 --- a/Ghost.Core/Result.cs +++ b/Ghost.Core/Result.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using TerraFX.Interop.DirectX; namespace Ghost.Core; diff --git a/Ghost.Entities.Test/ArcEntityTest.cs b/Ghost.Entities.Test/ArcEntityTest.cs index eb847b3..0c48168 100644 --- a/Ghost.Entities.Test/ArcEntityTest.cs +++ b/Ghost.Entities.Test/ArcEntityTest.cs @@ -15,49 +15,69 @@ internal struct TestEntityQueryJob : IJobEntityParallel public partial class ArcEntityTest : ITest { - private World _world = null!; private JobScheduler _jobScheduler = null!; + private World _world = null!; public void Setup() { - _world = World.Create(); _jobScheduler = new JobScheduler(4); + _world = World.Create(_jobScheduler); } public void Run() { var entity1 = _world.EntityManager.CreateEntity(ComponentTypeID.value); - _world.EntityManager.AddComponent(entity1, new Mesh { index = 1 }); + _world.EntityCommandBuffer.AddComponent(entity1, new Mesh { index = 1 }); - var queryID = new QueryBuilder().WithAll().Build(_world); - ref var query = ref _world.GetEntityQueryReference(queryID); + // var entity2 = _world.EntityManager.CreateEntity(ComponentTypeID.value); + // _world.EntityManager.SetComponentData(entity2, new Transform { position = new float3(1, 2, 3) }); - var testJob = new TestEntityQueryJob(); - var handle = query.ScheduleEntityParallel(_jobScheduler, testJob, Allocator.Temp, 64, JobHandle.Invalid); - _jobScheduler.WaitComplete(handle); + Console.WriteLine($"Entity {entity1} hash Mesh: {_world.EntityManager.HasComponent(entity1)}"); - query.ForEach((e, ref t) => - { - Console.WriteLine($"Entity {e} Has Position: {t.position}"); - }); + _world.EntityCommandBuffer.Playback(); + Console.WriteLine($"Entity {entity1} hash Mesh: {_world.EntityManager.HasComponent(entity1)}"); - //foreach (var chunk in query.GetChunkIterator()) - //{ - // var transforms = chunk.GetComponentData(); - // var entities = chunk.GetEntities(); - // var bits = chunk.GetEnableBits(); + _world.EntityCommandBuffer.RemoveComponent(entity1); + Console.WriteLine($"Entity {entity1} hash Mesh: {_world.EntityManager.HasComponent(entity1)}"); - // var it = bits.GetIterator(); - // while (it.Next(out var index) && index < chunk.Count) - // { - // Console.WriteLine($"Entity {entities[index]} Updated Position: {transforms[index].position}"); - // } - //} + _world.EntityCommandBuffer.Playback(); + Console.WriteLine($"Entity {entity1} hash Mesh: {_world.EntityManager.HasComponent(entity1)}"); + + // var queryID = new QueryBuilder().WithAll().Build(_world); + // ref var query = ref _world.GetEntityQueryReference(queryID); + + // var testJob = new TestEntityQueryJob(); + // var handle = query.ScheduleEntityParallel(_jobScheduler, testJob, Allocator.Temp, 64, JobHandle.Invalid); + // _jobScheduler.WaitComplete(handle); + // + // query.ForEach((e, ref t) => + // { + // Console.WriteLine($"Entity {e} Has Position: {t.position}"); + // }); + // + // foreach (ref var transform in query.GetComponentIterator()) + // { + // Console.WriteLine($"Entity Updated Position: {transform.position}"); + // } + // + // foreach (var chunk in query.GetChunkIterator()) + // { + // var transforms = chunk.GetComponentData(); + // var entities = chunk.GetEntities(); + // var bits = chunk.GetEnableBits(); + // + // var it = bits.GetIterator(); + // while (it.Next(out var index) && index < chunk.Count) + // { + // Console.WriteLine($"Entity {entities[index]} Updated Position: {transforms[index].position}"); + // } + // } } public void Cleanup() { _world.Dispose(); + _jobScheduler.Dispose(); } } diff --git a/Ghost.Entities/Archetype.cs b/Ghost.Entities/Archetype.cs index 10807ee..1c47db0 100644 --- a/Ghost.Entities/Archetype.cs +++ b/Ghost.Entities/Archetype.cs @@ -259,7 +259,7 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable var chunkBase = chunk.GetUnsafePtr(); var dst = chunkBase + _entityIdsOffset + (sizeof(Entity) * rowIndex); - MemoryUtility.MemCpy(&entity, dst, (nuint)sizeof(Entity)); + MemoryUtility.MemCpy(dst, &entity, (nuint)sizeof(Entity)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -278,18 +278,18 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable var size = ComponentRegister.GetComponentInfo(componentID).size; var dst = chunkBase + offset + (size * rowIndex); - MemoryUtility.MemCpy(pComponent, dst, (nuint)size); + MemoryUtility.MemCpy(dst, pComponent, (nuint)size); return ResultStatus.Success; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly ResultStatus GetComponentDataPtr(int chunkIndex, int rowIndex, Identifier componentID, void** ppv) + public readonly void* GetComponentData(int chunkIndex, int rowIndex, Identifier componentID) { var r = GetLayout(componentID); if (r.Status != ResultStatus.Success) { - return r.Status; + return null; } var offset = r.Value.offset; @@ -297,9 +297,7 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable var chunkBase = chunk.GetUnsafePtr(); var size = ComponentRegister.GetComponentInfo(componentID).size; - *ppv = chunkBase + offset + (size * rowIndex); - - return ResultStatus.Success; + return chunkBase + offset + (size * rowIndex); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -355,7 +353,7 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable } // Only operate the swap back after the update is succeed. - MemoryUtility.MemCpy(pLastEntity, pRowEntity, (nuint)sizeof(Entity)); + MemoryUtility.MemCpy(pRowEntity, pLastEntity, (nuint)sizeof(Entity)); for (var i = 0; i <= _layouts.Count; i++) { @@ -364,7 +362,7 @@ internal unsafe struct Archetype : IIdentifierType, IDisposable var pRow = chunk.GetUnsafePtr() + layout.offset + (layout.size * rowIndex); var pLast = chunk.GetUnsafePtr() + layout.offset + (layout.size * lastIndex); - MemoryUtility.MemCpy(pLast, pRow, (nuint)layout.size); + MemoryUtility.MemCpy(pRow, pLast, (nuint)layout.size); } } diff --git a/Ghost.Entities/AssemblyInfo.cs b/Ghost.Entities/AssemblyInfo.cs index 6d2c349..37da096 100644 --- a/Ghost.Entities/AssemblyInfo.cs +++ b/Ghost.Entities/AssemblyInfo.cs @@ -1,3 +1,12 @@ global using EntityID = System.Int32; global using GenerationID = System.Int32; +using Ghost.Core.Attributes; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Ghost.Engine")] +[assembly: InternalsVisibleTo("Ghost.Editor.Core")] +[assembly: InternalsVisibleTo("Ghost.Entities.Test")] + +[assembly: EngineAssembly] + diff --git a/Ghost.Entities/Common.cs b/Ghost.Entities/Common.cs new file mode 100644 index 0000000..9702590 --- /dev/null +++ b/Ghost.Entities/Common.cs @@ -0,0 +1,19 @@ +namespace Ghost.Entities; + +public readonly struct Time +{ + public int FrameCount + { + get; init; + } + + public float DeltaTime + { + get; init; + } + + public double ElapsedTime + { + get; init; + } +} diff --git a/Ghost.Entities/Component.cs b/Ghost.Entities/Component.cs index 1945ce2..cead545 100644 --- a/Ghost.Entities/Component.cs +++ b/Ghost.Entities/Component.cs @@ -30,9 +30,9 @@ public static class ComponentTypeID internal static class ComponentRegister { private static int s_nextComponentTypeID = 0; - private static readonly Dictionary> s_typeHandleToID = new(); private static readonly List s_registeredComponents = new(); + private static readonly Dictionary> s_typeHandleToID = new(); private static readonly Dictionary> s_nameToRuntimeID = new(); public static unsafe Identifier GetOrRegisterComponent() @@ -71,12 +71,26 @@ internal static class ComponentRegister } } + public static Identifier GetComponentID(Type type) + { + var typeHandle = type.TypeHandle.Value; + lock (s_registeredComponents) + { + if (s_typeHandleToID.TryGetValue(typeHandle, out var existingID)) + { + return existingID; + } + } + + throw new KeyNotFoundException($"Component type {type} is not registered."); + } + public static ComponentInfo GetComponentInfo(Identifier typeId) { return s_registeredComponents[typeId]; } - public static int GetHashCode(ReadOnlySpan> componentTypeIDs) + public static int GetHashCode(params ReadOnlySpan> componentTypeIDs) { var largestID = 0; foreach (var id in componentTypeIDs) diff --git a/Ghost.Entities/EntityCommandBuffer.cs b/Ghost.Entities/EntityCommandBuffer.cs index 7a4d6ad..e01d322 100644 --- a/Ghost.Entities/EntityCommandBuffer.cs +++ b/Ghost.Entities/EntityCommandBuffer.cs @@ -1,5 +1,8 @@ +using Ghost.Core; +using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Utilities; +using System.Runtime.InteropServices; namespace Ghost.Entities; @@ -17,18 +20,24 @@ public unsafe class EntityCommandBuffer : IDisposable private struct Command { public UnsafeArray data; - public CommandType type; public Entity entity; - public int componentTypeID; + public CommandType type; + public Identifier componentTypeID; } private readonly EntityManager _entityManager; - private UnsafeList _commands; // TODO: Maybe use UnsafeArray directly? + private UnsafeList _commands; // TODO: Maybe use UnsafeArray directly to save additional memory allocation in Unsafe data inside Command struct. + private bool _disposed; public EntityCommandBuffer(EntityManager entityManager) { _entityManager = entityManager; - _commands = new UnsafeList(32, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent); + _commands = new UnsafeList(32, Allocator.Persistent); + } + + ~EntityCommandBuffer() + { + Dispose(); } public void CreateEntity() @@ -44,48 +53,107 @@ public unsafe class EntityCommandBuffer : IDisposable _commands.Add(command); } - public void DestroyEntity(Entity entity) + public void CreateEntity(params ReadOnlySpan> componentTypeIDs) { + var data = new UnsafeArray(componentTypeIDs.Length * sizeof(int), Allocator.Temp); + MemoryMarshal.Cast, byte>(componentTypeIDs).CopyTo(data.AsSpan()); + var command = new Command { - type = CommandType.DestroyEntity, - data = default, - entity = entity, - componentTypeID = -1 + type = CommandType.CreateEntity, + data = data, + entity = Entity.Invalid, + componentTypeID = Identifier.Invalid }; _commands.Add(command); } - public void AddComponent(Entity entity, T component) + public void DestroyEntity(Entity entity) + { + _commands.Add(new Command + { + type = CommandType.DestroyEntity, + data = default, + entity = entity, + componentTypeID = Identifier.Invalid + }); + } + + public void AddComponent(Entity entity, T component = default) where T : unmanaged, IComponent { - var data = new UnsafeArray(sizeof(T), Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent); - MemoryUtility.MemCpy(&component, data.GetUnsafePtr(), (nuint)sizeof(T)); + var data = new UnsafeArray(sizeof(T), Allocator.Temp); + MemoryUtility.MemCpy(data.GetUnsafePtr(), &component, (nuint)sizeof(T)); - var command = new Command + _commands.Add(new Command { type = CommandType.AddComponent, data = data, entity = entity, componentTypeID = ComponentTypeID.value - }; - - _commands.Add(command); + }); } public void RemoveComponent(Entity entity) where T : unmanaged, IComponent { - var command = new Command + _commands.Add(new Command { type = CommandType.RemoveComponent, data = default, entity = entity, componentTypeID = ComponentTypeID.value - }; + }); + } - _commands.Add(command); + public void SetComponent(Entity entity, T component) + where T : unmanaged, IComponent + { + var data = new UnsafeArray(sizeof(T), Allocator.Temp); + MemoryUtility.MemCpy(data.GetUnsafePtr(), &component, (nuint)sizeof(T)); + + _commands.Add(new Command + { + type = CommandType.SetComponent, + data = data, + entity = entity, + componentTypeID = ComponentTypeID.value + }); + } + + internal void Playback() + { + foreach (ref var command in _commands) + { + switch (command.type) + { + case CommandType.CreateEntity: + if (command.data.Count > 0) + { + _entityManager.CreateEntity(MemoryMarshal.Cast>(command.data.AsSpan())); + } + else + { + _entityManager.CreateEntity(); + } + break; + case CommandType.DestroyEntity: + _entityManager.DestroyEntity(command.entity); + break; + case CommandType.AddComponent: + _entityManager.AddComponent(command.entity, command.componentTypeID, command.data.GetUnsafePtr()); + break; + case CommandType.RemoveComponent: + _entityManager.RemoveComponent(command.entity, command.componentTypeID); + break; + case CommandType.SetComponent: + _entityManager.SetComponent(command.entity, command.componentTypeID, command.data.GetUnsafePtr()); + break; + } + } + + Reset(); } public void Reset() @@ -100,11 +168,19 @@ public unsafe class EntityCommandBuffer : IDisposable public void Dispose() { + if (_disposed) + { + return; + } + foreach (ref var command in _commands) { command.data.Dispose(); } _commands.Dispose(); + + _disposed = true; + GC.SuppressFinalize(this); } } diff --git a/Ghost.Entities/EntityManager.cs b/Ghost.Entities/EntityManager.cs index af68cda..f9d3971 100644 --- a/Ghost.Entities/EntityManager.cs +++ b/Ghost.Entities/EntityManager.cs @@ -6,7 +6,7 @@ using System.Diagnostics; namespace Ghost.Entities; -public unsafe class EntityManager : IDisposable +public unsafe partial class EntityManager : IDisposable { private struct EntityLocation { @@ -45,6 +45,34 @@ public unsafe class EntityManager : IDisposable return ResultStatus.Success; } + /// + /// Create an entity with no components. + /// + /// The created entity. + public Entity CreateEntity() + { + // Put into empty archetype + ref var emptyArchetype = ref _world.GetArchetypeReference(World.EmptyArchetypeID); + emptyArchetype.AllocateEntity(out var chunkIndex, out var rowIndex); + + var id = _entityLocations.Add(new EntityLocation + { + archetypeID = World.EmptyArchetypeID, + chunkIndex = chunkIndex, + rowIndex = rowIndex + }, out var generation); + + var entity = new Entity(id, generation); + emptyArchetype.SetEntity(chunkIndex, rowIndex, entity); + + return entity; + } + + /// + /// Create an entity with specified components. + /// + /// The component type IDs to add to the entity. + /// The created entity. public Entity CreateEntity(params ReadOnlySpan> componentTypeIDs) { var signatureHash = ComponentRegister.GetHashCode(componentTypeIDs); @@ -71,26 +99,84 @@ public unsafe class EntityManager : IDisposable return entity; } - public Entity CreateEntity() + /// + /// Create multiple entities with specified components. + /// + /// The number of entities to create. + /// The allocator to use for the returned array. + /// The component type IDs to add to the entities. + /// An array of the created entities. + public UnsafeArray CreateEntities(int count, Allocator allocator, params ReadOnlySpan> componentTypeIDs) { - // Put into empty archetype - ref var emptyArchetype = ref _world.GetArchetypeReference(World.EmptyArchetypeID); - emptyArchetype.AllocateEntity(out var chunkIndex, out var rowIndex); + var signatureHash = ComponentRegister.GetHashCode(componentTypeIDs); + var arcID = _world.GetArchetypeIDBySignatureHash(signatureHash); - var id = _entityLocations.Add(new EntityLocation + if (arcID.IsNotValid) { - archetypeID = World.EmptyArchetypeID, - chunkIndex = chunkIndex, - rowIndex = rowIndex - }, out var generation); + arcID = _world.CreateArchetype(componentTypeIDs, signatureHash); + } - var entity = new Entity(id, generation); - emptyArchetype.SetEntity(chunkIndex, rowIndex, entity); + ref var archetype = ref _world.GetArchetypeReference(arcID); - return entity; + var entities = new UnsafeArray(count, allocator); + for (var i = 0; i < count; i++) + { + archetype.AllocateEntity(out var chunkIndex, out var rowIndex); + + var id = _entityLocations.Add(new EntityLocation + { + archetypeID = arcID, + chunkIndex = chunkIndex, + rowIndex = rowIndex + }, out var generation); + + var entity = new Entity(id, generation); + archetype.SetEntity(chunkIndex, rowIndex, entity); + + entities[i] = entity; + } + + return entities; } - public ResultStatus DestoryEntity(Entity entity) + /// + /// Create multiple entities with specified components. + /// + /// The number of entities to create. + /// The component type IDs to add to the entities. + public void CreateEntities(int count, params ReadOnlySpan> componentTypeIDs) + { + var signatureHash = ComponentRegister.GetHashCode(componentTypeIDs); + var arcID = _world.GetArchetypeIDBySignatureHash(signatureHash); + + if (arcID.IsNotValid) + { + arcID = _world.CreateArchetype(componentTypeIDs, signatureHash); + } + + ref var archetype = ref _world.GetArchetypeReference(arcID); + + for (var i = 0; i < count; i++) + { + archetype.AllocateEntity(out var chunkIndex, out var rowIndex); + + var id = _entityLocations.Add(new EntityLocation + { + archetypeID = arcID, + chunkIndex = chunkIndex, + rowIndex = rowIndex + }, out var generation); + + var entity = new Entity(id, generation); + archetype.SetEntity(chunkIndex, rowIndex, entity); + } + } + + /// + /// Destroy the specified entity. + /// + /// The result status of the operation. + public ResultStatus DestroyEntity(Entity entity) { if (!_entityLocations.TryGetElementAt(entity.ID, entity.Generation, out var location)) { @@ -112,6 +198,109 @@ public unsafe class EntityManager : IDisposable return ResultStatus.Success; } + /// + /// Check if the specified entity exists. + /// + /// The entity to check. + /// True if the entity exists, false otherwise. + public bool Exists(Entity entity) + { + return _entityLocations.Contains(entity.ID, entity.Generation); + } + + /// + /// Create a singleton entity with the specified component. + /// + /// The component type ID of the singleton. + /// Pointer to the component data. + /// The result status of the operation. + public ResultStatus CreateSingleton(Identifier componentID, void* pComponent) + { + if (pComponent == null) + { + return ResultStatus.InvalidArgument; + } + + // Check if singleton already exists + var signatureHash = ComponentRegister.GetHashCode(componentID); + var arcID = _world.GetArchetypeIDBySignatureHash(signatureHash); + + if (arcID.IsValid) + { + return ResultStatus.InvalidArgument; + } + + arcID = _world.CreateArchetype([componentID], signatureHash); + + ref var archetype = ref _world.GetArchetypeReference(arcID); + archetype.AllocateEntity(out var chunkIndex, out var rowIndex); + + var id = _entityLocations.Add(new EntityLocation + { + archetypeID = arcID, + chunkIndex = chunkIndex, + rowIndex = rowIndex + }, out var generation); + + var entity = new Entity(id, generation); + archetype.SetEntity(chunkIndex, rowIndex, entity); + archetype.SetComponentData(chunkIndex, rowIndex, componentID, pComponent); + + return ResultStatus.Success; + } + + /// + /// Create a singleton entity with the specified component. + /// + /// The component type. + /// The component data. + /// The result status of the operation. + public ResultStatus CreateSingleton(T component = default) + where T : unmanaged, IComponent + { + return CreateSingleton(ComponentTypeID.value, &component); + } + + /// + /// Get a pointer to the singleton component data. + /// + /// The component type ID of the singleton. + /// Pointer to the component data, or null if not found. + public void* GetSingleton(Identifier componentID) + { + var signatureHash = ComponentRegister.GetHashCode(componentID); + var arcID = _world.GetArchetypeIDBySignatureHash(signatureHash); + + if (arcID.IsNotValid) + { + return null; + } + + ref var archetype = ref _world.GetArchetypeReference(arcID); + var layoutResult = archetype.GetLayout(componentID); + if (layoutResult.Status != ResultStatus.Success) + { + return null; + } + + var chunk = archetype._chunks[0]; + var ptr = chunk.GetUnsafePtr() + layoutResult.Value.offset; + + return ptr; + } + + /// + /// Get a reference to the singleton component data. + /// + /// The component type. + /// Reference to the component data. null ref if not found. + public ref T GetSingleton() + where T : unmanaged, IComponent + { + var ptr = GetSingleton(ComponentTypeID.value); + return ref *(T*)ptr; // This will return null ref if ptr is null. + } + private static void CopyData(ref Archetype oldArch, int oldChunk, int oldRow, ref Archetype newArch, int newChunk, int newRow) { @@ -121,20 +310,27 @@ public unsafe class EntityManager : IDisposable var layout = oldArch._layouts[i]; var src = oldArch._chunks[oldChunk].GetUnsafePtr() + layout.offset + (layout.size * oldRow); - var layoutResult = newArch.GetLayout(layout.componentID); - Debug.Assert(layoutResult.Status == ResultStatus.Success); // This should always be true if the system is consistent. - if (layoutResult.Status != ResultStatus.Success) + var r = newArch.GetLayout(layout.componentID); + Debug.Assert(r.Status == ResultStatus.Success); // This should always be true if the system is consistent. + if (r.Status != ResultStatus.Success) { continue; } - var dst = newArch._chunks[newChunk].GetUnsafePtr() + layoutResult.Value.offset + (layout.size * newRow); + var dst = newArch._chunks[newChunk].GetUnsafePtr() + r.Value.offset + (layout.size * newRow); - MemoryUtility.MemCpy(src, dst, (nuint)layout.size); + MemoryUtility.MemCpy(dst, src, (nuint)layout.size); } } - public ResultStatus AddComponent(Entity entity, Identifier componentID, void* component) + /// + /// Add a component to the specified entity. + /// + /// The entity to add the component to. + /// The component type ID to add. + /// Pointer to the component data. + /// The result status of the operation. + public ResultStatus AddComponent(Entity entity, Identifier componentID, void* pComponent) { // Find current location ref var location = ref _entityLocations.GetElementReferenceAt(entity.ID, entity.Generation, out var exist); @@ -159,18 +355,11 @@ public unsafe class EntityManager : IDisposable var newSignature = new SpanBitSet(bits); - var iterator = 0; + var oldIt = oldSignature.GetIterator(); var compCount = 0; - while (true) + while (oldIt.Next(out var index)) { - var bit = oldSignature.NextSetBit(iterator); - if (bit == -1) - { - break; - } - - newSignature.SetBit(bit); - iterator = bit + 1; + newSignature.SetBit(index); compCount++; } @@ -184,19 +373,12 @@ public unsafe class EntityManager : IDisposable { // Create new archetype Span> componentTypeIDs = stackalloc Identifier[compCount]; - componentTypeIDs[0] = componentID; - iterator = 0; - while (true) + var newIt = newSignature.GetIterator(); + var i = 0; + while (newIt.Next(out var index)) { - var bit = oldSignature.NextSetBit(iterator); - if (bit == -1) - { - break; - } - - componentTypeIDs[--compCount] = bit; - iterator = bit + 1; + componentTypeIDs[i++] = index; } newArcID = _world.CreateArchetype(componentTypeIDs, newSignatureHash); @@ -212,7 +394,7 @@ public unsafe class EntityManager : IDisposable ref newArchetype, newChunkIndex, newRowIndex); newArchetype.SetEntity(newChunkIndex, newRowIndex, entity); - newArchetype.SetComponentData(newChunkIndex, newRowIndex, componentID, component); + newArchetype.SetComponentData(newChunkIndex, newRowIndex, componentID, pComponent); 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. @@ -229,13 +411,121 @@ public unsafe class EntityManager : IDisposable return ResultStatus.Success; } - public ResultStatus AddComponent(Entity entity, T component) + /// + /// Add a component to the specified entity. + /// + /// The component type. + /// The entity to add the component to. + /// The component data. + /// The result status of the operation. + public ResultStatus AddComponent(Entity entity, T component = default) where T : unmanaged, IComponent { return AddComponent(entity, ComponentTypeID.value, &component); } - public ResultStatus SetComponentData(Entity entity, Identifier componentID, void* pComponent) + /// + /// Remove a component from the specified entity. + /// + /// The entity to remove the component from. + /// The component type ID to remove. + /// The result status of the operation. + public ResultStatus RemoveComponent(Entity entity, Identifier componentID) + { + // Find current location + ref var location = ref _entityLocations.GetElementReferenceAt(entity.ID, entity.Generation, out var exist); + if (!exist) + { + return ResultStatus.NotFound; + } + + // Build new archetype signature + ref var oldArchetype = ref _world.GetArchetypeReference(location.archetypeID); + var oldSignature = oldArchetype._signature; + + var newArcID = oldArchetype.GetEdgeRemove(componentID); + if (newArcID.IsNotValid) + { + var largestComponentID = Math.Max(oldSignature.Count, componentID); + var length = UnsafeBitSet.RequiredLength(largestComponentID + 1); + + Span bits = stackalloc uint[length]; + bits.Clear(); + + var newSignature = new SpanBitSet(bits); + + var oldIt = oldSignature.GetIterator(); + var compCount = 0; + while (oldIt.Next(out var index)) + { + if (index != componentID) + { + newSignature.SetBit(index); + compCount++; + } + } + + // Find or create new archetype + var newSignatureHash = newSignature.GetHashCode(); + newArcID = _world.GetArchetypeIDBySignatureHash(newSignatureHash); + if (newArcID.IsNotValid) + { + // Create new archetype + Span> componentTypeIDs = stackalloc Identifier[compCount]; + + var newIt = newSignature.GetIterator(); + var i = 0; + while (newIt.Next(out var index)) + { + componentTypeIDs[i++] = index; + } + + newArcID = _world.CreateArchetype(componentTypeIDs, newSignatureHash); + } + + oldArchetype.AddEdgeRemove(componentID, newArcID); + } + + // Move entity data + ref var newArchetype = ref _world.GetArchetypeReference(newArcID); + newArchetype.AllocateEntity(out var newChunkIndex, out var newRowIndex); + newArchetype.SetEntity(newChunkIndex, newRowIndex, entity); + + var r = oldArchetype.RemoveEntity(location.chunkIndex, location.rowIndex); + Debug.Assert(r == ResultStatus.Success); // We assert it because the entity should exist if the whole system is consistent. + if (r != ResultStatus.Success) + { + return r; + } + + // Update location + location.archetypeID = newArcID; + location.chunkIndex = newChunkIndex; + location.rowIndex = newRowIndex; + + return ResultStatus.Success; + } + + /// + /// Remove a component from the specified entity. + /// + /// The component type. + /// The entity to remove the component from. + /// The result status of the operation. + public ResultStatus RemoveComponent(Entity entity) + where T : unmanaged, IComponent + { + return RemoveComponent(entity, ComponentTypeID.value); + } + + /// + /// Set the component data for the specified entity. + /// + /// The entity to set the component data for. + /// The component type ID to set. + /// Pointer to the component data. + /// The result status of the operation. + public ResultStatus SetComponent(Entity entity, Identifier componentID, void* pComponent) { if (!_entityLocations.TryGetElementAt(entity.ID, entity.Generation, out var location)) { @@ -248,12 +538,54 @@ public unsafe class EntityManager : IDisposable return ResultStatus.Success; } - public ResultStatus SetComponentData(Entity entity, T component) + /// + /// Set the component data for the specified entity. + /// + /// The component type. + /// The entity to set the component data for. + /// The component data. + public ResultStatus SetComponent(Entity entity, T component) where T : unmanaged, IComponent { - return SetComponentData(entity, ComponentTypeID.value, &component); + return SetComponent(entity, ComponentTypeID.value, &component); } + /// + /// Get a pointer to the component data for the specified entity. + /// + /// The entity to get the component data for. + /// The component type ID to get. + /// Pointer to the component data, or null if not found. + public void* GetComponent(Entity entity, Identifier componentID) + { + if (!_entityLocations.TryGetElementAt(entity.ID, entity.Generation, out var location)) + { + return null; + } + + ref var archetype = ref _world.GetArchetypeReference(location.archetypeID); + return archetype.GetComponentData(location.chunkIndex, location.rowIndex, componentID); + } + + /// + /// Get a reference to the component data for the specified entity. + /// + /// The component type. + /// The entity to get the component data for. + /// Reference to the component data. null ref if not found. + public ref T GetComponent(Entity entity) + where T : unmanaged, IComponent + { + var ptr = GetComponent(entity, ComponentTypeID.value); + return ref *(T*)ptr; // This will return null ref if ptr is null. + } + + /// + /// Check if the specified entity has the specified component. + /// + /// The entity to check. + /// The component type ID to check. + /// True if the entity has the component, false otherwise. public bool HasComponent(Entity entity, Identifier componentID) { if (!_entityLocations.TryGetElementAt(entity.ID, entity.Generation, out var location)) @@ -265,14 +597,26 @@ public unsafe class EntityManager : IDisposable return archetype.HasComponent(componentID); } + /// + /// Check if the specified entity has the specified component. + /// + /// The component type. + /// The entity to check. + /// True if the entity has the component, false otherwise. public bool HasComponent(Entity entity) where T : unmanaged, IComponent { return HasComponent(entity, ComponentTypeID.value); } - public ResultStatus SetEnabled(Entity entity, bool enabled) - where T : unmanaged, IEnableableComponent + /// + /// Set the enabled state of an enableable component for the specified entity. + /// + /// The entity to set the enabled state for. + /// The component type ID of the enableable component.True to enable the component, false to disable it. + /// The result status of the operation. + public ResultStatus SetEnabled(Entity entity, Identifier componentID, bool enabled) { if (!_entityLocations.TryGetElementAt(entity.ID, entity.Generation, out var location)) { @@ -283,7 +627,7 @@ public unsafe class EntityManager : IDisposable var chunkIndex = location.chunkIndex; var rowIndex = location.rowIndex; - var layoutResult = archetype.GetLayout(ComponentTypeID.value); + var layoutResult = archetype.GetLayout(componentID); if (layoutResult.Status != ResultStatus.Success) { return layoutResult.Status; @@ -308,6 +652,19 @@ public unsafe class EntityManager : IDisposable return ResultStatus.Success; } + /// + /// Set the enabled state of an enableable component for the specified entity. + /// + /// The enableable component type. + /// The entity to set the enabled state for. + /// True to enable the component, false to disable it.The result status of the operation. + public ResultStatus SetEnabled(Entity entity, bool enabled) + where T : unmanaged, IEnableableComponent + { + return SetEnabled(entity, ComponentTypeID.value, enabled); + } + public void Dispose() { if (_disposed) diff --git a/Ghost.Entities/Query.cs b/Ghost.Entities/Query.cs index d699ce8..2749020 100644 --- a/Ghost.Entities/Query.cs +++ b/Ghost.Entities/Query.cs @@ -37,17 +37,6 @@ public struct EntityQueryMask : IDisposable, IEquatable return hash; } - public void Dispose() - { - structuralAll.Dispose(); - structuralAny.Dispose(); - structuralAbsent.Dispose(); - - requireEnabled.Dispose(); - requireDisabled.Dispose(); - rejectIfEnabled.Dispose(); - } - public readonly bool Equals(EntityQueryMask other) { return structuralAll.Equals(other.structuralAll) @@ -72,98 +61,109 @@ public struct EntityQueryMask : IDisposable, IEquatable { return !(left == right); } + + public void Dispose() + { + structuralAll.Dispose(); + structuralAny.Dispose(); + structuralAbsent.Dispose(); + + requireEnabled.Dispose(); + requireDisabled.Dispose(); + rejectIfEnabled.Dispose(); + } } public unsafe partial struct EntityQuery : IIdentifierType, IDisposable { + /// + /// Provides a read-only view over a chunk of entities and their component data within an archetype. + /// + /// This does not filter disabled/enabled components. You must handle that manually. + public readonly ref struct ChunkView + { + private readonly ref Archetype _archetype; + private readonly ref Chunk _chunk; + + public readonly int Count => _chunk.Count; + + internal ChunkView(ref Archetype archetype, int chunkIndex) + { + _archetype = ref archetype; + _chunk = ref archetype.GetChunkReference(chunkIndex); + } + + /// + /// Returns a read-only span containing structuralAll entities stored in the current chunk. + /// + /// A read-only span of values representing the entities in the chunk. + public readonly ReadOnlySpan GetEntities() + { + var ptr = _chunk.GetUnsafePtr(); + var pEntity = (Entity*)(ptr + _archetype.EntityIDsOffset); + return new ReadOnlySpan(pEntity, _chunk.Count); + } + + /// + /// Gets a span providing direct access to the component data of type T0 for structuralAll entities in the chunk. + /// + /// The type of component to access. Must be an unmanaged type that implements . + /// A span of type containing the component data for each entity in the chunk. + /// Thrown if the specified component type is not present in the archetype. + public readonly Span GetComponentData() + where T : unmanaged, IComponent + { + var layout = _archetype.GetLayout(ComponentTypeID.value).GetValueOrThrow(ResultStatus.Success); + var ptr = _chunk.GetUnsafePtr() + layout.offset; + return new Span(ptr, _chunk.Count); + } + + /// + /// Gets a bit set representing the enabled state of each instance of the specified enableable component + /// type within the current chunk. + /// + /// The component type for which to retrieve enablement bits. Must be unmanaged and implement . + /// A that provides access to the enablement bits for all instances of the specified component type in the chunk. + /// Thrown if the specified component type does not support enablement. + public SpanBitSet GetEnableBits() + where T : unmanaged, IEnableableComponent + { + var layout = _archetype.GetLayout(ComponentTypeID.value).GetValueOrThrow(ResultStatus.Success); + if (layout.enableBitsOffset == -1) + { + throw new InvalidOperationException($"Component {typeof(T).FullName} is not enableable."); + } + + var maskBase = _chunk.GetUnsafePtr() + layout.enableBitsOffset; + return new SpanBitSet(new Span(maskBase, (_chunk.Count + 31) / 32)); + } + + /// + /// Determines whether the specified component of type at the given index is currently enabled. + /// + /// The type of the component to check. Must be an unmanaged type that implements . + /// The zero-based index of the component instance to check within the chunk. + /// true if the component at the specified index is enabled; otherwise, false. + /// Thrown if the specified component type does not support enable/disable functionality. + public readonly bool IsComponentEnabled(int index) + where T : unmanaged, IEnableableComponent + { + var layout = _archetype.GetLayout(ComponentTypeID.value).GetValueOrThrow(ResultStatus.Success); + if (layout.enableBitsOffset == -1) + { + throw new InvalidOperationException($"Component {typeof(T).FullName} is not enableable."); + } + + var maskBase = _chunk.GetUnsafePtr() + layout.enableBitsOffset; + return CheckBit(maskBase, index); + } + } + /// /// Provides an enumerator for iterating over chunks of entities and their component data that match a set of archetypes within a world. /// public readonly ref struct ChunkIterator { - /// - /// Provides a read-only view over a chunk of entities and their component data within an archetype. - /// - /// This does not filter disabled/enabled components. You must handle that manually. - public readonly ref struct ChunkView - { - private readonly ref Archetype _archetype; - private readonly ref Chunk _chunk; - - public readonly int Count => _chunk.Count; - - internal ChunkView(ref Archetype archetype, int chunkIndex) - { - _archetype = ref archetype; - _chunk = ref archetype.GetChunkReference(chunkIndex); - } - - /// - /// Returns a read-only span containing structuralAll entities stored in the current chunk. - /// - /// A read-only span of values representing the entities in the chunk. - public readonly ReadOnlySpan GetEntities() - { - var ptr = _chunk.GetUnsafePtr(); - var pEntity = (Entity*)(ptr + _archetype.EntityIDsOffset); - return new ReadOnlySpan(pEntity, _chunk.Count); - } - - /// - /// Gets a span providing direct access to the component data of type T0 for structuralAll entities in the chunk. - /// - /// The type of component to access. Must be an unmanaged type that implements . - /// A span of type containing the component data for each entity in the chunk. - /// Thrown if the specified component type is not present in the archetype. - public readonly Span GetComponentData() - where T : unmanaged, IComponent - { - var layout = _archetype.GetLayout(ComponentTypeID.value).GetValueOrThrow(ResultStatus.Success); - var ptr = _chunk.GetUnsafePtr() + layout.offset; - return new Span(ptr, _chunk.Count); - } - - /// - /// Gets a bit set representing the enabled state of each instance of the specified enableable component - /// type within the current chunk. - /// - /// The component type for which to retrieve enablement bits. Must be unmanaged and implement . - /// A that provides access to the enablement bits for all instances of the specified component type in the chunk. - /// Thrown if the specified component type does not support enablement. - public SpanBitSet GetEnableBits() - where T : unmanaged, IEnableableComponent - { - var layout = _archetype.GetLayout(ComponentTypeID.value).GetValueOrThrow(ResultStatus.Success); - if (layout.enableBitsOffset == -1) - { - throw new InvalidOperationException($"Component {typeof(T).FullName} is not enableable."); - } - - var maskBase = _chunk.GetUnsafePtr() + layout.enableBitsOffset; - return new SpanBitSet(new Span(maskBase, (_chunk.Count + 31) / 32)); - } - - /// - /// Determines whether the specified component of type at the given index is currently enabled. - /// - /// The type of the component to check. Must be an unmanaged type that implements . - /// The zero-based index of the component instance to check within the chunk. - /// true if the component at the specified index is enabled; otherwise, false. - /// Thrown if the specified component type does not support enable/disable functionality. - public readonly bool IsComponentEnabled(int index) - where T : unmanaged, IEnableableComponent - { - var layout = _archetype.GetLayout(ComponentTypeID.value).GetValueOrThrow(ResultStatus.Success); - if (layout.enableBitsOffset == -1) - { - throw new InvalidOperationException($"Component {typeof(T).FullName} is not enableable."); - } - - var maskBase = _chunk.GetUnsafePtr() + layout.enableBitsOffset; - return CheckBit(maskBase, index); - } - } - public ref struct Enumerator { private readonly ChunkIterator _iterator; @@ -210,10 +210,6 @@ public unsafe partial struct EntityQuery : IIdentifierType, IDisposable _archetypeIndex = 0; _chunkIndex = -1; } - - public readonly void Dispose() - { - } } private readonly ReadOnlyUnsafeCollection> _matchingArchetypes; @@ -245,6 +241,7 @@ public unsafe partial struct EntityQuery : IIdentifierType, IDisposable _matchingArchetypes = new UnsafeList>(8, Allocator.Persistent); } + // TODO: Fetching layout every time is not optimal. Cache them? private static bool IsEntityValid(byte* chunkBase, int entityIndex, ref readonly Archetype archetype, ref readonly EntityQueryMask mask) { // 1. Check "Require Enabled" (WithAll) @@ -358,12 +355,12 @@ public ref partial struct QueryBuilder { _scope = AllocationManager.CreateStackScope(); - _all = new UnsafeList>(4, Allocator.Stack); - _any = new UnsafeList>(4, Allocator.Stack); - _absent = new UnsafeList>(4, Allocator.Stack); - _none = new UnsafeList>(4, Allocator.Stack); - _disabled = new UnsafeList>(4, Allocator.Stack); - _present = new UnsafeList>(4, Allocator.Stack); + _all = new UnsafeList>(4, _scope.AllocationHandle); + _any = new UnsafeList>(4, _scope.AllocationHandle); + _absent = new UnsafeList>(4, _scope.AllocationHandle); + _none = new UnsafeList>(4, _scope.AllocationHandle); + _disabled = new UnsafeList>(4, _scope.AllocationHandle); + _present = new UnsafeList>(4, _scope.AllocationHandle); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -461,13 +458,6 @@ public ref partial struct QueryBuilder private void Dispose() { - _all.Dispose(); - _any.Dispose(); - _absent.Dispose(); - _none.Dispose(); - _disabled.Dispose(); - _present.Dispose(); - _scope.Dispose(); } } diff --git a/Ghost.Entities/System.cs b/Ghost.Entities/System.cs new file mode 100644 index 0000000..ffc27ae --- /dev/null +++ b/Ghost.Entities/System.cs @@ -0,0 +1,285 @@ +using Ghost.Core; +using Misaki.HighPerformance.Jobs; + +namespace Ghost.Entities; + +public readonly ref struct SystemAPI +{ + public World World + { + get; init; + } + + public Time Time + { + get; init; + } +} + +public interface ISystem +{ + void Initialize(ref readonly SystemAPI systemAPI); + void PreUpdate(ref readonly SystemAPI systemAPI); + void Update(ref readonly SystemAPI systemAPI); + void PostUpdate(ref readonly SystemAPI systemAPI); + void Cleanup(ref readonly SystemAPI systemAPI); +} + +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true)] +public class UpdateAfterAttribute : Attribute +{ + public Type SystemType { get; } + + public UpdateAfterAttribute(Type systemType) + { + SystemType = systemType; + } +} + +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true)] +public class UpdateBeforeAttribute : Attribute +{ + public Type SystemType { get; } + + public UpdateBeforeAttribute(Type systemType) + { + SystemType = systemType; + } +} + +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false)] +public class SystemGroupAttribute : Attribute +{ + public Type GroupType { get; } + + public SystemGroupAttribute(Type groupType) + { + GroupType = groupType; + } +} + +#if false +internal static partial class SystemGroupRegistry +{ + private static readonly Dictionary> _systemGroupMap = new(); + + // TODO: Use Source Generators to generate group registrations at compile time. + + public static void RegisterSystemGroup(Type groupType) + where T : ISystem, new() + { + if (!_systemGroupMap.ContainsKey(typeof(T))) + { + _systemGroupMap[typeof(T)] = new(); + } + + _systemGroupMap[groupType].Add(new T()); + } + + public static List GetSystemsForGroup(Type groupType) + { + if (_systemGroupMap.TryGetValue(groupType, out var systems)) + { + return systems; + } + + throw new InvalidOperationException($"No systems registered for System Group of type {groupType.FullName}"); + } +} +#endif + +public abstract class SystemGroup : ISystem +{ + private readonly List _systems = new (); + private List? _sortedSystems; + + private uint _version = 0; + private uint _sortedVersion = 0; + + // public SystemGroup() + // { + // _systems = SystemGroupRegistry.GetSystemsForGroup(GetType()); + // } + + private static List Sort(List systems) + { + // 1. Build the Graph + // Key: The System, Value: Systems that MUST run before the Key + var dependencies = new Dictionary>(); + var systemMap = systems.ToDictionary(s => s.GetType(), s => s); + + foreach (var sys in systems) + { + var type = sys.GetType(); + if (!dependencies.ContainsKey(type)) dependencies[type] = new HashSet(); + + // Handle [UpdateAfter(typeof(Other))] -> Other comes before This + foreach (var attr in type.GetCustomAttributes(typeof(UpdateAfterAttribute), true)) + { + var depType = ((UpdateAfterAttribute)attr).SystemType; + dependencies[type].Add(depType); + } + + // Handle [UpdateBefore(typeof(Other))] -> This comes before Other + // Which means: Other depends on This + foreach (var attr in type.GetCustomAttributes(typeof(UpdateBeforeAttribute), true)) + { + var targetType = ((UpdateBeforeAttribute)attr).SystemType; + if (!dependencies.ContainsKey(targetType)) dependencies[targetType] = new HashSet(); + dependencies[targetType].Add(type); + } + } + + // 2. Topological Sort (Kahn's Algorithm variant) + var sortedList = new List(); + var visited = new HashSet(); + + // We loop until we have sorted everyone + while (sortedList.Count < systems.Count) + { + bool addedAny = false; + + foreach (var sys in systems) + { + var type = sys.GetType(); + if (visited.Contains(type)) continue; + + // Check if all dependencies for this system are already visited/sorted + bool canRun = true; + if (dependencies.TryGetValue(type, out var deps)) + { + foreach (var dep in deps) + { + // If the dependency exists in our list but hasn't run yet, we can't run. + // (We check systemMap to ignore dependencies that don't exist in this world) + if (systemMap.ContainsKey(dep) && !visited.Contains(dep)) + { + canRun = false; + break; + } + } + } + + if (canRun) + { + sortedList.Add(sys); + visited.Add(type); + addedAny = true; + } + } + + if (!addedAny) + { + throw new InvalidOperationException("Circular Dependency detected in Systems! Check your [UpdateAfter] attributes."); + } + } + + return sortedList; + } + + public void AddSystem(ISystem system) + { + _systems.Add(system); + _version++; + } + + public void SortSystems() + { + _sortedSystems = Sort(_systems); + _sortedVersion = _version; + } + + private void ThrowIfNotSorted() + { + if (_sortedSystems == null || _sortedVersion != _version) + { + throw new InvalidOperationException("Systems must be sorted before calling this method. Call SortSystems() after adding all systems."); + } + } + + public void Initialize(ref readonly SystemAPI systemAPI) + { + ThrowIfNotSorted(); + + foreach (var system in _sortedSystems!) + { + system.Initialize(in systemAPI); + } + } + + public void PreUpdate(ref readonly SystemAPI systemAPI) + { + ThrowIfNotSorted(); + + foreach (var system in _sortedSystems!) + { + system.PreUpdate(in systemAPI); + } + } + + public void Update(ref readonly SystemAPI systemAPI) + { + ThrowIfNotSorted(); + + foreach (var system in _sortedSystems!) + { + system.Update(in systemAPI); + } + } + + public void PostUpdate(ref readonly SystemAPI systemAPI) + { + ThrowIfNotSorted(); + + foreach (var system in _sortedSystems!) + { + system.PostUpdate(in systemAPI); + } + } + + public void Cleanup(ref readonly SystemAPI systemAPI) + { + ThrowIfNotSorted(); + + foreach (var system in _sortedSystems!) + { + system.Cleanup(in systemAPI); + } + } +} + +public class DefaultSystemGroup : SystemGroup +{ +} + +public class SystemManager +{ + private readonly World _world; + + private readonly List _systems = new (); + private readonly Dictionary _systemTypeMap = new (); + + internal SystemManager(World world) + { + _world = world; + } + + public void AddSystem() + where T : ISystem, new() + { + var system = new T(); + _systems.Add(system); + _systemTypeMap[typeof(T)] = _systems.Count - 1; + } + + public T GetSystem() + where T : ISystem + { + if (_systemTypeMap.TryGetValue(typeof(T), out var index)) + { + return (T)_systems[index]; + } + + throw new InvalidOperationException($"System of type {typeof(T).FullName} not found in SystemManager."); + } +} diff --git a/Ghost.Entities/Templates/EntityQuery.ComponentIterator.gen.cs b/Ghost.Entities/Templates/EntityQuery.ComponentIterator.gen.cs new file mode 100644 index 0000000..15b9363 --- /dev/null +++ b/Ghost.Entities/Templates/EntityQuery.ComponentIterator.gen.cs @@ -0,0 +1,1554 @@ +using Ghost.Core; +using Misaki.HighPerformance.LowLevel; +using Misaki.HighPerformance.LowLevel.Collections; +using System.Runtime.CompilerServices; + +namespace Ghost.Entities; + +public unsafe partial struct EntityQuery +{ + public readonly ref struct ComponentIterator + where T0 : unmanaged, IComponent + { + public ref struct Enumerator + { + private fixed int _compTypeIDs[1]; + private fixed int _offsets[1]; + private fixed long _compBasePtrs[1]; + + private readonly ReadOnlyUnsafeCollection> _matchingArchetypes; + private readonly EntityQueryMask _mask; + private readonly World _world; + + private ref Archetype _currentArchetype; + private ref Chunk _currentChunk; + private byte* _chunkBasePtr; + + private int _currentChunkEntityCount; + private int _currentArchetypeIndex; + private int _currentChunkIndex; + private int _currentEntityIndex; + + internal Enumerator(ReadOnlyUnsafeCollection> matchingArchetypes, EntityQueryMask mask, World world) + { + _compTypeIDs[0] = ComponentTypeID.value; + _offsets[0] = 0; + _compBasePtrs[0] = 0; + + _matchingArchetypes = matchingArchetypes; + _mask = mask; + _world = world; + + Reset(); + } + + public ref T0 Current => ref *(T0*)(_compBasePtrs[0] + _currentEntityIndex * sizeof(T0)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void SetChunk(int chunkIndex) + { + _currentChunk = ref _currentArchetype.GetChunkReference(chunkIndex); + _chunkBasePtr = _currentChunk.GetUnsafePtr(); + _currentChunkEntityCount = _currentChunk.Count; + + for (var index = 0; index < 1; index++) + { + var layout = _currentArchetype.GetLayout(_compTypeIDs[index]) + .GetValueOrThrow(ResultStatus.Success); + _offsets[index] = layout.offset; + _compBasePtrs[index] = (long)(_chunkBasePtr + _offsets[index]); + } + } + + public bool MoveNext() + { + while (true) + { + _currentEntityIndex++; + if (_currentEntityIndex < _currentChunk.Count) + { + var pChunkData = _currentChunk.GetUnsafePtr(); + if (IsEntityValid(pChunkData, _currentEntityIndex, in _currentArchetype, in _mask)) + { + return true; + } + + continue; + } + + _currentChunkIndex++; + if (!Unsafe.IsNullRef(ref _currentArchetype) && _currentChunkIndex < _currentArchetype.ChunkCount) + { + SetChunk(_currentChunkIndex); + _currentEntityIndex = -1; // Reset for new chunk + + continue; + } + + _currentArchetypeIndex++; + if (_currentArchetypeIndex < _matchingArchetypes.Count) + { + _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); + + _currentChunkIndex = 0; + if (_currentArchetype.ChunkCount > 0) + { + SetChunk(0); + _currentEntityIndex = -1; + continue; + } + + // If archetype has no chunks, loop will try next archetype + } + else + { + return false; // End of all data + } + } + } + + public void Reset() + { + _currentArchetype = ref Unsafe.NullRef(); + _currentChunk = ref Unsafe.NullRef(); + _currentArchetypeIndex = 0; + _currentChunkIndex = 0; + _currentEntityIndex = -1; + + if (_matchingArchetypes.Count > 0) + { + _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[0]); + if (_currentArchetype.ChunkCount > 0) + { + SetChunk(0); + } + } + } + + } + + private readonly ReadOnlyUnsafeCollection> _matchingArchetypes; + private readonly EntityQueryMask _mask; + private readonly World _world; + + internal ComponentIterator(ReadOnlyUnsafeCollection> matchingArchetypes, EntityQueryMask mask, World world) + { + _matchingArchetypes = matchingArchetypes; + _mask = mask; + _world = world; + } + + public Enumerator GetEnumerator() + { + return new Enumerator(_matchingArchetypes, _mask, _world); + } + } + + public readonly ComponentIterator GetComponentIterator() + where T0 : unmanaged, IComponent + { + return new ComponentIterator(_matchingArchetypes.AsReadOnly(), _mask, World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success)); + } + + + public readonly ref struct ComponentIterator + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + { + public ref struct QueryItem + { + public ref T0 component0; + public ref T1 component1; + internal QueryItem(ref T0 component0, ref T1 component1) + { + this.component0 = ref component0; + this.component1 = ref component1; + } + + public void Deconstruct(out Ref component0, out Ref component1) + { + component0 = new Ref(ref this.component0); + component1 = new Ref(ref this.component1); + } + } + + public ref struct Enumerator + { + private fixed int _compTypeIDs[2]; + private fixed int _offsets[2]; + private fixed long _compBasePtrs[2]; + + private readonly ReadOnlyUnsafeCollection> _matchingArchetypes; + private readonly EntityQueryMask _mask; + private readonly World _world; + + private ref Archetype _currentArchetype; + private ref Chunk _currentChunk; + private byte* _chunkBasePtr; + + private int _currentChunkEntityCount; + private int _currentArchetypeIndex; + private int _currentChunkIndex; + private int _currentEntityIndex; + + internal Enumerator(ReadOnlyUnsafeCollection> matchingArchetypes, EntityQueryMask mask, World world) + { + _compTypeIDs[0] = ComponentTypeID.value; + _offsets[0] = 0; + _compBasePtrs[0] = 0; + + _compTypeIDs[1] = ComponentTypeID.value; + _offsets[1] = 0; + _compBasePtrs[1] = 0; + + _matchingArchetypes = matchingArchetypes; + _mask = mask; + _world = world; + + Reset(); + } + + public QueryItem Current => new( + ref *(T0*)(_compBasePtrs[0] + _currentEntityIndex * sizeof(T0)), + ref *(T1*)(_compBasePtrs[1] + _currentEntityIndex * sizeof(T1)) + ); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void SetChunk(int chunkIndex) + { + _currentChunk = ref _currentArchetype.GetChunkReference(chunkIndex); + _chunkBasePtr = _currentChunk.GetUnsafePtr(); + _currentChunkEntityCount = _currentChunk.Count; + + for (var index = 0; index < 2; index++) + { + var layout = _currentArchetype.GetLayout(_compTypeIDs[index]) + .GetValueOrThrow(ResultStatus.Success); + _offsets[index] = layout.offset; + _compBasePtrs[index] = (long)(_chunkBasePtr + _offsets[index]); + } + } + + public bool MoveNext() + { + while (true) + { + _currentEntityIndex++; + if (_currentEntityIndex < _currentChunk.Count) + { + var pChunkData = _currentChunk.GetUnsafePtr(); + if (IsEntityValid(pChunkData, _currentEntityIndex, in _currentArchetype, in _mask)) + { + return true; + } + + continue; + } + + _currentChunkIndex++; + if (!Unsafe.IsNullRef(ref _currentArchetype) && _currentChunkIndex < _currentArchetype.ChunkCount) + { + SetChunk(_currentChunkIndex); + _currentEntityIndex = -1; // Reset for new chunk + + continue; + } + + _currentArchetypeIndex++; + if (_currentArchetypeIndex < _matchingArchetypes.Count) + { + _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); + + _currentChunkIndex = 0; + if (_currentArchetype.ChunkCount > 0) + { + SetChunk(0); + _currentEntityIndex = -1; + continue; + } + + // If archetype has no chunks, loop will try next archetype + } + else + { + return false; // End of all data + } + } + } + + public void Reset() + { + _currentArchetype = ref Unsafe.NullRef(); + _currentChunk = ref Unsafe.NullRef(); + _currentArchetypeIndex = 0; + _currentChunkIndex = 0; + _currentEntityIndex = -1; + + if (_matchingArchetypes.Count > 0) + { + _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[0]); + if (_currentArchetype.ChunkCount > 0) + { + SetChunk(0); + } + } + } + + } + + private readonly ReadOnlyUnsafeCollection> _matchingArchetypes; + private readonly EntityQueryMask _mask; + private readonly World _world; + + internal ComponentIterator(ReadOnlyUnsafeCollection> matchingArchetypes, EntityQueryMask mask, World world) + { + _matchingArchetypes = matchingArchetypes; + _mask = mask; + _world = world; + } + + public Enumerator GetEnumerator() + { + return new Enumerator(_matchingArchetypes, _mask, _world); + } + } + + public readonly ComponentIterator GetComponentIterator() + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + { + return new ComponentIterator(_matchingArchetypes.AsReadOnly(), _mask, World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success)); + } + + + public readonly ref struct ComponentIterator + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + { + public ref struct QueryItem + { + public ref T0 component0; + public ref T1 component1; + public ref T2 component2; + internal QueryItem(ref T0 component0, ref T1 component1, ref T2 component2) + { + this.component0 = ref component0; + this.component1 = ref component1; + this.component2 = ref component2; + } + + public void Deconstruct(out Ref component0, out Ref component1, out Ref component2) + { + component0 = new Ref(ref this.component0); + component1 = new Ref(ref this.component1); + component2 = new Ref(ref this.component2); + } + } + + public ref struct Enumerator + { + private fixed int _compTypeIDs[3]; + private fixed int _offsets[3]; + private fixed long _compBasePtrs[3]; + + private readonly ReadOnlyUnsafeCollection> _matchingArchetypes; + private readonly EntityQueryMask _mask; + private readonly World _world; + + private ref Archetype _currentArchetype; + private ref Chunk _currentChunk; + private byte* _chunkBasePtr; + + private int _currentChunkEntityCount; + private int _currentArchetypeIndex; + private int _currentChunkIndex; + private int _currentEntityIndex; + + internal Enumerator(ReadOnlyUnsafeCollection> matchingArchetypes, EntityQueryMask mask, World world) + { + _compTypeIDs[0] = ComponentTypeID.value; + _offsets[0] = 0; + _compBasePtrs[0] = 0; + + _compTypeIDs[1] = ComponentTypeID.value; + _offsets[1] = 0; + _compBasePtrs[1] = 0; + + _compTypeIDs[2] = ComponentTypeID.value; + _offsets[2] = 0; + _compBasePtrs[2] = 0; + + _matchingArchetypes = matchingArchetypes; + _mask = mask; + _world = world; + + Reset(); + } + + public QueryItem Current => new( + ref *(T0*)(_compBasePtrs[0] + _currentEntityIndex * sizeof(T0)), + ref *(T1*)(_compBasePtrs[1] + _currentEntityIndex * sizeof(T1)), + ref *(T2*)(_compBasePtrs[2] + _currentEntityIndex * sizeof(T2)) + ); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void SetChunk(int chunkIndex) + { + _currentChunk = ref _currentArchetype.GetChunkReference(chunkIndex); + _chunkBasePtr = _currentChunk.GetUnsafePtr(); + _currentChunkEntityCount = _currentChunk.Count; + + for (var index = 0; index < 3; index++) + { + var layout = _currentArchetype.GetLayout(_compTypeIDs[index]) + .GetValueOrThrow(ResultStatus.Success); + _offsets[index] = layout.offset; + _compBasePtrs[index] = (long)(_chunkBasePtr + _offsets[index]); + } + } + + public bool MoveNext() + { + while (true) + { + _currentEntityIndex++; + if (_currentEntityIndex < _currentChunk.Count) + { + var pChunkData = _currentChunk.GetUnsafePtr(); + if (IsEntityValid(pChunkData, _currentEntityIndex, in _currentArchetype, in _mask)) + { + return true; + } + + continue; + } + + _currentChunkIndex++; + if (!Unsafe.IsNullRef(ref _currentArchetype) && _currentChunkIndex < _currentArchetype.ChunkCount) + { + SetChunk(_currentChunkIndex); + _currentEntityIndex = -1; // Reset for new chunk + + continue; + } + + _currentArchetypeIndex++; + if (_currentArchetypeIndex < _matchingArchetypes.Count) + { + _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); + + _currentChunkIndex = 0; + if (_currentArchetype.ChunkCount > 0) + { + SetChunk(0); + _currentEntityIndex = -1; + continue; + } + + // If archetype has no chunks, loop will try next archetype + } + else + { + return false; // End of all data + } + } + } + + public void Reset() + { + _currentArchetype = ref Unsafe.NullRef(); + _currentChunk = ref Unsafe.NullRef(); + _currentArchetypeIndex = 0; + _currentChunkIndex = 0; + _currentEntityIndex = -1; + + if (_matchingArchetypes.Count > 0) + { + _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[0]); + if (_currentArchetype.ChunkCount > 0) + { + SetChunk(0); + } + } + } + + } + + private readonly ReadOnlyUnsafeCollection> _matchingArchetypes; + private readonly EntityQueryMask _mask; + private readonly World _world; + + internal ComponentIterator(ReadOnlyUnsafeCollection> matchingArchetypes, EntityQueryMask mask, World world) + { + _matchingArchetypes = matchingArchetypes; + _mask = mask; + _world = world; + } + + public Enumerator GetEnumerator() + { + return new Enumerator(_matchingArchetypes, _mask, _world); + } + } + + public readonly ComponentIterator GetComponentIterator() + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + { + return new ComponentIterator(_matchingArchetypes.AsReadOnly(), _mask, World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success)); + } + + + public readonly ref struct ComponentIterator + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + { + public ref struct QueryItem + { + public ref T0 component0; + public ref T1 component1; + public ref T2 component2; + public ref T3 component3; + internal QueryItem(ref T0 component0, ref T1 component1, ref T2 component2, ref T3 component3) + { + this.component0 = ref component0; + this.component1 = ref component1; + this.component2 = ref component2; + this.component3 = ref component3; + } + + public void Deconstruct(out Ref component0, out Ref component1, out Ref component2, out Ref component3) + { + component0 = new Ref(ref this.component0); + component1 = new Ref(ref this.component1); + component2 = new Ref(ref this.component2); + component3 = new Ref(ref this.component3); + } + } + + public ref struct Enumerator + { + private fixed int _compTypeIDs[4]; + private fixed int _offsets[4]; + private fixed long _compBasePtrs[4]; + + private readonly ReadOnlyUnsafeCollection> _matchingArchetypes; + private readonly EntityQueryMask _mask; + private readonly World _world; + + private ref Archetype _currentArchetype; + private ref Chunk _currentChunk; + private byte* _chunkBasePtr; + + private int _currentChunkEntityCount; + private int _currentArchetypeIndex; + private int _currentChunkIndex; + private int _currentEntityIndex; + + internal Enumerator(ReadOnlyUnsafeCollection> matchingArchetypes, EntityQueryMask mask, World world) + { + _compTypeIDs[0] = ComponentTypeID.value; + _offsets[0] = 0; + _compBasePtrs[0] = 0; + + _compTypeIDs[1] = ComponentTypeID.value; + _offsets[1] = 0; + _compBasePtrs[1] = 0; + + _compTypeIDs[2] = ComponentTypeID.value; + _offsets[2] = 0; + _compBasePtrs[2] = 0; + + _compTypeIDs[3] = ComponentTypeID.value; + _offsets[3] = 0; + _compBasePtrs[3] = 0; + + _matchingArchetypes = matchingArchetypes; + _mask = mask; + _world = world; + + Reset(); + } + + public QueryItem Current => new( + ref *(T0*)(_compBasePtrs[0] + _currentEntityIndex * sizeof(T0)), + ref *(T1*)(_compBasePtrs[1] + _currentEntityIndex * sizeof(T1)), + ref *(T2*)(_compBasePtrs[2] + _currentEntityIndex * sizeof(T2)), + ref *(T3*)(_compBasePtrs[3] + _currentEntityIndex * sizeof(T3)) + ); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void SetChunk(int chunkIndex) + { + _currentChunk = ref _currentArchetype.GetChunkReference(chunkIndex); + _chunkBasePtr = _currentChunk.GetUnsafePtr(); + _currentChunkEntityCount = _currentChunk.Count; + + for (var index = 0; index < 4; index++) + { + var layout = _currentArchetype.GetLayout(_compTypeIDs[index]) + .GetValueOrThrow(ResultStatus.Success); + _offsets[index] = layout.offset; + _compBasePtrs[index] = (long)(_chunkBasePtr + _offsets[index]); + } + } + + public bool MoveNext() + { + while (true) + { + _currentEntityIndex++; + if (_currentEntityIndex < _currentChunk.Count) + { + var pChunkData = _currentChunk.GetUnsafePtr(); + if (IsEntityValid(pChunkData, _currentEntityIndex, in _currentArchetype, in _mask)) + { + return true; + } + + continue; + } + + _currentChunkIndex++; + if (!Unsafe.IsNullRef(ref _currentArchetype) && _currentChunkIndex < _currentArchetype.ChunkCount) + { + SetChunk(_currentChunkIndex); + _currentEntityIndex = -1; // Reset for new chunk + + continue; + } + + _currentArchetypeIndex++; + if (_currentArchetypeIndex < _matchingArchetypes.Count) + { + _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); + + _currentChunkIndex = 0; + if (_currentArchetype.ChunkCount > 0) + { + SetChunk(0); + _currentEntityIndex = -1; + continue; + } + + // If archetype has no chunks, loop will try next archetype + } + else + { + return false; // End of all data + } + } + } + + public void Reset() + { + _currentArchetype = ref Unsafe.NullRef(); + _currentChunk = ref Unsafe.NullRef(); + _currentArchetypeIndex = 0; + _currentChunkIndex = 0; + _currentEntityIndex = -1; + + if (_matchingArchetypes.Count > 0) + { + _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[0]); + if (_currentArchetype.ChunkCount > 0) + { + SetChunk(0); + } + } + } + + } + + private readonly ReadOnlyUnsafeCollection> _matchingArchetypes; + private readonly EntityQueryMask _mask; + private readonly World _world; + + internal ComponentIterator(ReadOnlyUnsafeCollection> matchingArchetypes, EntityQueryMask mask, World world) + { + _matchingArchetypes = matchingArchetypes; + _mask = mask; + _world = world; + } + + public Enumerator GetEnumerator() + { + return new Enumerator(_matchingArchetypes, _mask, _world); + } + } + + public readonly ComponentIterator GetComponentIterator() + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + { + return new ComponentIterator(_matchingArchetypes.AsReadOnly(), _mask, World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success)); + } + + + public readonly ref struct ComponentIterator + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + where T4 : unmanaged, IComponent + { + public ref struct QueryItem + { + public ref T0 component0; + public ref T1 component1; + public ref T2 component2; + public ref T3 component3; + public ref T4 component4; + internal QueryItem(ref T0 component0, ref T1 component1, ref T2 component2, ref T3 component3, ref T4 component4) + { + this.component0 = ref component0; + this.component1 = ref component1; + this.component2 = ref component2; + this.component3 = ref component3; + this.component4 = ref component4; + } + + public void Deconstruct(out Ref component0, out Ref component1, out Ref component2, out Ref component3, out Ref component4) + { + component0 = new Ref(ref this.component0); + component1 = new Ref(ref this.component1); + component2 = new Ref(ref this.component2); + component3 = new Ref(ref this.component3); + component4 = new Ref(ref this.component4); + } + } + + public ref struct Enumerator + { + private fixed int _compTypeIDs[5]; + private fixed int _offsets[5]; + private fixed long _compBasePtrs[5]; + + private readonly ReadOnlyUnsafeCollection> _matchingArchetypes; + private readonly EntityQueryMask _mask; + private readonly World _world; + + private ref Archetype _currentArchetype; + private ref Chunk _currentChunk; + private byte* _chunkBasePtr; + + private int _currentChunkEntityCount; + private int _currentArchetypeIndex; + private int _currentChunkIndex; + private int _currentEntityIndex; + + internal Enumerator(ReadOnlyUnsafeCollection> matchingArchetypes, EntityQueryMask mask, World world) + { + _compTypeIDs[0] = ComponentTypeID.value; + _offsets[0] = 0; + _compBasePtrs[0] = 0; + + _compTypeIDs[1] = ComponentTypeID.value; + _offsets[1] = 0; + _compBasePtrs[1] = 0; + + _compTypeIDs[2] = ComponentTypeID.value; + _offsets[2] = 0; + _compBasePtrs[2] = 0; + + _compTypeIDs[3] = ComponentTypeID.value; + _offsets[3] = 0; + _compBasePtrs[3] = 0; + + _compTypeIDs[4] = ComponentTypeID.value; + _offsets[4] = 0; + _compBasePtrs[4] = 0; + + _matchingArchetypes = matchingArchetypes; + _mask = mask; + _world = world; + + Reset(); + } + + public QueryItem Current => new( + ref *(T0*)(_compBasePtrs[0] + _currentEntityIndex * sizeof(T0)), + ref *(T1*)(_compBasePtrs[1] + _currentEntityIndex * sizeof(T1)), + ref *(T2*)(_compBasePtrs[2] + _currentEntityIndex * sizeof(T2)), + ref *(T3*)(_compBasePtrs[3] + _currentEntityIndex * sizeof(T3)), + ref *(T4*)(_compBasePtrs[4] + _currentEntityIndex * sizeof(T4)) + ); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void SetChunk(int chunkIndex) + { + _currentChunk = ref _currentArchetype.GetChunkReference(chunkIndex); + _chunkBasePtr = _currentChunk.GetUnsafePtr(); + _currentChunkEntityCount = _currentChunk.Count; + + for (var index = 0; index < 5; index++) + { + var layout = _currentArchetype.GetLayout(_compTypeIDs[index]) + .GetValueOrThrow(ResultStatus.Success); + _offsets[index] = layout.offset; + _compBasePtrs[index] = (long)(_chunkBasePtr + _offsets[index]); + } + } + + public bool MoveNext() + { + while (true) + { + _currentEntityIndex++; + if (_currentEntityIndex < _currentChunk.Count) + { + var pChunkData = _currentChunk.GetUnsafePtr(); + if (IsEntityValid(pChunkData, _currentEntityIndex, in _currentArchetype, in _mask)) + { + return true; + } + + continue; + } + + _currentChunkIndex++; + if (!Unsafe.IsNullRef(ref _currentArchetype) && _currentChunkIndex < _currentArchetype.ChunkCount) + { + SetChunk(_currentChunkIndex); + _currentEntityIndex = -1; // Reset for new chunk + + continue; + } + + _currentArchetypeIndex++; + if (_currentArchetypeIndex < _matchingArchetypes.Count) + { + _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); + + _currentChunkIndex = 0; + if (_currentArchetype.ChunkCount > 0) + { + SetChunk(0); + _currentEntityIndex = -1; + continue; + } + + // If archetype has no chunks, loop will try next archetype + } + else + { + return false; // End of all data + } + } + } + + public void Reset() + { + _currentArchetype = ref Unsafe.NullRef(); + _currentChunk = ref Unsafe.NullRef(); + _currentArchetypeIndex = 0; + _currentChunkIndex = 0; + _currentEntityIndex = -1; + + if (_matchingArchetypes.Count > 0) + { + _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[0]); + if (_currentArchetype.ChunkCount > 0) + { + SetChunk(0); + } + } + } + + } + + private readonly ReadOnlyUnsafeCollection> _matchingArchetypes; + private readonly EntityQueryMask _mask; + private readonly World _world; + + internal ComponentIterator(ReadOnlyUnsafeCollection> matchingArchetypes, EntityQueryMask mask, World world) + { + _matchingArchetypes = matchingArchetypes; + _mask = mask; + _world = world; + } + + public Enumerator GetEnumerator() + { + return new Enumerator(_matchingArchetypes, _mask, _world); + } + } + + public readonly ComponentIterator GetComponentIterator() + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + where T4 : unmanaged, IComponent + { + return new ComponentIterator(_matchingArchetypes.AsReadOnly(), _mask, World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success)); + } + + + public readonly ref struct ComponentIterator + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + where T4 : unmanaged, IComponent + where T5 : unmanaged, IComponent + { + public ref struct QueryItem + { + public ref T0 component0; + public ref T1 component1; + public ref T2 component2; + public ref T3 component3; + public ref T4 component4; + public ref T5 component5; + internal QueryItem(ref T0 component0, ref T1 component1, ref T2 component2, ref T3 component3, ref T4 component4, ref T5 component5) + { + this.component0 = ref component0; + this.component1 = ref component1; + this.component2 = ref component2; + this.component3 = ref component3; + this.component4 = ref component4; + this.component5 = ref component5; + } + + public void Deconstruct(out Ref component0, out Ref component1, out Ref component2, out Ref component3, out Ref component4, out Ref component5) + { + component0 = new Ref(ref this.component0); + component1 = new Ref(ref this.component1); + component2 = new Ref(ref this.component2); + component3 = new Ref(ref this.component3); + component4 = new Ref(ref this.component4); + component5 = new Ref(ref this.component5); + } + } + + public ref struct Enumerator + { + private fixed int _compTypeIDs[6]; + private fixed int _offsets[6]; + private fixed long _compBasePtrs[6]; + + private readonly ReadOnlyUnsafeCollection> _matchingArchetypes; + private readonly EntityQueryMask _mask; + private readonly World _world; + + private ref Archetype _currentArchetype; + private ref Chunk _currentChunk; + private byte* _chunkBasePtr; + + private int _currentChunkEntityCount; + private int _currentArchetypeIndex; + private int _currentChunkIndex; + private int _currentEntityIndex; + + internal Enumerator(ReadOnlyUnsafeCollection> matchingArchetypes, EntityQueryMask mask, World world) + { + _compTypeIDs[0] = ComponentTypeID.value; + _offsets[0] = 0; + _compBasePtrs[0] = 0; + + _compTypeIDs[1] = ComponentTypeID.value; + _offsets[1] = 0; + _compBasePtrs[1] = 0; + + _compTypeIDs[2] = ComponentTypeID.value; + _offsets[2] = 0; + _compBasePtrs[2] = 0; + + _compTypeIDs[3] = ComponentTypeID.value; + _offsets[3] = 0; + _compBasePtrs[3] = 0; + + _compTypeIDs[4] = ComponentTypeID.value; + _offsets[4] = 0; + _compBasePtrs[4] = 0; + + _compTypeIDs[5] = ComponentTypeID.value; + _offsets[5] = 0; + _compBasePtrs[5] = 0; + + _matchingArchetypes = matchingArchetypes; + _mask = mask; + _world = world; + + Reset(); + } + + public QueryItem Current => new( + ref *(T0*)(_compBasePtrs[0] + _currentEntityIndex * sizeof(T0)), + ref *(T1*)(_compBasePtrs[1] + _currentEntityIndex * sizeof(T1)), + ref *(T2*)(_compBasePtrs[2] + _currentEntityIndex * sizeof(T2)), + ref *(T3*)(_compBasePtrs[3] + _currentEntityIndex * sizeof(T3)), + ref *(T4*)(_compBasePtrs[4] + _currentEntityIndex * sizeof(T4)), + ref *(T5*)(_compBasePtrs[5] + _currentEntityIndex * sizeof(T5)) + ); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void SetChunk(int chunkIndex) + { + _currentChunk = ref _currentArchetype.GetChunkReference(chunkIndex); + _chunkBasePtr = _currentChunk.GetUnsafePtr(); + _currentChunkEntityCount = _currentChunk.Count; + + for (var index = 0; index < 6; index++) + { + var layout = _currentArchetype.GetLayout(_compTypeIDs[index]) + .GetValueOrThrow(ResultStatus.Success); + _offsets[index] = layout.offset; + _compBasePtrs[index] = (long)(_chunkBasePtr + _offsets[index]); + } + } + + public bool MoveNext() + { + while (true) + { + _currentEntityIndex++; + if (_currentEntityIndex < _currentChunk.Count) + { + var pChunkData = _currentChunk.GetUnsafePtr(); + if (IsEntityValid(pChunkData, _currentEntityIndex, in _currentArchetype, in _mask)) + { + return true; + } + + continue; + } + + _currentChunkIndex++; + if (!Unsafe.IsNullRef(ref _currentArchetype) && _currentChunkIndex < _currentArchetype.ChunkCount) + { + SetChunk(_currentChunkIndex); + _currentEntityIndex = -1; // Reset for new chunk + + continue; + } + + _currentArchetypeIndex++; + if (_currentArchetypeIndex < _matchingArchetypes.Count) + { + _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); + + _currentChunkIndex = 0; + if (_currentArchetype.ChunkCount > 0) + { + SetChunk(0); + _currentEntityIndex = -1; + continue; + } + + // If archetype has no chunks, loop will try next archetype + } + else + { + return false; // End of all data + } + } + } + + public void Reset() + { + _currentArchetype = ref Unsafe.NullRef(); + _currentChunk = ref Unsafe.NullRef(); + _currentArchetypeIndex = 0; + _currentChunkIndex = 0; + _currentEntityIndex = -1; + + if (_matchingArchetypes.Count > 0) + { + _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[0]); + if (_currentArchetype.ChunkCount > 0) + { + SetChunk(0); + } + } + } + + } + + private readonly ReadOnlyUnsafeCollection> _matchingArchetypes; + private readonly EntityQueryMask _mask; + private readonly World _world; + + internal ComponentIterator(ReadOnlyUnsafeCollection> matchingArchetypes, EntityQueryMask mask, World world) + { + _matchingArchetypes = matchingArchetypes; + _mask = mask; + _world = world; + } + + public Enumerator GetEnumerator() + { + return new Enumerator(_matchingArchetypes, _mask, _world); + } + } + + public readonly ComponentIterator GetComponentIterator() + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + where T4 : unmanaged, IComponent + where T5 : unmanaged, IComponent + { + return new ComponentIterator(_matchingArchetypes.AsReadOnly(), _mask, World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success)); + } + + + public readonly ref struct ComponentIterator + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + where T4 : unmanaged, IComponent + where T5 : unmanaged, IComponent + where T6 : unmanaged, IComponent + { + public ref struct QueryItem + { + public ref T0 component0; + public ref T1 component1; + public ref T2 component2; + public ref T3 component3; + public ref T4 component4; + public ref T5 component5; + public ref T6 component6; + internal QueryItem(ref T0 component0, ref T1 component1, ref T2 component2, ref T3 component3, ref T4 component4, ref T5 component5, ref T6 component6) + { + this.component0 = ref component0; + this.component1 = ref component1; + this.component2 = ref component2; + this.component3 = ref component3; + this.component4 = ref component4; + this.component5 = ref component5; + this.component6 = ref component6; + } + + public void Deconstruct(out Ref component0, out Ref component1, out Ref component2, out Ref component3, out Ref component4, out Ref component5, out Ref component6) + { + component0 = new Ref(ref this.component0); + component1 = new Ref(ref this.component1); + component2 = new Ref(ref this.component2); + component3 = new Ref(ref this.component3); + component4 = new Ref(ref this.component4); + component5 = new Ref(ref this.component5); + component6 = new Ref(ref this.component6); + } + } + + public ref struct Enumerator + { + private fixed int _compTypeIDs[7]; + private fixed int _offsets[7]; + private fixed long _compBasePtrs[7]; + + private readonly ReadOnlyUnsafeCollection> _matchingArchetypes; + private readonly EntityQueryMask _mask; + private readonly World _world; + + private ref Archetype _currentArchetype; + private ref Chunk _currentChunk; + private byte* _chunkBasePtr; + + private int _currentChunkEntityCount; + private int _currentArchetypeIndex; + private int _currentChunkIndex; + private int _currentEntityIndex; + + internal Enumerator(ReadOnlyUnsafeCollection> matchingArchetypes, EntityQueryMask mask, World world) + { + _compTypeIDs[0] = ComponentTypeID.value; + _offsets[0] = 0; + _compBasePtrs[0] = 0; + + _compTypeIDs[1] = ComponentTypeID.value; + _offsets[1] = 0; + _compBasePtrs[1] = 0; + + _compTypeIDs[2] = ComponentTypeID.value; + _offsets[2] = 0; + _compBasePtrs[2] = 0; + + _compTypeIDs[3] = ComponentTypeID.value; + _offsets[3] = 0; + _compBasePtrs[3] = 0; + + _compTypeIDs[4] = ComponentTypeID.value; + _offsets[4] = 0; + _compBasePtrs[4] = 0; + + _compTypeIDs[5] = ComponentTypeID.value; + _offsets[5] = 0; + _compBasePtrs[5] = 0; + + _compTypeIDs[6] = ComponentTypeID.value; + _offsets[6] = 0; + _compBasePtrs[6] = 0; + + _matchingArchetypes = matchingArchetypes; + _mask = mask; + _world = world; + + Reset(); + } + + public QueryItem Current => new( + ref *(T0*)(_compBasePtrs[0] + _currentEntityIndex * sizeof(T0)), + ref *(T1*)(_compBasePtrs[1] + _currentEntityIndex * sizeof(T1)), + ref *(T2*)(_compBasePtrs[2] + _currentEntityIndex * sizeof(T2)), + ref *(T3*)(_compBasePtrs[3] + _currentEntityIndex * sizeof(T3)), + ref *(T4*)(_compBasePtrs[4] + _currentEntityIndex * sizeof(T4)), + ref *(T5*)(_compBasePtrs[5] + _currentEntityIndex * sizeof(T5)), + ref *(T6*)(_compBasePtrs[6] + _currentEntityIndex * sizeof(T6)) + ); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void SetChunk(int chunkIndex) + { + _currentChunk = ref _currentArchetype.GetChunkReference(chunkIndex); + _chunkBasePtr = _currentChunk.GetUnsafePtr(); + _currentChunkEntityCount = _currentChunk.Count; + + for (var index = 0; index < 7; index++) + { + var layout = _currentArchetype.GetLayout(_compTypeIDs[index]) + .GetValueOrThrow(ResultStatus.Success); + _offsets[index] = layout.offset; + _compBasePtrs[index] = (long)(_chunkBasePtr + _offsets[index]); + } + } + + public bool MoveNext() + { + while (true) + { + _currentEntityIndex++; + if (_currentEntityIndex < _currentChunk.Count) + { + var pChunkData = _currentChunk.GetUnsafePtr(); + if (IsEntityValid(pChunkData, _currentEntityIndex, in _currentArchetype, in _mask)) + { + return true; + } + + continue; + } + + _currentChunkIndex++; + if (!Unsafe.IsNullRef(ref _currentArchetype) && _currentChunkIndex < _currentArchetype.ChunkCount) + { + SetChunk(_currentChunkIndex); + _currentEntityIndex = -1; // Reset for new chunk + + continue; + } + + _currentArchetypeIndex++; + if (_currentArchetypeIndex < _matchingArchetypes.Count) + { + _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); + + _currentChunkIndex = 0; + if (_currentArchetype.ChunkCount > 0) + { + SetChunk(0); + _currentEntityIndex = -1; + continue; + } + + // If archetype has no chunks, loop will try next archetype + } + else + { + return false; // End of all data + } + } + } + + public void Reset() + { + _currentArchetype = ref Unsafe.NullRef(); + _currentChunk = ref Unsafe.NullRef(); + _currentArchetypeIndex = 0; + _currentChunkIndex = 0; + _currentEntityIndex = -1; + + if (_matchingArchetypes.Count > 0) + { + _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[0]); + if (_currentArchetype.ChunkCount > 0) + { + SetChunk(0); + } + } + } + + } + + private readonly ReadOnlyUnsafeCollection> _matchingArchetypes; + private readonly EntityQueryMask _mask; + private readonly World _world; + + internal ComponentIterator(ReadOnlyUnsafeCollection> matchingArchetypes, EntityQueryMask mask, World world) + { + _matchingArchetypes = matchingArchetypes; + _mask = mask; + _world = world; + } + + public Enumerator GetEnumerator() + { + return new Enumerator(_matchingArchetypes, _mask, _world); + } + } + + public readonly ComponentIterator GetComponentIterator() + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + where T4 : unmanaged, IComponent + where T5 : unmanaged, IComponent + where T6 : unmanaged, IComponent + { + return new ComponentIterator(_matchingArchetypes.AsReadOnly(), _mask, World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success)); + } + + + public readonly ref struct ComponentIterator + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + where T4 : unmanaged, IComponent + where T5 : unmanaged, IComponent + where T6 : unmanaged, IComponent + where T7 : unmanaged, IComponent + { + public ref struct QueryItem + { + public ref T0 component0; + public ref T1 component1; + public ref T2 component2; + public ref T3 component3; + public ref T4 component4; + public ref T5 component5; + public ref T6 component6; + public ref T7 component7; + internal QueryItem(ref T0 component0, ref T1 component1, ref T2 component2, ref T3 component3, ref T4 component4, ref T5 component5, ref T6 component6, ref T7 component7) + { + this.component0 = ref component0; + this.component1 = ref component1; + this.component2 = ref component2; + this.component3 = ref component3; + this.component4 = ref component4; + this.component5 = ref component5; + this.component6 = ref component6; + this.component7 = ref component7; + } + + public void Deconstruct(out Ref component0, out Ref component1, out Ref component2, out Ref component3, out Ref component4, out Ref component5, out Ref component6, out Ref component7) + { + component0 = new Ref(ref this.component0); + component1 = new Ref(ref this.component1); + component2 = new Ref(ref this.component2); + component3 = new Ref(ref this.component3); + component4 = new Ref(ref this.component4); + component5 = new Ref(ref this.component5); + component6 = new Ref(ref this.component6); + component7 = new Ref(ref this.component7); + } + } + + public ref struct Enumerator + { + private fixed int _compTypeIDs[8]; + private fixed int _offsets[8]; + private fixed long _compBasePtrs[8]; + + private readonly ReadOnlyUnsafeCollection> _matchingArchetypes; + private readonly EntityQueryMask _mask; + private readonly World _world; + + private ref Archetype _currentArchetype; + private ref Chunk _currentChunk; + private byte* _chunkBasePtr; + + private int _currentChunkEntityCount; + private int _currentArchetypeIndex; + private int _currentChunkIndex; + private int _currentEntityIndex; + + internal Enumerator(ReadOnlyUnsafeCollection> matchingArchetypes, EntityQueryMask mask, World world) + { + _compTypeIDs[0] = ComponentTypeID.value; + _offsets[0] = 0; + _compBasePtrs[0] = 0; + + _compTypeIDs[1] = ComponentTypeID.value; + _offsets[1] = 0; + _compBasePtrs[1] = 0; + + _compTypeIDs[2] = ComponentTypeID.value; + _offsets[2] = 0; + _compBasePtrs[2] = 0; + + _compTypeIDs[3] = ComponentTypeID.value; + _offsets[3] = 0; + _compBasePtrs[3] = 0; + + _compTypeIDs[4] = ComponentTypeID.value; + _offsets[4] = 0; + _compBasePtrs[4] = 0; + + _compTypeIDs[5] = ComponentTypeID.value; + _offsets[5] = 0; + _compBasePtrs[5] = 0; + + _compTypeIDs[6] = ComponentTypeID.value; + _offsets[6] = 0; + _compBasePtrs[6] = 0; + + _compTypeIDs[7] = ComponentTypeID.value; + _offsets[7] = 0; + _compBasePtrs[7] = 0; + + _matchingArchetypes = matchingArchetypes; + _mask = mask; + _world = world; + + Reset(); + } + + public QueryItem Current => new( + ref *(T0*)(_compBasePtrs[0] + _currentEntityIndex * sizeof(T0)), + ref *(T1*)(_compBasePtrs[1] + _currentEntityIndex * sizeof(T1)), + ref *(T2*)(_compBasePtrs[2] + _currentEntityIndex * sizeof(T2)), + ref *(T3*)(_compBasePtrs[3] + _currentEntityIndex * sizeof(T3)), + ref *(T4*)(_compBasePtrs[4] + _currentEntityIndex * sizeof(T4)), + ref *(T5*)(_compBasePtrs[5] + _currentEntityIndex * sizeof(T5)), + ref *(T6*)(_compBasePtrs[6] + _currentEntityIndex * sizeof(T6)), + ref *(T7*)(_compBasePtrs[7] + _currentEntityIndex * sizeof(T7)) + ); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void SetChunk(int chunkIndex) + { + _currentChunk = ref _currentArchetype.GetChunkReference(chunkIndex); + _chunkBasePtr = _currentChunk.GetUnsafePtr(); + _currentChunkEntityCount = _currentChunk.Count; + + for (var index = 0; index < 8; index++) + { + var layout = _currentArchetype.GetLayout(_compTypeIDs[index]) + .GetValueOrThrow(ResultStatus.Success); + _offsets[index] = layout.offset; + _compBasePtrs[index] = (long)(_chunkBasePtr + _offsets[index]); + } + } + + public bool MoveNext() + { + while (true) + { + _currentEntityIndex++; + if (_currentEntityIndex < _currentChunk.Count) + { + var pChunkData = _currentChunk.GetUnsafePtr(); + if (IsEntityValid(pChunkData, _currentEntityIndex, in _currentArchetype, in _mask)) + { + return true; + } + + continue; + } + + _currentChunkIndex++; + if (!Unsafe.IsNullRef(ref _currentArchetype) && _currentChunkIndex < _currentArchetype.ChunkCount) + { + SetChunk(_currentChunkIndex); + _currentEntityIndex = -1; // Reset for new chunk + + continue; + } + + _currentArchetypeIndex++; + if (_currentArchetypeIndex < _matchingArchetypes.Count) + { + _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); + + _currentChunkIndex = 0; + if (_currentArchetype.ChunkCount > 0) + { + SetChunk(0); + _currentEntityIndex = -1; + continue; + } + + // If archetype has no chunks, loop will try next archetype + } + else + { + return false; // End of all data + } + } + } + + public void Reset() + { + _currentArchetype = ref Unsafe.NullRef(); + _currentChunk = ref Unsafe.NullRef(); + _currentArchetypeIndex = 0; + _currentChunkIndex = 0; + _currentEntityIndex = -1; + + if (_matchingArchetypes.Count > 0) + { + _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[0]); + if (_currentArchetype.ChunkCount > 0) + { + SetChunk(0); + } + } + } + + } + + private readonly ReadOnlyUnsafeCollection> _matchingArchetypes; + private readonly EntityQueryMask _mask; + private readonly World _world; + + internal ComponentIterator(ReadOnlyUnsafeCollection> matchingArchetypes, EntityQueryMask mask, World world) + { + _matchingArchetypes = matchingArchetypes; + _mask = mask; + _world = world; + } + + public Enumerator GetEnumerator() + { + return new Enumerator(_matchingArchetypes, _mask, _world); + } + } + + public readonly ComponentIterator GetComponentIterator() + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + where T4 : unmanaged, IComponent + where T5 : unmanaged, IComponent + where T6 : unmanaged, IComponent + where T7 : unmanaged, IComponent + { + return new ComponentIterator(_matchingArchetypes.AsReadOnly(), _mask, World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success)); + } + + +} diff --git a/Ghost.Entities/Templates/EntityQuery.ComponentIterator.tt b/Ghost.Entities/Templates/EntityQuery.ComponentIterator.tt new file mode 100644 index 0000000..9d1a526 --- /dev/null +++ b/Ghost.Entities/Templates/EntityQuery.ComponentIterator.tt @@ -0,0 +1,198 @@ +<#@ template language="C#" #> +<#@ output extension="gen.cs" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ include file="Helpers.ttinclude" #> +using Ghost.Core; +using Misaki.HighPerformance.LowLevel; +using Misaki.HighPerformance.LowLevel.Collections; +using System.Runtime.CompilerServices; + +namespace Ghost.Entities; + +public unsafe partial struct EntityQuery +{ +<# for (var i = 1; i <= Amount; i++) +{ + var generics = AppendParameters(i, "T{0}"); + var compGenerics = AppendParameters(i, "ref T{0} component{0}"); + var deconstrictOutPrams = AppendParameters(i, "out Ref component{0}"); + var restrictions = AppendGenericRestrictionsMultiline(i, "unmanaged, IComponent", 2); +#> + public readonly ref struct ComponentIterator<<#= generics#>> +<#= restrictions #> + { +<# if (i > 1) { #> + public ref struct QueryItem + { +<# for (var j = 0; j < i; j++) { #> + public ref T<#= j #> component<#= j #>; +<# } #> + internal QueryItem(<#= compGenerics #>) + { +<# for (var j = 0; j < i; j++) { #> + this.component<#= j #> = ref component<#= j #>; +<# } #> + } + + public void Deconstruct(<#= deconstrictOutPrams #>) + { +<# for (var j = 0; j < i; j++) { #> + component<#= j #> = new Ref>(ref this.component<#= j #>); +<# } #> + } + } + +<# } #> + public ref struct Enumerator + { + private fixed int _compTypeIDs[<#= i #>]; + private fixed int _offsets[<#= i #>]; + private fixed long _compBasePtrs[<#= i #>]; + + private readonly ReadOnlyUnsafeCollection> _matchingArchetypes; + private readonly EntityQueryMask _mask; + private readonly World _world; + + private ref Archetype _currentArchetype; + private ref Chunk _currentChunk; + private byte* _chunkBasePtr; + + private int _currentChunkEntityCount; + private int _currentArchetypeIndex; + private int _currentChunkIndex; + private int _currentEntityIndex; + + internal Enumerator(ReadOnlyUnsafeCollection> matchingArchetypes, EntityQueryMask mask, World world) + { +<# for (var j = 0; j < i; j++) { #> + _compTypeIDs[<#= j #>] = ComponentTypeID>.value; + _offsets[<#= j #>] = 0; + _compBasePtrs[<#= j #>] = 0; + +<# } #> + _matchingArchetypes = matchingArchetypes; + _mask = mask; + _world = world; + + Reset(); + } + +<# if (i > 1) { #> + public QueryItem Current => new( +<# for (var j = 0; j < i; j++) { #> + ref *(T<#= j #>*)(_compBasePtrs[<#= j #>] + _currentEntityIndex * sizeof(T<#= j #>))<#= j < i - 1 ? "," : "" #> +<# } #> + ); +<# } else { #> + public ref T0 Current => ref *(T0*)(_compBasePtrs[0] + _currentEntityIndex * sizeof(T0)); +<# } #> + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void SetChunk(int chunkIndex) + { + _currentChunk = ref _currentArchetype.GetChunkReference(chunkIndex); + _chunkBasePtr = _currentChunk.GetUnsafePtr(); + _currentChunkEntityCount = _currentChunk.Count; + + for (var index = 0; index < <#= i #>; index++) + { + var layout = _currentArchetype.GetLayout(_compTypeIDs[index]) + .GetValueOrThrow(ResultStatus.Success); + _offsets[index] = layout.offset; + _compBasePtrs[index] = (long)(_chunkBasePtr + _offsets[index]); + } + } + + public bool MoveNext() + { + while (true) + { + _currentEntityIndex++; + if (_currentEntityIndex < _currentChunk.Count) + { + var pChunkData = _currentChunk.GetUnsafePtr(); + if (IsEntityValid(pChunkData, _currentEntityIndex, in _currentArchetype, in _mask)) + { + return true; + } + + continue; + } + + _currentChunkIndex++; + if (!Unsafe.IsNullRef(ref _currentArchetype) && _currentChunkIndex < _currentArchetype.ChunkCount) + { + SetChunk(_currentChunkIndex); + _currentEntityIndex = -1; // Reset for new chunk + + continue; + } + + _currentArchetypeIndex++; + if (_currentArchetypeIndex < _matchingArchetypes.Count) + { + _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); + + _currentChunkIndex = 0; + if (_currentArchetype.ChunkCount > 0) + { + SetChunk(0); + _currentEntityIndex = -1; + continue; + } + + // If archetype has no chunks, loop will try next archetype + } + else + { + return false; // End of all data + } + } + } + + public void Reset() + { + _currentArchetype = ref Unsafe.NullRef(); + _currentChunk = ref Unsafe.NullRef(); + _currentArchetypeIndex = 0; + _currentChunkIndex = 0; + _currentEntityIndex = -1; + + if (_matchingArchetypes.Count > 0) + { + _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[0]); + if (_currentArchetype.ChunkCount > 0) + { + SetChunk(0); + } + } + } + } + + private readonly ReadOnlyUnsafeCollection> _matchingArchetypes; + private readonly EntityQueryMask _mask; + private readonly World _world; + + internal ComponentIterator(ReadOnlyUnsafeCollection> matchingArchetypes, EntityQueryMask mask, World world) + { + _matchingArchetypes = matchingArchetypes; + _mask = mask; + _world = world; + } + + public Enumerator GetEnumerator() + { + return new Enumerator(_matchingArchetypes, _mask, _world); + } + } + + public readonly ComponentIterator<<#= generics#>> GetComponentIterator<<#= generics#>>() +<#= restrictions #> + { + return new ComponentIterator<<#= generics#>>(_matchingArchetypes.AsReadOnly(), _mask, World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success)); + } + +<# } #> +} diff --git a/Ghost.Entities/Templates/EntityQuery.JobEntityParallel.gen.cs b/Ghost.Entities/Templates/EntityQuery.JobEntityParallel.gen.cs index a1ea9a8..a0af3e5 100644 --- a/Ghost.Entities/Templates/EntityQuery.JobEntityParallel.gen.cs +++ b/Ghost.Entities/Templates/EntityQuery.JobEntityParallel.gen.cs @@ -1,4 +1,3 @@ - using Ghost.Core; using Misaki.HighPerformance.Jobs; using Misaki.HighPerformance.LowLevel.Buffer; @@ -24,7 +23,7 @@ internal unsafe struct JobEntityBatch : IJobParallelFor public UnsafeList offsets0; public UnsafeList bitsOffsets0; - + public void Execute(int loopIndex, int threadIndex) { // 1. Get the specific pChunk for this thread @@ -33,7 +32,7 @@ internal unsafe struct JobEntityBatch : IJobParallelFor var off0 = offsets0[loopIndex]; var enableOff0 = bitsOffsets0[loopIndex]; - + var pEntity = (Entity*)(pChunk + entityOffset[loopIndex]); var ptr0 = (T0*)(pChunk + off0); @@ -69,10 +68,10 @@ internal unsafe struct JobEntityBatch : IJobParallelFor public UnsafeList offsets0; public UnsafeList bitsOffsets0; - + public UnsafeList offsets1; public UnsafeList bitsOffsets1; - + public void Execute(int loopIndex, int threadIndex) { // 1. Get the specific pChunk for this thread @@ -81,10 +80,10 @@ internal unsafe struct JobEntityBatch : IJobParallelFor var off0 = offsets0[loopIndex]; var enableOff0 = bitsOffsets0[loopIndex]; - + var off1 = offsets1[loopIndex]; var enableOff1 = bitsOffsets1[loopIndex]; - + var pEntity = (Entity*)(pChunk + entityOffset[loopIndex]); var ptr0 = (T0*)(pChunk + off0); var ptr1 = (T1*)(pChunk + off1); @@ -128,13 +127,13 @@ internal unsafe struct JobEntityBatch : IJobParallelFor public UnsafeList offsets0; public UnsafeList bitsOffsets0; - + public UnsafeList offsets1; public UnsafeList bitsOffsets1; - + public UnsafeList offsets2; public UnsafeList bitsOffsets2; - + public void Execute(int loopIndex, int threadIndex) { // 1. Get the specific pChunk for this thread @@ -143,13 +142,13 @@ internal unsafe struct JobEntityBatch : IJobParallelFor var off0 = offsets0[loopIndex]; var enableOff0 = bitsOffsets0[loopIndex]; - + var off1 = offsets1[loopIndex]; var enableOff1 = bitsOffsets1[loopIndex]; - + var off2 = offsets2[loopIndex]; var enableOff2 = bitsOffsets2[loopIndex]; - + var pEntity = (Entity*)(pChunk + entityOffset[loopIndex]); var ptr0 = (T0*)(pChunk + off0); var ptr1 = (T1*)(pChunk + off1); @@ -201,16 +200,16 @@ internal unsafe struct JobEntityBatch : IJobParallelFor public UnsafeList offsets0; public UnsafeList bitsOffsets0; - + public UnsafeList offsets1; public UnsafeList bitsOffsets1; - + public UnsafeList offsets2; public UnsafeList bitsOffsets2; - + public UnsafeList offsets3; public UnsafeList bitsOffsets3; - + public void Execute(int loopIndex, int threadIndex) { // 1. Get the specific pChunk for this thread @@ -219,16 +218,16 @@ internal unsafe struct JobEntityBatch : IJobParallelFor var off0 = offsets0[loopIndex]; var enableOff0 = bitsOffsets0[loopIndex]; - + var off1 = offsets1[loopIndex]; var enableOff1 = bitsOffsets1[loopIndex]; - + var off2 = offsets2[loopIndex]; var enableOff2 = bitsOffsets2[loopIndex]; - + var off3 = offsets3[loopIndex]; var enableOff3 = bitsOffsets3[loopIndex]; - + var pEntity = (Entity*)(pChunk + entityOffset[loopIndex]); var ptr0 = (T0*)(pChunk + off0); var ptr1 = (T1*)(pChunk + off1); @@ -288,19 +287,19 @@ internal unsafe struct JobEntityBatch : IJobParallelFo public UnsafeList offsets0; public UnsafeList bitsOffsets0; - + public UnsafeList offsets1; public UnsafeList bitsOffsets1; - + public UnsafeList offsets2; public UnsafeList bitsOffsets2; - + public UnsafeList offsets3; public UnsafeList bitsOffsets3; - + public UnsafeList offsets4; public UnsafeList bitsOffsets4; - + public void Execute(int loopIndex, int threadIndex) { // 1. Get the specific pChunk for this thread @@ -309,19 +308,19 @@ internal unsafe struct JobEntityBatch : IJobParallelFo var off0 = offsets0[loopIndex]; var enableOff0 = bitsOffsets0[loopIndex]; - + var off1 = offsets1[loopIndex]; var enableOff1 = bitsOffsets1[loopIndex]; - + var off2 = offsets2[loopIndex]; var enableOff2 = bitsOffsets2[loopIndex]; - + var off3 = offsets3[loopIndex]; var enableOff3 = bitsOffsets3[loopIndex]; - + var off4 = offsets4[loopIndex]; var enableOff4 = bitsOffsets4[loopIndex]; - + var pEntity = (Entity*)(pChunk + entityOffset[loopIndex]); var ptr0 = (T0*)(pChunk + off0); var ptr1 = (T1*)(pChunk + off1); @@ -389,22 +388,22 @@ internal unsafe struct JobEntityBatch : IJobParall public UnsafeList offsets0; public UnsafeList bitsOffsets0; - + public UnsafeList offsets1; public UnsafeList bitsOffsets1; - + public UnsafeList offsets2; public UnsafeList bitsOffsets2; - + public UnsafeList offsets3; public UnsafeList bitsOffsets3; - + public UnsafeList offsets4; public UnsafeList bitsOffsets4; - + public UnsafeList offsets5; public UnsafeList bitsOffsets5; - + public void Execute(int loopIndex, int threadIndex) { // 1. Get the specific pChunk for this thread @@ -413,22 +412,22 @@ internal unsafe struct JobEntityBatch : IJobParall var off0 = offsets0[loopIndex]; var enableOff0 = bitsOffsets0[loopIndex]; - + var off1 = offsets1[loopIndex]; var enableOff1 = bitsOffsets1[loopIndex]; - + var off2 = offsets2[loopIndex]; var enableOff2 = bitsOffsets2[loopIndex]; - + var off3 = offsets3[loopIndex]; var enableOff3 = bitsOffsets3[loopIndex]; - + var off4 = offsets4[loopIndex]; var enableOff4 = bitsOffsets4[loopIndex]; - + var off5 = offsets5[loopIndex]; var enableOff5 = bitsOffsets5[loopIndex]; - + var pEntity = (Entity*)(pChunk + entityOffset[loopIndex]); var ptr0 = (T0*)(pChunk + off0); var ptr1 = (T1*)(pChunk + off1); @@ -504,25 +503,25 @@ internal unsafe struct JobEntityBatch : IJobPa public UnsafeList offsets0; public UnsafeList bitsOffsets0; - + public UnsafeList offsets1; public UnsafeList bitsOffsets1; - + public UnsafeList offsets2; public UnsafeList bitsOffsets2; - + public UnsafeList offsets3; public UnsafeList bitsOffsets3; - + public UnsafeList offsets4; public UnsafeList bitsOffsets4; - + public UnsafeList offsets5; public UnsafeList bitsOffsets5; - + public UnsafeList offsets6; public UnsafeList bitsOffsets6; - + public void Execute(int loopIndex, int threadIndex) { // 1. Get the specific pChunk for this thread @@ -531,25 +530,25 @@ internal unsafe struct JobEntityBatch : IJobPa var off0 = offsets0[loopIndex]; var enableOff0 = bitsOffsets0[loopIndex]; - + var off1 = offsets1[loopIndex]; var enableOff1 = bitsOffsets1[loopIndex]; - + var off2 = offsets2[loopIndex]; var enableOff2 = bitsOffsets2[loopIndex]; - + var off3 = offsets3[loopIndex]; var enableOff3 = bitsOffsets3[loopIndex]; - + var off4 = offsets4[loopIndex]; var enableOff4 = bitsOffsets4[loopIndex]; - + var off5 = offsets5[loopIndex]; var enableOff5 = bitsOffsets5[loopIndex]; - + var off6 = offsets6[loopIndex]; var enableOff6 = bitsOffsets6[loopIndex]; - + var pEntity = (Entity*)(pChunk + entityOffset[loopIndex]); var ptr0 = (T0*)(pChunk + off0); var ptr1 = (T1*)(pChunk + off1); @@ -633,28 +632,28 @@ internal unsafe struct JobEntityBatch : IJ public UnsafeList offsets0; public UnsafeList bitsOffsets0; - + public UnsafeList offsets1; public UnsafeList bitsOffsets1; - + public UnsafeList offsets2; public UnsafeList bitsOffsets2; - + public UnsafeList offsets3; public UnsafeList bitsOffsets3; - + public UnsafeList offsets4; public UnsafeList bitsOffsets4; - + public UnsafeList offsets5; public UnsafeList bitsOffsets5; - + public UnsafeList offsets6; public UnsafeList bitsOffsets6; - + public UnsafeList offsets7; public UnsafeList bitsOffsets7; - + public void Execute(int loopIndex, int threadIndex) { // 1. Get the specific pChunk for this thread @@ -663,28 +662,28 @@ internal unsafe struct JobEntityBatch : IJ var off0 = offsets0[loopIndex]; var enableOff0 = bitsOffsets0[loopIndex]; - + var off1 = offsets1[loopIndex]; var enableOff1 = bitsOffsets1[loopIndex]; - + var off2 = offsets2[loopIndex]; var enableOff2 = bitsOffsets2[loopIndex]; - + var off3 = offsets3[loopIndex]; var enableOff3 = bitsOffsets3[loopIndex]; - + var off4 = offsets4[loopIndex]; var enableOff4 = bitsOffsets4[loopIndex]; - + var off5 = offsets5[loopIndex]; var enableOff5 = bitsOffsets5[loopIndex]; - + var off6 = offsets6[loopIndex]; var enableOff6 = bitsOffsets6[loopIndex]; - + var off7 = offsets7[loopIndex]; var enableOff7 = bitsOffsets7[loopIndex]; - + var pEntity = (Entity*)(pChunk + entityOffset[loopIndex]); var ptr0 = (T0*)(pChunk + off0); var ptr1 = (T1*)(pChunk + off1); @@ -765,10 +764,12 @@ public unsafe partial struct EntityQuery } } - public JobHandle ScheduleEntityParallel(JobScheduler scheduler, TJob jobData, Allocator allocator, int batchSize, JobHandle dependency) + public JobHandle ScheduleEntityParallel(TJob jobData, Allocator allocator, int batchSize, JobHandle dependency) where TJob : unmanaged, IJobEntityParallel where T0 : unmanaged, IComponent { + var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success); + // 1. Flatten the World var chunkList = new UnsafeList(128, allocator); var chunkEntityCounts = new UnsafeList(128, allocator); @@ -780,9 +781,7 @@ public unsafe partial struct EntityQuery // Iterate the Query's matching archetypes foreach (var archID in _matchingArchetypes) { - ref var arch = ref World.GetWorld(_worldID) - .GetValueOrThrow(ResultStatus.Success) - .GetArchetypeReference(archID); + ref var arch = ref world.GetArchetypeReference(archID); if (arch.ChunkCount == 0) { @@ -821,7 +820,7 @@ public unsafe partial struct EntityQuery }; - var jobHandle = scheduler.ScheduleParallel(ref runner, chunkList.Count, batchSize, dependency); + var jobHandle = world.JobScheduler.ScheduleParallel(ref runner, chunkList.Count, batchSize, dependency); // 3. Dispose the temp lists var disposeJob = new DisposeJobEntity1 @@ -835,7 +834,7 @@ public unsafe partial struct EntityQuery }; - scheduler.Schedule(ref disposeJob, jobHandle); + world.JobScheduler.Schedule(ref disposeJob, jobHandle); return jobHandle; } @@ -867,11 +866,13 @@ public unsafe partial struct EntityQuery } } - public JobHandle ScheduleEntityParallel(JobScheduler scheduler, TJob jobData, Allocator allocator, int batchSize, JobHandle dependency) + public JobHandle ScheduleEntityParallel(TJob jobData, Allocator allocator, int batchSize, JobHandle dependency) where TJob : unmanaged, IJobEntityParallel where T0 : unmanaged, IComponent where T1 : unmanaged, IComponent { + var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success); + // 1. Flatten the World var chunkList = new UnsafeList(128, allocator); var chunkEntityCounts = new UnsafeList(128, allocator); @@ -886,9 +887,7 @@ public unsafe partial struct EntityQuery // Iterate the Query's matching archetypes foreach (var archID in _matchingArchetypes) { - ref var arch = ref World.GetWorld(_worldID) - .GetValueOrThrow(ResultStatus.Success) - .GetArchetypeReference(archID); + ref var arch = ref world.GetArchetypeReference(archID); if (arch.ChunkCount == 0) { @@ -935,7 +934,7 @@ public unsafe partial struct EntityQuery }; - var jobHandle = scheduler.ScheduleParallel(ref runner, chunkList.Count, batchSize, dependency); + var jobHandle = world.JobScheduler.ScheduleParallel(ref runner, chunkList.Count, batchSize, dependency); // 3. Dispose the temp lists var disposeJob = new DisposeJobEntity2 @@ -952,7 +951,7 @@ public unsafe partial struct EntityQuery }; - scheduler.Schedule(ref disposeJob, jobHandle); + world.JobScheduler.Schedule(ref disposeJob, jobHandle); return jobHandle; } @@ -990,12 +989,14 @@ public unsafe partial struct EntityQuery } } - public JobHandle ScheduleEntityParallel(JobScheduler scheduler, TJob jobData, Allocator allocator, int batchSize, JobHandle dependency) + public JobHandle ScheduleEntityParallel(TJob jobData, Allocator allocator, int batchSize, JobHandle dependency) where TJob : unmanaged, IJobEntityParallel where T0 : unmanaged, IComponent where T1 : unmanaged, IComponent where T2 : unmanaged, IComponent { + var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success); + // 1. Flatten the World var chunkList = new UnsafeList(128, allocator); var chunkEntityCounts = new UnsafeList(128, allocator); @@ -1013,9 +1014,7 @@ public unsafe partial struct EntityQuery // Iterate the Query's matching archetypes foreach (var archID in _matchingArchetypes) { - ref var arch = ref World.GetWorld(_worldID) - .GetValueOrThrow(ResultStatus.Success) - .GetArchetypeReference(archID); + ref var arch = ref world.GetArchetypeReference(archID); if (arch.ChunkCount == 0) { @@ -1070,7 +1069,7 @@ public unsafe partial struct EntityQuery }; - var jobHandle = scheduler.ScheduleParallel(ref runner, chunkList.Count, batchSize, dependency); + var jobHandle = world.JobScheduler.ScheduleParallel(ref runner, chunkList.Count, batchSize, dependency); // 3. Dispose the temp lists var disposeJob = new DisposeJobEntity3 @@ -1090,7 +1089,7 @@ public unsafe partial struct EntityQuery }; - scheduler.Schedule(ref disposeJob, jobHandle); + world.JobScheduler.Schedule(ref disposeJob, jobHandle); return jobHandle; } @@ -1134,13 +1133,15 @@ public unsafe partial struct EntityQuery } } - public JobHandle ScheduleEntityParallel(JobScheduler scheduler, TJob jobData, Allocator allocator, int batchSize, JobHandle dependency) + public JobHandle ScheduleEntityParallel(TJob jobData, Allocator allocator, int batchSize, JobHandle dependency) where TJob : unmanaged, IJobEntityParallel where T0 : unmanaged, IComponent where T1 : unmanaged, IComponent where T2 : unmanaged, IComponent where T3 : unmanaged, IComponent { + var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success); + // 1. Flatten the World var chunkList = new UnsafeList(128, allocator); var chunkEntityCounts = new UnsafeList(128, allocator); @@ -1161,9 +1162,7 @@ public unsafe partial struct EntityQuery // Iterate the Query's matching archetypes foreach (var archID in _matchingArchetypes) { - ref var arch = ref World.GetWorld(_worldID) - .GetValueOrThrow(ResultStatus.Success) - .GetArchetypeReference(archID); + ref var arch = ref world.GetArchetypeReference(archID); if (arch.ChunkCount == 0) { @@ -1226,7 +1225,7 @@ public unsafe partial struct EntityQuery }; - var jobHandle = scheduler.ScheduleParallel(ref runner, chunkList.Count, batchSize, dependency); + var jobHandle = world.JobScheduler.ScheduleParallel(ref runner, chunkList.Count, batchSize, dependency); // 3. Dispose the temp lists var disposeJob = new DisposeJobEntity4 @@ -1249,7 +1248,7 @@ public unsafe partial struct EntityQuery }; - scheduler.Schedule(ref disposeJob, jobHandle); + world.JobScheduler.Schedule(ref disposeJob, jobHandle); return jobHandle; } @@ -1299,7 +1298,7 @@ public unsafe partial struct EntityQuery } } - public JobHandle ScheduleEntityParallel(JobScheduler scheduler, TJob jobData, Allocator allocator, int batchSize, JobHandle dependency) + public JobHandle ScheduleEntityParallel(TJob jobData, Allocator allocator, int batchSize, JobHandle dependency) where TJob : unmanaged, IJobEntityParallel where T0 : unmanaged, IComponent where T1 : unmanaged, IComponent @@ -1307,6 +1306,8 @@ public unsafe partial struct EntityQuery where T3 : unmanaged, IComponent where T4 : unmanaged, IComponent { + var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success); + // 1. Flatten the World var chunkList = new UnsafeList(128, allocator); var chunkEntityCounts = new UnsafeList(128, allocator); @@ -1330,9 +1331,7 @@ public unsafe partial struct EntityQuery // Iterate the Query's matching archetypes foreach (var archID in _matchingArchetypes) { - ref var arch = ref World.GetWorld(_worldID) - .GetValueOrThrow(ResultStatus.Success) - .GetArchetypeReference(archID); + ref var arch = ref world.GetArchetypeReference(archID); if (arch.ChunkCount == 0) { @@ -1403,7 +1402,7 @@ public unsafe partial struct EntityQuery }; - var jobHandle = scheduler.ScheduleParallel(ref runner, chunkList.Count, batchSize, dependency); + var jobHandle = world.JobScheduler.ScheduleParallel(ref runner, chunkList.Count, batchSize, dependency); // 3. Dispose the temp lists var disposeJob = new DisposeJobEntity5 @@ -1429,7 +1428,7 @@ public unsafe partial struct EntityQuery }; - scheduler.Schedule(ref disposeJob, jobHandle); + world.JobScheduler.Schedule(ref disposeJob, jobHandle); return jobHandle; } @@ -1485,7 +1484,7 @@ public unsafe partial struct EntityQuery } } - public JobHandle ScheduleEntityParallel(JobScheduler scheduler, TJob jobData, Allocator allocator, int batchSize, JobHandle dependency) + public JobHandle ScheduleEntityParallel(TJob jobData, Allocator allocator, int batchSize, JobHandle dependency) where TJob : unmanaged, IJobEntityParallel where T0 : unmanaged, IComponent where T1 : unmanaged, IComponent @@ -1494,6 +1493,8 @@ public unsafe partial struct EntityQuery where T4 : unmanaged, IComponent where T5 : unmanaged, IComponent { + var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success); + // 1. Flatten the World var chunkList = new UnsafeList(128, allocator); var chunkEntityCounts = new UnsafeList(128, allocator); @@ -1520,9 +1521,7 @@ public unsafe partial struct EntityQuery // Iterate the Query's matching archetypes foreach (var archID in _matchingArchetypes) { - ref var arch = ref World.GetWorld(_worldID) - .GetValueOrThrow(ResultStatus.Success) - .GetArchetypeReference(archID); + ref var arch = ref world.GetArchetypeReference(archID); if (arch.ChunkCount == 0) { @@ -1601,7 +1600,7 @@ public unsafe partial struct EntityQuery }; - var jobHandle = scheduler.ScheduleParallel(ref runner, chunkList.Count, batchSize, dependency); + var jobHandle = world.JobScheduler.ScheduleParallel(ref runner, chunkList.Count, batchSize, dependency); // 3. Dispose the temp lists var disposeJob = new DisposeJobEntity6 @@ -1630,7 +1629,7 @@ public unsafe partial struct EntityQuery }; - scheduler.Schedule(ref disposeJob, jobHandle); + world.JobScheduler.Schedule(ref disposeJob, jobHandle); return jobHandle; } @@ -1692,7 +1691,7 @@ public unsafe partial struct EntityQuery } } - public JobHandle ScheduleEntityParallel(JobScheduler scheduler, TJob jobData, Allocator allocator, int batchSize, JobHandle dependency) + public JobHandle ScheduleEntityParallel(TJob jobData, Allocator allocator, int batchSize, JobHandle dependency) where TJob : unmanaged, IJobEntityParallel where T0 : unmanaged, IComponent where T1 : unmanaged, IComponent @@ -1702,6 +1701,8 @@ public unsafe partial struct EntityQuery where T5 : unmanaged, IComponent where T6 : unmanaged, IComponent { + var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success); + // 1. Flatten the World var chunkList = new UnsafeList(128, allocator); var chunkEntityCounts = new UnsafeList(128, allocator); @@ -1731,9 +1732,7 @@ public unsafe partial struct EntityQuery // Iterate the Query's matching archetypes foreach (var archID in _matchingArchetypes) { - ref var arch = ref World.GetWorld(_worldID) - .GetValueOrThrow(ResultStatus.Success) - .GetArchetypeReference(archID); + ref var arch = ref world.GetArchetypeReference(archID); if (arch.ChunkCount == 0) { @@ -1820,7 +1819,7 @@ public unsafe partial struct EntityQuery }; - var jobHandle = scheduler.ScheduleParallel(ref runner, chunkList.Count, batchSize, dependency); + var jobHandle = world.JobScheduler.ScheduleParallel(ref runner, chunkList.Count, batchSize, dependency); // 3. Dispose the temp lists var disposeJob = new DisposeJobEntity7 @@ -1852,7 +1851,7 @@ public unsafe partial struct EntityQuery }; - scheduler.Schedule(ref disposeJob, jobHandle); + world.JobScheduler.Schedule(ref disposeJob, jobHandle); return jobHandle; } @@ -1920,7 +1919,7 @@ public unsafe partial struct EntityQuery } } - public JobHandle ScheduleEntityParallel(JobScheduler scheduler, TJob jobData, Allocator allocator, int batchSize, JobHandle dependency) + public JobHandle ScheduleEntityParallel(TJob jobData, Allocator allocator, int batchSize, JobHandle dependency) where TJob : unmanaged, IJobEntityParallel where T0 : unmanaged, IComponent where T1 : unmanaged, IComponent @@ -1931,6 +1930,8 @@ public unsafe partial struct EntityQuery where T6 : unmanaged, IComponent where T7 : unmanaged, IComponent { + var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success); + // 1. Flatten the World var chunkList = new UnsafeList(128, allocator); var chunkEntityCounts = new UnsafeList(128, allocator); @@ -1963,9 +1964,7 @@ public unsafe partial struct EntityQuery // Iterate the Query's matching archetypes foreach (var archID in _matchingArchetypes) { - ref var arch = ref World.GetWorld(_worldID) - .GetValueOrThrow(ResultStatus.Success) - .GetArchetypeReference(archID); + ref var arch = ref world.GetArchetypeReference(archID); if (arch.ChunkCount == 0) { @@ -2060,7 +2059,7 @@ public unsafe partial struct EntityQuery }; - var jobHandle = scheduler.ScheduleParallel(ref runner, chunkList.Count, batchSize, dependency); + var jobHandle = world.JobScheduler.ScheduleParallel(ref runner, chunkList.Count, batchSize, dependency); // 3. Dispose the temp lists var disposeJob = new DisposeJobEntity8 @@ -2095,9 +2094,9 @@ public unsafe partial struct EntityQuery }; - scheduler.Schedule(ref disposeJob, jobHandle); + world.JobScheduler.Schedule(ref disposeJob, jobHandle); return jobHandle; } -} \ No newline at end of file +} diff --git a/Ghost.Entities/Templates/EntityQuery.JobEntityParallel.tt b/Ghost.Entities/Templates/EntityQuery.JobEntityParallel.tt index 8ebecd1..f5ff328 100644 --- a/Ghost.Entities/Templates/EntityQuery.JobEntityParallel.tt +++ b/Ghost.Entities/Templates/EntityQuery.JobEntityParallel.tt @@ -35,7 +35,7 @@ internal unsafe struct JobEntityBatch> : IJobParallelFor <# for (var j = 0; j < i; j++){ #> public UnsafeList offsets<#= j #>; public UnsafeList bitsOffsets<#= j #>; - + <# } #> public void Execute(int loopIndex, int threadIndex) { @@ -46,7 +46,7 @@ internal unsafe struct JobEntityBatch> : IJobParallelFor <# for (var j = 0; j < i; j++){ #> var off<#= j #> = offsets<#= j #>[loopIndex]; var enableOff<#= j #> = bitsOffsets<#= j #>[loopIndex]; - + <# } #> var pEntity = (Entity*)(pChunk + entityOffset[loopIndex]); <# for (var j = 0; j < i; j++){ #> @@ -100,10 +100,12 @@ public unsafe partial struct EntityQuery } } - public JobHandle ScheduleEntityParallel>(JobScheduler scheduler, TJob jobData, Allocator allocator, int batchSize, JobHandle dependency) + public JobHandle ScheduleEntityParallel>(TJob jobData, Allocator allocator, int batchSize, JobHandle dependency) where TJob : unmanaged, IJobEntityParallel<<#= generics #>> <#= restrictions #> { + var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success); + // 1. Flatten the World var chunkList = new UnsafeList(128, allocator); var chunkEntityCounts = new UnsafeList(128, allocator); @@ -117,9 +119,7 @@ public unsafe partial struct EntityQuery // Iterate the Query's matching archetypes foreach (var archID in _matchingArchetypes) { - ref var arch = ref World.GetWorld(_worldID) - .GetValueOrThrow(ResultStatus.Success) - .GetArchetypeReference(archID); + ref var arch = ref world.GetArchetypeReference(archID); if (arch.ChunkCount == 0) { @@ -164,7 +164,7 @@ public unsafe partial struct EntityQuery <# } #> }; - var jobHandle = scheduler.ScheduleParallel(ref runner, chunkList.Count, batchSize, dependency); + var jobHandle = world.JobScheduler.ScheduleParallel(ref runner, chunkList.Count, batchSize, dependency); // 3. Dispose the temp lists var disposeJob = new DisposeJobEntity<#= i #> @@ -180,10 +180,10 @@ public unsafe partial struct EntityQuery <# } #> }; - scheduler.Schedule(ref disposeJob, jobHandle); + world.JobScheduler.Schedule(ref disposeJob, jobHandle); return jobHandle; } <# } #> -} \ No newline at end of file +} diff --git a/Ghost.Entities/Templates/ForEach.gen.cs b/Ghost.Entities/Templates/ForEach.gen.cs index 6354032..dd1105b 100644 --- a/Ghost.Entities/Templates/ForEach.gen.cs +++ b/Ghost.Entities/Templates/ForEach.gen.cs @@ -1,21 +1,92 @@ - namespace Ghost.Entities; -public delegate void ForEach(ref T0 t0Component); -public delegate void ForEach(ref T0 t0Component,ref T1 t1Component); -public delegate void ForEach(ref T0 t0Component,ref T1 t1Component,ref T2 t2Component); -public delegate void ForEach(ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component); -public delegate void ForEach(ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component,ref T4 t4Component); -public delegate void ForEach(ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component,ref T4 t4Component,ref T5 t5Component); -public delegate void ForEach(ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component,ref T4 t4Component,ref T5 t5Component,ref T6 t6Component); -public delegate void ForEach(ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component,ref T4 t4Component,ref T5 t5Component,ref T6 t6Component,ref T7 t7Component); +public delegate void ForEach(ref T0 component0) + where T0 : unmanaged, IComponent; +public delegate void ForEach(ref T0 component0, ref T1 component1) + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent; +public delegate void ForEach(ref T0 component0, ref T1 component1, ref T2 component2) + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent; +public delegate void ForEach(ref T0 component0, ref T1 component1, ref T2 component2, ref T3 component3) + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent; +public delegate void ForEach(ref T0 component0, ref T1 component1, ref T2 component2, ref T3 component3, ref T4 component4) + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + where T4 : unmanaged, IComponent; +public delegate void ForEach(ref T0 component0, ref T1 component1, ref T2 component2, ref T3 component3, ref T4 component4, ref T5 component5) + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + where T4 : unmanaged, IComponent + where T5 : unmanaged, IComponent; +public delegate void ForEach(ref T0 component0, ref T1 component1, ref T2 component2, ref T3 component3, ref T4 component4, ref T5 component5, ref T6 component6) + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + where T4 : unmanaged, IComponent + where T5 : unmanaged, IComponent + where T6 : unmanaged, IComponent; +public delegate void ForEach(ref T0 component0, ref T1 component1, ref T2 component2, ref T3 component3, ref T4 component4, ref T5 component5, ref T6 component6, ref T7 component7) + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + where T4 : unmanaged, IComponent + where T5 : unmanaged, IComponent + where T6 : unmanaged, IComponent + where T7 : unmanaged, IComponent; -public delegate void ForEachWithEntity(Entity entity, ref T0 t0Component); -public delegate void ForEachWithEntity(Entity entity, ref T0 t0Component,ref T1 t1Component); -public delegate void ForEachWithEntity(Entity entity, ref T0 t0Component,ref T1 t1Component,ref T2 t2Component); -public delegate void ForEachWithEntity(Entity entity, ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component); -public delegate void ForEachWithEntity(Entity entity, ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component,ref T4 t4Component); -public delegate void ForEachWithEntity(Entity entity, ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component,ref T4 t4Component,ref T5 t5Component); -public delegate void ForEachWithEntity(Entity entity, ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component,ref T4 t4Component,ref T5 t5Component,ref T6 t6Component); -public delegate void ForEachWithEntity(Entity entity, ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component,ref T4 t4Component,ref T5 t5Component,ref T6 t6Component,ref T7 t7Component); +public delegate void ForEachWithEntity(Entity entity, ref T0 component0) + where T0 : unmanaged, IComponent; +public delegate void ForEachWithEntity(Entity entity, ref T0 component0, ref T1 component1) + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent; +public delegate void ForEachWithEntity(Entity entity, ref T0 component0, ref T1 component1, ref T2 component2) + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent; +public delegate void ForEachWithEntity(Entity entity, ref T0 component0, ref T1 component1, ref T2 component2, ref T3 component3) + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent; +public delegate void ForEachWithEntity(Entity entity, ref T0 component0, ref T1 component1, ref T2 component2, ref T3 component3, ref T4 component4) + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + where T4 : unmanaged, IComponent; +public delegate void ForEachWithEntity(Entity entity, ref T0 component0, ref T1 component1, ref T2 component2, ref T3 component3, ref T4 component4, ref T5 component5) + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + where T4 : unmanaged, IComponent + where T5 : unmanaged, IComponent; +public delegate void ForEachWithEntity(Entity entity, ref T0 component0, ref T1 component1, ref T2 component2, ref T3 component3, ref T4 component4, ref T5 component5, ref T6 component6) + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + where T4 : unmanaged, IComponent + where T5 : unmanaged, IComponent + where T6 : unmanaged, IComponent; +public delegate void ForEachWithEntity(Entity entity, ref T0 component0, ref T1 component1, ref T2 component2, ref T3 component3, ref T4 component4, ref T5 component5, ref T6 component6, ref T7 component7) + where T0 : unmanaged, IComponent + where T1 : unmanaged, IComponent + where T2 : unmanaged, IComponent + where T3 : unmanaged, IComponent + where T4 : unmanaged, IComponent + where T5 : unmanaged, IComponent + where T6 : unmanaged, IComponent + where T7 : unmanaged, IComponent; diff --git a/Ghost.Entities/Templates/ForEach.tt b/Ghost.Entities/Templates/ForEach.tt index 7513fd2..8165864 100644 --- a/Ghost.Entities/Templates/ForEach.tt +++ b/Ghost.Entities/Templates/ForEach.tt @@ -4,21 +4,24 @@ <#@ import namespace="System.Linq" #> <#@ import namespace="System.Text" #> <#@ include file="Helpers.ttinclude" #> - namespace Ghost.Entities; <# for (var i = 1; i <= Amount; i++) { - var generics = AppendGenerics(i); - var compGenerics = AppendGenericRefParameters(i); + var generics = AppendParameters(i, "T{0}"); + var compGenerics = AppendParameters(i, "ref T{0} component{0}"); + var restrictions = AppendGenericRestrictionsMultiline(i, "unmanaged, IComponent", 1); #> -public delegate void ForEach<<#= generics #>>(<#= compGenerics #>); +public delegate void ForEach<<#= generics #>>(<#= compGenerics #>) +<#= restrictions #>; <# } #> <# for (var i = 1; i <= Amount; i++) { - var generics = AppendGenerics(i); - var compGenerics = AppendGenericRefParameters(i); + var generics = AppendParameters(i, "T{0}"); + var compGenerics = AppendParameters(i, "ref T{0} component{0}"); + var restrictions = AppendGenericRestrictionsMultiline(i, "unmanaged, IComponent", 1); #> -public delegate void ForEachWithEntity<<#= generics #>>(Entity entity, <#= compGenerics #>); -<# } #> \ No newline at end of file +public delegate void ForEachWithEntity<<#= generics #>>(Entity entity, <#= compGenerics #>) +<#= restrictions #>; +<# } #> diff --git a/Ghost.Entities/World.cs b/Ghost.Entities/World.cs index 2f1c19b..303ef76 100644 --- a/Ghost.Entities/World.cs +++ b/Ghost.Entities/World.cs @@ -1,4 +1,5 @@ using Ghost.Core; +using Misaki.HighPerformance.Jobs; using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Collections; using System.Runtime.CompilerServices; @@ -14,18 +15,18 @@ public partial class World public static int WorldCount => s_worlds.Count - s_freeWorldSlots.Count; - public static World Create(int entityCapacity = 16) + public static World Create(JobScheduler jobScheduler, int entityCapacity = 16) { lock (s_worlds) { if (s_freeWorldSlots.TryDequeue(out var index)) { - s_worlds[index.value] = new World(index, entityCapacity); + s_worlds[index.value] = new World(index, entityCapacity, jobScheduler); } else { index = new Identifier(s_worlds.Count); - s_worlds.Add(new World(index, entityCapacity)); + s_worlds.Add(new World(index, entityCapacity, jobScheduler)); } return s_worlds[index.value]!; @@ -67,6 +68,7 @@ public partial class World public partial class World : IIdentifierType, IDisposable, IEquatable { private readonly Identifier _id; + private readonly JobScheduler _jobScheduler; private readonly EntityManager _entityManager; private readonly EntityCommandBuffer _entityCommandBuffer; @@ -82,12 +84,14 @@ public partial class World : IIdentifierType, IDisposable, IEquatable internal int ArchetypeCount => _archetypes.Count; public Identifier ID => _id; + public JobScheduler JobScheduler => _jobScheduler; public EntityManager EntityManager => _entityManager; public EntityCommandBuffer EntityCommandBuffer => _entityCommandBuffer; - private World(Identifier id, int entityCapacity) + private World(Identifier id, int entityCapacity, JobScheduler jobScheduler) { _id = id; + _jobScheduler = jobScheduler; _archetypes = new UnsafeList(16, Allocator.Persistent); _entityQueries = new UnsafeList(16, Allocator.Persistent); diff --git a/Ghost.Graphics/D3D12/D3D12CommandBuffer.cs b/Ghost.Graphics/D3D12/D3D12CommandBuffer.cs index c064d49..3144d4f 100644 --- a/Ghost.Graphics/D3D12/D3D12CommandBuffer.cs +++ b/Ghost.Graphics/D3D12/D3D12CommandBuffer.cs @@ -601,7 +601,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer uploadResource.Get()->Map(0, null, &pMappedData); fixed (T* pData = data) { - MemoryUtility.MemCpy(pData, pMappedData, sizeInBytes); + MemoryUtility.MemCpy(pMappedData, pData, sizeInBytes); } uploadResource.Get()->Unmap(0, null); @@ -690,4 +690,4 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer _disposed = true; GC.SuppressFinalize(this); } -} \ No newline at end of file +} diff --git a/omnisharp.json b/omnisharp.json deleted file mode 100644 index b052e3d..0000000 --- a/omnisharp.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "Logging": - { - "LogLevel": "Error" - } -}