Add entities SerializationTest
This commit is contained in:
@@ -30,7 +30,6 @@ internal class WorldNodeSerializer : CustomSerializer<WorldNode>
|
|||||||
writer.WriteObject(() =>
|
writer.WriteObject(() =>
|
||||||
{
|
{
|
||||||
writer.WriteString(Property.NAME, value.Name);
|
writer.WriteString(Property.NAME, value.Name);
|
||||||
|
|
||||||
writer.WriteStartArray(Property.ENTITIES);
|
writer.WriteStartArray(Property.ENTITIES);
|
||||||
|
|
||||||
for (var i = 0; i < value.World.ArchetypeCount; i++)
|
for (var i = 0; i < value.World.ArchetypeCount; i++)
|
||||||
@@ -52,7 +51,9 @@ internal class WorldNodeSerializer : CustomSerializer<WorldNode>
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.WriteStartObject(type.AssemblyQualifiedName);
|
writer.WriteStartObject();
|
||||||
|
writer.WriteString("Type", type.AssemblyQualifiedName);
|
||||||
|
writer.WritePropertyName("Data");
|
||||||
|
|
||||||
var pComponentData = chunk.GetUnsafePtr() + layout.offset + (k * size);
|
var pComponentData = chunk.GetUnsafePtr() + layout.offset + (k * size);
|
||||||
ComponentSerializerRegistry.SerializeJson(layout.componentID, writer, pComponentData, options);
|
ComponentSerializerRegistry.SerializeJson(layout.componentID, writer, pComponentData, options);
|
||||||
|
|||||||
@@ -98,9 +98,19 @@ public partial class EntityQueryTest : ITest
|
|||||||
public struct Transform : IEnableableComponent
|
public struct Transform : IEnableableComponent
|
||||||
{
|
{
|
||||||
public float3 position;
|
public float3 position;
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"Position: {position}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct Mesh : IComponent
|
public struct Mesh : IComponent
|
||||||
{
|
{
|
||||||
public int index;
|
public int index;
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"Index: {index}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,7 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<PublishAot>True</PublishAot>
|
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||||
<PublishTrimmed>True</PublishTrimmed>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ using Ghost.Test.Core;
|
|||||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
|
|
||||||
AllocationManager.EnableDebugLayer();
|
AllocationManager.EnableDebugLayer();
|
||||||
TestRunner.Run<SystemTest>();
|
TestRunner.Run<SerializationTest>();
|
||||||
AllocationManager.Dispose();
|
AllocationManager.Dispose();
|
||||||
|
|||||||
157
Ghost.Entities.Test/SerializationTest.cs
Normal file
157
Ghost.Entities.Test/SerializationTest.cs
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
using Ghost.Test.Core;
|
||||||
|
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
|
using Misaki.HighPerformance.Mathematics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization.Metadata;
|
||||||
|
|
||||||
|
namespace Ghost.Entities.Test;
|
||||||
|
|
||||||
|
public class SerializationTest : ITest
|
||||||
|
{
|
||||||
|
private World _world = null!;
|
||||||
|
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
_world = World.Create();
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe void Run()
|
||||||
|
{
|
||||||
|
using var scope = AllocationManager.CreateStackScope();
|
||||||
|
var set1 = new ComponentSet(scope.AllocationHandle, ComponentTypeID<Transform>.Value);
|
||||||
|
var set2 = new ComponentSet(scope.AllocationHandle, ComponentTypeID<Transform>.Value, ComponentTypeID<Mesh>.Value);
|
||||||
|
|
||||||
|
var e1 = _world.EntityManager.CreateEntity(set1);
|
||||||
|
var e2 = _world.EntityManager.CreateEntity(set2);
|
||||||
|
|
||||||
|
_world.EntityManager.SetComponent(e1, new Transform { position = new float3(1, 2, 3) });
|
||||||
|
_world.EntityManager.SetComponent(e2, new Transform { position = new float3(4, 5, 6) });
|
||||||
|
_world.EntityManager.SetComponent(e2, new Mesh { index = 42 });
|
||||||
|
|
||||||
|
using var stream = new MemoryStream();
|
||||||
|
var serializeOptions = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
IncludeFields = true,
|
||||||
|
IgnoreReadOnlyProperties = true,
|
||||||
|
TypeInfoResolver = new DefaultJsonTypeInfoResolver
|
||||||
|
{
|
||||||
|
Modifiers = { typeInfo =>
|
||||||
|
{
|
||||||
|
// Remove everything from the serialization list that is not a field
|
||||||
|
foreach (var property in typeInfo.Properties)
|
||||||
|
{
|
||||||
|
if (property.AttributeProvider is not System.Reflection.FieldInfo)
|
||||||
|
{
|
||||||
|
property.ShouldSerialize = (_, _) => false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Indented = true });
|
||||||
|
|
||||||
|
writer.WriteStartObject();
|
||||||
|
writer.WriteString("Name", "world 1");
|
||||||
|
writer.WriteStartArray("Entities");
|
||||||
|
|
||||||
|
for (var i = 0; i < _world.ArchetypeCount; i++)
|
||||||
|
{
|
||||||
|
ref var archetype = ref _world.GetArchetypeReference(i);
|
||||||
|
|
||||||
|
for (var j = 0; j < archetype.ChunkCount; j++)
|
||||||
|
{
|
||||||
|
ref var chunk = ref archetype.GetChunkReference(j);
|
||||||
|
for (var k = 0; k < chunk._count; k++)
|
||||||
|
{
|
||||||
|
writer.WriteStartObject();
|
||||||
|
|
||||||
|
var entity = *(Entity*)(chunk.GetUnsafePtr() + archetype.EntityIDsOffset + k * sizeof(Entity));
|
||||||
|
writer.WriteNumber("ID", entity.ID);
|
||||||
|
writer.WriteStartArray("Components");
|
||||||
|
|
||||||
|
foreach (var layout in archetype._layouts)
|
||||||
|
{
|
||||||
|
var type = ComponentRegistry.s_runtimeIDToType[layout.componentID];
|
||||||
|
var size = ComponentRegistry.GetComponentInfo(layout.componentID).size;
|
||||||
|
|
||||||
|
if (type.AssemblyQualifiedName == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteStartObject();
|
||||||
|
writer.WriteString("Type", type.AssemblyQualifiedName);
|
||||||
|
writer.WritePropertyName("Data");
|
||||||
|
|
||||||
|
var pComponentData = chunk.GetUnsafePtr() + layout.offset + (k * size);
|
||||||
|
var instace = Marshal.PtrToStructure((nint)pComponentData, type);
|
||||||
|
JsonSerializer.Serialize(writer, instace, type, serializeOptions);
|
||||||
|
|
||||||
|
writer.WriteEndObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteEndArray();
|
||||||
|
writer.WriteEndObject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteEndArray();
|
||||||
|
writer.WriteEndObject();
|
||||||
|
|
||||||
|
writer.Flush();
|
||||||
|
|
||||||
|
var data = stream.ToArray();
|
||||||
|
|
||||||
|
var json = System.Text.Encoding.UTF8.GetString(data);
|
||||||
|
Console.WriteLine(json);
|
||||||
|
|
||||||
|
|
||||||
|
var reader = new Utf8JsonReader(data);
|
||||||
|
|
||||||
|
var root = JsonDocument.ParseValue(ref reader).RootElement;
|
||||||
|
var name = root.GetProperty("Name").GetString();
|
||||||
|
Console.WriteLine($"Deserialized World Name: {name}");
|
||||||
|
|
||||||
|
var entityData = new List<(int EntityID, Type ComponentType, object Instance)>();
|
||||||
|
|
||||||
|
foreach (var entityElement in root.GetProperty("Entities").EnumerateArray())
|
||||||
|
{
|
||||||
|
var id = entityElement.GetProperty("ID").GetInt32();
|
||||||
|
|
||||||
|
// Access the new "Components" array
|
||||||
|
var componentsElement = entityElement.GetProperty("Components");
|
||||||
|
|
||||||
|
foreach (var componentElement in componentsElement.EnumerateArray())
|
||||||
|
{
|
||||||
|
var typeName = componentElement.GetProperty("Type").GetString();
|
||||||
|
var dataElement = componentElement.GetProperty("Data");
|
||||||
|
|
||||||
|
var type = Type.GetType(typeName!);
|
||||||
|
if (type == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var instance = dataElement.Deserialize(type, serializeOptions);
|
||||||
|
if (instance != null)
|
||||||
|
{
|
||||||
|
entityData.Add((id, type, instance));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var (id, type, instance) in entityData)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Entity ID: {id}, Component: {type.Name}, Data: {instance}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Cleanup()
|
||||||
|
{
|
||||||
|
_world.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +1,14 @@
|
|||||||
using Ghost.Test.Core;
|
using Ghost.Test.Core;
|
||||||
using Misaki.HighPerformance.Jobs;
|
|
||||||
|
|
||||||
namespace Ghost.Entities.Test;
|
namespace Ghost.Entities.Test;
|
||||||
|
|
||||||
internal class SystemTest : ITest
|
internal class SystemTest : ITest
|
||||||
{
|
{
|
||||||
private JobScheduler _jobScheduler = null!;
|
|
||||||
private World _world = null!;
|
private World _world = null!;
|
||||||
|
|
||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
_jobScheduler = new JobScheduler(4);
|
_world = World.Create();
|
||||||
_world = World.Create(_jobScheduler);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Run()
|
public void Run()
|
||||||
@@ -29,8 +26,6 @@ internal class SystemTest : ITest
|
|||||||
public void Cleanup()
|
public void Cleanup()
|
||||||
{
|
{
|
||||||
_world.Dispose();
|
_world.Dispose();
|
||||||
_jobScheduler.Dispose();
|
|
||||||
JobScheduler.ReleaseTempAllocator();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -371,6 +371,16 @@ internal unsafe struct Archetype : IDisposable
|
|||||||
_chunks.Add(newChunk);
|
_chunks.Add(newChunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public readonly Entity GetEntity(int chunkIndex, int rowIndex)
|
||||||
|
{
|
||||||
|
var chunk = _chunks[chunkIndex];
|
||||||
|
var chunkBase = chunk.GetUnsafePtr();
|
||||||
|
var src = chunkBase + _entityIdsOffset + (sizeof(Entity) * rowIndex);
|
||||||
|
|
||||||
|
return *(Entity*)src;
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public readonly void SetEntity(int chunkIndex, int rowIndex, Entity entity)
|
public readonly void SetEntity(int chunkIndex, int rowIndex, Entity entity)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -161,16 +161,17 @@ public unsafe partial class EntityManager : IDisposable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create multiple entities with specified components.
|
/// Create multiple entities with specified components.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="allocator">The allocator to use for the returned array.</param>
|
/// <param name="entities">The span to store the created entities.</param>
|
||||||
/// <param name="set">A set of component type IDs to add to the entities.</param>
|
/// <param name="set">A set of component type IDs to add to the entities.</param>
|
||||||
/// <returns>An array of the created entities.</returns>
|
/// <returns>An array of the created entities.</returns>
|
||||||
public void CreateEntities(Span<Entity> entities, ComponentSet set)
|
public void CreateEntities(Span<Entity> entities, ComponentSet set)
|
||||||
{
|
{
|
||||||
var arcID = _world.GetArchetypeIDBySignatureHash(set.GetHashCode());
|
var hash = set.GetHashCode();
|
||||||
|
var arcID = _world.GetArchetypeIDBySignatureHash(hash);
|
||||||
|
|
||||||
if (arcID.IsInvalid)
|
if (arcID.IsInvalid)
|
||||||
{
|
{
|
||||||
arcID = _world.CreateArchetype(set.Components, set.GetHashCode());
|
arcID = _world.CreateArchetype(set.Components, hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
ref var archetype = ref _world.GetArchetypeReference(arcID);
|
ref var archetype = ref _world.GetArchetypeReference(arcID);
|
||||||
|
|||||||
@@ -48,6 +48,10 @@ public unsafe partial struct EntityQuery
|
|||||||
where TJob : unmanaged, IJobChunk
|
where TJob : unmanaged, IJobChunk
|
||||||
{
|
{
|
||||||
var world = World.GetWorld(_worldID).GetValueOrThrow();
|
var world = World.GetWorld(_worldID).GetValueOrThrow();
|
||||||
|
if (world.JobScheduler == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The World has no JobScheduler assigned.");
|
||||||
|
}
|
||||||
|
|
||||||
var chunkInfos = new UnsafeList<ChunkInfo>(_matchingArchetypes.Count * 2, JobScheduler.TempAllocatorHandle);
|
var chunkInfos = new UnsafeList<ChunkInfo>(_matchingArchetypes.Count * 2, JobScheduler.TempAllocatorHandle);
|
||||||
|
|
||||||
|
|||||||
@@ -597,7 +597,7 @@ public ref partial struct QueryBuilder
|
|||||||
return queryID;
|
return queryID;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Dispose()
|
private readonly void Dispose()
|
||||||
{
|
{
|
||||||
_scope.Dispose();
|
_scope.Dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1096,6 +1096,10 @@ public unsafe partial struct EntityQuery
|
|||||||
where T0 : unmanaged, IComponent
|
where T0 : unmanaged, IComponent
|
||||||
{
|
{
|
||||||
var world = World.GetWorld(_worldID).GetValueOrThrow();
|
var world = World.GetWorld(_worldID).GetValueOrThrow();
|
||||||
|
if (world.JobScheduler == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The World has no JobScheduler assigned.");
|
||||||
|
}
|
||||||
|
|
||||||
// 1. Flatten the World
|
// 1. Flatten the World
|
||||||
var chunks = new UnsafeList<IntPtr>(128, JobScheduler.TempAllocatorHandle);
|
var chunks = new UnsafeList<IntPtr>(128, JobScheduler.TempAllocatorHandle);
|
||||||
@@ -1229,6 +1233,10 @@ public unsafe partial struct EntityQuery
|
|||||||
where T1 : unmanaged, IComponent
|
where T1 : unmanaged, IComponent
|
||||||
{
|
{
|
||||||
var world = World.GetWorld(_worldID).GetValueOrThrow();
|
var world = World.GetWorld(_worldID).GetValueOrThrow();
|
||||||
|
if (world.JobScheduler == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The World has no JobScheduler assigned.");
|
||||||
|
}
|
||||||
|
|
||||||
// 1. Flatten the World
|
// 1. Flatten the World
|
||||||
var chunks = new UnsafeList<IntPtr>(128, JobScheduler.TempAllocatorHandle);
|
var chunks = new UnsafeList<IntPtr>(128, JobScheduler.TempAllocatorHandle);
|
||||||
@@ -1389,6 +1397,10 @@ public unsafe partial struct EntityQuery
|
|||||||
where T2 : unmanaged, IComponent
|
where T2 : unmanaged, IComponent
|
||||||
{
|
{
|
||||||
var world = World.GetWorld(_worldID).GetValueOrThrow();
|
var world = World.GetWorld(_worldID).GetValueOrThrow();
|
||||||
|
if (world.JobScheduler == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The World has no JobScheduler assigned.");
|
||||||
|
}
|
||||||
|
|
||||||
// 1. Flatten the World
|
// 1. Flatten the World
|
||||||
var chunks = new UnsafeList<IntPtr>(128, JobScheduler.TempAllocatorHandle);
|
var chunks = new UnsafeList<IntPtr>(128, JobScheduler.TempAllocatorHandle);
|
||||||
@@ -1576,6 +1588,10 @@ public unsafe partial struct EntityQuery
|
|||||||
where T3 : unmanaged, IComponent
|
where T3 : unmanaged, IComponent
|
||||||
{
|
{
|
||||||
var world = World.GetWorld(_worldID).GetValueOrThrow();
|
var world = World.GetWorld(_worldID).GetValueOrThrow();
|
||||||
|
if (world.JobScheduler == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The World has no JobScheduler assigned.");
|
||||||
|
}
|
||||||
|
|
||||||
// 1. Flatten the World
|
// 1. Flatten the World
|
||||||
var chunks = new UnsafeList<IntPtr>(128, JobScheduler.TempAllocatorHandle);
|
var chunks = new UnsafeList<IntPtr>(128, JobScheduler.TempAllocatorHandle);
|
||||||
@@ -1790,6 +1806,10 @@ public unsafe partial struct EntityQuery
|
|||||||
where T4 : unmanaged, IComponent
|
where T4 : unmanaged, IComponent
|
||||||
{
|
{
|
||||||
var world = World.GetWorld(_worldID).GetValueOrThrow();
|
var world = World.GetWorld(_worldID).GetValueOrThrow();
|
||||||
|
if (world.JobScheduler == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The World has no JobScheduler assigned.");
|
||||||
|
}
|
||||||
|
|
||||||
// 1. Flatten the World
|
// 1. Flatten the World
|
||||||
var chunks = new UnsafeList<IntPtr>(128, JobScheduler.TempAllocatorHandle);
|
var chunks = new UnsafeList<IntPtr>(128, JobScheduler.TempAllocatorHandle);
|
||||||
@@ -2031,6 +2051,10 @@ public unsafe partial struct EntityQuery
|
|||||||
where T5 : unmanaged, IComponent
|
where T5 : unmanaged, IComponent
|
||||||
{
|
{
|
||||||
var world = World.GetWorld(_worldID).GetValueOrThrow();
|
var world = World.GetWorld(_worldID).GetValueOrThrow();
|
||||||
|
if (world.JobScheduler == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The World has no JobScheduler assigned.");
|
||||||
|
}
|
||||||
|
|
||||||
// 1. Flatten the World
|
// 1. Flatten the World
|
||||||
var chunks = new UnsafeList<IntPtr>(128, JobScheduler.TempAllocatorHandle);
|
var chunks = new UnsafeList<IntPtr>(128, JobScheduler.TempAllocatorHandle);
|
||||||
@@ -2299,6 +2323,10 @@ public unsafe partial struct EntityQuery
|
|||||||
where T6 : unmanaged, IComponent
|
where T6 : unmanaged, IComponent
|
||||||
{
|
{
|
||||||
var world = World.GetWorld(_worldID).GetValueOrThrow();
|
var world = World.GetWorld(_worldID).GetValueOrThrow();
|
||||||
|
if (world.JobScheduler == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The World has no JobScheduler assigned.");
|
||||||
|
}
|
||||||
|
|
||||||
// 1. Flatten the World
|
// 1. Flatten the World
|
||||||
var chunks = new UnsafeList<IntPtr>(128, JobScheduler.TempAllocatorHandle);
|
var chunks = new UnsafeList<IntPtr>(128, JobScheduler.TempAllocatorHandle);
|
||||||
@@ -2594,6 +2622,10 @@ public unsafe partial struct EntityQuery
|
|||||||
where T7 : unmanaged, IComponent
|
where T7 : unmanaged, IComponent
|
||||||
{
|
{
|
||||||
var world = World.GetWorld(_worldID).GetValueOrThrow();
|
var world = World.GetWorld(_worldID).GetValueOrThrow();
|
||||||
|
if (world.JobScheduler == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The World has no JobScheduler assigned.");
|
||||||
|
}
|
||||||
|
|
||||||
// 1. Flatten the World
|
// 1. Flatten the World
|
||||||
var chunks = new UnsafeList<IntPtr>(128, JobScheduler.TempAllocatorHandle);
|
var chunks = new UnsafeList<IntPtr>(128, JobScheduler.TempAllocatorHandle);
|
||||||
|
|||||||
@@ -126,6 +126,10 @@ public unsafe partial struct EntityQuery
|
|||||||
<#= restrictions #>
|
<#= restrictions #>
|
||||||
{
|
{
|
||||||
var world = World.GetWorld(_worldID).GetValueOrThrow();
|
var world = World.GetWorld(_worldID).GetValueOrThrow();
|
||||||
|
if (world.JobScheduler == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The World has no JobScheduler assigned.");
|
||||||
|
}
|
||||||
|
|
||||||
// 1. Flatten the World
|
// 1. Flatten the World
|
||||||
var chunks = new UnsafeList<IntPtr>(128, JobScheduler.TempAllocatorHandle);
|
var chunks = new UnsafeList<IntPtr>(128, JobScheduler.TempAllocatorHandle);
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ public partial class World
|
|||||||
private static readonly List<World?> s_worlds = new(4);
|
private static readonly List<World?> s_worlds = new(4);
|
||||||
private static readonly Queue<Identifier<World>> s_freeWorldSlots = new();
|
private static readonly Queue<Identifier<World>> s_freeWorldSlots = new();
|
||||||
|
|
||||||
internal static Identifier<Archetype> EmptyArchetypeID => new (0);
|
internal static Identifier<Archetype> EmptyArchetypeID => new(0);
|
||||||
|
|
||||||
public static int WorldCount => s_worlds.Count - s_freeWorldSlots.Count;
|
public static int WorldCount => s_worlds.Count - s_freeWorldSlots.Count;
|
||||||
|
|
||||||
public static World Create(JobScheduler jobScheduler, int entityCapacity = 16)
|
public static World Create(JobScheduler? jobScheduler = null, int entityCapacity = 16)
|
||||||
{
|
{
|
||||||
lock (s_worlds)
|
lock (s_worlds)
|
||||||
{
|
{
|
||||||
@@ -79,11 +79,11 @@ public partial class World
|
|||||||
public partial class World : IDisposable, IEquatable<World>
|
public partial class World : IDisposable, IEquatable<World>
|
||||||
{
|
{
|
||||||
private readonly Identifier<World> _id;
|
private readonly Identifier<World> _id;
|
||||||
private readonly JobScheduler _jobScheduler;
|
private readonly JobScheduler? _jobScheduler;
|
||||||
|
|
||||||
private readonly EntityManager _entityManager;
|
private readonly EntityManager _entityManager;
|
||||||
private readonly EntityCommandBuffer _entityCommandBuffer;
|
private readonly EntityCommandBuffer _entityCommandBuffer;
|
||||||
private readonly EntityCommandBuffer[] _threadLocalECBs;
|
private readonly EntityCommandBuffer[]? _threadLocalECBs;
|
||||||
|
|
||||||
private readonly SystemManager _systemManager;
|
private readonly SystemManager _systemManager;
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ public partial class World : IDisposable, IEquatable<World>
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the job scheduler associated with this world.
|
/// Gets the job scheduler associated with this world.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public JobScheduler JobScheduler => _jobScheduler;
|
public JobScheduler? JobScheduler => _jobScheduler;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the publicntity manager for this world.
|
/// Gets the publicntity manager for this world.
|
||||||
@@ -131,14 +131,13 @@ public partial class World : IDisposable, IEquatable<World>
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
public EntityCommandBuffer EntityCommandBuffer => _entityCommandBuffer;
|
public EntityCommandBuffer EntityCommandBuffer => _entityCommandBuffer;
|
||||||
|
|
||||||
private World(Identifier<World> id, int entityCapacity, JobScheduler jobScheduler)
|
private World(Identifier<World> id, int entityCapacity, JobScheduler? jobScheduler)
|
||||||
{
|
{
|
||||||
_id = id;
|
_id = id;
|
||||||
_jobScheduler = jobScheduler;
|
_jobScheduler = jobScheduler;
|
||||||
|
|
||||||
_entityManager = new EntityManager(this, entityCapacity);
|
_entityManager = new EntityManager(this, entityCapacity);
|
||||||
_entityCommandBuffer = new EntityCommandBuffer(_entityManager);
|
_entityCommandBuffer = new EntityCommandBuffer(_entityManager);
|
||||||
_threadLocalECBs = new EntityCommandBuffer[jobScheduler.WorkerCount];
|
|
||||||
|
|
||||||
_systemManager = new SystemManager(this);
|
_systemManager = new SystemManager(this);
|
||||||
|
|
||||||
@@ -148,9 +147,13 @@ public partial class World : IDisposable, IEquatable<World>
|
|||||||
_archetypeLookup = new UnsafeHashMap<int, Identifier<Archetype>>(16, Allocator.Persistent);
|
_archetypeLookup = new UnsafeHashMap<int, Identifier<Archetype>>(16, Allocator.Persistent);
|
||||||
_querieLookup = new UnsafeHashMap<int, Identifier<EntityQuery>>(16, Allocator.Persistent);
|
_querieLookup = new UnsafeHashMap<int, Identifier<EntityQuery>>(16, Allocator.Persistent);
|
||||||
|
|
||||||
for (var i = 0; i < jobScheduler.WorkerCount; i++)
|
if (jobScheduler != null)
|
||||||
{
|
{
|
||||||
_threadLocalECBs[i] = 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
|
// Create the empty archetype
|
||||||
@@ -227,9 +230,12 @@ public partial class World : IDisposable, IEquatable<World>
|
|||||||
{
|
{
|
||||||
_entityCommandBuffer.Playback();
|
_entityCommandBuffer.Playback();
|
||||||
|
|
||||||
for (var i = 0; i < _threadLocalECBs.Length; i++)
|
if (_threadLocalECBs != null)
|
||||||
{
|
{
|
||||||
_threadLocalECBs[i].Playback();
|
for (var i = 0; i < _threadLocalECBs.Length; i++)
|
||||||
|
{
|
||||||
|
_threadLocalECBs[i].Playback();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,6 +260,11 @@ public partial class World : IDisposable, IEquatable<World>
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public EntityCommandBuffer GetThreadLocalEntityCommandBuffer(int threadIndex)
|
public EntityCommandBuffer GetThreadLocalEntityCommandBuffer(int threadIndex)
|
||||||
{
|
{
|
||||||
|
if (_threadLocalECBs == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("This world does not have a JobScheduler associated with it.");
|
||||||
|
}
|
||||||
|
|
||||||
return _threadLocalECBs[threadIndex];
|
return _threadLocalECBs[threadIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,9 +312,13 @@ public partial class World : IDisposable, IEquatable<World>
|
|||||||
|
|
||||||
_entityManager.Dispose();
|
_entityManager.Dispose();
|
||||||
_entityCommandBuffer.Dispose();
|
_entityCommandBuffer.Dispose();
|
||||||
foreach (var v in _threadLocalECBs)
|
|
||||||
|
if (_threadLocalECBs != null)
|
||||||
{
|
{
|
||||||
v.Dispose();
|
foreach (var v in _threadLocalECBs)
|
||||||
|
{
|
||||||
|
v.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_archetypes.Dispose();
|
_archetypes.Dispose();
|
||||||
|
|||||||
Reference in New Issue
Block a user