Refactor and enhance rendering pipeline

- Added new C# formatting rules in .editorconfig.
- Introduced `IKeyType`, `Key<T>`, and `Ptr<T>` structs.
- Updated `Result` and `Result<T>` for implicit conversions.
- Added AOT compatibility to project files.
- Introduced a `Camera` class and refactored namespaces.
- Enhanced rendering with bindless support and pipeline state management.
- Refactored `D3D12CommandBuffer` for new rendering features.
- Improved `D3D12PipelineLibrary` with disk caching methods.
- Added support for UAVs and raw buffers in `D3D12ResourceAllocator`.
- Improved shader compilation and reflection in `D3D12ShaderCompiler`.
- Refactored descriptor heap and swap chain initialization.
- Added enums and structs for rendering configurations.
- Expanded `ICommandBuffer` and `IPipelineLibrary` interfaces.
- Updated `MeshRenderPass` to align with the new pipeline.
- Consolidated namespaces and improved code maintainability.
This commit is contained in:
2025-11-01 22:30:08 +09:00
parent 9dc4f63e40
commit a8d7cd8828
41 changed files with 974 additions and 491 deletions

View File

@@ -1,15 +1,16 @@
using Ghost.Core;
using Ghost.Core.Utilities;
using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.Data;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel.Utilities;
using System.Runtime.CompilerServices;
using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows;
using static TerraFX.Aliases.D3D_Alias;
using static TerraFX.Aliases.D3D12_Alias;
using static TerraFX.Aliases.D3D_Alias;
using static TerraFX.Aliases.DXGI_Alias;
using Ghost.Core.Graphics;
using Ghost.Graphics.Core;
namespace Ghost.Graphics.D3D12;
@@ -26,17 +27,26 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
private readonly D3D12ResourceAllocator _resourceAllocator;
private readonly D3D12DescriptorAllocator _descriptorAllocator;
private string _name;
private readonly CommandBufferType _type;
private ushort _commandCount;
private bool _isRecording;
private bool _disposed;
public CommandBufferType Type => _type;
public ID3D12GraphicsCommandList10* NativeCommandList => _commandList.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,
@@ -45,11 +55,18 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
D3D12DescriptorAllocator descriptorAllocator,
CommandBufferType type)
{
_name = string.Empty;
_type = type;
ID3D12CommandAllocator* pAllocator = default;
ID3D12GraphicsCommandList10* pCommandList = default;
var commandListType = ConvertCommandBufferType(type);
device.NativeDevice->CreateCommandAllocator(commandListType, __uuidof<ID3D12CommandAllocator>(), _allocator.GetVoidAddressOf());
device.NativeDevice->CreateCommandList1(0u, commandListType, D3D12_COMMAND_LIST_FLAG_NONE, __uuidof<ID3D12GraphicsCommandList10>(), _commandList.GetVoidAddressOf());
device.NativeDevice->CreateCommandAllocator(commandListType, __uuidof(pAllocator), (void**)&pAllocator);
device.NativeDevice->CreateCommandList1(0u, commandListType, D3D12_COMMAND_LIST_FLAG_NONE, __uuidof(pCommandList), (void**)&pCommandList);
_allocator.Attach(pAllocator);
_commandList.Attach(pCommandList);
_pipelineLibrary = stateController;
_resourceDatabase = resourceDatabase;
@@ -89,6 +106,20 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
public void Begin()
{
void ResetCommandList()
{
ThrowIfFailed(_allocator.Get()->Reset());
ThrowIfFailed(_commandList.Get()->Reset(_allocator.Get(), null));
}
void SetBindlessHeap()
{
var heaps = stackalloc ID3D12DescriptorHeap*[2];
heaps[0] = _descriptorAllocator.GetCbvSrvUavHeap(); // Bindless resource heap
heaps[1] = _descriptorAllocator.GetSamplerHeap(); // Bindless sampler heap
_commandList.Get()->SetDescriptorHeaps(2, heaps);
}
ThrowIfDisposed();
if (_isRecording)
@@ -96,8 +127,9 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
throw new InvalidOperationException("Command buffer is already recording");
}
_allocator.Get()->Reset();
_commandList.Get()->Reset(_allocator.Get(), null);
ResetCommandList();
SetBindlessHeap();
_commandCount = 0;
_isRecording = true;
}
@@ -111,58 +143,6 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
_isRecording = false;
}
public void SetRenderTargets(ReadOnlySpan<Handle<Texture>> renderTargets, Handle<Texture> depthTarget)
{
ThrowIfDisposed();
ThrowIfNotRecording();
IncrementCommandCount();
var rtvHandles = stackalloc D3D12_CPU_DESCRIPTOR_HANDLE[renderTargets.Length];
for (var i = 0; i < renderTargets.Length; i++)
{
var handle = renderTargets[i];
if (!handle.IsValid)
{
throw new ArgumentException($"Render target at index {i} is not a valid texture handle");
}
var descriptor = _resourceDatabase.GetResourceInfo(handle.AsResource()).viewGroup;
rtvHandles[i] = _descriptorAllocator.GetCpuHandle(descriptor.rtv);
}
var dsvHandle = stackalloc D3D12_CPU_DESCRIPTOR_HANDLE[depthTarget.IsValid ? 1 : 0];
if (dsvHandle != null)
{
*dsvHandle = _descriptorAllocator.GetCpuHandle(_resourceDatabase.GetResourceInfo(depthTarget.AsResource()).viewGroup.dsv);
}
_commandList.Get()->OMSetRenderTargets((uint)renderTargets.Length, rtvHandles, FALSE, dsvHandle);
}
public void BeginRenderPass(Handle<Texture> renderTarget, Handle<Texture> depthTarget, Color128 clearColor)
{
// TODO: Implement render pass begin
}
public void EndRenderPass()
{
ThrowIfDisposed();
ThrowIfNotRecording();
IncrementCommandCount();
_commandList.Get()->EndRenderPass();
}
public void SetViewport(ViewportDesc viewport)
{
ThrowIfDisposed();
ThrowIfNotRecording();
IncrementCommandCount();
var d3d12Viewport = new D3D12_VIEWPORT(viewport.width, viewport.height, viewport.x, viewport.y, viewport.minDepth, viewport.maxDepth);
_commandList.Get()->RSSetViewports(1, &d3d12Viewport);
}
public void SetScissorRect(RectDesc rect)
{
ThrowIfDisposed();
@@ -186,16 +166,141 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
_commandList.Get()->ResourceBarrier(1, &barrier);
}
public void SetRootSignature(IRootSignature rootSignature)
public void SetRenderTargets(ReadOnlySpan<Handle<Texture>> renderTargets, Handle<Texture> depthTarget)
{
// TODO: Implement root signature setting
throw new NotImplementedException();
ThrowIfDisposed();
ThrowIfNotRecording();
IncrementCommandCount();
var pRtvHandles = stackalloc D3D12_CPU_DESCRIPTOR_HANDLE[renderTargets.Length];
for (var i = 0; i < renderTargets.Length; i++)
{
var handle = renderTargets[i];
if (!handle.IsValid)
{
throw new ArgumentException($"Render target at index {i} is not a valid texture handle");
}
var descriptor = _resourceDatabase.GetResourceInfo(handle.AsResource()).viewGroup;
pRtvHandles[i] = _descriptorAllocator.GetCpuHandle(descriptor.rtv);
}
var pDsvHandle = stackalloc D3D12_CPU_DESCRIPTOR_HANDLE[depthTarget.IsValid ? 1 : 0];
if (pDsvHandle != null)
{
pDsvHandle[0] = _descriptorAllocator.GetCpuHandle(_resourceDatabase.GetResourceInfo(depthTarget.AsResource()).viewGroup.dsv);
}
_commandList.Get()->OMSetRenderTargets((uint)renderTargets.Length, pRtvHandles, FALSE, pDsvHandle);
}
public void SetPipelineState(IShaderPipeline pipelineState)
public void BeginRenderPass(ReadOnlySpan<PassRenderTargetDesc> rtDescs, PassDepthStencilDesc depthDesc, bool allowUAVWrites = false)
{
// TODO: Implement pipeline state setting
throw new NotImplementedException();
// TODO: Implement render pass begin
var pRtvDescs = stackalloc D3D12_RENDER_PASS_RENDER_TARGET_DESC[rtDescs.Length];
for (var i = 0; i < rtDescs.Length; i++)
{
var rtDesc = rtDescs[i];
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 cpuHandle = _descriptorAllocator.GetCpuHandle(resourceInfo.viewGroup.rtv);
var desc = new D3D12_RENDER_PASS_RENDER_TARGET_DESC
{
cpuDescriptor = cpuHandle,
BeginningAccess = new D3D12_RENDER_PASS_BEGINNING_ACCESS
{
Type = D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR,
Clear = new D3D12_RENDER_PASS_BEGINNING_ACCESS_CLEAR_PARAMETERS
{
ClearValue = new D3D12_CLEAR_VALUE
{
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;
pRtvDescs[i] = desc;
}
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 cpuHandle = _descriptorAllocator.GetCpuHandle(resourceInfo.viewGroup.dsv);
var desc = new D3D12_RENDER_PASS_DEPTH_STENCIL_DESC
{
cpuDescriptor = cpuHandle,
DepthBeginningAccess = new D3D12_RENDER_PASS_BEGINNING_ACCESS
{
Type = D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR,
Clear = new D3D12_RENDER_PASS_BEGINNING_ACCESS_CLEAR_PARAMETERS
{
ClearValue = new D3D12_CLEAR_VALUE
{
Format = resourceInfo.desc.textureDescription.Format.ToDXGIFormat(),
DepthStencil = new D3D12_DEPTH_STENCIL_VALUE
{
Depth = depthDesc.clearDepth,
Stencil = depthDesc.clearStencil
}
}
}
}
};
pDsvDesc[0] = desc;
}
_commandList.Get()->BeginRenderPass((uint)rtDescs.Length, pRtvDescs, pDsvDesc,
allowUAVWrites ? D3D12_RENDER_PASS_FLAG_ALLOW_UAV_WRITES : D3D12_RENDER_PASS_FLAG_NONE);
}
public void EndRenderPass()
{
ThrowIfDisposed();
ThrowIfNotRecording();
IncrementCommandCount();
_commandList.Get()->EndRenderPass();
}
public void SetViewport(ViewportDesc viewport)
{
ThrowIfDisposed();
ThrowIfNotRecording();
IncrementCommandCount();
var d3d12Viewport = new D3D12_VIEWPORT(viewport.width, viewport.height, viewport.x, viewport.y, viewport.minDepth, viewport.maxDepth);
_commandList.Get()->RSSetViewports(1, &d3d12Viewport);
}
public void SetPipelineState(GraphicsPipelineKey pipelineKey)
{
ThrowIfDisposed();
ThrowIfNotRecording();
IncrementCommandCount();
var shaderPipeline = _pipelineLibrary.LoadGraphicsPSO(pipelineKey).GetValueOrThrow();
_commandList.Get()->SetPipelineState(shaderPipeline.value);
}
public void SetConstantBufferView(uint slot, Handle<GraphicsBuffer> buffer)
{
var resource = _resourceDatabase.GetResource(buffer.AsResource());
_commandList.Get()->SetGraphicsRootConstantBufferView(RootSignatureLayout.PER_MATERIAL_BUFFER_SLOT, resource->GetGPUVirtualAddress());
}
public void SetVertexBuffer(uint slot, Handle<GraphicsBuffer> buffer, ulong offset = 0)
@@ -232,6 +337,19 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
_commandList.Get()->IASetIndexBuffer(&ibView);
}
public void SetPrimitiveTopology(PrimitiveTopology topology)
{
var d3d12Topology = topology switch
{
PrimitiveTopology.Point => D3D_PRIMITIVE_TOPOLOGY_POINTLIST,
PrimitiveTopology.Line => D3D_PRIMITIVE_TOPOLOGY_LINELIST,
PrimitiveTopology.Triangle => D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
_ => D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST
};
_commandList.Get()->IASetPrimitiveTopology(d3d12Topology);
}
public void Draw(uint vertexCount, uint instanceCount = 1, uint startVertex = 0, uint startInstance = 0)
{
ThrowIfDisposed();
@@ -250,55 +368,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
_commandList.Get()->DrawIndexedInstanced(indexCount, instanceCount, startIndex, baseVertex, startInstance);
}
// TODO: Batch draw calls by material to minimize state changes
public void DrawMesh(Handle<Mesh> mesh, Handle<Material> material)
{
ThrowIfDisposed();
ThrowIfNotRecording();
IncrementCommandCount();
ref var meshRef = ref _resourceDatabase.GetMeshReference(mesh);
ref var materialRef = ref _resourceDatabase.GetMaterialReference(material);
ref var shaderRef = ref _resourceDatabase.GetShaderReference(materialRef.Shader);
var shaderPipeline = _pipelineLibrary.GetShaderPipeline(materialRef.Shader);
if (shaderPipeline is not D3D12ShaderPipeline d3d12Pipeline)
{
throw new InvalidOperationException("Shader pipeline is not compiled or invalid");
}
_commandList.Get()->SetPipelineState(d3d12Pipeline.pipelineState.Get());
_commandList.Get()->SetGraphicsRootSignature(_pipelineLibrary.DefaultRootSignature);
// Set viewGroup heaps - CRUCIAL: Use the specialized bindless heap for SM 6.6
var heaps = stackalloc ID3D12DescriptorHeap*[2];
heaps[0] = _descriptorAllocator.GetCbvSrvUavHeap(); // Bindless resource heap
heaps[1] = _descriptorAllocator.GetSamplerHeap(); // Bindless sampler heap
_commandList.Get()->SetDescriptorHeaps(2, heaps);
var rootParamIndex = 0u;
foreach (var cbufferInfo in shaderRef.PerMaterialBufferInfo)
{
ref var cache = ref materialRef._materialPropertiesCache[(int)cbufferInfo.RegisterSlot];
var resource = _resourceDatabase.GetResource(cache.GpuResource.AsResource());
_commandList.Get()->SetGraphicsRootConstantBufferView(rootParamIndex++, resource->GetGPUVirtualAddress());
}
var samplerGpuHandle = _descriptorAllocator.GetSamplerHeap()->GetGPUDescriptorHandleForHeapStart();
_commandList.Get()->SetGraphicsRootDescriptorTable(rootParamIndex, samplerGpuHandle);
// For fully bindless rendering, we don't use the Input Assembler stage
// Instead, we use instanced drawing where each "instance" represents a triangle
// The shader will use SV_InstanceID to index into the index buffer and then into the vertex buffer
_commandList.Get()->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// Draw without vertex/index buffers - use instanced drawing
// Each instance represents a triangle (3 vertices)
var triangleCount = (uint)meshRef.indices.Count / 3;
_commandList.Get()->DrawInstanced(3, triangleCount, 0, 0);
}
public void Dispatch(uint threadGroupCountX, uint threadGroupCountY = 1, uint threadGroupCountZ = 1)
public void DispatchCompute(uint threadGroupCountX, uint threadGroupCountY, uint threadGroupCountZ)
{
ThrowIfDisposed();
ThrowIfNotRecording();
@@ -307,6 +377,26 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
_commandList.Get()->Dispatch(threadGroupCountX, threadGroupCountY, threadGroupCountZ);
}
public void DispatchMesh(uint threadGroupCountX, uint threadGroupCountY, uint threadGroupCountZ)
{
ThrowIfDisposed();
ThrowIfNotRecording();
IncrementCommandCount();
_commandList.Get()->DispatchMesh(threadGroupCountX, threadGroupCountY, threadGroupCountZ);
}
public void DispatchRay()
{
throw new NotImplementedException();
// ThrowIfDisposed();
// ThrowIfNotRecording();
// IncrementCommandCount();
// _commandList.Get()->DispatchRays();
}
public void Upload<T>(Handle<GraphicsBuffer> buffer, ReadOnlySpan<T> data)
where T : unmanaged
{
@@ -323,7 +413,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
pUploadResource->Map(0, null, &pMappedData);
fixed (T* pData = data)
{
MemoryUtilities.MemCpy(pMappedData, pData, sizeInBytes);
MemoryUtility.MemCpy(pMappedData, pData, sizeInBytes);
}
pUploadResource->Unmap(0, null);

View File

@@ -36,12 +36,13 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
Flags = D3D12_COMMAND_QUEUE_FLAGS.D3D12_COMMAND_QUEUE_FLAG_NONE,
};
fixed (void* queuePtr = &_queue)
{
pDevice->CreateCommandQueue(&queueDesc, __uuidof<ID3D12CommandQueue>(), (void**)queuePtr);
}
ID3D12CommandQueue* pQueue = default;
ID3D12Fence1* pFence = default;
ThrowIfFailed(pDevice->CreateCommandQueue(&queueDesc, __uuidof(pQueue), (void**)&pQueue));
ThrowIfFailed(pDevice->CreateFence(0, D3D12_FENCE_FLAGS.D3D12_FENCE_FLAG_NONE, __uuidof(pFence), (void**)&pFence));
pDevice->CreateFence(0, D3D12_FENCE_FLAGS.D3D12_FENCE_FLAG_NONE, __uuidof<ID3D12Fence1>(), _fence.GetVoidAddressOf());
_queue.Attach(pQueue);
_fence.Attach(pFence);
}
~D3D12CommandQueue()

View File

@@ -1,6 +1,4 @@
using Ghost.Core.Utilities;
using Ghost.Graphics.D3D12.Utilities;
using TerraFX.Interop.DirectX;
using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows;
using static TerraFX.Aliases.DXGI_Alias;
@@ -15,20 +13,27 @@ internal unsafe class D3D12DebugLayer
public D3D12DebugLayer()
{
D3D12GetDebugInterface(__uuidof<ID3D12Debug6>(), _d3d12Debug.GetVoidAddressOf());
_d3d12Debug.Get()->EnableDebugLayer();
ID3D12Debug6* pDebug = default;
ThrowIfFailed(D3D12GetDebugInterface(__uuidof(pDebug), (void**)&pDebug));
pDebug->EnableDebugLayer();
DXGIGetDebugInterface1(0u, __uuidof<IDXGIDebug1>(), _dxgiDebug.GetVoidAddressOf());
_dxgiDebug.Get()->EnableLeakTrackingForThread();
IDXGIDebug1* pDxgiDebug = default;
ThrowIfFailed(DXGIGetDebugInterface1(0u, __uuidof(pDxgiDebug), (void**)&pDxgiDebug));
pDxgiDebug->EnableLeakTrackingForThread();
DXGIGetDebugInterface1(0u, __uuidof<IDXGIInfoQueue>(), _dxgiInfoQueue.GetVoidAddressOf());
_dxgiInfoQueue.Get()->SetBreakOnSeverity(DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR, true);
_dxgiInfoQueue.Get()->SetBreakOnSeverity(DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION, true);
IDXGIInfoQueue* pDxgiInfoQueue = default;
ThrowIfFailed(DXGIGetDebugInterface1(0u, __uuidof(pDxgiInfoQueue), (void**)&pDxgiInfoQueue));
ThrowIfFailed(pDxgiInfoQueue->SetBreakOnSeverity(DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR, true));
ThrowIfFailed(pDxgiInfoQueue->SetBreakOnSeverity(DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION, true));
_d3d12Debug.Attach(pDebug);
_dxgiDebug.Attach(pDxgiDebug);
_dxgiInfoQueue.Attach(pDxgiInfoQueue);
}
public void Dispose()
{
_dxgiDebug.Get()->ReportLiveObjects(DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_ALL | DXGI_DEBUG_RLO_IGNORE_INTERNAL);
ThrowIfFailed(_dxgiDebug.Get()->ReportLiveObjects(DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_ALL | DXGI_DEBUG_RLO_IGNORE_INTERNAL));
_d3d12Debug.Dispose();
_dxgiDebug.Dispose();

View File

@@ -7,6 +7,7 @@ using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
using Misaki.HighPerformance.LowLevel.Utilities;
using Misaki.HighPerformance.Utilities;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows;
@@ -35,10 +36,12 @@ internal struct D3D12PipelineState : IDisposable
// NOTE: This is just a temporary cache for compiled shader code. We will implement a proper disk cache later.
public D3D12GraphicsCompiledResult compileResult;
public D3DX12_MESH_SHADER_PIPELINE_STATE_DESC psoDesc;
public ComPtr<ID3D12PipelineState> pso;
public void Dispose()
{
compileResult.Dispose();
pso.Dispose();
}
}
@@ -62,31 +65,16 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
public ID3D12RootSignature* DefaultRootSignature => _defaultRootSignature.Get();
public D3D12PipelineLibrary(D3D12RenderDevice device, D3D12ResourceDatabase resourceDatabase, string? cachePath)
public D3D12PipelineLibrary(D3D12RenderDevice device, D3D12ResourceDatabase resourceDatabase)
{
_device = device;
_resourceDatabase = resourceDatabase;
_pipelineCache = new();
InitializeLibrary(cachePath);
CreateDefaultRootSignature();
}
private void InitializeLibrary(string? filePath)
{
if (!File.Exists(filePath))
{
_device.NativeDevice->CreatePipelineLibrary(null, 0, __uuidof<ID3D12PipelineLibrary1>(), _library.GetVoidAddressOf()).ThrowIfFailed();
}
var fileBytes = File.ReadAllBytes(filePath!);
fixed (byte* pFileBytes = fileBytes)
{
_device.NativeDevice->CreatePipelineLibrary(pFileBytes, (nuint)fileBytes.Length, __uuidof<ID3D12PipelineLibrary1>(), _library.GetVoidAddressOf()).ThrowIfFailed();
}
}
private void CreateDefaultRootSignature()
{
_defaultRootSignature = default;
@@ -97,28 +85,28 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
{
ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV,
ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL,
Descriptor = new D3D12_ROOT_DESCRIPTOR1(0, 0), // b0
Descriptor = new D3D12_ROOT_DESCRIPTOR1(RootSignatureLayout.GLOBAL_BUFFER_SLOT, 0), // b0
};
rootParameters[1] = new D3D12_ROOT_PARAMETER1
{
ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV,
ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL,
Descriptor = new D3D12_ROOT_DESCRIPTOR1(1, 0), // b1
Descriptor = new D3D12_ROOT_DESCRIPTOR1(RootSignatureLayout.PER_VIEW_BUFFER_SLOT, 0), // b1
};
rootParameters[2] = new D3D12_ROOT_PARAMETER1
{
ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV,
ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL,
Descriptor = new D3D12_ROOT_DESCRIPTOR1(2, 0), // b2
Descriptor = new D3D12_ROOT_DESCRIPTOR1(RootSignatureLayout.PER_OBJECT_BUFFER_SLOT, 0), // b2
};
rootParameters[3] = new D3D12_ROOT_PARAMETER1
{
ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV,
ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL,
Descriptor = new D3D12_ROOT_DESCRIPTOR1(3, 0), // b3
Descriptor = new D3D12_ROOT_DESCRIPTOR1(RootSignatureLayout.PER_MATERIAL_BUFFER_SLOT, 0), // b3
};
#if USE_TRADITIONAL_BINDLESS
@@ -166,7 +154,6 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
#endif
};
var versionedDesc = new D3D12_VERSIONED_ROOT_SIGNATURE_DESC
{
Version = D3D_ROOT_SIGNATURE_VERSION_1_1,
@@ -183,8 +170,48 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
throw new InvalidOperationException($"Failed to serialize default root signature: {errorMsg}");
}
ID3D12RootSignature* pRootSignature = default;
ThrowIfFailed(_device.NativeDevice->CreateRootSignature(0, signature.Get()->GetBufferPointer(), signature.Get()->GetBufferSize(),
__uuidof<ID3D12RootSignature>(), _defaultRootSignature.GetVoidAddressOf()));
__uuidof(pRootSignature), (void**)&pRootSignature));
_defaultRootSignature.Attach(pRootSignature);
}
public void LoadLibraryFromDisk(string? filePath)
{
ID3D12PipelineLibrary1* pLibrary = default;
if (File.Exists(filePath))
{
var fileBytes = File.ReadAllBytes(filePath!);
fixed (byte* pFileBytes = fileBytes)
{
ThrowIfFailed(_device.NativeDevice->CreatePipelineLibrary(pFileBytes, (nuint)fileBytes.Length, __uuidof(pLibrary), (void**)&pLibrary));
}
}
else
{
ThrowIfFailed(_device.NativeDevice->CreatePipelineLibrary(null, 0, __uuidof(pLibrary), (void**)&pLibrary));
}
_library.Attach(pLibrary);
}
public void SaveLibraryToDisk(string filePath)
{
var dir = Path.GetDirectoryName(filePath);
if (!Directory.Exists(dir))
{
throw new InvalidOperationException($"Directory does not exist: {dir}");
}
var size = _library.Get()->GetSerializedSize();
using var buffer = new UnsafeArray<byte>((int)size, Allocator.Persistent); // We use persistent heap allocation instead of stack allocation to avoid stack overflow for large pipeline libraries.
ThrowIfFailed(_library.Get()->Serialize(buffer.GetUnsafePtr(), size));
using var fs = File.Open(filePath, FileMode.Create, FileAccess.Write, FileShare.None);
fs.Write(buffer.AsSpan());
}
private static void ValidateReflectionData(ShaderReflectionData reflectionData)
@@ -206,19 +233,30 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
{
static CompileResult CompileAndValidate(ref CompilerConfig config)
{
var reflectionBlob = default(IDxcBlob*);
var result = D3D12ShaderCompiler.Compile(ref config, Allocator.Persistent, &reflectionBlob).GetValueOrThrow();
IDxcBlob* reflectionBlob = default;
if (reflectionBlob != null)
try
{
var reflection = D3D12ShaderCompiler.PerformDXCReflection(reflectionBlob).GetValueOrThrow();
ValidateReflectionData(reflection);
}
var result = D3D12ShaderCompiler.Compile(ref config, Allocator.Persistent, &reflectionBlob).GetValueOrThrow();
return result;
if (reflectionBlob != null)
{
var reflection = D3D12ShaderCompiler.PerformDXCReflection(reflectionBlob).GetValueOrThrow();
ValidateReflectionData(reflection);
}
return result;
}
finally
{
if (reflectionBlob != null)
{
reflectionBlob->Release();
}
}
}
var tsResult = default(CompileResult);
CompileResult tsResult = default;
var tsEntry = descriptor.taskShader;
if (tsEntry.IsCreated)
{
@@ -304,17 +342,17 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
_ => D3D12_COMPARISON_FUNC_LESS_EQUAL
};
private static D3D12_DEPTH_STENCIL_DESC BuildDepthStencil(ref readonly PipelineDescriptor pipeline)
private static D3D12_DEPTH_STENCIL_DESC BuildDepthStencil(ZTestOptions ztest, ZWriteOptions zwrite)
{
var depthEnabled = pipeline.zTest != ZTestOptions.Disabled;
var writeEnabled = pipeline.zWrite == ZWriteOptions.On;
var cmp = ToD3DCompare(pipeline.zTest);
var depthEnabled = ztest != ZTestOptions.Disabled;
var writeEnabled = zwrite == ZWriteOptions.On;
var cmp = ToD3DCompare(ztest);
return D3D12_DEPTH_STENCIL_DESC.Create(depthEnabled, writeEnabled, cmp);
}
private void StorePassState(ShaderPassKey id, ref readonly D3D12GraphicsCompiledResult compiled, ref readonly PipelineDescriptor pipelineDescriptor, ReadOnlySpan<TextureFormat> rtvs, TextureFormat dsv)
private GraphicsPipelineKey CompilePSO(ref readonly GraphicsPSODescriptor descriptor, ref readonly D3D12GraphicsCompiledResult compiled)
{
var rtvCount = (uint)Math.Min(rtvs.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
{
@@ -325,12 +363,12 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
SampleMask = UINT32_MAX,
SampleDesc = new DXGI_SAMPLE_DESC(1, 0),
NumRenderTargets = rtvCount,
DSVFormat = dsv.ToDXGIFormat(),
DepthStencilState = BuildDepthStencil(in pipelineDescriptor),
DSVFormat = descriptor.dsvFormat.ToDXGIFormat(),
DepthStencilState = BuildDepthStencil(descriptor.zTest, descriptor.zWrite),
NodeMask = 0,
Flags = D3D12_PIPELINE_STATE_FLAG_NONE,
BlendState = pipelineDescriptor.blend switch
BlendState = descriptor.blend switch
{
BlendOptions.Opaque => D3D12_BLEND_DESC.OPAQUE,
BlendOptions.Alpha => D3D12_BLEND_DESC.ALPHA_BLEND,
@@ -339,7 +377,7 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
BlendOptions.PremultipliedAlpha => D3D12_BLEND_DESC.PREMULTIPLIED,
_ => D3D12_BLEND_DESC.OPAQUE
},
RasterizerState = pipelineDescriptor.cull switch
RasterizerState = descriptor.cull switch
{
CullOptions.Off => D3D12_RASTERIZER_DESC.CULL_NONE,
CullOptions.Front => D3D12_RASTERIZER_DESC.CULL_CLOCKWISE,
@@ -355,36 +393,76 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
var hash = new GraphicsPipelineHash
{
id = id,
rtvCount = rtvCount,
dsvFormat = dsv,
id = descriptor.passId,
rtvCount = (uint)descriptor.rtvFormats.Length,
dsvFormat = descriptor.dsvFormat,
};
for (var i = 0; i < rtvCount && i < 6; i++)
for (var i = 0; i < rtvCount && i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; i++)
{
desc.RTVFormats[i] = rtvs[i].ToDXGIFormat();
desc.BlendState.RenderTarget[i].RenderTargetWriteMask = (byte)(pipelineDescriptor.colorMask & 0x0F);
hash.rtvFormats[i] = rtvs[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();
ref var existing = ref CollectionsMarshal.GetValueRefOrAddDefault(_pipelineCache, hash.GetKey(), out var exists);
if (exists)
ref var existing = ref CollectionsMarshal.GetValueRefOrAddDefault(_pipelineCache, key, out var exists);
if (!exists)
{
throw new InvalidOperationException($"Pass code cache already contains an entry for key: {key}");
existing.compileResult = compiled;
existing.psoDesc = desc;
var streamDesc = new D3D12_PIPELINE_STATE_STREAM_DESC
{
pPipelineStateSubobjectStream = &desc,
SizeInBytes = (nuint)sizeof(D3DX12_MESH_SHADER_PIPELINE_STATE_DESC)
};
ID3D12PipelineState* pPipelineState = default;
char* pKeyStr = stackalloc char[GraphicsPipelineKey.KEY_STRING_LENGTH];
var keySpan = new Span<char>(pKeyStr, GraphicsPipelineKey.KEY_STRING_LENGTH);
key.GetString(keySpan).ThrowIfFailed();
var hr = _library.Get()->LoadPipeline(pKeyStr, &streamDesc, __uuidof(pPipelineState), (void**)&pPipelineState);
if (hr == E.E_INVALIDARG)
{
// Pipeline not found in the library, create a new one.
ThrowIfFailed(_device.NativeDevice->CreatePipelineState(&streamDesc, __uuidof(pPipelineState), (void**)&pPipelineState));
ThrowIfFailed(_library.Get()->StorePipeline(pKeyStr, pPipelineState));
}
else
{
ThrowIfFailed(hr);
}
existing.pso.Attach(pPipelineState);
}
existing.compileResult = compiled;
existing.psoDesc = desc;
return key;
}
public void CompilePass(IPassDescriptor descriptor)
public GraphicsPipelineKey CompilePassPSO(IPassDescriptor descriptor, ReadOnlySpan<TextureFormat> rtvs, TextureFormat dsv)
{
var key = default(GraphicsPipelineKey);
switch (descriptor)
{
case FullPassDescriptor fullPass:
var result = CompileAndValidateFullPass(fullPass).GetValueOrThrow();
StorePassState(new(fullPass.Identifier), in result, in fullPass.localPipeline, [TextureFormat.B8G8R8A8_UNorm], TextureFormat.Unknown);
var psoDes = new GraphicsPSODescriptor
{
passId = new(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,
};
key = CompilePSO(in psoDes, in result);
break;
// Do we need to support other pass types?
@@ -392,82 +470,31 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
default:
break;
}
return key;
}
public void CompileShader(ShaderDescriptor descriptor)
public Result<Ptr<ID3D12PipelineState>> LoadGraphicsPSO(GraphicsPipelineKey key)
{
foreach (var pass in descriptor.passes)
ref var cacheEntry = ref CollectionsMarshal.GetValueRefOrNullRef(_pipelineCache, key);
if (Unsafe.IsNullRef(ref cacheEntry))
{
CompilePass(pass);
return Result.Fail("Pipeline state not found in cache.");
}
}
// TODO: Pipeline variants (keywords)
// TODO: Disk caching
// TODO: Async compilation
public void PreCookPipelineState()
{
foreach (var kvp in _pipelineCache)
{
var key = kvp.Key;
var state = kvp.Value;
var streamDesc = new D3D12_PIPELINE_STATE_STREAM_DESC
{
pPipelineStateSubobjectStream = &state.psoDesc,
SizeInBytes = (nuint)sizeof(D3DX12_MESH_SHADER_PIPELINE_STATE_DESC)
};
ComPtr<ID3D12PipelineState> pipelineState = default;
ThrowIfFailed(_device.NativeDevice->CreatePipelineState(&streamDesc, __uuidof<ID3D12PipelineState>(), pipelineState.GetVoidAddressOf()));
var name = key.ToString();
fixed (char* pName = name)
{
ThrowIfFailed(_library.Get()->StorePipeline(pName, pipelineState.Get()));
}
}
}
public ID3D12PipelineState* LoadPipelineState(GraphicsPipelineKey key)
{
var name = key.ToString();
var state = _pipelineCache[key];
var streamDesc = new D3D12_PIPELINE_STATE_STREAM_DESC
{
pPipelineStateSubobjectStream = &state.psoDesc,
SizeInBytes = (nuint)sizeof(D3DX12_MESH_SHADER_PIPELINE_STATE_DESC)
};
fixed (char* pName = name)
{
ID3D12PipelineState* pipelineState;
ThrowIfFailed(_library.Get()->LoadPipeline(pName, &streamDesc, __uuidof<ID3D12PipelineState>(), (void**)&pipelineState));
return pipelineState;
}
}
public void SaveLibraryToDisk(string filePath)
{
var size = _library.Get()->GetSerializedSize();
using var buffer = new UnsafeArray<byte>((int)size, Allocator.Persistent); // We use persistent heap allocation instead of stack allocation to avoid stack overflow for large pipeline libraries.
ThrowIfFailed(_library.Get()->Serialize(buffer.GetUnsafePtr(), size));
var fs = File.Open(filePath, FileMode.Create, FileAccess.Write, FileShare.None);
fs.Write(buffer.AsSpan());
return new Ptr<ID3D12PipelineState>(cacheEntry.pso.Get());
}
public void Dispose()
{
_defaultRootSignature.Dispose();
foreach (var kvp in _pipelineCache)
{
kvp.Value.Dispose();
}
_pipelineCache.Clear();
_defaultRootSignature.Dispose();
_library.Dispose();
}
}
}

View File

@@ -47,16 +47,20 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
private void InitializeDevice()
{
IDXGIFactory7* pFactory = default;
#if DEBUG
CreateDXGIFactory2(TRUE, __uuidof<IDXGIFactory7>(), _dxgiFactory.GetVoidAddressOf());
CreateDXGIFactory2(TRUE, __uuidof(pFactory), (void**)&pFactory);
#else
CreateDXGIFactory2(FALSE, __uuidof<IDXGIFactory7>(), _dxgiFactory.GetVoidAddressOf());
CreateDXGIFactory2(FALSE, __uuidof(pFactory), (void**)&pFactory);
#endif
_dxgiFactory.Attach(pFactory);
ID3D12Device14* pDevice = default;
ComPtr<IDXGIAdapter1> adapter = default;
for (uint adapterIndex = 0;
_dxgiFactory.Get()->EnumAdapterByGpuPreference(adapterIndex, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, __uuidof<IDXGIAdapter1>(), adapter.ReleaseAndGetVoidAddressOf()).SUCCEEDED;
_dxgiFactory.Get()->EnumAdapterByGpuPreference(adapterIndex, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, adapter.IID(), adapter.ReleaseAndGetVoidAddressOf()).SUCCEEDED;
adapterIndex++)
{
DXGI_ADAPTER_DESC1 desc = default;
@@ -68,18 +72,20 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
continue;
}
if (D3D12CreateDevice((IUnknown*)adapter.Get(), D3D_FEATURE_LEVEL_12_0, __uuidof<ID3D12Device14>(), _device.GetVoidAddressOf()).SUCCEEDED)
if (D3D12CreateDevice((IUnknown*)adapter.Get(), D3D_FEATURE_LEVEL_12_0, __uuidof(pDevice), (void**)&pDevice).SUCCEEDED)
{
_adapter = adapter.Move();
break;
}
}
if (_device.Get() == null)
if (pDevice == null)
{
adapter.Dispose(); // Dispose the last adapter we tried. If the operation succeeded, we would have moved it.
throw new PlatformNotSupportedException("Cannot create ID3D12Device with feature level 12.0");
}
_device.Attach(pDevice);
}
public void Dispose()

View File

@@ -1,8 +1,8 @@
using Ghost.Core;
using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.Data;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.Mathematics;
using Ghost.Graphics.Core;
namespace Ghost.Graphics.D3D12;

View File

@@ -1,7 +1,6 @@
using Ghost.Core;
using Ghost.Core.Graphics;
using Ghost.Core.Utilities;
using Ghost.Graphics.Data;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel.Collections;
using System.Runtime.CompilerServices;
@@ -11,6 +10,7 @@ using static TerraFX.Aliases.D3D12_Alias;
using static TerraFX.Aliases.D3D12MA_Alias;
using static TerraFX.Aliases.DXGI_Alias;
using static TerraFX.Interop.DirectX.D3D12MemAlloc;
using Ghost.Graphics.Core;
namespace Ghost.Graphics.D3D12;
@@ -22,8 +22,8 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator, IDisposable
private ComPtr<D3D12MA_Allocator> _allocator;
private readonly D3D12RenderDevice _device;
private readonly RenderSystem _renderSystem;
private readonly D3D12RenderDevice _device;
private readonly D3D12DescriptorAllocator _descriptorAllocator;
private readonly D3D12ResourceDatabase _resourceDatabase;
@@ -82,7 +82,7 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator, IDisposable
return handle;
}
private D3D12_SHADER_RESOURCE_VIEW_DESC CreateSrvDesc(ID3D12Resource* pResource, bool isCubeMap, uint mipLevels, uint arraySize)
private static D3D12_SHADER_RESOURCE_VIEW_DESC CreateSrvDesc(ID3D12Resource* pResource, bool isCubeMap, uint mipLevels, uint arraySize)
{
var resourceDesc = pResource->GetDesc();
var srvDesc = new D3D12_SHADER_RESOURCE_VIEW_DESC
@@ -93,17 +93,6 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator, IDisposable
switch (resourceDesc.Dimension)
{
case D3D12_RESOURCE_DIMENSION_BUFFER:
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
srvDesc.Buffer = new D3D12_BUFFER_SRV
{
FirstElement = 0,
NumElements = (uint)(resourceDesc.Width / 4),
StructureByteStride = 0,
Flags = D3D12_BUFFER_SRV_FLAG_RAW,
};
break;
case D3D12_RESOURCE_DIMENSION_TEXTURE1D:
if (resourceDesc.DepthOrArraySize > 1)
{
@@ -180,7 +169,7 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator, IDisposable
return srvDesc;
}
private D3D12_RENDER_TARGET_VIEW_DESC CreateRtvDesc(ID3D12Resource* pResource, uint mipSlice = 0, uint firstArraySlice = 0, uint planeSlice = 0)
private static D3D12_RENDER_TARGET_VIEW_DESC CreateRtvDesc(ID3D12Resource* pResource, uint mipSlice = 0, uint firstArraySlice = 0, uint planeSlice = 0)
{
var resourceDesc = pResource->GetDesc();
var rtvDesc = new D3D12_RENDER_TARGET_VIEW_DESC();
@@ -276,7 +265,7 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator, IDisposable
return rtvDesc;
}
private D3D12_DEPTH_STENCIL_VIEW_DESC CreateDsvDesc(ID3D12Resource* pResource, uint mipSlice = 0, uint firstArraySlice = 0, D3D12_DSV_FLAGS flags = D3D12_DSV_FLAG_NONE)
private static D3D12_DEPTH_STENCIL_VIEW_DESC CreateDsvDesc(ID3D12Resource* pResource, uint mipSlice = 0, uint firstArraySlice = 0, D3D12_DSV_FLAGS flags = D3D12_DSV_FLAG_NONE)
{
var resourceDesc = pResource->GetDesc();
var dsvDesc = new D3D12_DEPTH_STENCIL_VIEW_DESC
@@ -344,6 +333,88 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator, IDisposable
return dsvDesc;
}
private static D3D12_UNORDERED_ACCESS_VIEW_DESC CreateUavDesc(ID3D12Resource* pResource, uint mipSlice = 0, uint firstArraySlice = 0, uint planeSlice = 0)
{
var resourceDesc = pResource->GetDesc();
var uavDesc = new D3D12_UNORDERED_ACCESS_VIEW_DESC
{
Format = resourceDesc.Format
};
switch (resourceDesc.Dimension)
{
case D3D12_RESOURCE_DIMENSION_TEXTURE1D:
if (resourceDesc.DepthOrArraySize > 1)
{
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE1DARRAY;
uavDesc.Texture1DArray = new D3D12_TEX1D_ARRAY_UAV
{
MipSlice = mipSlice,
FirstArraySlice = firstArraySlice,
ArraySize = resourceDesc.ArraySize() - firstArraySlice
};
}
else
{
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE1D;
uavDesc.Texture1D = new D3D12_TEX1D_UAV
{
MipSlice = mipSlice
};
}
break;
case D3D12_RESOURCE_DIMENSION_TEXTURE2D:
if (resourceDesc.DepthOrArraySize > 1)
{
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY;
uavDesc.Texture2DArray = new D3D12_TEX2D_ARRAY_UAV
{
MipSlice = mipSlice,
FirstArraySlice = firstArraySlice,
ArraySize = resourceDesc.ArraySize() - firstArraySlice,
PlaneSlice = planeSlice
};
}
else
{
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
uavDesc.Texture2D = new D3D12_TEX2D_UAV
{
MipSlice = mipSlice,
PlaneSlice = planeSlice
};
}
break;
case D3D12_RESOURCE_DIMENSION_TEXTURE3D:
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE3D;
uavDesc.Texture3D = new D3D12_TEX3D_UAV
{
MipSlice = mipSlice,
FirstWSlice = firstArraySlice,
WSize = resourceDesc.Depth() - firstArraySlice
};
break;
case D3D12_RESOURCE_DIMENSION_BUFFER:
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
uavDesc.Buffer = new D3D12_BUFFER_UAV
{
FirstElement = 0,
NumElements = (uint)(resourceDesc.Width / 4), // Assuming R32_TYPELESS RAW
StructureByteStride = 0,
Flags = D3D12_BUFFER_UAV_FLAG_RAW
};
break;
default:
throw new ArgumentException($"Unsupported texture dimension for UAV: {resourceDesc.Dimension}");
}
return uavDesc;
}
public Handle<Texture> CreateTexture(ref readonly TextureDesc desc, bool isTemp = false)
{
CheckTexture2DSize(desc.Width, desc.Height);
@@ -434,17 +505,10 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator, IDisposable
if (desc.Usage.HasFlag(TextureUsage.UnorderedAccess))
{
resourceDescriptor.uav = _descriptorAllocator.AllocateCbvSrvUav(isTemp);
var uavDesc = new D3D12_UNORDERED_ACCESS_VIEW_DESC
{
ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D,
Format = d3d12Format,
Texture2D = new D3D12_TEX2D_UAV
{
MipSlice = 0,
PlaneSlice = 0,
}
};
_device.NativeDevice->CreateUnorderedAccessView(allocation.Get()->GetResource(), null, &uavDesc, _descriptorAllocator.GetCpuHandle(resourceDescriptor.uav));
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.uav);
var uavDesc = CreateUavDesc(allocation.Get()->GetResource());
_device.NativeDevice->CreateUnorderedAccessView(allocation.Get()->GetResource(), null, &uavDesc, cpuHandle);
}
var handle = TrackResource(allocation, initialState, resourceDescriptor, ResourceDesc.Texture(desc), isTemp);
@@ -458,11 +522,14 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator, IDisposable
return CreateTexture(ref textureDesc, isTemp);
}
// FIX: This is not correct! Fix it!
public Handle<GraphicsBuffer> CreateBuffer(ref readonly BufferDesc desc, bool isTemp = false)
{
CheckBufferSize(desc.Size);
var resourceDescription = D3D12_RESOURCE_DESC.Buffer(desc.Size, ConvertBufferUsage(desc.Usage));
resourceDescription.Format = desc.Usage.HasFlag(BufferUsage.Raw) ? DXGI_FORMAT_R32_TYPELESS : DXGI_FORMAT_UNKNOWN;
var allocationDesc = new D3D12MA_ALLOCATION_DESC
{
HeapType = ConvertMemoryType(desc.MemoryType),
@@ -473,7 +540,7 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator, IDisposable
ComPtr<D3D12MA_Allocation> allocation = default;
ThrowIfFailed(_allocator.Get()->CreateResource(&allocationDesc, &resourceDescription, initialState, null, allocation.GetAddressOf(), Win32Utility.IID_NULL, null));
var resourceDescriptor = ResourceViewGroup.Invalid;
if (desc.Usage.HasFlag(BufferUsage.ShaderResource))
{
@@ -752,4 +819,4 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator, IDisposable
GC.SuppressFinalize(this);
}
}
}

View File

@@ -1,5 +1,4 @@
using Ghost.Core;
using Ghost.Graphics.Data;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.Collections;
using Misaki.HighPerformance.LowLevel.Buffer;
@@ -8,6 +7,7 @@ using System.Diagnostics;
using System.Runtime.InteropServices;
using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows;
using Ghost.Graphics.Core;
namespace Ghost.Graphics.D3D12;
@@ -369,7 +369,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
var id = _shaders.Count;
_shaders.Add(shader);
return new Identifier<SDL>(id);
return new Identifier<Shader>(id);
}
public bool HasShader(Identifier<Shader> id)
@@ -487,4 +487,4 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
GC.SuppressFinalize(this);
}
}
}

View File

@@ -198,6 +198,7 @@ internal static unsafe class D3D12ShaderCompiler
public static Result<CompileResult> Compile(ref readonly CompilerConfig config, Allocator allocator, IDxcBlob** ppReflectionBlob)
{
// NOTE: Should we cache the compiler and utils instances for better performance?
using ComPtr<IDxcCompiler3> compiler = default;
using ComPtr<IDxcUtils> utils = default;
using ComPtr<IDxcIncludeHandler> includeHandler = default;
@@ -206,8 +207,8 @@ internal static unsafe class D3D12ShaderCompiler
var pDxcCompiler = (Guid*)Unsafe.AsPointer(in CLSID.CLSID_DxcCompiler);
var pDxcUtils = (Guid*)Unsafe.AsPointer(in CLSID.CLSID_DxcUtils);
ThrowIfFailed(DxcCreateInstance(pDxcCompiler, __uuidof<IDxcCompiler3>(), compiler.GetVoidAddressOf()));
ThrowIfFailed(DxcCreateInstance(pDxcUtils, __uuidof<IDxcUtils>(), utils.GetVoidAddressOf()));
ThrowIfFailed(DxcCreateInstance(pDxcCompiler, compiler.IID(), compiler.PPV()));
ThrowIfFailed(DxcCreateInstance(pDxcUtils, utils.IID(), utils.PPV()));
//includeHandler.Get()->LoadSource();
utils.Get()->CreateDefaultIncludeHandler(includeHandler.GetAddressOf());
@@ -216,7 +217,7 @@ internal static unsafe class D3D12ShaderCompiler
using ComPtr<IDxcBlobEncoding> sourceBlob = default;
if (utils.Get()->LoadFile(config.shaderPath.AsSpan().GetUnsafePtr(), null, sourceBlob.GetAddressOf()).FAILED)
{
return Result<CompileResult>.Fail($"Failed to load shader file: {config.shaderPath}");
return Result.Fail($"Failed to load shader file: {config.shaderPath}");
}
var argsArray = GetCompilerArguments(in config);
@@ -237,7 +238,7 @@ internal static unsafe class D3D12ShaderCompiler
Encoding = DXC.DXC_CP_UTF8
};
ThrowIfFailed(compiler.Get()->Compile(&buffer, argPtrs, (uint)argsArray.Count, includeHandler.Get(), __uuidof<IDxcResult>(), result.GetVoidAddressOf()));
ThrowIfFailed(compiler.Get()->Compile(&buffer, argPtrs, (uint)argsArray.Count, includeHandler.Get(), result.IID(), result.PPV()));
// Check compilation result
HRESULT hrStatus;
@@ -251,11 +252,11 @@ internal static unsafe class D3D12ShaderCompiler
if (errorBlob.Get() != null)
{
var errorMessage = Marshal.PtrToStringUni((IntPtr)errorBlob.Get()->GetBufferPointer());
return Result<CompileResult>.Fail($"DXC shader compilation failed:\n{errorMessage}");
return Result.Fail($"DXC shader compilation failed:\n{errorMessage}");
}
else
{
return Result<CompileResult>.Fail("DXC shader compilation failed with unknown error.");
return Result.Fail("DXC shader compilation failed with unknown error.");
}
}
@@ -300,7 +301,7 @@ internal static unsafe class D3D12ShaderCompiler
// Create DXC utils to parse reflection data
var pDxcUtils = (Guid*)Unsafe.AsPointer(in CLSID.CLSID_DxcUtils);
using ComPtr<IDxcUtils> utils = default;
ThrowIfFailed(DxcCreateInstance(pDxcUtils, __uuidof<IDxcUtils>(), utils.GetVoidAddressOf()));
ThrowIfFailed(DxcCreateInstance(pDxcUtils, utils.IID(), utils.PPV()));
// Create reflection interface from blob
var reflectionBuffer = new DxcBuffer
@@ -311,7 +312,7 @@ internal static unsafe class D3D12ShaderCompiler
};
using ComPtr<ID3D12ShaderReflection> reflection = default;
ThrowIfFailed(utils.Get()->CreateReflection(&reflectionBuffer, __uuidof<ID3D12ShaderReflection>(), reflection.GetVoidAddressOf()));
ThrowIfFailed(utils.Get()->CreateReflection(&reflectionBuffer, reflection.IID(), reflection.PPV()));
D3D12_SHADER_DESC shaderDesc;
ThrowIfFailed(reflection.Get()->GetDesc(&shaderDesc));
@@ -326,7 +327,7 @@ internal static unsafe class D3D12ShaderCompiler
var resourceName = Marshal.PtrToStringUTF8((IntPtr)bindDesc.Name);
if (resourceName == null)
{
return Result<ShaderReflectionData>.Fail("Failed to get resource name from reflection data.");
return Result.Fail("Failed to get resource name from reflection data.");
}
switch (bindDesc.Type)
@@ -391,4 +392,4 @@ internal static unsafe class D3D12ShaderCompiler
return reflectionData;
}
}
}

View File

@@ -2,7 +2,6 @@ using Ghost.Core;
using Ghost.Core.Utilities;
using Ghost.Graphics.Contracts;
using Ghost.Graphics.D3D12.Utilities;
using Ghost.Graphics.Data;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
@@ -12,6 +11,8 @@ using TerraFX.Interop.Windows;
using static TerraFX.Aliases.DXGI_Alias;
using Ghost.Graphics.Core;
namespace Ghost.Graphics.D3D12;
/// <summary>
@@ -105,10 +106,10 @@ internal unsafe class D3D12SwapChain : ISwapChain
throw new ArgumentException("Unsupported swap chain target type.");
}
if (tempSwapChain.Get()->QueryInterface(__uuidof<IDXGISwapChain4>(), _swapChain.GetVoidAddressOf()).FAILED)
{
throw new InvalidOperationException("Failed to create IDXGISwapChain4 interface.");
}
IDXGISwapChain4* pSwapChain = default;
tempSwapChain.Get()->QueryInterface(__uuidof(pSwapChain), (void**)&pSwapChain);
_swapChain.Attach(pSwapChain);
}
private void CreateBackBuffers()
@@ -116,10 +117,10 @@ internal unsafe class D3D12SwapChain : ISwapChain
for (uint i = 0; i < BufferCount; i++)
{
ComPtr<ID3D12Resource> backBuffer = default;
_swapChain.Get()->GetBuffer(i, __uuidof<ID3D12Resource>(), backBuffer.GetVoidAddressOf());
_swapChain.Get()->GetBuffer(i, backBuffer.IID(), backBuffer.PPV());
backBuffer.Get()->SetName($"SwapChain_BackBuffer_{i}");
_backBuffers[i] = _resourceDatabase.ImportExternalResource(backBuffer.Move(), ResourceState.Present).AsTexture();
_backBuffers[i] = _resourceDatabase.ImportExternalResource(backBuffer, ResourceState.Present).AsTexture();
}
}
@@ -182,4 +183,4 @@ internal unsafe class D3D12SwapChain : ISwapChain
_backBuffers.Dispose();
_disposed = true;
}
}
}

View File

@@ -1,4 +1,5 @@
using Misaki.HighPerformance.LowLevel.Collections;
using Ghost.Core.Utilities;
using Misaki.HighPerformance.LowLevel.Collections;
using Misaki.HighPerformance.LowLevel.Utilities;
using System.Diagnostics;
using System.Numerics;
@@ -277,33 +278,33 @@ internal unsafe struct D3D12DescriptorHeap : IDisposable
NodeMask = 0
};
fixed (void* heapPtr = &_heap)
ID3D12DescriptorHeap* pHeap = default;
var hr = _device.NativeDevice->CreateDescriptorHeap(&heapDesc, __uuidof(pHeap), (void**)&pHeap);
if (hr.FAILED)
{
var hr = _device.NativeDevice->CreateDescriptorHeap(&heapDesc, __uuidof<ID3D12DescriptorHeap>(), (void**)heapPtr);
if (hr.FAILED)
{
return false;
}
return false;
}
_heap.Attach(pHeap);
_startCpuHandle = _heap.Get()->GetCPUDescriptorHandleForHeapStart();
_allocatedDescriptors.Resize(numDescriptors);
if (ShaderVisible)
{
heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
ID3D12DescriptorHeap* pShaderVisibleHeap = default;
fixed (void* heapPtr = &_shaderVisibleHeap)
heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
hr = _device.NativeDevice->CreateDescriptorHeap(&heapDesc, __uuidof(pShaderVisibleHeap), (void**)&pShaderVisibleHeap);
if (hr.FAILED)
{
var hr = _device.NativeDevice->CreateDescriptorHeap(&heapDesc, __uuidof<ID3D12DescriptorHeap>(), (void**)heapPtr);
if (hr.FAILED)
{
return false;
}
return false;
}
_startCpuHandleShaderVisible = _shaderVisibleHeap.Get()->GetCPUDescriptorHandleForHeapStart();
_startGpuHandleShaderVisible = _shaderVisibleHeap.Get()->GetGPUDescriptorHandleForHeapStart();
_startCpuHandleShaderVisible = pShaderVisibleHeap->GetCPUDescriptorHandleForHeapStart();
_startGpuHandleShaderVisible = pShaderVisibleHeap->GetGPUDescriptorHandleForHeapStart();
_shaderVisibleHeap.Attach(pShaderVisibleHeap);
}
return true;

View File

@@ -1,6 +1,6 @@
using Ghost.Graphics.Data;
using System.Runtime.CompilerServices;
using System.Runtime.CompilerServices;
using TerraFX.Interop.DirectX;
using Ghost.Graphics.Core;
namespace Ghost.Graphics.D3D12.Utilities;
@@ -9,11 +9,11 @@ internal unsafe static class D3D12PipelineResource
public const int BACK_BUFFER_COUNT = 2;
private readonly static D3D12_INPUT_ELEMENT_DESC[] s_inputElementDescs = [
new D3D12_INPUT_ELEMENT_DESC{ SemanticName = (sbyte*)Vertex.Semantic.position.GetUnsafePointer(), SemanticIndex = 0u, Format = Vertex.Semantic.ALIGNED_FORMAT, InputSlot = 0u, AlignedByteOffset = 0u, InputSlotClass = D3D12_INPUT_CLASSIFICATION.D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, InstanceDataStepRate = 0 },
new D3D12_INPUT_ELEMENT_DESC{ SemanticName = (sbyte*)Vertex.Semantic.normal.GetUnsafePointer(), SemanticIndex = 0u, Format = Vertex.Semantic.ALIGNED_FORMAT, InputSlot = 0u, AlignedByteOffset = 16u, InputSlotClass = D3D12_INPUT_CLASSIFICATION.D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, InstanceDataStepRate = 0 },
new D3D12_INPUT_ELEMENT_DESC{ SemanticName = (sbyte*)Vertex.Semantic.tangent.GetUnsafePointer(), SemanticIndex = 0u, Format = Vertex.Semantic.ALIGNED_FORMAT, InputSlot = 0u, AlignedByteOffset = 32u, InputSlotClass = D3D12_INPUT_CLASSIFICATION.D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, InstanceDataStepRate = 0 },
new D3D12_INPUT_ELEMENT_DESC{ SemanticName = (sbyte*)Vertex.Semantic.uv.GetUnsafePointer(), SemanticIndex = 0u, Format = Vertex.Semantic.ALIGNED_FORMAT, InputSlot = 0u, AlignedByteOffset = 48u, InputSlotClass = D3D12_INPUT_CLASSIFICATION.D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, InstanceDataStepRate = 0 },
new D3D12_INPUT_ELEMENT_DESC{ SemanticName = (sbyte*)Vertex.Semantic.color.GetUnsafePointer(), SemanticIndex = 0u, Format = Vertex.Semantic.ALIGNED_FORMAT, InputSlot = 0u, AlignedByteOffset = 64u, InputSlotClass = D3D12_INPUT_CLASSIFICATION.D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, InstanceDataStepRate = 0 },
new D3D12_INPUT_ELEMENT_DESC{ SemanticName = (sbyte*)Ghost.Graphics.Core.Semantic.position.GetUnsafePointer(), SemanticIndex = 0u, Format = Ghost.Graphics.Core.Semantic.ALIGNED_FORMAT, InputSlot = 0u, AlignedByteOffset = 0u, InputSlotClass = D3D12_INPUT_CLASSIFICATION.D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, InstanceDataStepRate = 0 },
new D3D12_INPUT_ELEMENT_DESC{ SemanticName = (sbyte*)Ghost.Graphics.Core.Semantic.normal.GetUnsafePointer(), SemanticIndex = 0u, Format = Ghost.Graphics.Core.Semantic.ALIGNED_FORMAT, InputSlot = 0u, AlignedByteOffset = 16u, InputSlotClass = D3D12_INPUT_CLASSIFICATION.D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, InstanceDataStepRate = 0 },
new D3D12_INPUT_ELEMENT_DESC{ SemanticName = (sbyte*)Ghost.Graphics.Core.Semantic.tangent.GetUnsafePointer(), SemanticIndex = 0u, Format = Ghost.Graphics.Core.Semantic.ALIGNED_FORMAT, InputSlot = 0u, AlignedByteOffset = 32u, InputSlotClass = D3D12_INPUT_CLASSIFICATION.D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, InstanceDataStepRate = 0 },
new D3D12_INPUT_ELEMENT_DESC{ SemanticName = (sbyte*)Ghost.Graphics.Core.Semantic.uv.GetUnsafePointer(), SemanticIndex = 0u, Format = Ghost.Graphics.Core.Semantic.ALIGNED_FORMAT, InputSlot = 0u, AlignedByteOffset = 48u, InputSlotClass = D3D12_INPUT_CLASSIFICATION.D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, InstanceDataStepRate = 0 },
new D3D12_INPUT_ELEMENT_DESC{ SemanticName = (sbyte*)Ghost.Graphics.Core.Semantic.color.GetUnsafePointer(), SemanticIndex = 0u, Format = Ghost.Graphics.Core.Semantic.ALIGNED_FORMAT, InputSlot = 0u, AlignedByteOffset = 64u, InputSlotClass = D3D12_INPUT_CLASSIFICATION.D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, InstanceDataStepRate = 0 },
];
public const DXGI_FORMAT SWAP_CHAIN_BACK_BUFFER_FORMAT = DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM;

View File

@@ -144,9 +144,13 @@ internal static class D3D12_DEPTH_STENCILOP_DESC_Extensions
internal unsafe static class D3D12Utility
{
public static void SetName(ref this ID3D12Resource resource, ReadOnlySpan<char> name)
public static void SetName<T>(ref this T obj, ReadOnlySpan<char> name)
where T : unmanaged, ID3D12Object.Interface
{
resource.SetName(name.GetUnsafePtr());
fixed (char* pName = name)
{
obj.SetName(pName);
}
}
public static TextureDimension ToTextureDimension(this D3D12_RESOURCE_DIMENSION dimension)
@@ -187,4 +191,22 @@ internal unsafe static class D3D12Utility
_ => TextureFormat.Unknown,
};
}
}
public static D3D12_RESOURCE_STATES ToD3D12States(this ResourceState state)
{
return state switch
{
ResourceState.Common or ResourceState.Present => D3D12_RESOURCE_STATES.D3D12_RESOURCE_STATE_COMMON,
ResourceState.VertexAndConstantBuffer => D3D12_RESOURCE_STATES.D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER,
ResourceState.IndexBuffer => D3D12_RESOURCE_STATES.D3D12_RESOURCE_STATE_INDEX_BUFFER,
ResourceState.RenderTarget => D3D12_RESOURCE_STATES.D3D12_RESOURCE_STATE_RENDER_TARGET,
ResourceState.UnorderedAccess => D3D12_RESOURCE_STATES.D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
ResourceState.DepthWrite => D3D12_RESOURCE_STATES.D3D12_RESOURCE_STATE_DEPTH_WRITE,
ResourceState.DepthRead => D3D12_RESOURCE_STATES.D3D12_RESOURCE_STATE_DEPTH_READ,
ResourceState.PixelShaderResource => D3D12_RESOURCE_STATES.D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
ResourceState.CopyDest => D3D12_RESOURCE_STATES.D3D12_RESOURCE_STATE_COPY_DEST,
ResourceState.CopySource => D3D12_RESOURCE_STATES.D3D12_RESOURCE_STATE_COPY_SOURCE,
_ => throw new ArgumentException($"Unknown resource state: {state}")
};
}
}