feat(rhi): refactor resource & barrier management for D3D12

Modernizes resource and barrier management for the D3D12 backend. Key changes:
- Simplifies BarrierDesc by removing nullable "before" states; now inferred from resource database.
- Adds IsAliasing flag to BarrierDesc for aliasing transitions.
- Replaces ResourceMemoryType with HeapType in BufferDesc and related APIs.
- Enhances ResourceViewGroup with usage inference methods.
- Adds D3D12 utility helpers for heap/flag conversions and resource description extraction.
- Optimizes command buffer barrier emission, skipping redundant barriers.
- Refactors Material and RenderContext to use new APIs and state tracking.
- Updates ResourceManager pooling to use HeapType and standard Queue.
- Simplifies RenderGraphExecutor barrier logic and aliasing handling.
- Improves RenderSystem frame synchronization and resource retirement.
- Cleans up obsolete code and improves debug output.

BREAKING CHANGE: Updates to resource and barrier APIs require changes to all code interfacing with resource creation, barriers, and memory types.
This commit is contained in:
2026-04-03 17:03:41 +09:00
parent 6321b36ef5
commit ba9e24c46c
20 changed files with 316 additions and 422 deletions

View File

@@ -1,7 +1,5 @@
using Ghost.Core;
using Ghost.Core.Graphics;
using Ghost.DSL.ShaderCompiler;
using Ghost.Engine.Components;
using Ghost.Graphics.Core;
using Ghost.Graphics.RenderGraphModule;
using Ghost.Graphics.RenderPipeline;
@@ -77,9 +75,9 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
private static float3 IntersectFrustumPlanes(float4 p0, float4 p1, float4 p2)
{
float3 n0 = p0.xyz;
float3 n1 = p1.xyz;
float3 n2 = p2.xyz;
var n0 = p0.xyz;
var n1 = p1.xyz;
var n2 = p2.xyz;
float det = math.dot(math.cross(n0, n1), n2);
return (math.cross(n2, n1) * p0.w + math.cross(n0, n2) * p1.w - math.cross(n0, n1) * p2.w) * (1.0f / det);
@@ -225,7 +223,7 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
Size = instanceDataSize,
Stride = (uint)sizeof(InstanceData),
Usage = BufferUsage.Raw | BufferUsage.ShaderResource,
MemoryType = ResourceMemoryType.Upload, // Upload directly for simplicity in testing
HeapType = HeapType.Upload, // Upload directly for simplicity in testing
};
instanceBufferHandle = resourceManager.CreateTransientBuffer(in instanceBufferDesc, "Instance Buffer");
@@ -255,9 +253,9 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
};
}
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(instanceBufferResource, null, BarrierSync.Copy, null, BarrierAccess.CopyDest));
ctx.CommandBuffer.UploadBuffer(instanceBufferHandle, instanceDataArray.AsSpan());
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(instanceBufferResource, BarrierSync.Copy, BarrierSync.AllShading, BarrierAccess.CopyDest, BarrierAccess.ShaderResource));
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(instanceBufferResource, BarrierSync.Copy, BarrierAccess.CopyDest));
ctx.UploadBuffer(instanceBufferHandle, instanceDataArray.AsSpan());
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(instanceBufferResource, BarrierSync.AllShading, BarrierAccess.ShaderResource));
// 2. Allocate and populate View Data buffer
var viewBufferDesc = new BufferDesc
@@ -265,7 +263,7 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
Size = (uint)sizeof(ViewData),
Stride = (uint)sizeof(ViewData),
Usage = BufferUsage.Raw | BufferUsage.ShaderResource,
MemoryType = ResourceMemoryType.Upload,
HeapType = HeapType.Upload,
};
viewBufferHandle = resourceManager.CreateTransientBuffer(in viewBufferDesc, "View Buffer");
@@ -282,9 +280,9 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
screenSize = new float4(request.view.sensorSize.x, request.view.sensorSize.y, 1.0f / request.view.sensorSize.x, 1.0f / request.view.sensorSize.y)
};
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(viewBufferResource, null, BarrierSync.Copy, null, BarrierAccess.CopyDest));
ctx.CommandBuffer.UploadBuffer(viewBufferHandle, new ReadOnlySpan<ViewData>(in viewData));
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(viewBufferResource, BarrierSync.Copy, BarrierSync.AllShading, BarrierAccess.CopyDest, BarrierAccess.ShaderResource));
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(viewBufferResource, BarrierSync.Copy, BarrierAccess.CopyDest));
ctx.UploadBuffer(viewBufferHandle, new ReadOnlySpan<ViewData>(in viewData));
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(viewBufferResource, BarrierSync.AllShading, BarrierAccess.ShaderResource));
// 3. Allocate and populate Global Frame Data buffer
var frameDataSize = (uint)sizeof(FrameData);
@@ -293,7 +291,7 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
Size = frameDataSize,
Stride = frameDataSize,
Usage = BufferUsage.Raw | BufferUsage.ShaderResource,
MemoryType = ResourceMemoryType.Upload,
HeapType = HeapType.Upload,
};
frameBufferHandle = resourceManager.CreateTransientBuffer(in frameBufferDesc, "Frame Buffer");
@@ -305,9 +303,9 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
instanceBufferIndex = resourceDatabase.GetBindlessIndex(instanceBufferResource),
};
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(frameBufferResource, null, BarrierSync.Copy, null, BarrierAccess.CopyDest));
ctx.CommandBuffer.UploadBuffer(frameBufferHandle, new ReadOnlySpan<FrameData>(in frameData));
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(frameBufferResource, BarrierSync.Copy, BarrierSync.AllShading, BarrierAccess.CopyDest, BarrierAccess.ShaderResource));
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(frameBufferResource, BarrierSync.Copy, BarrierAccess.CopyDest));
ctx.UploadBuffer(frameBufferHandle, new ReadOnlySpan<FrameData>(in frameData));
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(frameBufferResource, BarrierSync.AllShading, BarrierAccess.ShaderResource));
if (request.renderFunc != null)
{
@@ -319,7 +317,7 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
var backBuffer = _renderGraph.ImportTexture(rt, "BackBuffer");
MeshletDebugPass(backBuffer, request.opaqueRenderList,
MeshletDebugPass(backBuffer, request.opaqueRenderList,
resourceDatabase.GetBindlessIndex(frameBufferResource),
resourceDatabase.GetBindlessIndex(viewBufferResource),
resourceDatabase.GetBindlessIndex(instanceBufferResource));
@@ -360,7 +358,7 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
builder.SetColorAttachment(backbuffer, 0);
builder.SetDepthAttachment(depth);
builder.SetRenderFunc<MeshletDebugPassData>(static (data, ctx)=>
builder.SetRenderFunc<MeshletDebugPassData>(static (data, ctx) =>
{
ctx.SetGlobalData(data.globalIndex, data.viewIndex);
ctx.SetInstanceData(data.instanceIndex);

View File

@@ -10,6 +10,7 @@ using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.Mathematics;
using System.Diagnostics;
namespace Ghost.Graphics.Test.Windows;
@@ -113,9 +114,9 @@ public sealed partial class GraphicsTestWindow : Window
//MeshBuilder.CreateCube(0.75f, default, Allocator.Persistent, out var vertices, out var indices);
Utilities.MeshUtility.LoadMesh("F:/c/SimpleRayTracer/native/assets/bunny.obj", Allocator.Persistent, out var vertices, out var indices).ThrowIfFailed();
// TODO: Put this to the beginning of the frame without createing another command buffer?
// TODO: Put this to the beginning of the frame without creating another command buffer?
using var directCmd = _renderSystem.GraphicsEngine.CreateCommandBuffer(CommandBufferType.Graphics);
var ctx = new RenderingContext(_renderSystem.GraphicsEngine, _renderSystem.ResourceManager, directCmd);
var ctx = new RenderContext(_renderSystem.GraphicsEngine, _renderSystem.ResourceManager, directCmd);
using var cmdAllocator = _renderSystem.GraphicsEngine.CreateCommandAllocator(CommandBufferType.Graphics);
directCmd.Begin(cmdAllocator);
@@ -158,7 +159,6 @@ public sealed partial class GraphicsTestWindow : Window
_renderSystem?.ResourceManager.ReleaseMesh(_meshHandle);
_swapChain?.Dispose();
//_jobScheduler.Dispose();
_renderSystem?.Dispose();
@@ -203,8 +203,9 @@ public sealed partial class GraphicsTestWindow : Window
return;
}
if (_renderSystem.CPUFenceValue < _renderSystem.GPUFenceValue + _renderSystem.MaxFrameLatency)
if (_renderSystem.TryAcquireCPUFrame())
{
//Debug.WriteLine($"CPU: Frame started.");
_world.SystemManager.UpdateAll(default);
_renderSystem.SignalCPUReady();
}