Render graph integration and resource management refactor

Introduces a full-featured render graph system with pass culling, resource aliasing, and automatic barrier generation. Refactors resource and barrier APIs, improves error handling, and unifies result types. Renderer and render passes now use the new graph-based workflow. Updates shader includes, adds a blit shader, and improves HLSL parsing. Removes dynamic descriptor heaps in favor of persistent ones. Project file now includes the render graph module. Lays the foundation for advanced rendering features and improved memory efficiency.
This commit is contained in:
2026-01-21 18:32:03 +09:00
parent 1c155f962c
commit 92b966fe0d
62 changed files with 4843 additions and 621 deletions

View File

@@ -5,7 +5,6 @@ using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel;
using Misaki.HighPerformance.LowLevel.Utilities;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows;
@@ -115,7 +114,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#if DEBUG
[DoesNotReturn]
[System.Diagnostics.CodeAnalysis.DoesNotReturn]
private static void RecordError(string cmdName, ErrorStatus status)
#else
private void RecordError(string cmdName, ErrorStatus status)
@@ -206,51 +205,82 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
#endif
IncrementCommandCount();
if (barrierDescs.IsEmpty)
{
return;
}
var count = 0u;
var pBarriers = stackalloc D3D12_RESOURCE_BARRIER[barrierDescs.Length];
for (var i = 0; i < barrierDescs.Length; i++)
{
var desc = barrierDescs[i];
if (desc.StateBefore == desc.StateAfter)
{
continue;
}
D3D12_RESOURCE_BARRIER barrier = default;
if (!desc.Resource.IsValid)
switch (desc.type)
{
RecordError(nameof(ResourceBarrier), ErrorStatus.InvalidArgument);
continue;
}
case BarrierType.Transition:
if (desc.transition.stateBefore == desc.transition.stateAfter)
{
continue;
}
var recordResult = _resourceDatabase.GetResourceRecord(desc.Resource);
if (recordResult.Error != ErrorStatus.None)
{
RecordError(nameof(ResourceBarrier), recordResult.Error);
continue;
}
var recordResult = _resourceDatabase.GetResourceRecord(desc.transition.resource);
if (recordResult.Error != ErrorStatus.None)
{
RecordError(nameof(TransitionBarrier), recordResult.Error);
continue;
}
ref var record = ref recordResult.Value;
if (record.state != desc.StateBefore)
{
RecordError(nameof(ResourceBarrier), ErrorStatus.InvalidState);
continue;
}
ref var record = ref recordResult.Value;
var stateBefore = desc.transition.stateBefore == ResourceState.Auto ? record.state : desc.transition.stateBefore;
barrier = D3D12_RESOURCE_BARRIER.InitTransition(record.ResourcePtr,
stateBefore.ToD3D12States(), desc.transition.stateAfter.ToD3D12States());
var barrier = D3D12_RESOURCE_BARRIER.InitTransition(record.ResourcePtr,
desc.StateBefore.ToD3D12States(), desc.StateAfter.ToD3D12States());
record.state = desc.transition.stateAfter;
break;
case BarrierType.Aliasing:
var recordBeforeResult = _resourceDatabase.GetResourceRecord(desc.aliasing.resourceBefore);
if (recordBeforeResult.Error != ErrorStatus.None)
{
RecordError(nameof(TransitionBarrier), recordBeforeResult.Error);
continue;
}
var recordAfterResult = _resourceDatabase.GetResourceRecord(desc.aliasing.resourceAfter);
if (recordAfterResult.Error != ErrorStatus.None)
{
RecordError(nameof(TransitionBarrier), recordAfterResult.Error);
continue;
}
barrier = D3D12_RESOURCE_BARRIER.InitAliasing(
recordBeforeResult.Value.ResourcePtr,
recordAfterResult.Value.ResourcePtr);
break;
case BarrierType.UAV:
var recordUavResult = _resourceDatabase.GetResourceRecord(desc.uav.resource);
if (recordUavResult.Error != ErrorStatus.None)
{
RecordError(nameof(TransitionBarrier), recordUavResult.Error);
continue;
}
barrier = D3D12_RESOURCE_BARRIER.InitUAV(recordUavResult.Value.ResourcePtr);
break;
}
pBarriers[count] = barrier;
count++;
// Update the resource state in the database
record.state = desc.StateAfter;
}
_commandList.Get()->ResourceBarrier(count, pBarriers);
}
public void ResourceBarrier(Handle<GPUResource> resource, ResourceState stateBefore, ResourceState stateAfter)
public void TransitionBarrier(Handle<GPUResource> resource, ResourceState stateBefore, ResourceState stateAfter)
{
ThrowIfDisposed();
ThrowIfNotRecording();
@@ -270,7 +300,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
var recordResult = _resourceDatabase.GetResourceRecord(resource);
if (recordResult.Error != ErrorStatus.None)
{
RecordError(nameof(ResourceBarrier), recordResult.Error);
RecordError(nameof(TransitionBarrier), recordResult.Error);
return;
}
@@ -282,7 +312,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
record.state = stateAfter;
}
public void ResourceBarrier(Handle<GPUResource> resource, ResourceState stateAfter)
public void TransitionBarrier(Handle<GPUResource> resource, ResourceState stateAfter)
{
ThrowIfDisposed();
ThrowIfNotRecording();
@@ -297,7 +327,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
var recordResult = _resourceDatabase.GetResourceRecord(resource);
if (recordResult.Error != ErrorStatus.None)
{
RecordError(nameof(ResourceBarrier), recordResult.Error);
RecordError(nameof(TransitionBarrier), recordResult.Error);
return;
}
@@ -314,6 +344,38 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
record.state = stateAfter;
}
public void AliasBarrier(Handle<GPUResource> resourceBefore, Handle<GPUResource> resourceAfter)
{
ThrowIfDisposed();
ThrowIfNotRecording();
#if !DEBUG
if (_lastError.Status != ErrorStatus.None)
{
return;
}
#endif
IncrementCommandCount();
var recordBeforeResult = _resourceDatabase.GetResourceRecord(resourceBefore);
if (recordBeforeResult.Error != ErrorStatus.None)
{
RecordError(nameof(AliasBarrier), recordBeforeResult.Error);
return;
}
var recordAfterResult = _resourceDatabase.GetResourceRecord(resourceAfter);
if (recordAfterResult.Error != ErrorStatus.None)
{
RecordError(nameof(AliasBarrier), recordAfterResult.Error);
return;
}
var barrier = D3D12_RESOURCE_BARRIER.InitAliasing(
recordBeforeResult.Value.ResourcePtr,
recordAfterResult.Value.ResourcePtr);
_commandList.Get()->ResourceBarrier(1, &barrier);
}
public void SetRenderTargets(ReadOnlySpan<Handle<Texture>> renderTargets, Handle<Texture> depthTarget)
{
ThrowIfDisposed();
@@ -367,6 +429,69 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
_commandList.Get()->OMSetRenderTargets(rtvCount, pRtvHandles, FALSE, pDsvHandle);
}
public void ClearRenderTargetView(Handle<Texture> renderTarget, Color128 clearColor)
{
ThrowIfDisposed();
ThrowIfNotRecording();
#if !DEBUG
if (_lastError.Status != ErrorStatus.None)
{
return;
}
#endif
IncrementCommandCount();
var recordResult = _resourceDatabase.GetResourceRecord(renderTarget.AsResource());
if (recordResult.Error != ErrorStatus.None)
{
RecordError(nameof(ClearRenderTargetView), recordResult.Error);
return;
}
ref var record = ref recordResult.Value;
var cpuHandle = _descriptorAllocator.GetCpuHandle(record.viewGroup.rtv);
var color = stackalloc float[4]
{
clearColor.r,
clearColor.g,
clearColor.b,
clearColor.a
};
_commandList.Get()->ClearRenderTargetView(cpuHandle, color, 0, null);
}
public void ClearDepthStencilView(Handle<Texture> depthStencil, bool inlcudeDepth, bool includeStencil, float clearDepth = 1.0f, byte clearStencil = 0)
{
ThrowIfDisposed();
ThrowIfNotRecording();
#if !DEBUG
if (_lastError.Status != ErrorStatus.None)
{
return;
}
#endif
IncrementCommandCount();
var recordResult = _resourceDatabase.GetResourceRecord(depthStencil.AsResource());
if (recordResult.Error != ErrorStatus.None)
{
RecordError(nameof(ClearDepthStencilView), recordResult.Error);
return;
}
ref var record = ref recordResult.Value;
var cpuHandle = _descriptorAllocator.GetCpuHandle(record.viewGroup.dsv);
var flag = (inlcudeDepth ? D3D12_CLEAR_FLAG_DEPTH : 0) | (includeStencil ? D3D12_CLEAR_FLAG_STENCIL : 0);
_commandList.Get()->ClearDepthStencilView(cpuHandle,
flag,
clearDepth,
clearStencil,
0,
null);
}
public void BeginRenderPass(ReadOnlySpan<PassRenderTargetDesc> rtDescs, PassDepthStencilDesc depthDesc, bool allowUAVWrites = false)
{
ThrowIfDisposed();