Files
GhostEngine/Ghost.Entities/World.cs
Misaki 1c155f962c Render graph: native pass merging & heap-based aliasing
Major architecture upgrade:
- Add native render pass merging (hardware pass grouping, load/store op inference)
- Implement heap-based aliasing for textures & buffers (D3D12-style)
- Unify resource model: buffers and textures in one registry
- Extend builder API for buffer creation/usage, access flags, hints
- Improve barrier/state tracking (buffer hints, indirect argument state)
- Update caching, hashing, and debug output for new model
- Add enums/structs: AttachmentLoadOp, StoreOp, BufferHint, etc.
- D3D12 backend: support named resources, temp upload buffers, correct heap usage
- Update docs, benchmarks, and project files for new features

Brings render graph closer to AAA engine standards, enabling efficient memory usage, lower driver overhead, and a more flexible API.
2026-01-16 01:59:33 +09:00

242 lines
6.5 KiB
C#

using Ghost.Core;
using Misaki.HighPerformance.Jobs;
using System.Runtime.CompilerServices;
namespace Ghost.Entities;
public partial class World
{
private static readonly List<World?> s_worlds = new(4);
private static readonly Queue<Identifier<World>> s_freeWorldSlots = new();
internal static Identifier<Archetype> EmptyArchetypeID => new(0);
public static int WorldCount => s_worlds.Count - s_freeWorldSlots.Count;
public static World Create(JobScheduler? jobScheduler = null, int entityCapacity = 16)
{
lock (s_worlds)
{
if (s_freeWorldSlots.TryDequeue(out var index))
{
s_worlds[index.Value] = new World(index, entityCapacity, jobScheduler);
}
else
{
index = new Identifier<World>(s_worlds.Count);
s_worlds.Add(new World(index, entityCapacity, jobScheduler));
}
return s_worlds[index.Value]!;
}
}
public static void Destroy(Identifier<World> id)
{
lock (s_worlds)
{
if (id.Value < 0 || id.Value >= s_worlds.Count)
{
return;
}
var world = s_worlds[id.Value];
world?.Dispose();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static World GetWorldUncheck(Identifier<World> id)
{
#if DEBUG || GHOST_EDITOR
if (id.Value < 0 || id.Value >= s_worlds.Count)
{
throw new ArgumentOutOfRangeException(nameof(id), "World ID is out of range.");
}
var world = s_worlds[id.Value];
return world is null ? throw new InvalidOperationException("World not found.") : world;
#else
return s_worlds[id.Value]!;
#endif
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Result<World, ErrorStatus> GetWorld(Identifier<World> id)
{
if (id.Value < 0 || id.Value >= s_worlds.Count)
{
return ErrorStatus.InvalidArgument;
}
var world = s_worlds[id.Value];
return world is null ? ErrorStatus.NotFound : world;
}
}
public partial class World : IDisposable, IEquatable<World>
{
private readonly Identifier<World> _id;
private readonly JobScheduler? _jobScheduler;
private readonly EntityManager _entityManager;
private readonly EntityCommandBuffer _entityCommandBuffer;
private readonly EntityCommandBuffer[]? _threadLocalECBs;
private readonly ComponentManager _componentManager;
private readonly SystemManager _systemManager;
private int _version;
private bool _disposed = false;
/// <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 component manager for this world.
/// </summary>
public ComponentManager ComponentManager => _componentManager;
/// <summary>
/// Gets the system manager for this world.
/// </summary>
public SystemManager SystemManager => _systemManager;
/// <summary>
/// Gets the current version number of the world.
/// </summary>
public int Version => Interlocked.CompareExchange(ref _version, 0, 0);
/// <summary>
/// Gets the main entity command buffer for this world.
/// </summary>
/// <remarks>
/// Use <see cref="GetThreadLocalEntityCommandBuffer(int)"/> to get thread-local command buffers for multi-threaded jobs.
/// </remarks>
public EntityCommandBuffer EntityCommandBuffer => _entityCommandBuffer;
private World(Identifier<World> id, int entityCapacity, JobScheduler? jobScheduler)
{
_id = id;
_jobScheduler = jobScheduler;
_entityManager = new EntityManager(this, entityCapacity);
_entityCommandBuffer = new EntityCommandBuffer(_entityManager);
_componentManager = new ComponentManager(this);
_systemManager = new SystemManager(this);
if (jobScheduler != null)
{
_threadLocalECBs = new EntityCommandBuffer[jobScheduler.WorkerCount];
for (var i = 0; i < jobScheduler.WorkerCount; i++)
{
_threadLocalECBs[i] = new EntityCommandBuffer(_entityManager);
}
}
}
~World()
{
Dispose();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void PlaybackEntityCommandBuffers()
{
_entityCommandBuffer.Playback();
if (_threadLocalECBs != null)
{
for (var i = 0; i < _threadLocalECBs.Length; i++)
{
_threadLocalECBs[i].Playback();
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal int AdvanceVersion()
{
return Interlocked.Increment(ref _version);
}
/// <summary>
/// Gets the thread-local entity command buffer for the specified thread index.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EntityCommandBuffer GetThreadLocalEntityCommandBuffer(int threadIndex)
{
if (_threadLocalECBs == null)
{
throw new InvalidOperationException("This world does not have a JobScheduler associated with it.");
}
return _threadLocalECBs[threadIndex];
}
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();
_entityCommandBuffer.Dispose();
if (_threadLocalECBs != null)
{
foreach (var v in _threadLocalECBs)
{
v.Dispose();
}
}
s_freeWorldSlots.Enqueue(_id);
s_worlds[_id] = null;
_disposed = true;
GC.SuppressFinalize(this);
}
}