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:
@@ -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<byte> _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<byte>(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<IComponent> componentID;
|
||||
public Identifier<Archetype> targetArchetype;
|
||||
}
|
||||
|
||||
internal struct ComponentMemoryLayout
|
||||
{
|
||||
public int offset;
|
||||
public int size;
|
||||
public Identifier<IComponent> componentID;
|
||||
}
|
||||
|
||||
internal unsafe struct Archetype : IIdentifierType, IDisposable
|
||||
{
|
||||
private readonly Identifier<Archetype> _id;
|
||||
private readonly Identifier<World> _worldID;
|
||||
|
||||
private UnsafeBitSet _signature;
|
||||
private UnsafeList<Chuck> _chunks;
|
||||
private UnsafeArray<ComponentMemoryLayout> _layouts;
|
||||
private UnsafeArray<int> _componentIDToOffset;
|
||||
|
||||
// TODO: Is hash map better?
|
||||
private UnsafeList<Edge> _edgesAdd;
|
||||
private UnsafeList<Edge> _edgesRemove;
|
||||
|
||||
private int _hash;
|
||||
private int _entityCapacity;
|
||||
private int _maxComponentID;
|
||||
private int _entityIdsOffset;
|
||||
|
||||
public Identifier<Archetype> ID => _id;
|
||||
|
||||
public UnsafeBitSet Signature => _signature;
|
||||
public UnsafeList<Chuck> Chunks => _chunks;
|
||||
public UnsafeArray<ComponentMemoryLayout> Layouts => _layouts;
|
||||
|
||||
public int EntityCapacity => _entityCapacity;
|
||||
public int ChunkCount => _chunks.Count;
|
||||
|
||||
public Archetype(Identifier<Archetype> id, Identifier<World> worldID, ReadOnlySpan<Identifier<IComponent>> componentIds)
|
||||
{
|
||||
_id = id;
|
||||
_worldID = worldID;
|
||||
|
||||
if (componentIds.IsEmpty)
|
||||
{
|
||||
_signature = new UnsafeBitSet(1, Allocator.Persistent, AllocationOption.Clear);
|
||||
_chunks = new UnsafeList<Chuck>(4, Allocator.Persistent);
|
||||
|
||||
_edgesAdd = new UnsafeList<Edge>(4, Allocator.Persistent);
|
||||
_edgesRemove = new UnsafeList<Edge>(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<Chuck>(4, Allocator.Persistent);
|
||||
|
||||
_edgesAdd = new UnsafeList<Edge>(4, Allocator.Persistent);
|
||||
_edgesRemove = new UnsafeList<Edge>(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<ComponentInfo>(pComponents, componentIds.Length));
|
||||
}
|
||||
|
||||
private void CalculateLayout(Span<ComponentInfo> components)
|
||||
{
|
||||
var entitySize = sizeof(Entity);
|
||||
var entityAlign = (int)MemoryUtility.AlignOf<Entity>();
|
||||
|
||||
// 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<ComponentMemoryLayout>(components.Length, Allocator.Persistent);
|
||||
_componentIDToOffset = new UnsafeArray<int>(_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<IComponent> 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<IComponent> componentID)
|
||||
{
|
||||
return _signature.IsSet(componentID);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AddEdgeAdd(Identifier<IComponent> componentID, Identifier<Archetype> targetArchetype)
|
||||
{
|
||||
_edgesAdd.Add(new Edge
|
||||
{
|
||||
componentID = componentID,
|
||||
targetArchetype = targetArchetype
|
||||
});
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Identifier<Archetype> GetEdgeAdd(Identifier<IComponent> componentID)
|
||||
{
|
||||
for (var i = 0; i < _edgesAdd.Count; i++)
|
||||
{
|
||||
var edge = _edgesAdd[i];
|
||||
if (edge.componentID == componentID)
|
||||
{
|
||||
return edge.targetArchetype;
|
||||
}
|
||||
}
|
||||
|
||||
return Identifier<Archetype>.Invalid;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AddEdgeRemove(Identifier<IComponent> componentID, Identifier<Archetype> targetArchetype)
|
||||
{
|
||||
_edgesRemove.Add(new Edge
|
||||
{
|
||||
componentID = componentID,
|
||||
targetArchetype = targetArchetype
|
||||
});
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Identifier<Archetype> GetEdgeRemove(Identifier<IComponent> componentID)
|
||||
{
|
||||
for (var i = 0; i < _edgesRemove.Count; i++)
|
||||
{
|
||||
var edge = _edgesRemove[i];
|
||||
if (edge.componentID == componentID)
|
||||
{
|
||||
return edge.targetArchetype;
|
||||
}
|
||||
}
|
||||
|
||||
return Identifier<Archetype>.Invalid;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Span<T> GetComponentArray<T>(int chunkIndex)
|
||||
where T : unmanaged, IComponent
|
||||
{
|
||||
var id = ComponentTypeID<T>.value;
|
||||
if (id >= _componentIDToOffset.Count)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
var offset = _componentIDToOffset[id];
|
||||
if (offset == -1)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
var chunk = _chunks[chunkIndex];
|
||||
return new Span<T>((T*)((byte*)chunk.GetUnsafePtr() + offset), chunk.Count);
|
||||
}
|
||||
|
||||
public override 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();
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
global using EntityID = System.Int32;
|
||||
global using GenerationID = System.Int32;
|
||||
|
||||
@@ -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<IComponent> id;
|
||||
}
|
||||
|
||||
public static unsafe class ComponentTypeID<T>
|
||||
where T : unmanaged, IComponent
|
||||
{
|
||||
public static readonly Identifier<IComponent> value = ComponentRegister.GetOrRegisterComponent<T>();
|
||||
}
|
||||
|
||||
internal static class ComponentRegister
|
||||
{
|
||||
private static int s_nextComponentTypeID = 0;
|
||||
private static Dictionary<IntPtr, Identifier<IComponent>> s_typeHandleToID = new();
|
||||
|
||||
private static List<ComponentInfo> s_registeredComponents = new();
|
||||
private static Dictionary<string, Identifier<IComponent>> s_nameToRuntimeID = new();
|
||||
|
||||
public unsafe static Identifier<IComponent> GetOrRegisterComponent<T>()
|
||||
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<IComponent>(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<T>(),
|
||||
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<IComponent> typeId)
|
||||
{
|
||||
return s_registeredComponents[typeId];
|
||||
}
|
||||
|
||||
public static int GetHashCode(ReadOnlySpan<Identifier<IComponent>> 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<uint>)stackalloc uint[length];
|
||||
bits.Clear();
|
||||
|
||||
var bitSet = new SpanBitSet(bits);
|
||||
foreach (var id in componentTypeIDs)
|
||||
{
|
||||
bitSet.SetBit(id.value);
|
||||
}
|
||||
|
||||
return bitSet.GetHashCode();
|
||||
}
|
||||
}
|
||||
@@ -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<Entity>, IComparable<Entity>
|
||||
{
|
||||
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} }}";
|
||||
}
|
||||
}
|
||||
@@ -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<byte> data;
|
||||
public CommandType type;
|
||||
public Entity entity;
|
||||
public int componentTypeID;
|
||||
}
|
||||
|
||||
private readonly EntityManager _entityManager;
|
||||
private UnsafeList<Command> _commands; // TODO: Maybe use UnsafeArray<byte> directly?
|
||||
|
||||
public EntityCommandBuffer(EntityManager entityManager)
|
||||
{
|
||||
_entityManager = entityManager;
|
||||
_commands = new UnsafeList<Command>(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<T>(Entity entity, T component)
|
||||
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 command = 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
|
||||
{
|
||||
type = CommandType.RemoveComponent,
|
||||
data = default,
|
||||
entity = entity,
|
||||
componentTypeID = ComponentTypeID<T>.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();
|
||||
}
|
||||
}
|
||||
@@ -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<Archetype> archetypeID;
|
||||
public int chunkIndex;
|
||||
public int rowIndex;
|
||||
}
|
||||
|
||||
private World _world;
|
||||
private UnsafeSlotMap<EntityLocation> _entityLocations;
|
||||
|
||||
internal EntityManager(World world, int initialCapacity)
|
||||
{
|
||||
_world = world;
|
||||
_entityLocations = new UnsafeSlotMap<EntityLocation>(initialCapacity, Allocator.Persistent);
|
||||
}
|
||||
|
||||
internal ResultStatus UpdateEntityLocation(Entity entity, Identifier<Archetype> 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<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);
|
||||
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<IComponent> 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<uint> 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<Identifier<IComponent>> componentTypeIDs = stackalloc Identifier<IComponent>[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<T>(Entity entity, ref T component)
|
||||
where T : unmanaged, IComponent
|
||||
{
|
||||
return AddComponent(entity, ComponentTypeID<T>.value, UnsafeUtility.AddressOf(ref component));
|
||||
}
|
||||
|
||||
public ResultStatus SetComponentData(Entity entity, Identifier<IComponent> 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<T>(Entity entity, ref T component)
|
||||
where T : unmanaged, IComponent
|
||||
{
|
||||
return SetComponentData(entity, ComponentTypeID<T>.value, UnsafeUtility.AddressOf(ref component));
|
||||
}
|
||||
|
||||
public bool HasComponent(Entity entity, Identifier<IComponent> 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<T>(Entity entity)
|
||||
where T : unmanaged, IComponent
|
||||
{
|
||||
return HasComponent(entity, ComponentTypeID<T>.value);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_entityLocations.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
namespace Ghost.ArcEntities;
|
||||
|
||||
public unsafe class EntityQuery<T1, T2>
|
||||
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<ArchetypeCache> _cache = new();
|
||||
|
||||
internal void AddMatchingArchetype(Archetype archetype)
|
||||
{
|
||||
// We look up the offsets ONCE when the archetype is registered
|
||||
int off1 = archetype.GetOffset(ComponentTypeID<T1>.value);
|
||||
int off2 = archetype.GetOffset(ComponentTypeID<T2>.value);
|
||||
|
||||
_cache.Add(new ArchetypeCache
|
||||
{
|
||||
Archetype = archetype,
|
||||
Offset1 = off1,
|
||||
Offset2 = off2
|
||||
});
|
||||
}
|
||||
|
||||
// The Optimized Iteration Loop
|
||||
public void ForEach(delegate*<ref T1, ref T2, void> action)
|
||||
{
|
||||
foreach (var cache in _cache)
|
||||
{
|
||||
var archetype = cache.Archetype;
|
||||
var offset1 = cache.Offset1;
|
||||
var offset2 = cache.Offset2;
|
||||
|
||||
// Iterate Chunks
|
||||
for (int i = 0; i < archetype.ChunkCount; i++)
|
||||
{
|
||||
var chunk = archetype.GetChunkReference(i);
|
||||
var chunkPtr = (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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<IsAotCompatible>True</IsAotCompatible>
|
||||
<IsTrimmable>True</IsTrimmable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<IsAotCompatible>True</IsAotCompatible>
|
||||
<IsTrimmable>True</IsTrimmable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ghost.Core\Ghost.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,5 +0,0 @@
|
||||
namespace Ghost.ArcEntities;
|
||||
|
||||
public static class Utility
|
||||
{
|
||||
}
|
||||
@@ -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<World?> s_worlds = new(4);
|
||||
private static Queue<Identifier<World>> s_freeWorldSlots = new();
|
||||
|
||||
internal static Identifier<Archetype> EmptyArchetypeID => new Identifier<Archetype>(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<World>(s_worlds.Count);
|
||||
s_worlds.Add(new World(index, entityCapacity));
|
||||
}
|
||||
|
||||
return s_worlds[index.value]!;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Result<World, ResultStatus> GetWorld(Identifier<World> 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<World>
|
||||
{
|
||||
private readonly Identifier<World> _id;
|
||||
|
||||
private UnsafeList<Archetype> _archetypes;
|
||||
private UnsafeHashMap<int, Identifier<Archetype>> _archetypeLookup; // Signature Hash to Archetype ID
|
||||
private EntityManager _entityManager;
|
||||
private EntityCommandBuffer _entityCommandBuffer;
|
||||
|
||||
private bool _disposed = false;
|
||||
|
||||
public Identifier<World> ID => _id;
|
||||
public EntityManager EntityManager => _entityManager;
|
||||
public EntityCommandBuffer EntityCommandBuffer => _entityCommandBuffer;
|
||||
|
||||
private World(Identifier<World> id, int entityCapacity)
|
||||
{
|
||||
_id = id;
|
||||
|
||||
_archetypes = new UnsafeList<Archetype>(16, Allocator.Persistent);
|
||||
_archetypeLookup = new UnsafeHashMap<int, Identifier<Archetype>>(16, Allocator.Persistent);
|
||||
|
||||
_entityManager = new EntityManager(this, entityCapacity);
|
||||
_entityCommandBuffer = new EntityCommandBuffer(_entityManager);
|
||||
|
||||
// Create the empty archetype
|
||||
CreateArchetype(ReadOnlySpan<Identifier<IComponent>>.Empty, 0);
|
||||
}
|
||||
|
||||
~World()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
internal Identifier<Archetype> CreateArchetype(ReadOnlySpan<Identifier<IComponent>> componentTypeIDs, int signatureHash)
|
||||
{
|
||||
var arcID = new Identifier<Archetype>(_archetypes.Count);
|
||||
_archetypes.Add(new Archetype(arcID, _id, componentTypeIDs));
|
||||
_archetypeLookup.Add(signatureHash, arcID);
|
||||
|
||||
return arcID;
|
||||
}
|
||||
|
||||
internal ref Archetype GetArchetypeReference(Identifier<Archetype> id)
|
||||
{
|
||||
return ref _archetypes[id.value];
|
||||
}
|
||||
|
||||
internal Identifier<Archetype> GetArchetypeIDBySignatureHash(int signatureHash)
|
||||
{
|
||||
if (_archetypeLookup.TryGetValue(signatureHash, out var arcID))
|
||||
{
|
||||
return arcID;
|
||||
}
|
||||
|
||||
return Identifier<Archetype>.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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Misaki.HighPerformance" Version="1.0.1" />
|
||||
<PackageReference Include="Misaki.HighPerformance.Jobs" Version="1.1.0" />
|
||||
<PackageReference Include="Misaki.HighPerformance.LowLevel" Version="1.2.8" />
|
||||
<PackageReference Include="Misaki.HighPerformance.Jobs" Version="1.2.0" />
|
||||
<PackageReference Include="Misaki.HighPerformance.LowLevel" Version="1.3.0" />
|
||||
<PackageReference Include="Misaki.HighPerformance.Mathematics" Version="1.2.6" />
|
||||
<PackageReference Include="System.IO.Hashing" Version="10.0.0" />
|
||||
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.26100.5" />
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using TerraFX.Interop.DirectX;
|
||||
|
||||
namespace Ghost.Core;
|
||||
|
||||
|
||||
@@ -15,38 +15,57 @@ internal struct TestEntityQueryJob : IJobEntityParallel<Transform>
|
||||
|
||||
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<Transform>.value);
|
||||
_world.EntityManager.AddComponent(entity1, new Mesh { index = 1 });
|
||||
_world.EntityCommandBuffer.AddComponent(entity1, new Mesh { index = 1 });
|
||||
|
||||
var queryID = new QueryBuilder().WithAll<Transform>().Build(_world);
|
||||
ref var query = ref _world.GetEntityQueryReference(queryID);
|
||||
// var entity2 = _world.EntityManager.CreateEntity(ComponentTypeID<Transform>.value);
|
||||
// _world.EntityManager.SetComponentData(entity2, new Transform { position = new float3(1, 2, 3) });
|
||||
|
||||
var testJob = new TestEntityQueryJob();
|
||||
var handle = query.ScheduleEntityParallel<TestEntityQueryJob, Transform>(_jobScheduler, testJob, Allocator.Temp, 64, JobHandle.Invalid);
|
||||
_jobScheduler.WaitComplete(handle);
|
||||
Console.WriteLine($"Entity {entity1} hash Mesh: {_world.EntityManager.HasComponent<Mesh>(entity1)}");
|
||||
|
||||
query.ForEach<Transform>((e, ref t) =>
|
||||
{
|
||||
Console.WriteLine($"Entity {e} Has Position: {t.position}");
|
||||
});
|
||||
_world.EntityCommandBuffer.Playback();
|
||||
Console.WriteLine($"Entity {entity1} hash Mesh: {_world.EntityManager.HasComponent<Mesh>(entity1)}");
|
||||
|
||||
_world.EntityCommandBuffer.RemoveComponent<Mesh>(entity1);
|
||||
Console.WriteLine($"Entity {entity1} hash Mesh: {_world.EntityManager.HasComponent<Mesh>(entity1)}");
|
||||
|
||||
_world.EntityCommandBuffer.Playback();
|
||||
Console.WriteLine($"Entity {entity1} hash Mesh: {_world.EntityManager.HasComponent<Mesh>(entity1)}");
|
||||
|
||||
// var queryID = new QueryBuilder().WithAll<Transform>().Build(_world);
|
||||
// ref var query = ref _world.GetEntityQueryReference(queryID);
|
||||
|
||||
// var testJob = new TestEntityQueryJob();
|
||||
// var handle = query.ScheduleEntityParallel<TestEntityQueryJob, Transform>(_jobScheduler, testJob, Allocator.Temp, 64, JobHandle.Invalid);
|
||||
// _jobScheduler.WaitComplete(handle);
|
||||
//
|
||||
// query.ForEach<Transform>((e, ref t) =>
|
||||
// {
|
||||
// Console.WriteLine($"Entity {e} Has Position: {t.position}");
|
||||
// });
|
||||
//
|
||||
// foreach (ref var transform in query.GetComponentIterator<Transform>())
|
||||
// {
|
||||
// Console.WriteLine($"Entity Updated Position: {transform.position}");
|
||||
// }
|
||||
//
|
||||
// foreach (var chunk in query.GetChunkIterator())
|
||||
// {
|
||||
// var transforms = chunk.GetComponentData<Transform>();
|
||||
// var entities = chunk.GetEntities();
|
||||
// var bits = chunk.GetEnableBits<Transform>();
|
||||
|
||||
//
|
||||
// var it = bits.GetIterator();
|
||||
// while (it.Next(out var index) && index < chunk.Count)
|
||||
// {
|
||||
@@ -58,6 +77,7 @@ public partial class ArcEntityTest : ITest
|
||||
public void Cleanup()
|
||||
{
|
||||
_world.Dispose();
|
||||
_jobScheduler.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
19
Ghost.Entities/Common.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
if (arcID.IsNotValid)
|
||||
{
|
||||
arcID = _world.CreateArchetype(componentTypeIDs, signatureHash);
|
||||
}
|
||||
|
||||
ref var archetype = ref _world.GetArchetypeReference(arcID);
|
||||
|
||||
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 = World.EmptyArchetypeID,
|
||||
archetypeID = arcID,
|
||||
chunkIndex = chunkIndex,
|
||||
rowIndex = rowIndex
|
||||
}, out var generation);
|
||||
|
||||
var entity = new Entity(id, generation);
|
||||
emptyArchetype.SetEntity(chunkIndex, rowIndex, entity);
|
||||
archetype.SetEntity(chunkIndex, rowIndex, entity);
|
||||
|
||||
return entity;
|
||||
entities[i] = entity;
|
||||
}
|
||||
|
||||
public ResultStatus DestoryEntity(Entity entity)
|
||||
return entities;
|
||||
}
|
||||
|
||||
/// <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)
|
||||
|
||||
@@ -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,14 +61,20 @@ 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 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.
|
||||
@@ -164,6 +159,11 @@ public unsafe partial struct EntityQuery : IIdentifierType, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides an enumerator for iterating over chunks of entities and their component data that match a set of archetypes within a world.
|
||||
/// </summary>
|
||||
public readonly ref struct ChunkIterator
|
||||
{
|
||||
public 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
285
Ghost.Entities/System.cs
Normal 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.");
|
||||
}
|
||||
}
|
||||
1554
Ghost.Entities/Templates/EntityQuery.ComponentIterator.gen.cs
Normal file
1554
Ghost.Entities/Templates/EntityQuery.ComponentIterator.gen.cs
Normal file
File diff suppressed because it is too large
Load Diff
198
Ghost.Entities/Templates/EntityQuery.ComponentIterator.tt
Normal file
198
Ghost.Entities/Templates/EntityQuery.ComponentIterator.tt
Normal 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));
|
||||
}
|
||||
|
||||
<# } #>
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
using Ghost.Core;
|
||||
using Misaki.HighPerformance.Jobs;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
@@ -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,7 +2094,7 @@ public unsafe partial struct EntityQuery
|
||||
|
||||
};
|
||||
|
||||
scheduler.Schedule(ref disposeJob, jobHandle);
|
||||
world.JobScheduler.Schedule(ref disposeJob, jobHandle);
|
||||
|
||||
return jobHandle;
|
||||
}
|
||||
|
||||
@@ -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,7 +180,7 @@ public unsafe partial struct EntityQuery
|
||||
<# } #>
|
||||
};
|
||||
|
||||
scheduler.Schedule(ref disposeJob, jobHandle);
|
||||
world.JobScheduler.Schedule(ref disposeJob, jobHandle);
|
||||
|
||||
return jobHandle;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 #>;
|
||||
<# } #>
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"Logging":
|
||||
{
|
||||
"LogLevel": "Error"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user