Removed Ghost.ArcEntities project, it's replaced by Ghost.Entities

Added Playback to EntityCommandBuffer
Added JobSchedular to world
Added ISystem and SystemGroup
Updated packages
This commit is contained in:
2025-12-08 20:44:56 +09:00
parent f44208b502
commit 5e276b289d
30 changed files with 2974 additions and 1597 deletions

View File

@@ -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<IComponent> componentID, void** ppv)
public readonly void* GetComponentData(int chunkIndex, int rowIndex, Identifier<IComponent> 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);
}
}

View File

@@ -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]

19
Ghost.Entities/Common.cs Normal file
View File

@@ -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;
}
}

View File

@@ -30,9 +30,9 @@ public static class ComponentTypeID<T>
internal static class ComponentRegister
{
private static int s_nextComponentTypeID = 0;
private static readonly Dictionary<IntPtr, Identifier<IComponent>> s_typeHandleToID = new();
private static readonly List<ComponentInfo> s_registeredComponents = new();
private static readonly Dictionary<IntPtr, Identifier<IComponent>> s_typeHandleToID = new();
private static readonly Dictionary<string, Identifier<IComponent>> s_nameToRuntimeID = new();
public static unsafe Identifier<IComponent> GetOrRegisterComponent<T>()
@@ -71,12 +71,26 @@ internal static class ComponentRegister
}
}
public static Identifier<IComponent> 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<IComponent> typeId)
{
return s_registeredComponents[typeId];
}
public static int GetHashCode(ReadOnlySpan<Identifier<IComponent>> componentTypeIDs)
public static int GetHashCode(params ReadOnlySpan<Identifier<IComponent>> componentTypeIDs)
{
var largestID = 0;
foreach (var id in componentTypeIDs)

View File

@@ -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<byte> data;
public CommandType type;
public Entity entity;
public int componentTypeID;
public CommandType type;
public Identifier<IComponent> componentTypeID;
}
private readonly EntityManager _entityManager;
private UnsafeList<Command> _commands; // TODO: Maybe use UnsafeArray<byte> directly?
private UnsafeList<Command> _commands; // TODO: Maybe use UnsafeArray<byte> directly to save additional memory allocation in Unsafe<byte> data inside Command struct.
private bool _disposed;
public EntityCommandBuffer(EntityManager entityManager)
{
_entityManager = entityManager;
_commands = new UnsafeList<Command>(32, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent);
_commands = new UnsafeList<Command>(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<Identifier<IComponent>> componentTypeIDs)
{
var data = new UnsafeArray<byte>(componentTypeIDs.Length * sizeof(int), Allocator.Temp);
MemoryMarshal.Cast<Identifier<IComponent>, 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<IComponent>.Invalid
};
_commands.Add(command);
}
public void AddComponent<T>(Entity entity, T component)
public void DestroyEntity(Entity entity)
{
_commands.Add(new Command
{
type = CommandType.DestroyEntity,
data = default,
entity = entity,
componentTypeID = Identifier<IComponent>.Invalid
});
}
public void AddComponent<T>(Entity entity, T component = default)
where T : unmanaged, IComponent
{
var data = new UnsafeArray<byte>(sizeof(T), Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent);
MemoryUtility.MemCpy(&component, data.GetUnsafePtr(), (nuint)sizeof(T));
var data = new UnsafeArray<byte>(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<T>.value
};
_commands.Add(command);
});
}
public void RemoveComponent<T>(Entity entity)
where T : unmanaged, IComponent
{
var command = new Command
_commands.Add(new Command
{
type = CommandType.RemoveComponent,
data = default,
entity = entity,
componentTypeID = ComponentTypeID<T>.value
};
});
}
_commands.Add(command);
public void SetComponent<T>(Entity entity, T component)
where T : unmanaged, IComponent
{
var data = new UnsafeArray<byte>(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<T>.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<byte, Identifier<IComponent>>(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);
}
}

View File

@@ -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;
}
/// <summary>
/// Create an entity with no components.
/// </summary>
/// <returns>The created entity.</returns>
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;
}
/// <summary>
/// Create an entity with specified components.
/// </summary>
/// <param name="componentTypeIDs">The component type IDs to add to the entity.</param>
/// <returns>The created entity.</returns>
public Entity CreateEntity(params ReadOnlySpan<Identifier<IComponent>> componentTypeIDs)
{
var signatureHash = ComponentRegister.GetHashCode(componentTypeIDs);
@@ -71,26 +99,84 @@ public unsafe class EntityManager : IDisposable
return entity;
}
public Entity CreateEntity()
/// <summary>
/// Create multiple entities with specified components.
/// </summary>
/// <param name="count">The number of entities to create.</param>
/// <param name="allocator">The allocator to use for the returned array.</param>
/// <param name="componentTypeIDs">The component type IDs to add to the entities. </param>
/// <returns>An array of the created entities.</returns>
public UnsafeArray<Entity> CreateEntities(int count, Allocator allocator, params ReadOnlySpan<Identifier<IComponent>> 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<Entity>(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)
/// <summary>
/// Create multiple entities with specified components.
/// </summary>
/// <param name="count">The number of entities to create.</param>
/// <param name="componentTypeIDs">The component type IDs to add to the entities. </param>
public void CreateEntities(int count, params ReadOnlySpan<Identifier<IComponent>> 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);
}
}
/// <summary>
/// Destroy the specified entity.
/// </summary>
/// <returns>The result status of the operation.</returns>
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;
}
/// <summary>
/// Check if the specified entity exists.
/// </summary>
/// <param name="entity">The entity to check.</param>
/// <returns>True if the entity exists, false otherwise.</returns>
public bool Exists(Entity entity)
{
return _entityLocations.Contains(entity.ID, entity.Generation);
}
/// <summary>
/// Create a singleton entity with the specified component.
/// </summary>
/// <param name="componentID">The component type ID of the singleton.</param>
/// <param name="pComponent">Pointer to the component data.</param>
/// <returns>The result status of the operation.</returns>
public ResultStatus CreateSingleton(Identifier<IComponent> 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;
}
/// <summary>
/// Create a singleton entity with the specified component.
/// </summary>
/// <typeparam name="T">The component type.</typeparam>
/// <param name="component">The component data.</param>
/// <returns>The result status of the operation.</returns>
public ResultStatus CreateSingleton<T>(T component = default)
where T : unmanaged, IComponent
{
return CreateSingleton(ComponentTypeID<T>.value, &component);
}
/// <summary>
/// Get a pointer to the singleton component data.
/// </summary>
/// <param name="componentID">The component type ID of the singleton.</param>
/// <returns>Pointer to the component data, or null if not found.</returns>
public void* GetSingleton(Identifier<IComponent> 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;
}
/// <summary>
/// Get a reference to the singleton component data.
/// </summary>
/// <typeparam name="T">The component type.</typeparam>
/// <returns>Reference to the component data. null ref if not found.</returns>
public ref T GetSingleton<T>()
where T : unmanaged, IComponent
{
var ptr = GetSingleton(ComponentTypeID<T>.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<IComponent> componentID, void* component)
/// <summary>
/// Add a component to the specified entity.
/// </summary>
/// <param name="entity">The entity to add the component to.</param>
/// <param name="componentID">The component type ID to add.</param>
/// <param name="pComponent">Pointer to the component data.</param>
/// <returns>The result status of the operation.</returns>
public ResultStatus AddComponent(Entity entity, Identifier<IComponent> 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<Identifier<IComponent>> componentTypeIDs = stackalloc Identifier<IComponent>[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<T>(Entity entity, T component)
/// <summary>
/// Add a component to the specified entity.
/// </summary>
/// <typeparam name="T">The component type.</typeparam>
/// <param name="entity">The entity to add the component to.</param>
/// <param name="component">The component data.</param>
/// <returns>The result status of the operation.</returns>
public ResultStatus AddComponent<T>(Entity entity, T component = default)
where T : unmanaged, IComponent
{
return AddComponent(entity, ComponentTypeID<T>.value, &component);
}
public ResultStatus SetComponentData(Entity entity, Identifier<IComponent> componentID, void* pComponent)
/// <summary>
/// Remove a component from the specified entity.
/// </summary>
/// <param name="entity">The entity to remove the component from.</param>
/// <param name="componentID">The component type ID to remove.</param>
/// <returns>The result status of the operation.</returns>
public ResultStatus RemoveComponent(Entity entity, Identifier<IComponent> 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<uint> 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<Identifier<IComponent>> componentTypeIDs = stackalloc Identifier<IComponent>[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;
}
/// <summary>
/// Remove a component from the specified entity.
/// </summary>
/// <typeparam name="T">The component type.</typeparam>
/// <param name="entity">The entity to remove the component from.</param>
/// <returns>The result status of the operation.</returns>
public ResultStatus RemoveComponent<T>(Entity entity)
where T : unmanaged, IComponent
{
return RemoveComponent(entity, ComponentTypeID<T>.value);
}
/// <summary>
/// Set the component data for the specified entity.
/// </summary>
/// <param name="entity">The entity to set the component data for.</param>
/// <param name="componentID">The component type ID to set.</param>
/// <param name="pComponent">Pointer to the component data.</param>
/// <returns>The result status of the operation.</returns>
public ResultStatus SetComponent(Entity entity, Identifier<IComponent> 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<T>(Entity entity, T component)
/// <summary>
/// Set the component data for the specified entity.
/// </summary>
/// <typeparam name="T">The component type.</typeparam>
/// <param name="entity">The entity to set the component data for.</param>
/// <param name="component">The component data.</param>
public ResultStatus SetComponent<T>(Entity entity, T component)
where T : unmanaged, IComponent
{
return SetComponentData(entity, ComponentTypeID<T>.value, &component);
return SetComponent(entity, ComponentTypeID<T>.value, &component);
}
/// <summary>
/// Get a pointer to the component data for the specified entity.
/// </summary>
/// <param name="entity">The entity to get the component data for.</param>
/// <param name="componentID">The component type ID to get.</param>
/// <returns>Pointer to the component data, or null if not found.</returns>
public void* GetComponent(Entity entity, Identifier<IComponent> 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);
}
/// <summary>
/// Get a reference to the component data for the specified entity.
/// </summary>
/// <typeparam name="T">The component type.</typeparam>
/// <param name="entity">The entity to get the component data for.</param>
/// <returns>Reference to the component data. null ref if not found.</returns>
public ref T GetComponent<T>(Entity entity)
where T : unmanaged, IComponent
{
var ptr = GetComponent(entity, ComponentTypeID<T>.value);
return ref *(T*)ptr; // This will return null ref if ptr is null.
}
/// <summary>
/// Check if the specified entity has the specified component.
/// </summary>
/// <param name="entity">The entity to check.</param>
/// <param name="componentID">The component type ID to check.</param>
/// <returns>True if the entity has the component, false otherwise.</returns>
public bool HasComponent(Entity entity, Identifier<IComponent> componentID)
{
if (!_entityLocations.TryGetElementAt(entity.ID, entity.Generation, out var location))
@@ -265,14 +597,26 @@ public unsafe class EntityManager : IDisposable
return archetype.HasComponent(componentID);
}
/// <summary>
/// Check if the specified entity has the specified component.
/// </summary>
/// <typeparam name="T">The component type.</typeparam>
/// <param name="entity">The entity to check.</param>
/// <returns>True if the entity has the component, false otherwise.</returns>
public bool HasComponent<T>(Entity entity)
where T : unmanaged, IComponent
{
return HasComponent(entity, ComponentTypeID<T>.value);
}
public ResultStatus SetEnabled<T>(Entity entity, bool enabled)
where T : unmanaged, IEnableableComponent
/// <summary>
/// Set the enabled state of an enableable component for the specified entity.
/// </summary>
/// <param name="entity">The entity to set the enabled state for.</param>
/// <param name="componentID">The component type ID of the enableable component.</
/// <param name="enabled">True to enable the component, false to disable it.</param>
/// <returns>The result status of the operation.</returns>
public ResultStatus SetEnabled(Entity entity, Identifier<IComponent> 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<T>.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;
}
/// <summary>
/// Set the enabled state of an enableable component for the specified entity.
/// </summary>
/// <typeparam name="T">The enableable component type.</typeparam>
/// <param name="entity">The entity to set the enabled state for.</param>
/// <param name="enabled">True to enable the component, false to disable it.</
/// <returns>The result status of the operation.</returns>
public ResultStatus SetEnabled<T>(Entity entity, bool enabled)
where T : unmanaged, IEnableableComponent
{
return SetEnabled(entity, ComponentTypeID<T>.value, enabled);
}
public void Dispose()
{
if (_disposed)

View File

@@ -37,17 +37,6 @@ public struct EntityQueryMask : IDisposable, IEquatable<EntityQueryMask>
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<EntityQueryMask>
{
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
{
/// <summary>
/// Provides a read-only view over a chunk of entities and their component data within an archetype.
/// </summary>
/// <remarks>This does not filter disabled/enabled components. You must handle that manually.</remarks>
public readonly ref struct ChunkView
{
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);
}
/// <summary>
/// Returns a read-only span containing structuralAll entities stored in the current chunk.
/// </summary>
/// <returns>A read-only span of <see cref="Entity"/> values representing the entities in the chunk.</returns>
public readonly ReadOnlySpan<Entity> GetEntities()
{
var ptr = _chunk.GetUnsafePtr();
var pEntity = (Entity*)(ptr + _archetype.EntityIDsOffset);
return new ReadOnlySpan<Entity>(pEntity, _chunk.Count);
}
/// <summary>
/// Gets a span providing direct access to the component data of type T0 for structuralAll entities in the chunk.
/// </summary>
/// <typeparam name="T">The type of component to access. Must be an unmanaged type that implements <see cref="Component"/>.</typeparam>
/// <returns>A span of type <see cref="{T}"/> containing the component data for each entity in the chunk.</returns>
/// <exception cref="InvalidOperationException">Thrown if the specified component type is not present in the archetype.</exception>
public readonly Span<T> GetComponentData<T>()
where T : unmanaged, IComponent
{
var layout = _archetype.GetLayout(ComponentTypeID<T>.value).GetValueOrThrow(ResultStatus.Success);
var ptr = _chunk.GetUnsafePtr() + layout.offset;
return new Span<T>(ptr, _chunk.Count);
}
/// <summary>
/// Gets a bit set representing the enabled state of each instance of the specified enableable component
/// type within the current chunk.
/// </summary>
/// <typeparam name="T">The component type for which to retrieve enablement bits. Must be unmanaged and implement <see cref="IEnableableComponent"/>.</typeparam>
/// <returns>A <see cref="SpanBitSet"/> that provides access to the enablement bits for all instances of the specified component type in the chunk.</returns>
/// <exception cref="InvalidOperationException">Thrown if the specified component type does not support enablement.</exception>
public SpanBitSet GetEnableBits<T>()
where T : unmanaged, IEnableableComponent
{
var layout = _archetype.GetLayout(ComponentTypeID<T>.value).GetValueOrThrow(ResultStatus.Success);
if (layout.enableBitsOffset == -1)
{
throw new InvalidOperationException($"Component {typeof(T).FullName} is not enableable.");
}
var maskBase = _chunk.GetUnsafePtr() + layout.enableBitsOffset;
return new SpanBitSet(new Span<uint>(maskBase, (_chunk.Count + 31) / 32));
}
/// <summary>
/// Determines whether the specified component of type <typeparamref name="T"/> at the given index is currently enabled.
/// </summary>
/// <typeparam name="T">The type of the component to check. Must be an unmanaged type that implements <see cref="IEnableableComponent"/>.</typeparam>
/// <param name="index">The zero-based index of the component instance to check within the chunk.</param>
/// <returns>true if the component at the specified index is enabled; otherwise, false.</returns>
/// <exception cref="InvalidOperationException">Thrown if the specified component type <typeparamref name="T"/> does not support enable/disable functionality.</exception>
public readonly bool IsComponentEnabled<T>(int index)
where T : unmanaged, IEnableableComponent
{
var layout = _archetype.GetLayout(ComponentTypeID<T>.value).GetValueOrThrow(ResultStatus.Success);
if (layout.enableBitsOffset == -1)
{
throw new InvalidOperationException($"Component {typeof(T).FullName} is not enableable.");
}
var maskBase = _chunk.GetUnsafePtr() + layout.enableBitsOffset;
return CheckBit(maskBase, index);
}
}
/// <summary>
/// Provides an enumerator for iterating over chunks of entities and their component data that match a set of archetypes within a world.
/// </summary>
public readonly ref struct ChunkIterator
{
/// <summary>
/// Provides a read-only view over a chunk of entities and their component data within an archetype.
/// </summary>
/// <remarks>This does not filter disabled/enabled components. You must handle that manually.</remarks>
public readonly ref struct ChunkView
{
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);
}
/// <summary>
/// Returns a read-only span containing structuralAll entities stored in the current chunk.
/// </summary>
/// <returns>A read-only span of <see cref="Entity"/> values representing the entities in the chunk.</returns>
public readonly ReadOnlySpan<Entity> GetEntities()
{
var ptr = _chunk.GetUnsafePtr();
var pEntity = (Entity*)(ptr + _archetype.EntityIDsOffset);
return new ReadOnlySpan<Entity>(pEntity, _chunk.Count);
}
/// <summary>
/// Gets a span providing direct access to the component data of type T0 for structuralAll entities in the chunk.
/// </summary>
/// <typeparam name="T">The type of component to access. Must be an unmanaged type that implements <see cref="Component"/>.</typeparam>
/// <returns>A span of type <see cref="{T}"/> containing the component data for each entity in the chunk.</returns>
/// <exception cref="InvalidOperationException">Thrown if the specified component type is not present in the archetype.</exception>
public readonly Span<T> GetComponentData<T>()
where T : unmanaged, IComponent
{
var layout = _archetype.GetLayout(ComponentTypeID<T>.value).GetValueOrThrow(ResultStatus.Success);
var ptr = _chunk.GetUnsafePtr() + layout.offset;
return new Span<T>(ptr, _chunk.Count);
}
/// <summary>
/// Gets a bit set representing the enabled state of each instance of the specified enableable component
/// type within the current chunk.
/// </summary>
/// <typeparam name="T">The component type for which to retrieve enablement bits. Must be unmanaged and implement <see cref="IEnableableComponent"/>.</typeparam>
/// <returns>A <see cref="SpanBitSet"/> that provides access to the enablement bits for all instances of the specified component type in the chunk.</returns>
/// <exception cref="InvalidOperationException">Thrown if the specified component type does not support enablement.</exception>
public SpanBitSet GetEnableBits<T>()
where T : unmanaged, IEnableableComponent
{
var layout = _archetype.GetLayout(ComponentTypeID<T>.value).GetValueOrThrow(ResultStatus.Success);
if (layout.enableBitsOffset == -1)
{
throw new InvalidOperationException($"Component {typeof(T).FullName} is not enableable.");
}
var maskBase = _chunk.GetUnsafePtr() + layout.enableBitsOffset;
return new SpanBitSet(new Span<uint>(maskBase, (_chunk.Count + 31) / 32));
}
/// <summary>
/// Determines whether the specified component of type <typeparamref name="T"/> at the given index is currently enabled.
/// </summary>
/// <typeparam name="T">The type of the component to check. Must be an unmanaged type that implements <see cref="IEnableableComponent"/>.</typeparam>
/// <param name="index">The zero-based index of the component instance to check within the chunk.</param>
/// <returns>true if the component at the specified index is enabled; otherwise, false.</returns>
/// <exception cref="InvalidOperationException">Thrown if the specified component type <typeparamref name="T"/> does not support enable/disable functionality.</exception>
public readonly bool IsComponentEnabled<T>(int index)
where T : unmanaged, IEnableableComponent
{
var layout = _archetype.GetLayout(ComponentTypeID<T>.value).GetValueOrThrow(ResultStatus.Success);
if (layout.enableBitsOffset == -1)
{
throw new InvalidOperationException($"Component {typeof(T).FullName} is not enableable.");
}
var maskBase = _chunk.GetUnsafePtr() + layout.enableBitsOffset;
return CheckBit(maskBase, index);
}
}
public ref struct Enumerator
{
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<Identifier<Archetype>> _matchingArchetypes;
@@ -245,6 +241,7 @@ public unsafe partial struct EntityQuery : IIdentifierType, IDisposable
_matchingArchetypes = new UnsafeList<Identifier<Archetype>>(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<Identifier<IComponent>>(4, Allocator.Stack);
_any = new UnsafeList<Identifier<IComponent>>(4, Allocator.Stack);
_absent = new UnsafeList<Identifier<IComponent>>(4, Allocator.Stack);
_none = new UnsafeList<Identifier<IComponent>>(4, Allocator.Stack);
_disabled = new UnsafeList<Identifier<IComponent>>(4, Allocator.Stack);
_present = new UnsafeList<Identifier<IComponent>>(4, Allocator.Stack);
_all = new UnsafeList<Identifier<IComponent>>(4, _scope.AllocationHandle);
_any = new UnsafeList<Identifier<IComponent>>(4, _scope.AllocationHandle);
_absent = new UnsafeList<Identifier<IComponent>>(4, _scope.AllocationHandle);
_none = new UnsafeList<Identifier<IComponent>>(4, _scope.AllocationHandle);
_disabled = new UnsafeList<Identifier<IComponent>>(4, _scope.AllocationHandle);
_present = new UnsafeList<Identifier<IComponent>>(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();
}
}

285
Ghost.Entities/System.cs Normal file
View File

@@ -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<Type, List<ISystem>> _systemGroupMap = new();
// TODO: Use Source Generators to generate group registrations at compile time.
public static void RegisterSystemGroup<T>(Type groupType)
where T : ISystem, new()
{
if (!_systemGroupMap.ContainsKey(typeof(T)))
{
_systemGroupMap[typeof(T)] = new();
}
_systemGroupMap[groupType].Add(new T());
}
public static List<ISystem> 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<ISystem> _systems = new ();
private List<ISystem>? _sortedSystems;
private uint _version = 0;
private uint _sortedVersion = 0;
// public SystemGroup()
// {
// _systems = SystemGroupRegistry.GetSystemsForGroup(GetType());
// }
private static List<ISystem> Sort(List<ISystem> systems)
{
// 1. Build the Graph
// Key: The System, Value: Systems that MUST run before the Key
var dependencies = new Dictionary<Type, HashSet<Type>>();
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<Type>();
// 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<Type>();
dependencies[targetType].Add(type);
}
}
// 2. Topological Sort (Kahn's Algorithm variant)
var sortedList = new List<ISystem>();
var visited = new HashSet<Type>();
// 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<ISystem> _systems = new ();
private readonly Dictionary<Type, int> _systemTypeMap = new ();
internal SystemManager(World world)
{
_world = world;
}
public void AddSystem<T>()
where T : ISystem, new()
{
var system = new T();
_systems.Add(system);
_systemTypeMap[typeof(T)] = _systems.Count - 1;
}
public T GetSystem<T>()
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.");
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -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<T{0}> 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<T<#= j #>>(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<Identifier<Archetype>> _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<Identifier<Archetype>> matchingArchetypes, EntityQueryMask mask, World world)
{
<# for (var j = 0; j < i; j++) { #>
_compTypeIDs[<#= j #>] = ComponentTypeID<T<#= j #>>.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<Archetype>();
_currentChunk = ref Unsafe.NullRef<Chunk>();
_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<Identifier<Archetype>> _matchingArchetypes;
private readonly EntityQueryMask _mask;
private readonly World _world;
internal ComponentIterator(ReadOnlyUnsafeCollection<Identifier<Archetype>> 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));
}
<# } #>
}

View File

@@ -1,4 +1,3 @@
using Ghost.Core;
using Misaki.HighPerformance.Jobs;
using Misaki.HighPerformance.LowLevel.Buffer;
@@ -24,7 +23,7 @@ internal unsafe struct JobEntityBatch<TJob, T0> : IJobParallelFor
public UnsafeList<int> offsets0;
public UnsafeList<int> bitsOffsets0;
public void Execute(int loopIndex, int threadIndex)
{
// 1. Get the specific pChunk for this thread
@@ -33,7 +32,7 @@ internal unsafe struct JobEntityBatch<TJob, T0> : 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<TJob, T0, T1> : IJobParallelFor
public UnsafeList<int> offsets0;
public UnsafeList<int> bitsOffsets0;
public UnsafeList<int> offsets1;
public UnsafeList<int> bitsOffsets1;
public void Execute(int loopIndex, int threadIndex)
{
// 1. Get the specific pChunk for this thread
@@ -81,10 +80,10 @@ internal unsafe struct JobEntityBatch<TJob, T0, T1> : 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<TJob, T0, T1, T2> : IJobParallelFor
public UnsafeList<int> offsets0;
public UnsafeList<int> bitsOffsets0;
public UnsafeList<int> offsets1;
public UnsafeList<int> bitsOffsets1;
public UnsafeList<int> offsets2;
public UnsafeList<int> bitsOffsets2;
public void Execute(int loopIndex, int threadIndex)
{
// 1. Get the specific pChunk for this thread
@@ -143,13 +142,13 @@ internal unsafe struct JobEntityBatch<TJob, T0, T1, T2> : 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<TJob, T0, T1, T2, T3> : IJobParallelFor
public UnsafeList<int> offsets0;
public UnsafeList<int> bitsOffsets0;
public UnsafeList<int> offsets1;
public UnsafeList<int> bitsOffsets1;
public UnsafeList<int> offsets2;
public UnsafeList<int> bitsOffsets2;
public UnsafeList<int> offsets3;
public UnsafeList<int> bitsOffsets3;
public void Execute(int loopIndex, int threadIndex)
{
// 1. Get the specific pChunk for this thread
@@ -219,16 +218,16 @@ internal unsafe struct JobEntityBatch<TJob, T0, T1, T2, T3> : 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<TJob, T0, T1, T2, T3, T4> : IJobParallelFo
public UnsafeList<int> offsets0;
public UnsafeList<int> bitsOffsets0;
public UnsafeList<int> offsets1;
public UnsafeList<int> bitsOffsets1;
public UnsafeList<int> offsets2;
public UnsafeList<int> bitsOffsets2;
public UnsafeList<int> offsets3;
public UnsafeList<int> bitsOffsets3;
public UnsafeList<int> offsets4;
public UnsafeList<int> bitsOffsets4;
public void Execute(int loopIndex, int threadIndex)
{
// 1. Get the specific pChunk for this thread
@@ -309,19 +308,19 @@ internal unsafe struct JobEntityBatch<TJob, T0, T1, T2, T3, T4> : 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<TJob, T0, T1, T2, T3, T4, T5> : IJobParall
public UnsafeList<int> offsets0;
public UnsafeList<int> bitsOffsets0;
public UnsafeList<int> offsets1;
public UnsafeList<int> bitsOffsets1;
public UnsafeList<int> offsets2;
public UnsafeList<int> bitsOffsets2;
public UnsafeList<int> offsets3;
public UnsafeList<int> bitsOffsets3;
public UnsafeList<int> offsets4;
public UnsafeList<int> bitsOffsets4;
public UnsafeList<int> offsets5;
public UnsafeList<int> bitsOffsets5;
public void Execute(int loopIndex, int threadIndex)
{
// 1. Get the specific pChunk for this thread
@@ -413,22 +412,22 @@ internal unsafe struct JobEntityBatch<TJob, T0, T1, T2, T3, T4, T5> : 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<TJob, T0, T1, T2, T3, T4, T5, T6> : IJobPa
public UnsafeList<int> offsets0;
public UnsafeList<int> bitsOffsets0;
public UnsafeList<int> offsets1;
public UnsafeList<int> bitsOffsets1;
public UnsafeList<int> offsets2;
public UnsafeList<int> bitsOffsets2;
public UnsafeList<int> offsets3;
public UnsafeList<int> bitsOffsets3;
public UnsafeList<int> offsets4;
public UnsafeList<int> bitsOffsets4;
public UnsafeList<int> offsets5;
public UnsafeList<int> bitsOffsets5;
public UnsafeList<int> offsets6;
public UnsafeList<int> bitsOffsets6;
public void Execute(int loopIndex, int threadIndex)
{
// 1. Get the specific pChunk for this thread
@@ -531,25 +530,25 @@ internal unsafe struct JobEntityBatch<TJob, T0, T1, T2, T3, T4, T5, T6> : 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<TJob, T0, T1, T2, T3, T4, T5, T6, T7> : IJ
public UnsafeList<int> offsets0;
public UnsafeList<int> bitsOffsets0;
public UnsafeList<int> offsets1;
public UnsafeList<int> bitsOffsets1;
public UnsafeList<int> offsets2;
public UnsafeList<int> bitsOffsets2;
public UnsafeList<int> offsets3;
public UnsafeList<int> bitsOffsets3;
public UnsafeList<int> offsets4;
public UnsafeList<int> bitsOffsets4;
public UnsafeList<int> offsets5;
public UnsafeList<int> bitsOffsets5;
public UnsafeList<int> offsets6;
public UnsafeList<int> bitsOffsets6;
public UnsafeList<int> offsets7;
public UnsafeList<int> bitsOffsets7;
public void Execute(int loopIndex, int threadIndex)
{
// 1. Get the specific pChunk for this thread
@@ -663,28 +662,28 @@ internal unsafe struct JobEntityBatch<TJob, T0, T1, T2, T3, T4, T5, T6, T7> : 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<TJob, T0>(JobScheduler scheduler, TJob jobData, Allocator allocator, int batchSize, JobHandle dependency)
public JobHandle ScheduleEntityParallel<TJob, T0>(TJob jobData, Allocator allocator, int batchSize, JobHandle dependency)
where TJob : unmanaged, IJobEntityParallel<T0>
where T0 : unmanaged, IComponent
{
var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success);
// 1. Flatten the World
var chunkList = new UnsafeList<IntPtr>(128, allocator);
var chunkEntityCounts = new UnsafeList<int>(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<TJob, T0, T1>(JobScheduler scheduler, TJob jobData, Allocator allocator, int batchSize, JobHandle dependency)
public JobHandle ScheduleEntityParallel<TJob, T0, T1>(TJob jobData, Allocator allocator, int batchSize, JobHandle dependency)
where TJob : unmanaged, IJobEntityParallel<T0, T1>
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
{
var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success);
// 1. Flatten the World
var chunkList = new UnsafeList<IntPtr>(128, allocator);
var chunkEntityCounts = new UnsafeList<int>(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<TJob, T0, T1, T2>(JobScheduler scheduler, TJob jobData, Allocator allocator, int batchSize, JobHandle dependency)
public JobHandle ScheduleEntityParallel<TJob, T0, T1, T2>(TJob jobData, Allocator allocator, int batchSize, JobHandle dependency)
where TJob : unmanaged, IJobEntityParallel<T0, T1, T2>
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<IntPtr>(128, allocator);
var chunkEntityCounts = new UnsafeList<int>(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<TJob, T0, T1, T2, T3>(JobScheduler scheduler, TJob jobData, Allocator allocator, int batchSize, JobHandle dependency)
public JobHandle ScheduleEntityParallel<TJob, T0, T1, T2, T3>(TJob jobData, Allocator allocator, int batchSize, JobHandle dependency)
where TJob : unmanaged, IJobEntityParallel<T0, T1, T2, T3>
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<IntPtr>(128, allocator);
var chunkEntityCounts = new UnsafeList<int>(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<TJob, T0, T1, T2, T3, T4>(JobScheduler scheduler, TJob jobData, Allocator allocator, int batchSize, JobHandle dependency)
public JobHandle ScheduleEntityParallel<TJob, T0, T1, T2, T3, T4>(TJob jobData, Allocator allocator, int batchSize, JobHandle dependency)
where TJob : unmanaged, IJobEntityParallel<T0, T1, T2, T3, T4>
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<IntPtr>(128, allocator);
var chunkEntityCounts = new UnsafeList<int>(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<TJob, T0, T1, T2, T3, T4, T5>(JobScheduler scheduler, TJob jobData, Allocator allocator, int batchSize, JobHandle dependency)
public JobHandle ScheduleEntityParallel<TJob, T0, T1, T2, T3, T4, T5>(TJob jobData, Allocator allocator, int batchSize, JobHandle dependency)
where TJob : unmanaged, IJobEntityParallel<T0, T1, T2, T3, T4, T5>
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<IntPtr>(128, allocator);
var chunkEntityCounts = new UnsafeList<int>(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<TJob, T0, T1, T2, T3, T4, T5, T6>(JobScheduler scheduler, TJob jobData, Allocator allocator, int batchSize, JobHandle dependency)
public JobHandle ScheduleEntityParallel<TJob, T0, T1, T2, T3, T4, T5, T6>(TJob jobData, Allocator allocator, int batchSize, JobHandle dependency)
where TJob : unmanaged, IJobEntityParallel<T0, T1, T2, T3, T4, T5, T6>
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<IntPtr>(128, allocator);
var chunkEntityCounts = new UnsafeList<int>(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<TJob, T0, T1, T2, T3, T4, T5, T6, T7>(JobScheduler scheduler, TJob jobData, Allocator allocator, int batchSize, JobHandle dependency)
public JobHandle ScheduleEntityParallel<TJob, T0, T1, T2, T3, T4, T5, T6, T7>(TJob jobData, Allocator allocator, int batchSize, JobHandle dependency)
where TJob : unmanaged, IJobEntityParallel<T0, T1, T2, T3, T4, T5, T6, T7>
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<IntPtr>(128, allocator);
var chunkEntityCounts = new UnsafeList<int>(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;
}
}
}

View File

@@ -35,7 +35,7 @@ internal unsafe struct JobEntityBatch<TJob, <#= generics #>> : IJobParallelFor
<# for (var j = 0; j < i; j++){ #>
public UnsafeList<int> offsets<#= j #>;
public UnsafeList<int> bitsOffsets<#= j #>;
<# } #>
public void Execute(int loopIndex, int threadIndex)
{
@@ -46,7 +46,7 @@ internal unsafe struct JobEntityBatch<TJob, <#= generics #>> : 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<TJob, <#= generics #>>(JobScheduler scheduler, TJob jobData, Allocator allocator, int batchSize, JobHandle dependency)
public JobHandle ScheduleEntityParallel<TJob, <#= generics #>>(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<IntPtr>(128, allocator);
var chunkEntityCounts = new UnsafeList<int>(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;
}
<# } #>
}
}

View File

@@ -1,21 +1,92 @@
namespace Ghost.Entities;
public delegate void ForEach<T0>(ref T0 t0Component);
public delegate void ForEach<T0, T1>(ref T0 t0Component,ref T1 t1Component);
public delegate void ForEach<T0, T1, T2>(ref T0 t0Component,ref T1 t1Component,ref T2 t2Component);
public delegate void ForEach<T0, T1, T2, T3>(ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component);
public delegate void ForEach<T0, T1, T2, T3, T4>(ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component,ref T4 t4Component);
public delegate void ForEach<T0, T1, T2, T3, T4, T5>(ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component,ref T4 t4Component,ref T5 t5Component);
public delegate void ForEach<T0, T1, T2, T3, T4, T5, T6>(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<T0, T1, T2, T3, T4, T5, T6, T7>(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<T0>(ref T0 component0)
where T0 : unmanaged, IComponent;
public delegate void ForEach<T0, T1>(ref T0 component0, ref T1 component1)
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent;
public delegate void ForEach<T0, T1, T2>(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<T0, T1, T2, T3>(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<T0, T1, T2, T3, T4>(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<T0, T1, T2, T3, T4, T5>(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<T0, T1, T2, T3, T4, T5, T6>(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<T0, T1, T2, T3, T4, T5, T6, T7>(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<T0>(Entity entity, ref T0 t0Component);
public delegate void ForEachWithEntity<T0, T1>(Entity entity, ref T0 t0Component,ref T1 t1Component);
public delegate void ForEachWithEntity<T0, T1, T2>(Entity entity, ref T0 t0Component,ref T1 t1Component,ref T2 t2Component);
public delegate void ForEachWithEntity<T0, T1, T2, T3>(Entity entity, ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component);
public delegate void ForEachWithEntity<T0, T1, T2, T3, T4>(Entity entity, ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component,ref T4 t4Component);
public delegate void ForEachWithEntity<T0, T1, T2, T3, T4, T5>(Entity entity, ref T0 t0Component,ref T1 t1Component,ref T2 t2Component,ref T3 t3Component,ref T4 t4Component,ref T5 t5Component);
public delegate void ForEachWithEntity<T0, T1, T2, T3, T4, T5, T6>(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<T0, T1, T2, T3, T4, T5, T6, T7>(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<T0>(Entity entity, ref T0 component0)
where T0 : unmanaged, IComponent;
public delegate void ForEachWithEntity<T0, T1>(Entity entity, ref T0 component0, ref T1 component1)
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent;
public delegate void ForEachWithEntity<T0, T1, T2>(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<T0, T1, T2, T3>(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<T0, T1, T2, T3, T4>(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<T0, T1, T2, T3, T4, T5>(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<T0, T1, T2, T3, T4, T5, T6>(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<T0, T1, T2, T3, T4, T5, T6, T7>(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;

View File

@@ -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 #>);
<# } #>
public delegate void ForEachWithEntity<<#= generics #>>(Entity entity, <#= compGenerics #>)
<#= restrictions #>;
<# } #>

View File

@@ -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<World>(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<World>
{
private readonly Identifier<World> _id;
private readonly JobScheduler _jobScheduler;
private readonly EntityManager _entityManager;
private readonly EntityCommandBuffer _entityCommandBuffer;
@@ -82,12 +84,14 @@ public partial class World : IIdentifierType, IDisposable, IEquatable<World>
internal int ArchetypeCount => _archetypes.Count;
public Identifier<World> ID => _id;
public JobScheduler JobScheduler => _jobScheduler;
public EntityManager EntityManager => _entityManager;
public EntityCommandBuffer EntityCommandBuffer => _entityCommandBuffer;
private World(Identifier<World> id, int entityCapacity)
private World(Identifier<World> id, int entityCapacity, JobScheduler jobScheduler)
{
_id = id;
_jobScheduler = jobScheduler;
_archetypes = new UnsafeList<Archetype>(16, Allocator.Persistent);
_entityQueries = new UnsafeList<EntityQuery>(16, Allocator.Persistent);