Remove old project and continue improving ecs.

Updated packages version;
Removed Ghost.SparseEntities;
Added new EntityQuery.EntityComponentIterator;
Added new thread local command buffer in World;
Changed commands in EntityCommandBuffer from UnsafeList<Command> to UnsafeList<byte> for better performance;
Changed the name of IJobEntityParallel to IJobEntity;
This commit is contained in:
2025-12-09 15:10:10 +09:00
parent 5e276b289d
commit 97d1118caa
38 changed files with 2093 additions and 5165 deletions

View File

@@ -2,37 +2,30 @@ using Ghost.Core;
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
using Misaki.HighPerformance.LowLevel.Utilities;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
namespace Ghost.Entities;
public unsafe class EntityCommandBuffer : IDisposable
{
private enum CommandType
private enum ECBOpCode : byte
{
CreateEntity,
CreateEntityWithComponents,
DestroyEntity,
AddComponent,
RemoveComponent,
SetComponent,
}
private struct Command
{
public UnsafeArray<byte> data;
public Entity entity;
public CommandType type;
public Identifier<IComponent> componentTypeID;
}
private readonly EntityManager _entityManager;
private UnsafeList<Command> _commands; // TODO: Maybe use UnsafeArray<byte> directly to save additional memory allocation in Unsafe<byte> data inside Command struct.
private UnsafeList<byte> _buffer;
private bool _disposed;
public EntityCommandBuffer(EntityManager entityManager)
{
_entityManager = entityManager;
_commands = new UnsafeList<Command>(32, Allocator.Persistent);
_buffer = new UnsafeList<byte>(4096, Allocator.Persistent);
}
~EntityCommandBuffer()
@@ -40,115 +33,165 @@ public unsafe class EntityCommandBuffer : IDisposable
Dispose();
}
private void WriteHeader(ECBOpCode op)
{
_buffer.Add((byte)op);
}
private void Write<T>(T value)
where T : unmanaged
{
var size = sizeof(T);
var idx = _buffer.Count;
if (idx + size > _buffer.Capacity)
{
_buffer.Resize(idx + size);
}
MemoryUtility.MemCpy((byte*)_buffer.GetUnsafePtr() + idx, &value, (nuint)size);
}
private void WriteSpan<T>(ReadOnlySpan<T> span)
where T : unmanaged
{
var size = sizeof(T) * span.Length;
var idx = _buffer.Count;
if (idx + size > _buffer.Capacity)
{
_buffer.Resize(idx + size);
}
fixed (T* ptr = span)
{
MemoryUtility.MemCpy((byte*)_buffer.GetUnsafePtr() + idx, ptr, (nuint)size);
}
}
private T Read<T>(ref int cursor)
where T : unmanaged
{
var size = sizeof(T);
var ptr = (byte*)_buffer.GetUnsafePtr();
var value = *(T*)&ptr[cursor];
cursor += size;
return value;
}
private Span<T> ReadSpan<T>(ref int cursor, int length)
where T : unmanaged
{
var size = sizeof(T) * length;
var ptr = (byte*)_buffer.GetUnsafePtr();
var span = new Span<T>(&ptr[cursor], length);
cursor += size;
return span;
}
private void* ReadBuffer(ref int cursor, int size)
{
var ptr = (byte*)_buffer.GetUnsafePtr();
var bufferPtr = ptr + cursor;
cursor += size;
return bufferPtr;
}
public void CreateEntity()
{
var command = new Command
{
type = CommandType.CreateEntity,
data = default,
entity = default,
componentTypeID = -1
};
_commands.Add(command);
WriteHeader(ECBOpCode.CreateEntity);
}
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.CreateEntity,
data = data,
entity = Entity.Invalid,
componentTypeID = Identifier<IComponent>.Invalid
};
_commands.Add(command);
WriteHeader(ECBOpCode.CreateEntityWithComponents);
Write(componentTypeIDs.Length);
WriteSpan(componentTypeIDs);
}
public void DestroyEntity(Entity entity)
{
_commands.Add(new Command
{
type = CommandType.DestroyEntity,
data = default,
entity = entity,
componentTypeID = Identifier<IComponent>.Invalid
});
WriteHeader(ECBOpCode.DestroyEntity);
Write(entity);
}
public void AddComponent<T>(Entity entity, T component = default)
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.AddComponent,
data = data,
entity = entity,
componentTypeID = ComponentTypeID<T>.value
});
WriteHeader(ECBOpCode.AddComponent);
Write(entity);
Write(ComponentTypeID<T>.value);
Write(component);
}
public void RemoveComponent<T>(Entity entity)
where T : unmanaged, IComponent
{
_commands.Add(new Command
{
type = CommandType.RemoveComponent,
data = default,
entity = entity,
componentTypeID = ComponentTypeID<T>.value
});
WriteHeader(ECBOpCode.RemoveComponent);
Write(entity);
Write(ComponentTypeID<T>.value);
}
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
});
WriteHeader(ECBOpCode.SetComponent);
Write(entity);
Write(ComponentTypeID<T>.value);
Write(component);
}
internal void Playback()
{
foreach (ref var command in _commands)
var cursor = 0;
var length = _buffer.Count;
var ptr = (byte*)_buffer.GetUnsafePtr();
while (cursor < length)
{
switch (command.type)
var op = *(ECBOpCode*)ptr[cursor];
cursor += sizeof(ECBOpCode);
switch (op)
{
case CommandType.CreateEntity:
if (command.data.Count > 0)
{
_entityManager.CreateEntity(MemoryMarshal.Cast<byte, Identifier<IComponent>>(command.data.AsSpan()));
}
else
{
_entityManager.CreateEntity();
}
case ECBOpCode.CreateEntity:
_entityManager.CreateEntity();
break;
case CommandType.DestroyEntity:
_entityManager.DestroyEntity(command.entity);
case ECBOpCode.CreateEntityWithComponents:
var compCount = Read<int>(ref cursor);
var compTypeIDs = ReadSpan<Identifier<IComponent>>(ref cursor, compCount);
_entityManager.CreateEntity(compTypeIDs);
break;
case CommandType.AddComponent:
_entityManager.AddComponent(command.entity, command.componentTypeID, command.data.GetUnsafePtr());
case ECBOpCode.DestroyEntity:
var entityToDestroy = Read<Entity>(ref cursor);
_entityManager.DestroyEntity(entityToDestroy);
break;
case CommandType.RemoveComponent:
_entityManager.RemoveComponent(command.entity, command.componentTypeID);
case ECBOpCode.AddComponent:
var entityToAdd = Read<Entity>(ref cursor);
var addCompTypeID = Read<Identifier<IComponent>>(ref cursor);
var pAddCompData = ReadBuffer(ref cursor, ComponentRegister.GetComponentInfo(addCompTypeID).size);
_entityManager.AddComponent(entityToAdd, addCompTypeID, pAddCompData);
break;
case CommandType.SetComponent:
_entityManager.SetComponent(command.entity, command.componentTypeID, command.data.GetUnsafePtr());
case ECBOpCode.RemoveComponent:
var entityToRemove = Read<Entity>(ref cursor);
var removeCompTypeID = Read<Identifier<IComponent>>(ref cursor);
_entityManager.RemoveComponent(entityToRemove, removeCompTypeID);
break;
case ECBOpCode.SetComponent:
var entityToSet = Read<Entity>(ref cursor);
var setCompTypeID = Read<Identifier<IComponent>>(ref cursor);
var pSetCompData = ReadBuffer(ref cursor, ComponentRegister.GetComponentInfo(setCompTypeID).size);
_entityManager.SetComponent(entityToSet, setCompTypeID, pSetCompData);
break;
}
}
@@ -156,14 +199,10 @@ public unsafe class EntityCommandBuffer : IDisposable
Reset();
}
public void Reset()
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Reset()
{
foreach (ref var command in _commands)
{
command.data.Dispose();
}
_commands.Clear();
_buffer.Clear();
}
public void Dispose()
@@ -173,12 +212,7 @@ public unsafe class EntityCommandBuffer : IDisposable
return;
}
foreach (ref var command in _commands)
{
command.data.Dispose();
}
_commands.Dispose();
_buffer.Dispose();
_disposed = true;
GC.SuppressFinalize(this);

View File

@@ -19,9 +19,7 @@ public readonly ref struct SystemAPI
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);
}
@@ -207,16 +205,6 @@ public abstract class SystemGroup : ISystem
}
}
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();
@@ -227,16 +215,6 @@ public abstract class SystemGroup : ISystem
}
}
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();
@@ -282,4 +260,28 @@ public class SystemManager
throw new InvalidOperationException($"System of type {typeof(T).FullName} not found in SystemManager.");
}
internal void InitializeAll(ref readonly SystemAPI systemAPI)
{
foreach (var system in _systems)
{
system.Initialize(in systemAPI);
}
}
internal void UpdateAll(ref readonly SystemAPI systemAPI)
{
foreach (var system in _systems)
{
system.Update(in systemAPI);
}
}
internal void CleanupAll(ref readonly SystemAPI systemAPI)
{
foreach (var system in _systems)
{
system.Cleanup(in systemAPI);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,199 @@
<#@ 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 EntityComponentIterator<<#= generics #>>
<#= restrictions #>
{
public ref struct QueryItem
{
public Entity entity;
<# for (var j = 0; j < i; j++) { #>
public ref T<#= j #> component<#= j #>;
<# } #>
internal QueryItem(Entity entity, <#= compGenerics #>)
{
this.entity = entity;
<# for (var j = 0; j < i; j++) { #>
this.component<#= j #> = ref component<#= j #>;
<# } #>
}
public void Deconstruct(out Entity entity, <#= deconstrictOutPrams #>)
{
entity = this.entity;
<# 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();
}
public QueryItem Current => new(
*(Entity*)(_chunkBasePtr + _currentArchetype.EntityIDsOffset + _currentEntityIndex * sizeof(Entity)),
<# for (var j = 0; j < i; j++) { #>
ref *(T<#= j #>*)(_compBasePtrs[<#= j #>] + _currentEntityIndex * sizeof(T<#= j #>))<#= j < i - 1 ? "," : "" #>
<# } #>
);
[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 EntityComponentIterator(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 EntityComponentIterator<<#= generics#>> GetEntityComponentIterator<<#= generics#>>()
<#= restrictions #>
{
return new EntityComponentIterator<<#= generics#>>(_matchingArchetypes.AsReadOnly(), _mask, World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success));
}
<# } #>
}

View File

@@ -5,14 +5,14 @@ using Misaki.HighPerformance.LowLevel.Collections;
namespace Ghost.Entities;
public interface IJobEntityParallel<T0>
public interface IJobEntity<T0>
where T0 : unmanaged, IComponent
{
void Execute(Entity entity, ref T0 component0);
}
internal unsafe struct JobEntityBatch<TJob, T0> : IJobParallelFor
where TJob : unmanaged, IJobEntityParallel<T0>
where TJob : unmanaged, IJobEntity<T0>
where T0 : unmanaged, IComponent
{
public TJob userJob;
@@ -48,7 +48,7 @@ internal unsafe struct JobEntityBatch<TJob, T0> : IJobParallelFor
}
}
public interface IJobEntityParallel<T0, T1>
public interface IJobEntity<T0, T1>
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
{
@@ -56,7 +56,7 @@ public interface IJobEntityParallel<T0, T1>
}
internal unsafe struct JobEntityBatch<TJob, T0, T1> : IJobParallelFor
where TJob : unmanaged, IJobEntityParallel<T0, T1>
where TJob : unmanaged, IJobEntity<T0, T1>
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
{
@@ -105,7 +105,7 @@ internal unsafe struct JobEntityBatch<TJob, T0, T1> : IJobParallelFor
}
}
public interface IJobEntityParallel<T0, T1, T2>
public interface IJobEntity<T0, T1, T2>
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
where T2 : unmanaged, IComponent
@@ -114,7 +114,7 @@ public interface IJobEntityParallel<T0, T1, T2>
}
internal unsafe struct JobEntityBatch<TJob, T0, T1, T2> : IJobParallelFor
where TJob : unmanaged, IJobEntityParallel<T0, T1, T2>
where TJob : unmanaged, IJobEntity<T0, T1, T2>
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
where T2 : unmanaged, IComponent
@@ -176,7 +176,7 @@ internal unsafe struct JobEntityBatch<TJob, T0, T1, T2> : IJobParallelFor
}
}
public interface IJobEntityParallel<T0, T1, T2, T3>
public interface IJobEntity<T0, T1, T2, T3>
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
where T2 : unmanaged, IComponent
@@ -186,7 +186,7 @@ public interface IJobEntityParallel<T0, T1, T2, T3>
}
internal unsafe struct JobEntityBatch<TJob, T0, T1, T2, T3> : IJobParallelFor
where TJob : unmanaged, IJobEntityParallel<T0, T1, T2, T3>
where TJob : unmanaged, IJobEntity<T0, T1, T2, T3>
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
where T2 : unmanaged, IComponent
@@ -261,7 +261,7 @@ internal unsafe struct JobEntityBatch<TJob, T0, T1, T2, T3> : IJobParallelFor
}
}
public interface IJobEntityParallel<T0, T1, T2, T3, T4>
public interface IJobEntity<T0, T1, T2, T3, T4>
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
where T2 : unmanaged, IComponent
@@ -272,7 +272,7 @@ public interface IJobEntityParallel<T0, T1, T2, T3, T4>
}
internal unsafe struct JobEntityBatch<TJob, T0, T1, T2, T3, T4> : IJobParallelFor
where TJob : unmanaged, IJobEntityParallel<T0, T1, T2, T3, T4>
where TJob : unmanaged, IJobEntity<T0, T1, T2, T3, T4>
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
where T2 : unmanaged, IComponent
@@ -360,7 +360,7 @@ internal unsafe struct JobEntityBatch<TJob, T0, T1, T2, T3, T4> : IJobParallelFo
}
}
public interface IJobEntityParallel<T0, T1, T2, T3, T4, T5>
public interface IJobEntity<T0, T1, T2, T3, T4, T5>
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
where T2 : unmanaged, IComponent
@@ -372,7 +372,7 @@ public interface IJobEntityParallel<T0, T1, T2, T3, T4, T5>
}
internal unsafe struct JobEntityBatch<TJob, T0, T1, T2, T3, T4, T5> : IJobParallelFor
where TJob : unmanaged, IJobEntityParallel<T0, T1, T2, T3, T4, T5>
where TJob : unmanaged, IJobEntity<T0, T1, T2, T3, T4, T5>
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
where T2 : unmanaged, IComponent
@@ -473,7 +473,7 @@ internal unsafe struct JobEntityBatch<TJob, T0, T1, T2, T3, T4, T5> : IJobParall
}
}
public interface IJobEntityParallel<T0, T1, T2, T3, T4, T5, T6>
public interface IJobEntity<T0, T1, T2, T3, T4, T5, T6>
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
where T2 : unmanaged, IComponent
@@ -486,7 +486,7 @@ public interface IJobEntityParallel<T0, T1, T2, T3, T4, T5, T6>
}
internal unsafe struct JobEntityBatch<TJob, T0, T1, T2, T3, T4, T5, T6> : IJobParallelFor
where TJob : unmanaged, IJobEntityParallel<T0, T1, T2, T3, T4, T5, T6>
where TJob : unmanaged, IJobEntity<T0, T1, T2, T3, T4, T5, T6>
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
where T2 : unmanaged, IComponent
@@ -600,7 +600,7 @@ internal unsafe struct JobEntityBatch<TJob, T0, T1, T2, T3, T4, T5, T6> : IJobPa
}
}
public interface IJobEntityParallel<T0, T1, T2, T3, T4, T5, T6, T7>
public interface IJobEntity<T0, T1, T2, T3, T4, T5, T6, T7>
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
where T2 : unmanaged, IComponent
@@ -614,7 +614,7 @@ public interface IJobEntityParallel<T0, T1, T2, T3, T4, T5, T6, T7>
}
internal unsafe struct JobEntityBatch<TJob, T0, T1, T2, T3, T4, T5, T6, T7> : IJobParallelFor
where TJob : unmanaged, IJobEntityParallel<T0, T1, T2, T3, T4, T5, T6, T7>
where TJob : unmanaged, IJobEntity<T0, T1, T2, T3, T4, T5, T6, T7>
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
where T2 : unmanaged, IComponent
@@ -765,7 +765,7 @@ public unsafe partial struct EntityQuery
}
public JobHandle ScheduleEntityParallel<TJob, T0>(TJob jobData, Allocator allocator, int batchSize, JobHandle dependency)
where TJob : unmanaged, IJobEntityParallel<T0>
where TJob : unmanaged, IJobEntity<T0>
where T0 : unmanaged, IComponent
{
var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success);
@@ -867,7 +867,7 @@ public unsafe partial struct EntityQuery
}
public JobHandle ScheduleEntityParallel<TJob, T0, T1>(TJob jobData, Allocator allocator, int batchSize, JobHandle dependency)
where TJob : unmanaged, IJobEntityParallel<T0, T1>
where TJob : unmanaged, IJobEntity<T0, T1>
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
{
@@ -990,7 +990,7 @@ public unsafe partial struct EntityQuery
}
public JobHandle ScheduleEntityParallel<TJob, T0, T1, T2>(TJob jobData, Allocator allocator, int batchSize, JobHandle dependency)
where TJob : unmanaged, IJobEntityParallel<T0, T1, T2>
where TJob : unmanaged, IJobEntity<T0, T1, T2>
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
where T2 : unmanaged, IComponent
@@ -1134,7 +1134,7 @@ public unsafe partial struct EntityQuery
}
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 TJob : unmanaged, IJobEntity<T0, T1, T2, T3>
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
where T2 : unmanaged, IComponent
@@ -1299,7 +1299,7 @@ public unsafe partial struct EntityQuery
}
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 TJob : unmanaged, IJobEntity<T0, T1, T2, T3, T4>
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
where T2 : unmanaged, IComponent
@@ -1485,7 +1485,7 @@ public unsafe partial struct EntityQuery
}
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 TJob : unmanaged, IJobEntity<T0, T1, T2, T3, T4, T5>
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
where T2 : unmanaged, IComponent
@@ -1692,7 +1692,7 @@ public unsafe partial struct EntityQuery
}
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 TJob : unmanaged, IJobEntity<T0, T1, T2, T3, T4, T5, T6>
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
where T2 : unmanaged, IComponent
@@ -1920,7 +1920,7 @@ public unsafe partial struct EntityQuery
}
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 TJob : unmanaged, IJobEntity<T0, T1, T2, T3, T4, T5, T6, T7>
where T0 : unmanaged, IComponent
where T1 : unmanaged, IComponent
where T2 : unmanaged, IComponent

View File

@@ -16,14 +16,14 @@ namespace Ghost.Entities;
var generics = AppendGenerics(i);
var restrictions = AppendGenericRestrictionsMultiline(i, "unmanaged, IComponent", 1);
#>
public interface IJobEntityParallel<<#= generics #>>
public interface IJobEntity<<#= generics #>>
<#= restrictions #>
{
void Execute(Entity entity, <#= AppendParameters(i, "ref T{0} component{0}") #>);
}
internal unsafe struct JobEntityBatch<TJob, <#= generics #>> : IJobParallelFor
where TJob : unmanaged, IJobEntityParallel<<#= generics #>>
where TJob : unmanaged, IJobEntity<<#= generics #>>
<#= restrictions #>
{
public TJob userJob;
@@ -101,7 +101,7 @@ public unsafe partial struct EntityQuery
}
public JobHandle ScheduleEntityParallel<TJob, <#= generics #>>(TJob jobData, Allocator allocator, int batchSize, JobHandle dependency)
where TJob : unmanaged, IJobEntityParallel<<#= generics #>>
where TJob : unmanaged, IJobEntity<<#= generics #>>
<#= restrictions #>
{
var world = World.GetWorld(_worldID).GetValueOrThrow(ResultStatus.Success);

View File

@@ -72,6 +72,7 @@ public partial class World : IIdentifierType, IDisposable, IEquatable<World>
private readonly EntityManager _entityManager;
private readonly EntityCommandBuffer _entityCommandBuffer;
private readonly EntityCommandBuffer[] _threadLocalECBs;
private UnsafeList<Archetype> _archetypes;
private UnsafeList<EntityQuery> _entityQueries;
@@ -83,9 +84,24 @@ public partial class World : IIdentifierType, IDisposable, IEquatable<World>
internal int ArchetypeCount => _archetypes.Count;
/// <summary>
/// Gets the unique identifier of this world.
/// </summary>
public Identifier<World> ID => _id;
/// <summary>
/// Gets the job scheduler associated with this world.
/// </summary>
public JobScheduler JobScheduler => _jobScheduler;
/// <summary>
/// Gets the publicntity manager for this world.
/// </summary>
public EntityManager EntityManager => _entityManager;
/// <summary>
/// Gets the main entity command buffer for this world.
/// </summary>
public EntityCommandBuffer EntityCommandBuffer => _entityCommandBuffer;
private World(Identifier<World> id, int entityCapacity, JobScheduler jobScheduler)
@@ -101,6 +117,12 @@ public partial class World : IIdentifierType, IDisposable, IEquatable<World>
_entityManager = new EntityManager(this, entityCapacity);
_entityCommandBuffer = new EntityCommandBuffer(_entityManager);
_threadLocalECBs = new EntityCommandBuffer[jobScheduler.WorkerCount];
for (var i = 0; i < jobScheduler.WorkerCount; i++)
{
_threadLocalECBs[i] = new EntityCommandBuffer(_entityManager);
}
// Create the empty archetype
CreateArchetype(ReadOnlySpan<Identifier<IComponent>>.Empty, 0);
@@ -171,12 +193,34 @@ public partial class World : IIdentifierType, IDisposable, IEquatable<World>
return Identifier<EntityQuery>.Invalid;
}
internal void PlaybackEntityCommandBuffers()
{
_entityCommandBuffer.Playback();
for (var i = 0; i < _threadLocalECBs.Length; i++)
{
_threadLocalECBs[i].Playback();
}
}
/// <summary>
/// Gets a reference to the entity query with the specified identifier.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref EntityQuery GetEntityQueryReference(Identifier<EntityQuery> id)
{
return ref _entityQueries[id.value];
}
/// <summary>
/// Gets the thread-local entity command buffer for the specified thread index.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EntityCommandBuffer GetThreadLocalEntityCommandBuffer(int threadIndex)
{
return _threadLocalECBs[threadIndex];
}
public bool Equals(World? other)
{
return other is not null && _id == other._id;
@@ -221,6 +265,10 @@ public partial class World : IIdentifierType, IDisposable, IEquatable<World>
_entityManager.Dispose();
_entityCommandBuffer.Dispose();
for (var i = 0; i < _threadLocalECBs.Length; i++)
{
_threadLocalECBs[i].Dispose();
}
_archetypes.Dispose();
_entityQueries.Dispose();