Refactor: add command allocator & render target strategies

Major refactor of graphics infrastructure:
- Introduce ICommandAllocator and D3D12CommandAllocator for explicit command buffer management.
- Change ICommandBuffer.Begin to require an allocator.
- Add IRenderTargetStrategy abstraction with swap chain and texture implementations.
- Update IRenderer to use RenderTargetStrategy instead of direct handle.
- Add DPI scaling support to swap chains (ScaleX/ScaleY, SetScale).
- RenderSystem now supports thread-safe swap chain resize requests.
- Remove persistent copy command buffer; use per-frame allocators.
- Make Logger public/static and clean up API visibility.
- Update .editorconfig and debug layer enablement.
These changes improve modularity, DPI-awareness, and future extensibility.
This commit is contained in:
2025-12-23 00:35:34 +09:00
parent d23e701f0a
commit aa3d9c749b
24 changed files with 456 additions and 242 deletions

View File

@@ -1,4 +1,8 @@
using Ghost.Core;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.Mathematics;
using System.Collections.Concurrent;
using System.Diagnostics;
namespace Ghost.Graphics;
@@ -61,6 +65,7 @@ public interface IRenderSystem : IFenceSynchronizer, IDisposable
void Start();
void Stop();
void RequestSwapChainResize(ISwapChain swapChain, uint2 newSize);
}
/// <summary>
@@ -82,7 +87,7 @@ internal class RenderSystem : IRenderSystem
get; init;
}
public required ICommandBuffer CommandBuffer
public required ICommandAllocator CommandAllocator
{
get; init;
}
@@ -96,7 +101,7 @@ internal class RenderSystem : IRenderSystem
{
CpuReadyEvent.Dispose();
GpuReadyEvent.Dispose();
CommandBuffer.Dispose();
CommandAllocator.Dispose();
}
}
@@ -106,6 +111,7 @@ internal class RenderSystem : IRenderSystem
private readonly FrameResource[] _frameResources;
private readonly Thread _renderThread;
private readonly AutoResetEvent _shutdownEvent;
private readonly ConcurrentDictionary<ISwapChain, uint2> _resizeRequest;
private uint _frameIndex;
private uint _cpuFenceValue;
@@ -131,8 +137,6 @@ internal class RenderSystem : IRenderSystem
_ => throw new NotSupportedException($"Graphics API {config.GraphicsAPI} is not supported.")
};
_shutdownEvent = new AutoResetEvent(false);
// Create frame resources for synchronization
_frameResources = new FrameResource[config.FrameBufferCount];
for (var i = 0; i < config.FrameBufferCount; i++)
@@ -141,7 +145,7 @@ internal class RenderSystem : IRenderSystem
{
CpuReadyEvent = new AutoResetEvent(false),
GpuReadyEvent = new AutoResetEvent(true),
CommandBuffer = _graphicsEngine.CreateCommandBuffer(CommandBufferType.Graphics),
CommandAllocator = _graphicsEngine.CreateCommandAllocator(CommandBufferType.Graphics)
};
}
@@ -152,6 +156,9 @@ internal class RenderSystem : IRenderSystem
Priority = ThreadPriority.Normal
};
_shutdownEvent = new AutoResetEvent(false);
_resizeRequest = new ConcurrentDictionary<ISwapChain, uint2>();
_isRunning = false;
_disposed = false;
}
@@ -188,6 +195,12 @@ internal class RenderSystem : IRenderSystem
_renderThread.Join();
}
public void RequestSwapChainResize(ISwapChain swapChain, uint2 newSize)
{
ObjectDisposedException.ThrowIf(_disposed, this);
_resizeRequest.AddOrUpdate(swapChain, newSize, (_, _) => newSize);
}
public bool WaitForGPUReady(int timeOut = -1)
{
ObjectDisposedException.ThrowIf(_disposed, this);
@@ -243,13 +256,31 @@ internal class RenderSystem : IRenderSystem
_graphicsEngine.Device.GraphicsQueue.WaitForValue(frameResource.FenceValue);
}
_graphicsEngine.RenderFrame(frameResource.CommandBuffer);
if (!_resizeRequest.IsEmpty)
{
WaitIdle();
foreach (var kvp in _resizeRequest)
{
var swapChain = kvp.Key;
var newSize = kvp.Value;
swapChain.Resize(newSize.x, newSize.y);
}
}
var r = _graphicsEngine.RenderFrame(frameResource.CommandAllocator);
if (r.IsFailure)
{
_isRunning = false;
#if DEBUG
System.Diagnostics.Debugger.Break();
#endif
Logger.LogError($"RenderFrame failed: {r.Message}");
}
_gpuFenceValue++;
frameResource.GpuReadyEvent.Set();
frameResource.FenceValue = _graphicsEngine.Device.GraphicsQueue.Signal(_frameIndex);
_frameIndex++;
frameResource.GpuReadyEvent.Set();
frameResource.FenceValue = _graphicsEngine.Device.GraphicsQueue.Signal(_gpuFenceValue);
}
}
}