Added IShaderCompiler

This commit is contained in:
2025-11-16 19:50:24 +09:00
parent d91d6f6e57
commit 5c4e1a3350
15 changed files with 595 additions and 372 deletions

View File

@@ -1,8 +1,9 @@
using Ghost.Core;
using Ghost.Core.Graphics;
using Ghost.Core.Utilities;
using Ghost.Graphics.Core;
using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel;
using Misaki.HighPerformance.LowLevel.Utilities;
using System.Runtime.CompilerServices;
using TerraFX.Interop.DirectX;
@@ -13,10 +14,9 @@ using static TerraFX.Aliases.DXGI_Alias;
namespace Ghost.Graphics.D3D12;
internal unsafe class D3D12CommandBuffer : ICommandBuffer
internal unsafe class D3D12CommandBuffer : D3D12RHIObject<ID3D12GraphicsCommandList10>, ICommandBuffer
{
private ComPtr<ID3D12CommandAllocator> _allocator;
private ComPtr<ID3D12GraphicsCommandList10> _commandList;
private readonly D3D12PipelineLibrary _pipelineLibrary;
private readonly D3D12ResourceDatabase _resourceDatabase;
@@ -24,26 +24,14 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
private readonly D3D12DescriptorAllocator _descriptorAllocator;
private readonly CommandBufferType _type;
private string _name;
private ushort _commandCount;
private bool _isRecording;
private bool _disposed;
public ID3D12GraphicsCommandList10* NativeCommandList => _commandList.Get();
public ID3D12GraphicsCommandList10* NativeCommandList => nativeObject.Get();
public CommandBufferType Type => _type;
public bool IsEmpty => _commandCount == 0;
public string Name
{
get => _name;
set
{
_name = value;
_commandList.Get()->SetName(value);
}
}
public D3D12CommandBuffer(
D3D12RenderDevice device,
D3D12PipelineLibrary stateController,
@@ -52,7 +40,6 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
D3D12DescriptorAllocator descriptorAllocator,
CommandBufferType type)
{
_name = string.Empty;
_type = type;
ID3D12CommandAllocator* pAllocator = default;
@@ -63,7 +50,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
device.NativeDevice->CreateCommandList1(0u, commandListType, D3D12_COMMAND_LIST_FLAG_NONE, __uuidof(pCommandList), (void**)&pCommandList);
_allocator.Attach(pAllocator);
_commandList.Attach(pCommandList);
nativeObject.Attach(pCommandList);
_pipelineLibrary = stateController;
_resourceDatabase = resourceDatabase;
@@ -71,12 +58,6 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
_descriptorAllocator = descriptorAllocator;
_isRecording = false;
_disposed = false;
}
~D3D12CommandBuffer()
{
Dispose();
}
private static D3D12_COMMAND_LIST_TYPE ConvertCommandBufferType(CommandBufferType type)
@@ -90,12 +71,6 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ThrowIfDisposed()
{
ObjectDisposedException.ThrowIf(_disposed, this);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ThrowIfRecording()
{
@@ -125,7 +100,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
void ResetCommandList()
{
ThrowIfFailed(_allocator.Get()->Reset());
ThrowIfFailed(_commandList.Get()->Reset(_allocator.Get(), null));
ThrowIfFailed(nativeObject.Get()->Reset(_allocator.Get(), null));
}
void SetBindlessHeap()
@@ -133,7 +108,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
var heaps = stackalloc ID3D12DescriptorHeap*[2];
heaps[0] = _descriptorAllocator.GetCbvSrvUavHeap(); // Bindless resource heap
heaps[1] = _descriptorAllocator.GetSamplerHeap(); // Bindless sampler heap
_commandList.Get()->SetDescriptorHeaps(2, heaps);
nativeObject.Get()->SetDescriptorHeaps(2, heaps);
}
ThrowIfDisposed();
@@ -151,7 +126,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
ThrowIfDisposed();
ThrowIfNotRecording();
_commandList.Get()->Close();
nativeObject.Get()->Close();
_isRecording = false;
}
@@ -161,8 +136,8 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
ThrowIfNotRecording();
IncrementCommandCount();
var d3d12Rect = new RECT((int)rect.left, (int)rect.top, (int)rect.right, (int)rect.bottom);
_commandList.Get()->RSSetScissorRects(1, &d3d12Rect);
var d3d12Rect = new RECT((int)rect.Left, (int)rect.Top, (int)rect.Right, (int)rect.Bottom);
nativeObject.Get()->RSSetScissorRects(1, &d3d12Rect);
}
public void ResourceBarrier(Handle<GPUResource> resource, ResourceState before, ResourceState after)
@@ -175,7 +150,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
var barrier = D3D12_RESOURCE_BARRIER.InitTransition(d3d12Resource,
before.ToD3D12States(), after.ToD3D12States());
_commandList.Get()->ResourceBarrier(1, &barrier);
nativeObject.Get()->ResourceBarrier(1, &barrier);
}
public void SetRenderTargets(ReadOnlySpan<Handle<Texture>> renderTargets, Handle<Texture> depthTarget)
@@ -203,7 +178,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
pDsvHandle[0] = _descriptorAllocator.GetCpuHandle(_resourceDatabase.GetResourceInfo(depthTarget.AsResource()).viewGroup.dsv);
}
_commandList.Get()->OMSetRenderTargets((uint)renderTargets.Length, pRtvHandles, FALSE, pDsvHandle);
nativeObject.Get()->OMSetRenderTargets((uint)renderTargets.Length, pRtvHandles, FALSE, pDsvHandle);
}
public void BeginRenderPass(ReadOnlySpan<PassRenderTargetDesc> rtDescs, PassDepthStencilDesc depthDesc, bool allowUAVWrites = false)
@@ -216,12 +191,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
for (var i = 0; i < rtDescs.Length; i++)
{
var rtDesc = rtDescs[i];
if (!rtDesc.texture.IsValid)
if (!rtDesc.Texture.IsValid)
{
throw new ArgumentException($"Render target at index {i} is not a valid texture handle");
}
var resourceInfo = _resourceDatabase.GetResourceInfo(rtDesc.texture.AsResource());
var resourceInfo = _resourceDatabase.GetResourceInfo(rtDesc.Texture.AsResource());
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceInfo.viewGroup.rtv);
var desc = new D3D12_RENDER_PASS_RENDER_TARGET_DESC
@@ -234,24 +209,24 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
{
ClearValue = new D3D12_CLEAR_VALUE
{
Format = resourceInfo.desc.textureDescription.Format.ToDXGIFormat(),
Format = resourceInfo.desc.TextureDescription.Format.ToDXGIFormat(),
}
}
}
};
desc.BeginningAccess.Clear.ClearValue.Color[0] = rtDesc.clearColor.r;
desc.BeginningAccess.Clear.ClearValue.Color[1] = rtDesc.clearColor.g;
desc.BeginningAccess.Clear.ClearValue.Color[2] = rtDesc.clearColor.b;
desc.BeginningAccess.Clear.ClearValue.Color[3] = rtDesc.clearColor.a;
desc.BeginningAccess.Clear.ClearValue.Color[0] = rtDesc.ClearColor.r;
desc.BeginningAccess.Clear.ClearValue.Color[1] = rtDesc.ClearColor.g;
desc.BeginningAccess.Clear.ClearValue.Color[2] = rtDesc.ClearColor.b;
desc.BeginningAccess.Clear.ClearValue.Color[3] = rtDesc.ClearColor.a;
pRtvDescs[i] = desc;
}
var pDsvDesc = stackalloc D3D12_RENDER_PASS_DEPTH_STENCIL_DESC[depthDesc.texture.IsValid ? 1 : 0];
var pDsvDesc = stackalloc D3D12_RENDER_PASS_DEPTH_STENCIL_DESC[depthDesc.Texture.IsValid ? 1 : 0];
if (pDsvDesc != null)
{
var resourceInfo = _resourceDatabase.GetResourceInfo(depthDesc.texture.AsResource());
var resourceInfo = _resourceDatabase.GetResourceInfo(depthDesc.Texture.AsResource());
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceInfo.viewGroup.dsv);
var desc = new D3D12_RENDER_PASS_DEPTH_STENCIL_DESC
@@ -264,11 +239,11 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
{
ClearValue = new D3D12_CLEAR_VALUE
{
Format = resourceInfo.desc.textureDescription.Format.ToDXGIFormat(),
Format = resourceInfo.desc.TextureDescription.Format.ToDXGIFormat(),
DepthStencil = new D3D12_DEPTH_STENCIL_VALUE
{
Depth = depthDesc.clearDepth,
Stencil = depthDesc.clearStencil
Depth = depthDesc.ClearDepth,
Stencil = depthDesc.ClearStencil
}
}
}
@@ -278,7 +253,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
pDsvDesc[0] = desc;
}
_commandList.Get()->BeginRenderPass((uint)rtDescs.Length, pRtvDescs, pDsvDesc,
nativeObject.Get()->BeginRenderPass((uint)rtDescs.Length, pRtvDescs, pDsvDesc,
allowUAVWrites ? D3D12_RENDER_PASS_FLAG_ALLOW_UAV_WRITES : D3D12_RENDER_PASS_FLAG_NONE);
}
@@ -288,7 +263,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
ThrowIfNotRecording();
IncrementCommandCount();
_commandList.Get()->EndRenderPass();
nativeObject.Get()->EndRenderPass();
}
public void SetViewport(ViewportDesc viewport)
@@ -297,8 +272,8 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
ThrowIfNotRecording();
IncrementCommandCount();
var d3d12Viewport = new D3D12_VIEWPORT(viewport.width, viewport.height, viewport.x, viewport.y, viewport.minDepth, viewport.maxDepth);
_commandList.Get()->RSSetViewports(1, &d3d12Viewport);
var d3d12Viewport = new D3D12_VIEWPORT(viewport.Width, viewport.Height, viewport.X, viewport.Y, viewport.MinDepth, viewport.MaxDepth);
nativeObject.Get()->RSSetViewports(1, &d3d12Viewport);
}
public void SetPipelineState(GraphicsPipelineKey pipelineKey)
@@ -308,7 +283,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
IncrementCommandCount();
var shaderPipeline = _pipelineLibrary.GetGraphicsPSO(pipelineKey).GetValueOrThrow();
_commandList.Get()->SetPipelineState(shaderPipeline.value);
nativeObject.Get()->SetPipelineState(shaderPipeline.value);
}
public void SetConstantBufferView(uint slot, Handle<GraphicsBuffer> buffer)
@@ -318,7 +293,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
IncrementCommandCount();
var resource = _resourceDatabase.GetResource(buffer.AsResource());
_commandList.Get()->SetGraphicsRootConstantBufferView(RootSignatureLayout.PER_MATERIAL_BUFFER_SLOT, resource->GetGPUVirtualAddress());
nativeObject.Get()->SetGraphicsRootConstantBufferView(RootSignatureLayout.PER_MATERIAL_BUFFER_SLOT, resource->GetGPUVirtualAddress());
}
public void SetVertexBuffer(uint slot, Handle<GraphicsBuffer> buffer, ulong offset = 0)
@@ -332,10 +307,10 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
{
BufferLocation = pResource->GetGPUVirtualAddress() + offset,
SizeInBytes = (uint)(pResource->GetDesc().Width - offset),
StrideInBytes = _resourceDatabase.GetResourceDescription(buffer.AsResource()).bufferDescription.Stride
StrideInBytes = _resourceDatabase.GetResourceDescription(buffer.AsResource()).BufferDescription.Stride
};
_commandList.Get()->IASetVertexBuffers(slot, 1, &vbView);
nativeObject.Get()->IASetVertexBuffers(slot, 1, &vbView);
}
public void SetIndexBuffer(Handle<GraphicsBuffer> buffer, IndexType type, ulong offset = 0)
@@ -352,7 +327,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
Format = type == IndexType.UInt16 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT
};
_commandList.Get()->IASetIndexBuffer(&ibView);
nativeObject.Get()->IASetIndexBuffer(&ibView);
}
public void SetPrimitiveTopology(PrimitiveTopology topology)
@@ -369,7 +344,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
_ => D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST
};
_commandList.Get()->IASetPrimitiveTopology(d3d12Topology);
nativeObject.Get()->IASetPrimitiveTopology(d3d12Topology);
}
public void Draw(uint vertexCount, uint instanceCount = 1, uint startVertex = 0, uint startInstance = 0)
@@ -378,7 +353,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
ThrowIfNotRecording();
IncrementCommandCount();
_commandList.Get()->DrawInstanced(vertexCount, instanceCount, startVertex, startInstance);
nativeObject.Get()->DrawInstanced(vertexCount, instanceCount, startVertex, startInstance);
}
public void DrawIndexed(uint indexCount, uint instanceCount = 1, uint startIndex = 0, int baseVertex = 0, uint startInstance = 0)
@@ -387,7 +362,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
ThrowIfNotRecording();
IncrementCommandCount();
_commandList.Get()->DrawIndexedInstanced(indexCount, instanceCount, startIndex, baseVertex, startInstance);
nativeObject.Get()->DrawIndexedInstanced(indexCount, instanceCount, startIndex, baseVertex, startInstance);
}
public void DispatchCompute(uint threadGroupCountX, uint threadGroupCountY, uint threadGroupCountZ)
@@ -396,7 +371,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
ThrowIfNotRecording();
IncrementCommandCount();
_commandList.Get()->Dispatch(threadGroupCountX, threadGroupCountY, threadGroupCountZ);
nativeObject.Get()->Dispatch(threadGroupCountX, threadGroupCountY, threadGroupCountZ);
}
public void DispatchMesh(uint threadGroupCountX, uint threadGroupCountY, uint threadGroupCountZ)
@@ -405,7 +380,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
ThrowIfNotRecording();
IncrementCommandCount();
_commandList.Get()->DispatchMesh(threadGroupCountX, threadGroupCountY, threadGroupCountZ);
nativeObject.Get()->DispatchMesh(threadGroupCountX, threadGroupCountY, threadGroupCountZ);
}
public void DispatchRay()
@@ -416,7 +391,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
// ThrowIfNotRecording();
// IncrementCommandCount();
// _commandList.Get()->DispatchRays();
// nativeObject.Get()->DispatchRays();
}
public void UploadBuffer<T>(Handle<GraphicsBuffer> buffer, ReadOnlySpan<T> data)
@@ -441,7 +416,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
var pResource = _resourceDatabase.GetResource(buffer.AsResource());
_commandList.Get()->CopyBufferRegion(pResource, 0, pUploadResource, 0, sizeInBytes);
nativeObject.Get()->CopyBufferRegion(pResource, 0, pUploadResource, 0, sizeInBytes);
}
public void UploadTexture(Handle<Texture> texture, params ReadOnlySpan<SubResourceData> subresources)
@@ -470,7 +445,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
}
UpdateSubresources(
(ID3D12GraphicsCommandList*)_commandList.Get(),
(ID3D12GraphicsCommandList*)nativeObject.Get(),
pResource,
pUploadResource,
0,
@@ -494,17 +469,17 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
if (numBytes == 0)
{
_commandList.Get()->CopyResource(pDestResource, pSrcResource);
nativeObject.Get()->CopyResource(pDestResource, pSrcResource);
}
else
{
_commandList.Get()->CopyBufferRegion(pDestResource, destOffset, pSrcResource, srcOffset, numBytes);
nativeObject.Get()->CopyBufferRegion(pDestResource, destOffset, pSrcResource, srcOffset, numBytes);
}
}
public void Dispose()
protected override void Dispose(bool disposing)
{
if (_disposed)
if (IsDisposed)
{
return;
}
@@ -514,12 +489,9 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
throw new InvalidOperationException("Command buffer is still recording");
}
_commandList.Dispose();
_allocator.Dispose();
_isRecording = false;
MemoryLeakException.ThrowIfRefCountNonZero(_allocator.Reset());
_commandCount = 0;
_disposed = true;
GC.SuppressFinalize(this);
base.Dispose(disposing);
}
}
}

View File

@@ -1,6 +1,7 @@
using Ghost.Core;
using Ghost.Core.Graphics;
using Ghost.Core.Utilities;
using Ghost.Graphics.Core;
using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel.Buffer;
@@ -45,14 +46,6 @@ internal struct D3D12PipelineState : IDisposable
internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
{
private const int rootParamCount =
#if USE_TRADITIONAL_BINDLESS
6
#else
4
#endif
;
private readonly D3D12RenderDevice _device;
private readonly D3D12ResourceDatabase _resourceDatabase;
@@ -81,7 +74,7 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
_defaultRootSignature = default;
// NOTE: Since we are targeting SM 6.6, we can use ResourceDescriptorHeap and SamplerDescriptorHeap directly without needing to set up viewGroup tables.
var rootParameters = stackalloc D3D12_ROOT_PARAMETER1[rootParamCount];
var rootParameters = stackalloc D3D12_ROOT_PARAMETER1[RootSignatureLayout.ROOT_PARAMETER_COUNT];
rootParameters[0] = new D3D12_ROOT_PARAMETER1
{
ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV,
@@ -144,7 +137,7 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
var rootSignatureDesc = new D3D12_ROOT_SIGNATURE_DESC1
{
NumParameters = rootParamCount,
NumParameters = RootSignatureLayout.ROOT_PARAMETER_COUNT,
pParameters = rootParameters,
NumStaticSamplers = 0,
pStaticSamplers = null,
@@ -215,19 +208,38 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
fs.Write(buffer.AsSpan());
}
private static CBufferInfo ValidateReflectionData(FullPassDescriptor descriptor, ShaderReflectionData reflectionData)
private static Result<CBufferInfo> ValidateReflectionData(FullPassDescriptor descriptor, ShaderReflectionData reflectionData)
{
if (reflectionData.ConstantBuffers.Count != rootParamCount)
CBufferInfo cbufferInfo = default;
foreach (var info in reflectionData.ResourcesBindings)
{
throw new InvalidOperationException($"Shader reflection data has {reflectionData.ConstantBuffers.Count} constant buffers, expected {rootParamCount}");
if (info.BindPoint > 3)
{
return Result.Fail($"Resource binding point {info.BindPoint} is out of range. Only binding points 0-3 are supported in the current root signature.");
}
if (info.Type != D3D_SHADER_INPUT_TYPE.D3D_SIT_CBUFFER)
{
return Result.Fail($"Resource binding type {info.Type} is not supported. Only constant buffers are supported in the current root signature.");
}
if (info.BindPoint == RootSignatureLayout.PER_MATERIAL_BUFFER_SLOT)
{
cbufferInfo = new CBufferInfo
{
Name = info.Name,
RegisterSlot = info.BindPoint,
RegisterSpace = info.Space,
SizeInBytes = info.Size,
Properties = info.Properties ?? Array.Empty<CBufferPropertyInfo>(),
};
return Result.Success(cbufferInfo);
}
}
if (reflectionData.OtherResources.Count != 0)
{
throw new NotSupportedException("Shader reflection data contains unsupported resource types. Only constant buffers are supported in the current root signature.");
}
return reflectionData.ConstantBuffers[RootSignatureLayout.PER_MATERIAL_BUFFER_SLOT];
return Result.Fail("Per-material constant buffer not found in shader reflection data.");
// TODO: Validate Cbuffer sizes and bindings.
}
@@ -242,11 +254,11 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
try
{
// TODO: This does not include generated code. This will cause a root signature mismatch.
var result = D3D12ShaderCompiler.Compile(ref config, Allocator.Persistent, &reflectionBlob).GetValueOrThrow();
var result = D3D12ShaderCompiler.Compile(ref config, Allocator.Persistent, (void**)&reflectionBlob).GetValueOrThrow();
if (reflectionBlob != null)
{
var reflection = D3D12ShaderCompiler.PerformDXCReflection(reflectionBlob).GetValueOrThrow();
cbufferInfo = ValidateReflectionData(descriptor, reflection);
cbufferInfo = ValidateReflectionData(descriptor, reflection).GetValueOrThrow();
}
return result;
@@ -361,7 +373,7 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
private GraphicsPipelineKey CompilePSO(ref readonly GraphicsPSODescriptor descriptor, ref readonly D3D12GraphicsCompiledResult compiled)
{
var rtvCount = (uint)Math.Min(descriptor.rtvFormats.Length, D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT);
var rtvCount = (uint)Math.Min(descriptor.RtvFormats.Length, D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT);
var desc = new D3DX12_MESH_SHADER_PIPELINE_STATE_DESC
{
@@ -372,12 +384,12 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
SampleMask = UINT32_MAX,
SampleDesc = new DXGI_SAMPLE_DESC(1, 0),
NumRenderTargets = rtvCount,
DSVFormat = descriptor.dsvFormat.ToDXGIFormat(),
DepthStencilState = BuildDepthStencil(descriptor.zTest, descriptor.zWrite),
DSVFormat = descriptor.DsvFormat.ToDXGIFormat(),
DepthStencilState = BuildDepthStencil(descriptor.ZTest, descriptor.ZWrite),
NodeMask = 0,
Flags = D3D12_PIPELINE_STATE_FLAG_NONE,
BlendState = descriptor.blend switch
BlendState = descriptor.Blend switch
{
BlendOptions.Opaque => D3D12Utility.D3D12_BLEND_DESC_OPAQUE,
BlendOptions.Alpha => D3D12Utility.D3D12_BLEND_DESC_ALPHA_BLEND,
@@ -386,7 +398,7 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
BlendOptions.PremultipliedAlpha => D3D12Utility.D3D12_BLEND_DESC_PREMULTIPLIED,
_ => D3D12Utility.D3D12_BLEND_DESC_OPAQUE
},
RasterizerState = descriptor.cull switch
RasterizerState = descriptor.Cull switch
{
CullOptions.Off => D3D12Utility.D3D12_RASTERIZER_DESC_CULL_NONE,
CullOptions.Front => D3D12Utility.D3D12_RASTERIZER_DESC_CULL_CLOCKWISE,
@@ -402,16 +414,16 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
var hash = new GraphicsPipelineHash
{
id = descriptor.passId,
rtvCount = (uint)descriptor.rtvFormats.Length,
dsvFormat = descriptor.dsvFormat,
Id = descriptor.PassId,
RtvCount = (uint)descriptor.RtvFormats.Length,
DsvFormat = descriptor.DsvFormat,
};
for (var i = 0; i < rtvCount && i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; i++)
{
desc.RTVFormats[i] = descriptor.rtvFormats[i].ToDXGIFormat();
desc.BlendState.RenderTarget[i].RenderTargetWriteMask = (byte)(descriptor.colorMask & 0x0F);
hash.rtvFormats[i] = descriptor.rtvFormats[i];
desc.RTVFormats[i] = descriptor.RtvFormats[i].ToDXGIFormat();
desc.BlendState.RenderTarget[i].RenderTargetWriteMask = (byte)(descriptor.ColorMask & 0x0F);
hash.RtvFormats[i] = descriptor.RtvFormats[i];
}
var key = hash.GetKey();
@@ -468,15 +480,15 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
var psoDes = new GraphicsPSODescriptor
{
passId = new ShaderPassKey(fullPass.Identifier),
zTest = fullPass.localPipeline.zTest,
zWrite = fullPass.localPipeline.zWrite,
cull = fullPass.localPipeline.cull,
blend = fullPass.localPipeline.blend,
colorMask = fullPass.localPipeline.colorMask,
PassId = new ShaderPassKey(fullPass.Identifier),
ZTest = fullPass.localPipeline.zTest,
ZWrite = fullPass.localPipeline.zWrite,
Cull = fullPass.localPipeline.cull,
Blend = fullPass.localPipeline.blend,
ColorMask = fullPass.localPipeline.colorMask,
rtvFormats = rtvs,
dsvFormat = dsv,
RtvFormats = rtvs,
DsvFormat = dsv,
};
key = CompilePSO(in psoDes, in compiled);

View File

@@ -0,0 +1,67 @@
using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel;
using System.Runtime.CompilerServices;
using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows;
namespace Ghost.Graphics.D3D12;
internal abstract unsafe class D3D12RHIObject<T> : IRHIObject, IDisposable
where T : unmanaged, ID3D12Object.Interface
{
private bool _disposed;
private string _name = string.Empty;
protected ComPtr<T> nativeObject;
protected bool IsDisposed => _disposed;
public string Name
{
get => _name;
set
{
if (_name == value)
{
return;
}
_name = value;
if (nativeObject.Get() != null)
{
nativeObject.Get()->SetName(value);
}
}
}
~D3D12RHIObject()
{
Dispose(false);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected void ThrowIfDisposed()
{
ObjectDisposedException.ThrowIf(_disposed, this);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
MemoryLeakException.ThrowIfRefCountNonZero(nativeObject.Reset());
_disposed = true;
}
}

View File

@@ -199,15 +199,15 @@ internal unsafe class D3D12Renderer : IRenderer
Span<PassRenderTargetDesc> rtDesc = stackalloc PassRenderTargetDesc[1];
rtDesc[0] = new PassRenderTargetDesc
{
texture = target,
clearColor = clearColor,
Texture = target,
ClearColor = clearColor,
};
var depthDesc = new PassDepthStencilDesc
{
texture = Handle<Texture>.Invalid,
clearDepth = 1.0f,
clearStencil = 0,
Texture = Handle<Texture>.Invalid,
ClearDepth = 1.0f,
ClearStencil = 0,
};
// NOTE: Testing only.
@@ -219,8 +219,8 @@ internal unsafe class D3D12Renderer : IRenderer
cmd.BeginRenderPass(rtDesc, depthDesc, false);
var viewport = new ViewportDesc { width = _currentSize.x, height = _currentSize.y, minDepth = 0, maxDepth = 1 };
var scissor = new RectDesc { right = _currentSize.x, bottom = _currentSize.y };
var viewport = new ViewportDesc { Width = _currentSize.x, Height = _currentSize.y, MinDepth = 0, MaxDepth = 1 };
var scissor = new RectDesc { Right = _currentSize.x, Bottom = _currentSize.y };
cmd.SetViewport(viewport);
cmd.SetScissorRect(scissor);

View File

@@ -10,70 +10,7 @@ using static TerraFX.Interop.DirectX.DXC;
namespace Ghost.Graphics.D3D12;
internal struct CompileResult : IDisposable
{
public UnsafeArray<byte> bytecode;
public readonly bool IsCreated => bytecode.IsCreated;
public void Dispose()
{
bytecode.Dispose();
}
}
internal readonly struct ResourceBindingInfo
{
public string Name
{
get; init;
}
public D3D_SHADER_INPUT_TYPE Type
{
get; init;
}
public uint BindPoint
{
get; init;
}
public uint BindCount
{
get; init;
}
public uint Space
{
get; init;
}
}
internal readonly struct ShaderReflectionData
{
public List<CBufferInfo> ConstantBuffers
{
get;
}
public List<ResourceBindingInfo> OtherResources
{
get;
}
// public List<ResourceBindingInfo> Samplers { get; } = new();
// public List<ResourceBindingInfo> ShaderResourceViews { get; } = new();
// public List<ResourceBindingInfo> UnorderedAccessViews { get; } = new();
public ShaderReflectionData()
{
ConstantBuffers = new List<CBufferInfo>();
OtherResources = new List<ResourceBindingInfo>();
}
}
internal static unsafe class D3D12ShaderCompiler
internal partial class D3D12ShaderCompiler
{
private static string GetProfileString(ShaderStage stage, CompilerTier version)
{
@@ -150,29 +87,63 @@ internal static unsafe class D3D12ShaderCompiler
return argsArray;
}
public static Result<CompileResult> Compile(ref readonly CompilerConfig config, Allocator allocator, IDxcBlob** ppReflectionBlob)
private static ShaderInputType ToInputType(D3D_SHADER_INPUT_TYPE type)
{
// NOTE: Should we cache the pCompiler and pUtils instances for better performance?
return type switch
{
D3D_SHADER_INPUT_TYPE.D3D_SIT_CBUFFER => ShaderInputType.ConstantBuffer,
D3D_SHADER_INPUT_TYPE.D3D_SIT_TBUFFER => ShaderInputType.Texture,
D3D_SHADER_INPUT_TYPE.D3D_SIT_TEXTURE => ShaderInputType.Texture,
D3D_SHADER_INPUT_TYPE.D3D_SIT_SAMPLER => ShaderInputType.Sampler,
D3D_SHADER_INPUT_TYPE.D3D_SIT_UAV_RWTYPED => ShaderInputType.UAV,
D3D_SHADER_INPUT_TYPE.D3D_SIT_STRUCTURED => ShaderInputType.StructuredBuffer,
D3D_SHADER_INPUT_TYPE.D3D_SIT_BYTEADDRESS => ShaderInputType.ByteAddressBuffer,
D3D_SHADER_INPUT_TYPE.D3D_SIT_UAV_RWSTRUCTURED => ShaderInputType.RWStructuredBuffer,
D3D_SHADER_INPUT_TYPE.D3D_SIT_UAV_RWBYTEADDRESS => ShaderInputType.RWByteAddressBuffer,
_ => throw new ArgumentOutOfRangeException(nameof(type), "Unsupported shader input type")
};
}
}
internal unsafe partial class D3D12ShaderCompiler : IShaderCompiler
{
private ComPtr<IDxcCompiler3> _compiler;
private ComPtr<IDxcUtils> _utils;
public D3D12ShaderCompiler()
{
// Initialize DXC _compiler.Get() and _utils.Get()
var dxccID = CLSID.CLSID_DxcCompiler;
var dxcuID = CLSID.CLSID_DxcUtils;
IDxcCompiler3* pCompiler = default;
IDxcUtils* pUtils = default;
ThrowIfFailed(DxcCreateInstance(&dxccID, __uuidof(pCompiler), (void**)&pCompiler));
ThrowIfFailed(DxcCreateInstance(&dxcuID, __uuidof(pUtils), (void**)&pUtils));
_compiler.Attach(pCompiler);
_utils.Attach(pUtils);
}
public Result<CompileResult> Compile(ref readonly CompilerConfig config, Allocator allocator, void** ppReflection)
{
// NOTE: Should we cache the _compiler.Get() and _utils.Get() instances for better performance?
IDxcIncludeHandler* pIncludeHandler = default;
try
{
// Create DXC pCompiler and pUtils
// Create DXC _compiler.Get() and _utils.Get()
var dxccID = CLSID.CLSID_DxcCompiler;
var dxcuID = CLSID.CLSID_DxcUtils;
ThrowIfFailed(DxcCreateInstance(&dxccID, __uuidof(pCompiler), (void**)&pCompiler));
ThrowIfFailed(DxcCreateInstance(&dxcuID, __uuidof(pUtils), (void**)&pUtils));
ThrowIfFailed(pUtils->CreateDefaultIncludeHandler(&pIncludeHandler));
ThrowIfFailed(_utils.Get()->CreateDefaultIncludeHandler(&pIncludeHandler));
// Create source blob
using ComPtr<IDxcBlobEncoding> sourceBlob = default;
fixed (char* pPath = config.shaderPath)
{
if (pUtils->LoadFile(pPath, null, sourceBlob.GetAddressOf()).FAILED)
if (_utils.Get()->LoadFile(pPath, null, sourceBlob.GetAddressOf()).FAILED)
{
return Result.Fail($"Failed to load shader file: {config.shaderPath}");
}
@@ -197,7 +168,7 @@ internal static unsafe class D3D12ShaderCompiler
Encoding = DXC_CP_UTF8
};
ThrowIfFailed(pCompiler->Compile(&buffer, argPtrs, (uint)argsArray.Count, pIncludeHandler, __uuidof(pResult), (void**)&pResult));
ThrowIfFailed(_compiler.Get()->Compile(&buffer, argPtrs, (uint)argsArray.Count, pIncludeHandler, __uuidof(pResult), (void**)&pResult));
// Check compilation pResult
HRESULT hrStatus;
@@ -224,9 +195,9 @@ internal static unsafe class D3D12ShaderCompiler
ThrowIfFailed(pResult->GetResult(bytecodeBlob.GetAddressOf()));
// Get pReflection data using DXC API
if (ppReflectionBlob != null)
if (ppReflection != null)
{
ThrowIfFailed(pResult->GetOutput(DXC_OUT_KIND.DXC_OUT_REFLECTION, __uuidof<IDxcBlob>(), (void**)ppReflectionBlob, null));
ThrowIfFailed(pResult->GetOutput(DXC_OUT_KIND.DXC_OUT_REFLECTION, __uuidof<IDxcBlob>(), ppReflection, null));
}
var bytecodeSize = bytecodeBlob.Get()->GetBufferSize();
@@ -251,39 +222,37 @@ internal static unsafe class D3D12ShaderCompiler
}
finally
{
pCompiler->Release();
pUtils->Release();
pIncludeHandler->Release();
}
}
// TODO: Since we are using fixed root signature layout, the pReflection pass should only validate the layout, not generate it.
// TODO: Ideally this should return a structured pReflection data instead of populating raw lists/dictionaries.
public static Result<ShaderReflectionData> PerformDXCReflection(IDxcBlob* reflectionBlob)
public Result<ShaderReflectionData> PerformDXCReflection<T>(T* pReflectionBlob)
where T : unmanaged
{
if (reflectionBlob == null)
if (typeof(T) != typeof(IDxcBlob))
{
return Result<ShaderReflectionData>.Fail("Reflection blob is null.");
return Result<ShaderReflectionData>.Fail("Unsupported reflection type. Only IDxcBlob is supported.");
}
IDxcUtils* pUtils = default;
ID3D12ShaderReflection* pReflection = default;
IDxcBlob* pDxcReflectionBlob = (IDxcBlob*)pReflectionBlob;
try
{
// Create DXC pUtils to parse pReflection data
// Create DXC _utils.Get() to parse pReflection data
var dxcuID = CLSID.CLSID_DxcUtils;
ThrowIfFailed(DxcCreateInstance(&dxcuID, __uuidof(pUtils), (void**)&pUtils));
// Create pReflection interface from blob
var reflectionBuffer = new DxcBuffer
{
Ptr = reflectionBlob->GetBufferPointer(),
Size = reflectionBlob->GetBufferSize(),
Ptr = pDxcReflectionBlob->GetBufferPointer(),
Size = pDxcReflectionBlob->GetBufferSize(),
Encoding = DXC_CP_ACP
};
ThrowIfFailed(pUtils->CreateReflection(&reflectionBuffer, __uuidof(pReflection), (void**)&pReflection));
ThrowIfFailed(_utils.Get()->CreateReflection(&reflectionBuffer, __uuidof(pReflection), (void**)&pReflection));
D3D12_SHADER_DESC shaderDesc;
ThrowIfFailed(pReflection->GetDesc(&shaderDesc));
@@ -301,6 +270,15 @@ internal static unsafe class D3D12ShaderCompiler
return Result.Fail("Failed to get resource name from reflection data.");
}
var info = new ResourceBindingInfo
{
Name = resourceName,
Type = ToInputType(bindDesc.Type),
BindPoint = bindDesc.BindPoint,
BindCount = bindDesc.BindCount,
Space = bindDesc.Space
};
switch (bindDesc.Type)
{
case D3D_SHADER_INPUT_TYPE.D3D_SIT_CBUFFER:
@@ -332,41 +310,22 @@ internal static unsafe class D3D12ShaderCompiler
});
}
reflectionData.ConstantBuffers.Add(new CBufferInfo
{
Name = resourceName,
RegisterSlot = bindDesc.BindPoint,
RegisterSpace = bindDesc.Space,
SizeInBytes = cbufferDesc.Size,
Properties = variables
});
info.Size = cbufferDesc.Size;
info.Properties = variables;
break;
}
// NOTE: Currently we do not support resource bindings yet, everything access through bindless heaps.
default:
{
reflectionData.OtherResources.Add(new ResourceBindingInfo
{
Name = resourceName,
Type = bindDesc.Type,
BindPoint = bindDesc.BindPoint,
BindCount = bindDesc.BindCount,
Space = bindDesc.Space
});
break;
}
}
reflectionData.ResourcesBindings.Add(info);
}
return reflectionData;
}
finally
{
pUtils->Release();
pReflection->Release();
}
}