forked from Misaki/GhostEngine
Refactor and enhance codebase for maintainability
Refactored and reorganized the codebase to improve readability, performance, and maintainability. Introduced new interfaces and structs for better resource management, updated project configuration files, and refactored shader and graphics pipeline management. Improved error handling, code formatting, and removed unused code and namespaces. Updated DLL references and method signatures for consistency and maintainability.
This commit is contained in:
@@ -1,14 +1,15 @@
|
||||
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.DXGI_Alias;
|
||||
using static TerraFX.Aliases.D3D_Alias;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
@@ -65,11 +66,13 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
Dispose();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void ThrowIfDisposed()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void ThrowIfNotRecording()
|
||||
{
|
||||
if (!_isRecording)
|
||||
@@ -78,6 +81,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void IncrementCommandCount()
|
||||
{
|
||||
_commandCount++;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Ghost.Core.Utilities;
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Ghost.Graphics.RHI;
|
||||
using TerraFX.Interop.DirectX;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Ghost.Core.Utilities;
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ internal unsafe class D3D12DescriptorAllocator : IDisposable
|
||||
{
|
||||
_rtvHeap = new D3D12DescriptorHeap("rtv", device, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, initialRtvCount, initialRtvCount / 2);
|
||||
_dsvHeap = new D3D12DescriptorHeap("dsv", device, D3D12_DESCRIPTOR_HEAP_TYPE_DSV, initialDsvCount, initialDsvCount / 2);
|
||||
_cbvSrvUavHeap = new D3D12DescriptorHeap("srv", device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, initialSrvCount, initialSrvCount /2);
|
||||
_cbvSrvUavHeap = new D3D12DescriptorHeap("srv", device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, initialSrvCount, initialSrvCount / 2);
|
||||
_samplerHeap = new D3D12DescriptorHeap("sampler", device, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, initialSamplerCount, initialSamplerCount);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,52 +1,64 @@
|
||||
#undef USE_TRADITIONAL_BINDLESS
|
||||
|
||||
using Ghost.Core;
|
||||
using Ghost.Core;
|
||||
using Ghost.Core.Graphics;
|
||||
using Ghost.Core.Utilities;
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Ghost.Graphics.Data;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||
using Misaki.HighPerformance.Utilities;
|
||||
using System.Runtime.InteropServices;
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||
|
||||
using static TerraFX.Aliases.D3D12_Alias;
|
||||
using static TerraFX.Aliases.D3D_Alias;
|
||||
using static TerraFX.Aliases.DXGI_Alias;
|
||||
using static TerraFX.Aliases.D3D12_Alias;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
// TODO: Fixed root signature and use bindless samplers and textures.
|
||||
// This can dramatically reduce the number of root parameters needed and improve performance.
|
||||
internal class D3D12ShaderPipeline : IShaderPipeline, IDisposable
|
||||
internal struct D3D12GraphicsCompiledResult : IDisposable
|
||||
{
|
||||
public ComPtr<ID3D12PipelineState> pipelineState;
|
||||
public D3D12ShaderCompiler.CompileResult vsResult;
|
||||
public D3D12ShaderCompiler.CompileResult psResult;
|
||||
public D3D12ShaderCompiler.CompileResult csResult;
|
||||
|
||||
public PipelineType Type
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
public CompileResult tsResult;
|
||||
public CompileResult msResult;
|
||||
public CompileResult psResult;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
pipelineState.Dispose();
|
||||
vsResult.Dispose();
|
||||
tsResult.Dispose();
|
||||
msResult.Dispose();
|
||||
psResult.Dispose();
|
||||
csResult.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
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 void Dispose()
|
||||
{
|
||||
compileResult.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
|
||||
{
|
||||
private const int _ROOT_PARAM_COUNT =
|
||||
#if USE_TRADITIONAL_BINDLESS
|
||||
6
|
||||
#else
|
||||
4
|
||||
#endif
|
||||
;
|
||||
|
||||
private readonly D3D12RenderDevice _device;
|
||||
private readonly D3D12ResourceDatabase _resourceDatabase;
|
||||
|
||||
private ComPtr<ID3D12PipelineLibrary1> _library;
|
||||
private ComPtr<ID3D12RootSignature> _defaultRootSignature;
|
||||
|
||||
private readonly Dictionary<Identifier<Shader>, D3D12ShaderPipeline> _shaderPipelines;
|
||||
private readonly Dictionary<GraphicsPipelineKey, D3D12PipelineState> _pipelineCache;
|
||||
|
||||
public ID3D12RootSignature* DefaultRootSignature => _defaultRootSignature.Get();
|
||||
|
||||
@@ -55,20 +67,20 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
|
||||
_device = device;
|
||||
_resourceDatabase = resourceDatabase;
|
||||
|
||||
_shaderPipelines = new();
|
||||
_pipelineCache = new();
|
||||
|
||||
InitializePipelineLibrary(cachePath);
|
||||
InitializeLibrary(cachePath);
|
||||
CreateDefaultRootSignature();
|
||||
}
|
||||
|
||||
private void InitializePipelineLibrary(string? cachePath)
|
||||
private void InitializeLibrary(string? filePath)
|
||||
{
|
||||
if (!File.Exists(cachePath))
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
_device.NativeDevice->CreatePipelineLibrary(null, 0, __uuidof<ID3D12PipelineLibrary1>(), _library.GetVoidAddressOf()).ThrowIfFailed();
|
||||
}
|
||||
|
||||
var fileBytes = File.ReadAllBytes(cachePath!);
|
||||
var fileBytes = File.ReadAllBytes(filePath!);
|
||||
fixed (byte* pFileBytes = fileBytes)
|
||||
{
|
||||
_device.NativeDevice->CreatePipelineLibrary(pFileBytes, (nuint)fileBytes.Length, __uuidof<ID3D12PipelineLibrary1>(), _library.GetVoidAddressOf()).ThrowIfFailed();
|
||||
@@ -77,18 +89,10 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
|
||||
|
||||
private void CreateDefaultRootSignature()
|
||||
{
|
||||
const int rootParamCount =
|
||||
#if USE_TRADITIONAL_BINDLESS
|
||||
6
|
||||
#else
|
||||
4
|
||||
#endif
|
||||
;
|
||||
|
||||
_defaultRootSignature = default;
|
||||
|
||||
// NOTE: Since we are targeting SM 6.6, we can use ResourceDescriptorHeap and SamplerDescriptorHeap directly without needing to set up descriptor tables.
|
||||
var rootParameters = stackalloc D3D12_ROOT_PARAMETER1[rootParamCount];
|
||||
var rootParameters = stackalloc D3D12_ROOT_PARAMETER1[_ROOT_PARAM_COUNT];
|
||||
rootParameters[0] = new D3D12_ROOT_PARAMETER1
|
||||
{
|
||||
ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV,
|
||||
@@ -151,7 +155,7 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
|
||||
|
||||
var rootSignatureDesc = new D3D12_ROOT_SIGNATURE_DESC1
|
||||
{
|
||||
NumParameters = rootParamCount,
|
||||
NumParameters = _ROOT_PARAM_COUNT,
|
||||
pParameters = rootParameters,
|
||||
NumStaticSamplers = 0,
|
||||
pStaticSamplers = null,
|
||||
@@ -175,71 +179,227 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
|
||||
var serializeResult = D3D12SerializeVersionedRootSignature(&versionedDesc, signature.GetAddressOf(), error.GetAddressOf());
|
||||
if (serializeResult.FAILED)
|
||||
{
|
||||
var errorMsg = error.Get() != null ? Marshal.PtrToStringAnsi((nint)error.Get()->GetBufferPointer()) : "Unknown error";
|
||||
var errorMsg = error.Get() != null ? Marshal.PtrToStringUTF8((nint)error.Get()->GetBufferPointer()) : "Unknown error";
|
||||
throw new InvalidOperationException($"Failed to serialize default root signature: {errorMsg}");
|
||||
}
|
||||
|
||||
_device.NativeDevice->CreateRootSignature(0, signature.Get()->GetBufferPointer(), signature.Get()->GetBufferSize(),
|
||||
__uuidof<ID3D12RootSignature>(), _defaultRootSignature.GetVoidAddressOf()).ThrowIfFailed();
|
||||
ThrowIfFailed(_device.NativeDevice->CreateRootSignature(0, signature.Get()->GetBufferPointer(), signature.Get()->GetBufferSize(),
|
||||
__uuidof<ID3D12RootSignature>(), _defaultRootSignature.GetVoidAddressOf()));
|
||||
}
|
||||
|
||||
public void StorePipeline(string psoIdentifier, ID3D12PipelineState* pso)
|
||||
private static void ValidateReflectionData(ShaderReflectionData reflectionData)
|
||||
{
|
||||
_library.Get()->StorePipeline(psoIdentifier.AsSpan().GetUnsafePtr(), pso);
|
||||
}
|
||||
|
||||
public void* LoadGraphicsPipeline(string psoIdentifier)
|
||||
{
|
||||
if (_library.Get()->LoadGraphicsPipeline(psoIdentifier.AsSpan().GetUnsafePtr(), __uuidof<ID3D12PipelineState>(), out var pso).Failure)
|
||||
if (reflectionData.ConstantBuffers.Count != _ROOT_PARAM_COUNT)
|
||||
{
|
||||
return null;
|
||||
throw new InvalidOperationException($"Shader reflection data has {reflectionData.ConstantBuffers.Count} constant buffers, expected {_ROOT_PARAM_COUNT}");
|
||||
}
|
||||
|
||||
return pso;
|
||||
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.");
|
||||
}
|
||||
|
||||
// TODO: Validate Cbuffer sizes and bindings.
|
||||
}
|
||||
|
||||
public void CompileShader(Identifier<Shader> id, string shaderPath)
|
||||
private static Result<D3D12GraphicsCompiledResult> CompileAndValidateFullPass(FullPassDescriptor descriptor)
|
||||
{
|
||||
var vsResult = D3D12ShaderCompiler.Compile(shaderPath, D3D12ShaderCompiler.ShaderStage.VertexShader, D3D12ShaderCompiler.CompilerVersion.SM_6_6);
|
||||
var psResult = D3D12ShaderCompiler.Compile(shaderPath, D3D12ShaderCompiler.ShaderStage.PixelShader, D3D12ShaderCompiler.CompilerVersion.SM_6_6);
|
||||
|
||||
ref var shader = ref _resourceDatabase.GetShaderReference(id);
|
||||
|
||||
D3D12ShaderCompiler.PerformDXCReflection(ref shader, vsResult.reflection.Get());
|
||||
D3D12ShaderCompiler.PerformDXCReflection(ref shader, psResult.reflection.Get());
|
||||
|
||||
var shaderPipeline = new D3D12ShaderPipeline
|
||||
static CompileResult CompileAndValidate(ref CompilerConfig config)
|
||||
{
|
||||
Type = PipelineType.Graphics,
|
||||
vsResult = vsResult,
|
||||
var reflectionBlob = default(IDxcBlob*);
|
||||
var result = D3D12ShaderCompiler.Compile(ref config, Allocator.Persistent, &reflectionBlob).GetValueOrThrow();
|
||||
|
||||
if (reflectionBlob != null)
|
||||
{
|
||||
var reflection = D3D12ShaderCompiler.PerformDXCReflection(reflectionBlob).GetValueOrThrow();
|
||||
ValidateReflectionData(reflection);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
var tsResult = default(CompileResult);
|
||||
var tsEntry = descriptor.taskShader;
|
||||
if (tsEntry.IsCreated)
|
||||
{
|
||||
var config = new CompilerConfig
|
||||
{
|
||||
defines = descriptor.defines.AsSpan(),
|
||||
includes = descriptor.includes.AsSpan(),
|
||||
shaderPath = tsEntry.shader,
|
||||
entryPoint = tsEntry.entry,
|
||||
stage = ShaderStage.TaskShader,
|
||||
tier = CompilerTier.Tier0,
|
||||
optimizeLevel = CompilerOptimizeLevel.O3,
|
||||
options = CompilerOption.KeepReflections,
|
||||
};
|
||||
|
||||
tsResult = CompileAndValidate(ref config);
|
||||
}
|
||||
|
||||
CompileResult msResult;
|
||||
var msEntry = descriptor.meshShader;
|
||||
if (msEntry.IsCreated)
|
||||
{
|
||||
var config = new CompilerConfig
|
||||
{
|
||||
defines = descriptor.defines.AsSpan(),
|
||||
includes = descriptor.includes.AsSpan(),
|
||||
shaderPath = msEntry.shader,
|
||||
entryPoint = msEntry.entry,
|
||||
stage = ShaderStage.MeshShader,
|
||||
tier = CompilerTier.Tier0,
|
||||
optimizeLevel = CompilerOptimizeLevel.O3,
|
||||
options = CompilerOption.KeepReflections,
|
||||
};
|
||||
|
||||
msResult = CompileAndValidate(ref config);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Result<D3D12GraphicsCompiledResult>.Fail("Mesh shader expected.");
|
||||
}
|
||||
|
||||
CompileResult psResult;
|
||||
var psEntry = descriptor.pixelShader;
|
||||
if (psEntry.IsCreated)
|
||||
{
|
||||
var config = new CompilerConfig
|
||||
{
|
||||
defines = descriptor.defines.AsSpan(),
|
||||
includes = descriptor.includes.AsSpan(),
|
||||
shaderPath = psEntry.shader,
|
||||
entryPoint = psEntry.entry,
|
||||
stage = ShaderStage.PixelShader,
|
||||
tier = CompilerTier.Tier0,
|
||||
optimizeLevel = CompilerOptimizeLevel.O3,
|
||||
options = CompilerOption.KeepReflections,
|
||||
};
|
||||
|
||||
psResult = CompileAndValidate(ref config);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Result<D3D12GraphicsCompiledResult>.Fail("Pixel shader expected.");
|
||||
}
|
||||
|
||||
return new D3D12GraphicsCompiledResult
|
||||
{
|
||||
tsResult = tsResult,
|
||||
msResult = msResult,
|
||||
psResult = psResult
|
||||
};
|
||||
|
||||
_shaderPipelines[id] = shaderPipeline;
|
||||
}
|
||||
|
||||
// Create PSO from SDL (Shader Definition Language) file
|
||||
private void CreatePipelineStateObject(D3D12ShaderPipeline shaderPipeline)
|
||||
private static D3D12_COMPARISON_FUNC ToD3DCompare(ZTestOptions z) => z switch
|
||||
{
|
||||
var psoDesc = new D3D12_GRAPHICS_PIPELINE_STATE_DESC
|
||||
ZTestOptions.Disabled => D3D12_COMPARISON_FUNC_ALWAYS,
|
||||
ZTestOptions.Less => D3D12_COMPARISON_FUNC_LESS,
|
||||
ZTestOptions.LessEqual => D3D12_COMPARISON_FUNC_LESS_EQUAL,
|
||||
ZTestOptions.Equal => D3D12_COMPARISON_FUNC_EQUAL,
|
||||
ZTestOptions.GreaterEqual => D3D12_COMPARISON_FUNC_GREATER_EQUAL,
|
||||
ZTestOptions.Greater => D3D12_COMPARISON_FUNC_GREATER,
|
||||
ZTestOptions.NotEqual => D3D12_COMPARISON_FUNC_NOT_EQUAL,
|
||||
ZTestOptions.Always => D3D12_COMPARISON_FUNC_ALWAYS,
|
||||
_ => D3D12_COMPARISON_FUNC_LESS_EQUAL
|
||||
};
|
||||
|
||||
private static D3D12_DEPTH_STENCIL_DESC BuildDepthStencil(ref readonly PipelineDescriptor pipeline)
|
||||
{
|
||||
var depthEnabled = pipeline.zTest != ZTestOptions.Disabled;
|
||||
var writeEnabled = pipeline.zWrite == ZWriteOptions.On;
|
||||
var cmp = ToD3DCompare(pipeline.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)
|
||||
{
|
||||
var rtvCount = (uint)Math.Min(rtvs.Length, D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT);
|
||||
|
||||
var desc = new D3DX12_MESH_SHADER_PIPELINE_STATE_DESC
|
||||
{
|
||||
pRootSignature = _defaultRootSignature.Get(),
|
||||
VS = new D3D12_SHADER_BYTECODE(shaderPipeline.vsResult.bytecode.GetUnsafePtr(), (nuint)shaderPipeline.vsResult.bytecode.Count),
|
||||
PS = new D3D12_SHADER_BYTECODE(shaderPipeline.psResult.bytecode.GetUnsafePtr(), (nuint)shaderPipeline.psResult.bytecode.Count),
|
||||
InputLayout = D3D12PipelineResource.InputLayoutDescription,
|
||||
RasterizerState = D3D12_RASTERIZER_DESC.CULL_NONE,
|
||||
BlendState = D3D12_BLEND_DESC.OPAQUE,
|
||||
DepthStencilState = D3D12_DEPTH_STENCIL_DESC.DEFAULT,
|
||||
SampleMask = uint.MaxValue,
|
||||
MS = new D3D12_SHADER_BYTECODE(compiled.msResult.bytecode.GetUnsafePtr(), (nuint)compiled.msResult.bytecode.Count),
|
||||
PS = new D3D12_SHADER_BYTECODE(compiled.psResult.bytecode.GetUnsafePtr(), (nuint)compiled.psResult.bytecode.Count),
|
||||
PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE,
|
||||
NumRenderTargets = 1,
|
||||
SampleMask = UINT32_MAX,
|
||||
SampleDesc = new DXGI_SAMPLE_DESC(1, 0),
|
||||
DSVFormat = DXGI_FORMAT_UNKNOWN,
|
||||
NumRenderTargets = rtvCount,
|
||||
DSVFormat = dsv.ToD3D12Format(),
|
||||
DepthStencilState = BuildDepthStencil(in pipelineDescriptor),
|
||||
NodeMask = 0,
|
||||
Flags = D3D12_PIPELINE_STATE_FLAG_NONE,
|
||||
|
||||
BlendState = pipelineDescriptor.blend switch
|
||||
{
|
||||
BlendOptions.Opaque => D3D12_BLEND_DESC.OPAQUE,
|
||||
BlendOptions.Alpha => D3D12_BLEND_DESC.ALPHA_BLEND,
|
||||
BlendOptions.Additive => D3D12_BLEND_DESC.ADDITIVE,
|
||||
BlendOptions.Multiply => D3D12_BLEND_DESC.MULTIPLY,
|
||||
BlendOptions.PremultipliedAlpha => D3D12_BLEND_DESC.PREMULTIPLIED,
|
||||
_ => D3D12_BLEND_DESC.OPAQUE
|
||||
},
|
||||
RasterizerState = pipelineDescriptor.cull switch
|
||||
{
|
||||
CullOptions.Off => D3D12_RASTERIZER_DESC.CULL_NONE,
|
||||
CullOptions.Front => D3D12_RASTERIZER_DESC.CULL_CLOCKWISE,
|
||||
CullOptions.Back => D3D12_RASTERIZER_DESC.CULL_COUNTER_CLOCKWISE,
|
||||
_ => D3D12_RASTERIZER_DESC.CULL_NONE
|
||||
},
|
||||
};
|
||||
|
||||
psoDesc.RTVFormats[0] = D3D12PipelineResource.SWAP_CHAIN_BACK_BUFFER_FORMAT;
|
||||
if (compiled.tsResult.IsCreated)
|
||||
{
|
||||
desc.AS = new D3D12_SHADER_BYTECODE(compiled.tsResult.bytecode.GetUnsafePtr(), (nuint)compiled.tsResult.bytecode.Count);
|
||||
}
|
||||
|
||||
_device.NativeDevice->CreateGraphicsPipelineState(&psoDesc, __uuidof<ID3D12PipelineState>(), shaderPipeline.pipelineState.GetVoidAddressOf());
|
||||
var hash = new GraphicsPipelineHash
|
||||
{
|
||||
id = id,
|
||||
rtvCount = rtvCount,
|
||||
dsvFormat = dsv,
|
||||
};
|
||||
|
||||
for (var i = 0; i < rtvCount && i < 6; i++)
|
||||
{
|
||||
desc.RTVFormats[i] = rtvs[i].ToD3D12Format();
|
||||
desc.BlendState.RenderTarget[i].RenderTargetWriteMask = (byte)(pipelineDescriptor.colorMask & 0x0F);
|
||||
hash.rtvFormats[i] = rtvs[i];
|
||||
}
|
||||
|
||||
var key = hash.GetKey();
|
||||
ref var existing = ref CollectionsMarshal.GetValueRefOrAddDefault(_pipelineCache, hash.GetKey(), out var exists);
|
||||
if (exists)
|
||||
{
|
||||
throw new InvalidOperationException($"Pass code cache already contains an entry for key: {key}");
|
||||
}
|
||||
|
||||
existing.compileResult = compiled;
|
||||
existing.psoDesc = desc;
|
||||
}
|
||||
|
||||
public void CompilePass(IPassDescriptor descriptor)
|
||||
{
|
||||
switch (descriptor)
|
||||
{
|
||||
case FullPassDescriptor fullPass:
|
||||
var result = CompileAndValidateFullPass(fullPass).GetValueOrThrow();
|
||||
StorePassState(new(fullPass.Identifier), in result, in fullPass.localPipeline, [TextureFormat.B8G8R8A8_UNorm], TextureFormat.Unknown);
|
||||
break;
|
||||
|
||||
// Do we need to support other pass types?
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void CompileShader(ShaderDescriptor descriptor)
|
||||
{
|
||||
foreach (var pass in descriptor.passes)
|
||||
{
|
||||
CompilePass(pass);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Pipeline variants (keywords)
|
||||
@@ -247,33 +407,67 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
|
||||
// TODO: Async compilation
|
||||
public void PreCookPipelineState()
|
||||
{
|
||||
foreach (var kvp in _shaderPipelines)
|
||||
foreach (var kvp in _pipelineCache)
|
||||
{
|
||||
ref var shader = ref _resourceDatabase.GetShaderReference(kvp.Key);
|
||||
var key = kvp.Key;
|
||||
var state = kvp.Value;
|
||||
|
||||
CreatePipelineStateObject(kvp.Value);
|
||||
var streamDesc = new D3D12_PIPELINE_STATE_STREAM_DESC
|
||||
{
|
||||
pPipelineStateSubobjectStream = &state.psoDesc,
|
||||
SizeInBytes = (nuint)sizeof(D3DX12_MESH_SHADER_PIPELINE_STATE_DESC)
|
||||
};
|
||||
|
||||
kvp.Value.vsResult.Dispose();
|
||||
kvp.Value.psResult.Dispose();
|
||||
kvp.Value.csResult.Dispose();
|
||||
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 IShaderPipeline GetShaderPipeline(Identifier<Shader> id)
|
||||
public ID3D12PipelineState* LoadPipelineState(GraphicsPipelineKey key)
|
||||
{
|
||||
if (_shaderPipelines.TryGetValue(id, out var pipeline))
|
||||
var name = key.ToString();
|
||||
var state = _pipelineCache[key];
|
||||
var streamDesc = new D3D12_PIPELINE_STATE_STREAM_DESC
|
||||
{
|
||||
return pipeline;
|
||||
}
|
||||
pPipelineStateSubobjectStream = &state.psoDesc,
|
||||
SizeInBytes = (nuint)sizeof(D3DX12_MESH_SHADER_PIPELINE_STATE_DESC)
|
||||
};
|
||||
|
||||
throw new KeyNotFoundException($"Shader pipeline not found for shader ID: {id}");
|
||||
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());
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var kvp in _shaderPipelines)
|
||||
_defaultRootSignature.Dispose();
|
||||
|
||||
foreach (var kvp in _pipelineCache)
|
||||
{
|
||||
kvp.Value.Dispose();
|
||||
}
|
||||
|
||||
_library.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using Ghost.Core.Utilities;
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Ghost.Graphics.RHI;
|
||||
using TerraFX.Interop.DirectX;
|
||||
@@ -34,9 +35,9 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
|
||||
{
|
||||
InitializeDevice();
|
||||
|
||||
_graphicsQueue = new D3D12CommandQueue(_device, CommandQueueType.Graphics);
|
||||
_computeQueue = new D3D12CommandQueue(_device, CommandQueueType.Compute);
|
||||
_copyQueue = new D3D12CommandQueue(_device, CommandQueueType.Copy);
|
||||
_graphicsQueue = new D3D12CommandQueue(_device.Get(), CommandQueueType.Graphics);
|
||||
_computeQueue = new D3D12CommandQueue(_device.Get(), CommandQueueType.Compute);
|
||||
_copyQueue = new D3D12CommandQueue(_device.Get(), CommandQueueType.Copy);
|
||||
}
|
||||
|
||||
~D3D12RenderDevice()
|
||||
@@ -52,7 +53,7 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
|
||||
CreateDXGIFactory2(FALSE, __uuidof<IDXGIFactory7>(), _dxgiFactory.GetVoidAddressOf());
|
||||
#endif
|
||||
|
||||
using ComPtr<IDXGIAdapter1> adapter = default;
|
||||
ComPtr<IDXGIAdapter1> adapter = default;
|
||||
|
||||
for (uint adapterIndex = 0;
|
||||
_dxgiFactory.Get()->EnumAdapterByGpuPreference(adapterIndex, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, __uuidof<IDXGIAdapter1>(), adapter.ReleaseAndGetVoidAddressOf()).SUCCEEDED;
|
||||
@@ -76,6 +77,7 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
|
||||
|
||||
if (_device.Get() == 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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +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;
|
||||
@@ -28,17 +29,6 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator, IDisposable
|
||||
|
||||
private UnsafeQueue<Handle<GPUResource>> _temResources = new(64, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent);
|
||||
|
||||
private Guid* IID_NULL
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (Guid* pGuid = &Guid.Empty)
|
||||
{
|
||||
return pGuid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public D3D12ResourceAllocator(RenderSystem renderSystem, D3D12RenderDevice device, D3D12DescriptorAllocator descriptorAllocator, D3D12ResourceDatabase resourceDatabase)
|
||||
{
|
||||
var desc = new D3D12MA_ALLOCATOR_DESC
|
||||
@@ -62,7 +52,7 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator, IDisposable
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void CheckBufferSize(uint sizeInBytes)
|
||||
private static void CheckBufferSize(ulong sizeInBytes)
|
||||
{
|
||||
if (sizeInBytes > _MAX_BYTES)
|
||||
{
|
||||
@@ -409,7 +399,7 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator, IDisposable
|
||||
var initialState = DetermineInitialTextureState(desc.Usage);
|
||||
|
||||
ComPtr<D3D12MA_Allocation> allocation = default;
|
||||
ThrowIfFailed(_allocator.Get()->CreateResource(&allocationDesc, &resourceDesc, initialState, null, allocation.GetAddressOf(), IID_NULL, null));
|
||||
ThrowIfFailed(_allocator.Get()->CreateResource(&allocationDesc, &resourceDesc, initialState, null, allocation.GetAddressOf(), Win32Utility.IID_NULL, null));
|
||||
|
||||
var resourceDescriptor = ResourceViewGroup.Invalid;
|
||||
if (desc.Usage.HasFlag(TextureUsage.ShaderResource))
|
||||
@@ -482,8 +472,8 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator, IDisposable
|
||||
var initialState = DetermineInitialBufferState(desc.Usage, desc.MemoryType);
|
||||
|
||||
ComPtr<D3D12MA_Allocation> allocation = default;
|
||||
ThrowIfFailed(_allocator.Get()->CreateResource(&allocationDesc, &resourceDescription, initialState, null, allocation.GetAddressOf(), IID_NULL, null));
|
||||
|
||||
ThrowIfFailed(_allocator.Get()->CreateResource(&allocationDesc, &resourceDescription, initialState, null, allocation.GetAddressOf(), Win32Utility.IID_NULL, null));
|
||||
|
||||
var resourceDescriptor = ResourceViewGroup.Invalid;
|
||||
if (desc.Usage.HasFlag(BufferUsage.ShaderResource))
|
||||
{
|
||||
@@ -499,7 +489,7 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator, IDisposable
|
||||
{
|
||||
srvDesc.Format = DXGI_FORMAT_R32_TYPELESS;
|
||||
srvDesc.Buffer.FirstElement = 0;
|
||||
srvDesc.Buffer.NumElements = desc.Size / 4;
|
||||
srvDesc.Buffer.NumElements = (uint)(desc.Size / 4u);
|
||||
srvDesc.Buffer.StructureByteStride = 0;
|
||||
srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_RAW;
|
||||
}
|
||||
@@ -507,7 +497,7 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator, IDisposable
|
||||
{
|
||||
srvDesc.Format = DXGI_FORMAT_UNKNOWN;
|
||||
srvDesc.Buffer.FirstElement = 0;
|
||||
srvDesc.Buffer.NumElements = desc.Size / desc.Stride;
|
||||
srvDesc.Buffer.NumElements = (uint)(desc.Size / desc.Stride);
|
||||
srvDesc.Buffer.StructureByteStride = desc.Stride;
|
||||
srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE;
|
||||
}
|
||||
@@ -519,13 +509,13 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator, IDisposable
|
||||
return handle.AsGraphicsBuffer();
|
||||
}
|
||||
|
||||
public Handle<GraphicsBuffer> CreateUploadBuffer(uint size, bool isTemp = true)
|
||||
public Handle<GraphicsBuffer> CreateUploadBuffer(ulong size, bool isTemp = true)
|
||||
{
|
||||
var desc = new BufferDesc
|
||||
{
|
||||
Size = size,
|
||||
Usage = BufferUsage.Upload,
|
||||
MemoryType = MemoryType.Upload,
|
||||
MemoryType = ResourceMemoryType.Upload,
|
||||
};
|
||||
|
||||
return CreateBuffer(ref desc, isTemp);
|
||||
@@ -538,7 +528,7 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator, IDisposable
|
||||
Size = (uint)(vertices.Count * Unsafe.SizeOf<Vertex>()),
|
||||
Stride = (uint)Unsafe.SizeOf<Vertex>(),
|
||||
Usage = BufferUsage.Vertex | BufferUsage.ShaderResource,
|
||||
MemoryType = MemoryType.Default,
|
||||
MemoryType = ResourceMemoryType.Default,
|
||||
};
|
||||
|
||||
var indexBufferDesc = new BufferDesc
|
||||
@@ -546,7 +536,7 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator, IDisposable
|
||||
Size = (uint)(indices.Count * sizeof(uint)),
|
||||
Stride = sizeof(uint),
|
||||
Usage = BufferUsage.Index | BufferUsage.ShaderResource,
|
||||
MemoryType = MemoryType.Default,
|
||||
MemoryType = ResourceMemoryType.Default,
|
||||
};
|
||||
|
||||
var vertexBuffer = CreateBuffer(ref vertexBufferDesc);
|
||||
@@ -572,11 +562,13 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator, IDisposable
|
||||
|
||||
ref var shaderRef = ref _resourceDatabase.GetShaderReference(shader);
|
||||
|
||||
// TODO: Get per-material constant buffer size from database
|
||||
|
||||
var desc = new BufferDesc
|
||||
{
|
||||
Size = shaderRef.PerMaterialBufferInfo.Size,
|
||||
Usage = BufferUsage.Constant,
|
||||
MemoryType = MemoryType.Default,
|
||||
MemoryType = ResourceMemoryType.Default,
|
||||
};
|
||||
|
||||
var buffer = CreateBuffer(ref desc);
|
||||
@@ -641,13 +633,13 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator, IDisposable
|
||||
return flags;
|
||||
}
|
||||
|
||||
private static D3D12_HEAP_TYPE ConvertMemoryType(MemoryType memoryType)
|
||||
private static D3D12_HEAP_TYPE ConvertMemoryType(ResourceMemoryType memoryType)
|
||||
{
|
||||
return memoryType switch
|
||||
{
|
||||
MemoryType.Default => D3D12_HEAP_TYPE_DEFAULT,
|
||||
MemoryType.Upload => D3D12_HEAP_TYPE_UPLOAD,
|
||||
MemoryType.Readback => D3D12_HEAP_TYPE_READBACK,
|
||||
ResourceMemoryType.Default => D3D12_HEAP_TYPE_DEFAULT,
|
||||
ResourceMemoryType.Upload => D3D12_HEAP_TYPE_UPLOAD,
|
||||
ResourceMemoryType.Readback => D3D12_HEAP_TYPE_READBACK,
|
||||
_ => throw new ArgumentException($"Unsupported memory type: {memoryType}")
|
||||
};
|
||||
}
|
||||
@@ -672,14 +664,14 @@ internal unsafe class D3D12ResourceAllocator : IResourceAllocator, IDisposable
|
||||
return D3D12_RESOURCE_STATE_COMMON;
|
||||
}
|
||||
|
||||
private static D3D12_RESOURCE_STATES DetermineInitialBufferState(BufferUsage usage, MemoryType memoryType)
|
||||
private static D3D12_RESOURCE_STATES DetermineInitialBufferState(BufferUsage usage, ResourceMemoryType memoryType)
|
||||
{
|
||||
if (memoryType == MemoryType.Upload)
|
||||
if (memoryType == ResourceMemoryType.Upload)
|
||||
{
|
||||
return D3D12_RESOURCE_STATE_GENERIC_READ;
|
||||
}
|
||||
|
||||
if (memoryType == MemoryType.Readback)
|
||||
if (memoryType == ResourceMemoryType.Readback)
|
||||
{
|
||||
return D3D12_RESOURCE_STATE_COPY_DEST;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Ghost.Graphics.Data;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.Collections;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using System.Runtime.InteropServices;
|
||||
using TerraFX.Interop.DirectX;
|
||||
@@ -12,7 +12,7 @@ namespace Ghost.Graphics.D3D12;
|
||||
|
||||
internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
|
||||
{
|
||||
internal unsafe struct ResourceInfo
|
||||
internal unsafe struct ResourceRecord
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct ResourceUnion
|
||||
@@ -36,14 +36,14 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
|
||||
public ResourceDesc desc;
|
||||
public ResourceViewGroup descriptor;
|
||||
public ResourceUnion resourceUnion;
|
||||
public uint cpuFenceValue;
|
||||
public ResourceState state;
|
||||
public uint cpuFenceValue;
|
||||
public readonly bool isExternal;
|
||||
|
||||
public readonly bool Allocated => isExternal ? resourceUnion.resource.Get() != null : resourceUnion.allocation.Get()->IsNotNull;
|
||||
public readonly bool Allocated => isExternal ? resourceUnion.resource.Get() != null : resourceUnion.allocation.Get() != null;
|
||||
public readonly ID3D12Resource* ResourcePtr => isExternal ? resourceUnion.resource.Get() : resourceUnion.allocation.Get()->GetResource();
|
||||
|
||||
public ResourceInfo(ComPtr<D3D12MA_Allocation> allocation, uint cpuFenceValue, ResourceState state, ResourceViewGroup resourceDescriptor, ResourceDesc desc)
|
||||
public ResourceRecord(ComPtr<D3D12MA_Allocation> allocation, uint cpuFenceValue, ResourceState state, ResourceViewGroup resourceDescriptor, ResourceDesc desc)
|
||||
{
|
||||
this.resourceUnion = new ResourceUnion(allocation);
|
||||
this.isExternal = false;
|
||||
@@ -54,7 +54,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public ResourceInfo(ComPtr<ID3D12Resource> resource, ResourceState state)
|
||||
public ResourceRecord(ComPtr<ID3D12Resource> resource, ResourceState state)
|
||||
{
|
||||
this.resourceUnion = new ResourceUnion(resource);
|
||||
this.isExternal = true;
|
||||
@@ -65,24 +65,26 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
|
||||
this.desc = ResourceDesc.FromD3D12(resource.Get()->GetDesc());
|
||||
}
|
||||
|
||||
public uint Release()
|
||||
public uint Release(D3D12DescriptorAllocator descriptorAllocator)
|
||||
{
|
||||
var refCount = 0u;
|
||||
if (Allocated)
|
||||
{
|
||||
if (isExternal)
|
||||
{
|
||||
refCount = resourceUnion.resource.Get()->Release();
|
||||
refCount = resourceUnion.resource.Reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
refCount = resourceUnion.allocation.Get()->Release();
|
||||
refCount = resourceUnion.allocation.Reset();
|
||||
}
|
||||
|
||||
resourceUnion = default;
|
||||
descriptor = default;
|
||||
}
|
||||
|
||||
descriptorAllocator.Release(descriptor);
|
||||
|
||||
return refCount;
|
||||
}
|
||||
}
|
||||
@@ -93,31 +95,33 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
|
||||
public bool occupied;
|
||||
}
|
||||
|
||||
private UnsafeSlotMap<ResourceInfo> _resources;
|
||||
private readonly D3D12DescriptorAllocator _descriptorAllocator;
|
||||
|
||||
private UnsafeSlotMap<ResourceRecord> _resources;
|
||||
#if DEBUG || GHOST_EDITOR
|
||||
private readonly Dictionary<ResourceInfo, string> _resourceName;
|
||||
private readonly Dictionary<ResourceRecord, string> _resourceName;
|
||||
#endif
|
||||
|
||||
private readonly UnsafeSlotMap<Mesh> _meshes;
|
||||
private readonly UnsafeSlotMap<Material> _materials;
|
||||
|
||||
// NOTE: We use a simple list since shader is not frequently added/removed. This can save 4 bytes for each ecs component.
|
||||
// NOTE: We use a simple list since shaderSlot is not frequently added/removed. This can save 4 bytes for each ecs component.
|
||||
private readonly DynamicArray<Slot<Shader>> _shaders;
|
||||
|
||||
private readonly D3D12DescriptorAllocator _descriptorAllocator;
|
||||
private readonly Dictionary<ShaderPassKey, ShaderPass> _shaderPasses;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public D3D12ResourceDatabase(D3D12DescriptorAllocator descriptorAllocator)
|
||||
{
|
||||
_resources = new(64, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent);
|
||||
_resources = new(64, Allocator.Persistent);
|
||||
#if DEBUG || GHOST_EDITOR
|
||||
_resourceName = new(64);
|
||||
#endif
|
||||
|
||||
_meshes = new(64, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent);
|
||||
_materials = new(16, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent);
|
||||
_meshes = new(64, Allocator.Persistent);
|
||||
_materials = new(16, Allocator.Persistent);
|
||||
_shaders = new(16);
|
||||
_shaderPasses = new(16);
|
||||
|
||||
_descriptorAllocator = descriptorAllocator;
|
||||
}
|
||||
@@ -127,6 +131,12 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private void ReleaseResource<T>(ref T resource)
|
||||
where T : IResourceReleasable
|
||||
{
|
||||
resource.ReleaseResource(this);
|
||||
}
|
||||
|
||||
public Handle<GPUResource> ImportExternalResource<T>(T resource, ResourceState initialState)
|
||||
where T : unmanaged
|
||||
{
|
||||
@@ -137,7 +147,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
|
||||
throw new InvalidOperationException($"Expect ComPtr<ID3D12Resource> in D3D12ResourceDatabase, but got {typeof(T)}.");
|
||||
}
|
||||
|
||||
var id = _resources.Add(new ResourceInfo(d3d12Resource, initialState), out var generation);
|
||||
var id = _resources.Add(new ResourceRecord(d3d12Resource, initialState), out var generation);
|
||||
return new Handle<GPUResource>(id, generation);
|
||||
}
|
||||
|
||||
@@ -145,11 +155,11 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var id = _resources.Add(new ResourceInfo(allocation, cpuFenceValue, initialState, resourceDescriptor, desc), out var generation);
|
||||
var id = _resources.Add(new ResourceRecord(allocation, cpuFenceValue, initialState, resourceDescriptor, desc), out var generation);
|
||||
return new Handle<GPUResource>(id, generation);
|
||||
}
|
||||
|
||||
public ref ResourceInfo GetResourceInfo(Handle<GPUResource> handle)
|
||||
public ref ResourceRecord GetResourceInfo(Handle<GPUResource> handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
@@ -162,7 +172,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
|
||||
return ref info;
|
||||
}
|
||||
|
||||
public ref ResourceInfo GetResourceInfo(Handle<GPUResource> handle, out bool exist)
|
||||
public ref ResourceRecord GetResourceInfo(Handle<GPUResource> handle, out bool exist)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
return ref _resources.GetElementReferenceAt(handle.id, handle.generation, out exist);
|
||||
@@ -232,7 +242,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
var refCount = info.Release();
|
||||
var refCount = info.Release(_descriptorAllocator);
|
||||
#if DEBUG || GHOST_EDITOR
|
||||
if (refCount > 0)
|
||||
{
|
||||
@@ -268,28 +278,15 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
|
||||
return ref mesh;
|
||||
}
|
||||
|
||||
private void ReleaseMeshResources(ref readonly Mesh mesh)
|
||||
{
|
||||
mesh.ReleaseCpuResources();
|
||||
|
||||
ref var vertexRef = ref GetResourceInfo(mesh.vertexBuffer.AsResource());
|
||||
ref var indexRef = ref GetResourceInfo(mesh.indexBuffer.AsResource());
|
||||
_descriptorAllocator.Release(vertexRef.descriptor);
|
||||
_descriptorAllocator.Release(indexRef.descriptor);
|
||||
|
||||
ReleaseResource(mesh.vertexBuffer.AsResource());
|
||||
ReleaseResource(mesh.indexBuffer.AsResource());
|
||||
}
|
||||
|
||||
public void ReleaseMesh(Handle<Mesh> handle)
|
||||
{
|
||||
ref var meshSlot = ref _meshes.GetElementReferenceAt(handle.id, handle.generation, out var exist);
|
||||
ref var mesh = ref _meshes.GetElementReferenceAt(handle.id, handle.generation, out var exist);
|
||||
if (!exist)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ReleaseMeshResources(ref meshSlot);
|
||||
ReleaseResource(ref mesh);
|
||||
_meshes.Remove(handle.id, handle.generation);
|
||||
}
|
||||
|
||||
@@ -326,7 +323,8 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
material.Dispose();
|
||||
ReleaseResource(ref material);
|
||||
_materials.Remove(handle.id, handle.generation);
|
||||
}
|
||||
|
||||
public Identifier<Shader> AddShader(ref readonly Shader shader)
|
||||
@@ -355,17 +353,39 @@ internal class D3D12ResourceDatabase : IResourceDatabase, IDisposable
|
||||
return ref shader;
|
||||
}
|
||||
|
||||
public void ReleaseShader(Identifier<Shader> handle)
|
||||
public void ReleaseShader(Identifier<Shader> id)
|
||||
{
|
||||
if (!HasShader(handle))
|
||||
if (!HasShader(id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ref var shader = ref _shaders[handle.value].value;
|
||||
shader.Dispose();
|
||||
ref var shaderSlot = ref _shaders[id.value];
|
||||
|
||||
ReleaseResource(ref shaderSlot.value);
|
||||
shaderSlot.occupied = false;
|
||||
}
|
||||
|
||||
public void AddShaderPass(ShaderPassKey passKey, ShaderPass pass)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
_shaderPasses.Add(passKey, pass);
|
||||
}
|
||||
|
||||
public ShaderPass GetShaderPass(ShaderPassKey passKey)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (!_shaderPasses.TryGetValue(passKey, out var pass))
|
||||
{
|
||||
throw new KeyNotFoundException($"Shader pass '{passKey}' not found.");
|
||||
}
|
||||
|
||||
return pass;
|
||||
}
|
||||
|
||||
// Should we need to release the shaderSlot pass?
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#undef SUPPORT_TEXTURE_BINDING
|
||||
|
||||
using Ghost.Core;
|
||||
using Ghost.Core.Utilities;
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Ghost.Graphics.Data;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using TerraFX.Interop.DirectX;
|
||||
@@ -10,63 +12,192 @@ using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
internal unsafe static class D3D12ShaderCompiler
|
||||
internal unsafe struct CompileResult : IDisposable
|
||||
{
|
||||
public enum CompilerVersion
|
||||
public UnsafeArray<byte> bytecode;
|
||||
|
||||
public readonly bool IsCreated => bytecode.IsCreated;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
SM_6_6,
|
||||
SM_7_0
|
||||
bytecode.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
internal readonly struct CBufferVariableInfo
|
||||
{
|
||||
public string Name
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
|
||||
public enum ShaderStage
|
||||
public uint StartOffset
|
||||
{
|
||||
VertexShader,
|
||||
PixelShader,
|
||||
MeshShader,
|
||||
ComputeShader
|
||||
get; init;
|
||||
}
|
||||
|
||||
public struct CompileResult : IDisposable
|
||||
public uint Size
|
||||
{
|
||||
public UnsafeArray<byte> bytecode;
|
||||
public ComPtr<IDxcBlob> reflection;
|
||||
get; init;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
bytecode.Dispose();
|
||||
reflection.Dispose();
|
||||
}
|
||||
internal readonly struct CBufferInfo
|
||||
{
|
||||
public string Name
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
|
||||
private static string GetProfileString(ShaderStage stage, CompilerVersion version)
|
||||
public uint RegisterSlot
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
|
||||
public uint RegisterSpace
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
|
||||
public uint SizeInBytes
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
|
||||
public IReadOnlyList<CBufferVariableInfo> Variables
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
private static string GetProfileString(ShaderStage stage, CompilerTier version)
|
||||
{
|
||||
return (stage, version) switch
|
||||
{
|
||||
(ShaderStage.VertexShader, CompilerVersion.SM_6_6) => "vs_6_6",
|
||||
(ShaderStage.PixelShader, CompilerVersion.SM_6_6) => "ps_6_6",
|
||||
(ShaderStage.MeshShader, CompilerVersion.SM_6_6) => "ms_6_6",
|
||||
(ShaderStage.ComputeShader, CompilerVersion.SM_6_6) => "cs_6_6",
|
||||
(ShaderStage.VertexShader, CompilerVersion.SM_7_0) => "vs_7_0",
|
||||
(ShaderStage.PixelShader, CompilerVersion.SM_7_0) => "ps_7_0",
|
||||
(ShaderStage.MeshShader, CompilerVersion.SM_7_0) => "ms_7_0",
|
||||
(ShaderStage.ComputeShader, CompilerVersion.SM_7_0) => "cs_7_0",
|
||||
(ShaderStage.TaskShader, CompilerTier.Tier0) => "as_6_6",
|
||||
(ShaderStage.PixelShader, CompilerTier.Tier0) => "ps_6_6",
|
||||
(ShaderStage.MeshShader, CompilerTier.Tier0) => "ms_6_6",
|
||||
(ShaderStage.ComputeShader, CompilerTier.Tier0) => "cs_6_6",
|
||||
(ShaderStage.TaskShader, CompilerTier.Tier1) => "as_6_7",
|
||||
(ShaderStage.PixelShader, CompilerTier.Tier1) => "ps_6_7",
|
||||
(ShaderStage.MeshShader, CompilerTier.Tier1) => "ms_6_7",
|
||||
(ShaderStage.ComputeShader, CompilerTier.Tier1) => "cs_6_7",
|
||||
(ShaderStage.TaskShader, CompilerTier.Tier2) => "as_6_8",
|
||||
(ShaderStage.PixelShader, CompilerTier.Tier2) => "ps_6_8",
|
||||
(ShaderStage.MeshShader, CompilerTier.Tier2) => "ms_6_8",
|
||||
(ShaderStage.ComputeShader, CompilerTier.Tier2) => "cs_6_8",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(stage), "Unsupported shader stage or compiler version")
|
||||
};
|
||||
}
|
||||
|
||||
private static string GetEntryPoint(ShaderStage stage)
|
||||
private static string GetOptimizeLevelString(CompilerOptimizeLevel level)
|
||||
{
|
||||
return stage switch
|
||||
return level switch
|
||||
{
|
||||
ShaderStage.VertexShader => "VSMain",
|
||||
ShaderStage.PixelShader => "PSMain",
|
||||
ShaderStage.MeshShader => "MSMain",
|
||||
ShaderStage.ComputeShader => "CSMain",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(stage), "Unsupported shader stage")
|
||||
CompilerOptimizeLevel.O0 => "-O0",
|
||||
CompilerOptimizeLevel.O1 => "-O1",
|
||||
CompilerOptimizeLevel.O2 => "-O2",
|
||||
CompilerOptimizeLevel.O3 => "-O3",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(level), "Unsupported optimization level")
|
||||
};
|
||||
}
|
||||
|
||||
public static CompileResult Compile(string shaderPath, ShaderStage stage, CompilerVersion version)
|
||||
private static List<string> GetCompilerArguments(ref readonly CompilerConfig config)
|
||||
{
|
||||
var argsArray = new List<string>
|
||||
{
|
||||
"-T", GetProfileString(config.stage, config.tier), // Target profile (ms_6_6, ps_6_6)
|
||||
"-E", config.entryPoint, // Entry point
|
||||
"-HV", "2021", // HLSL version 2021
|
||||
"-enable-16bit-types", // Enable 16-bit types
|
||||
GetOptimizeLevelString(config.optimizeLevel), // Optimization level
|
||||
};
|
||||
|
||||
foreach (var include in config.includes)
|
||||
{
|
||||
argsArray.Add("-I");
|
||||
argsArray.Add(include);
|
||||
}
|
||||
|
||||
foreach (var define in config.defines)
|
||||
{
|
||||
argsArray.Add("-D");
|
||||
argsArray.Add(define);
|
||||
}
|
||||
|
||||
if (!config.options.HasFlag(CompilerOption.KeepDebugInfo))
|
||||
{
|
||||
argsArray.Add("-Qstrip_debug");
|
||||
}
|
||||
|
||||
if (!config.options.HasFlag(CompilerOption.KeepReflections))
|
||||
{
|
||||
argsArray.Add("-Qstrip_reflect");
|
||||
}
|
||||
|
||||
if (config.options.HasFlag(CompilerOption.WarnAsError))
|
||||
{
|
||||
argsArray.Add("-WX");
|
||||
}
|
||||
|
||||
return argsArray;
|
||||
}
|
||||
|
||||
public static Result<CompileResult> Compile(ref readonly CompilerConfig config, Allocator allocator, IDxcBlob** ppReflectionBlob)
|
||||
{
|
||||
using ComPtr<IDxcCompiler3> compiler = default;
|
||||
using ComPtr<IDxcUtils> utils = default;
|
||||
@@ -76,56 +207,38 @@ internal unsafe static class D3D12ShaderCompiler
|
||||
var pDxcCompiler = (Guid*)Unsafe.AsPointer(in CLSID.CLSID_DxcCompiler);
|
||||
var pDxcUtils = (Guid*)Unsafe.AsPointer(in CLSID.CLSID_DxcUtils);
|
||||
|
||||
DxcCreateInstance(pDxcCompiler, __uuidof<IDxcCompiler3>(), compiler.GetVoidAddressOf());
|
||||
DxcCreateInstance(pDxcUtils, __uuidof<IDxcUtils>(), utils.GetVoidAddressOf());
|
||||
ThrowIfFailed(DxcCreateInstance(pDxcCompiler, __uuidof<IDxcCompiler3>(), compiler.GetVoidAddressOf()));
|
||||
ThrowIfFailed(DxcCreateInstance(pDxcUtils, __uuidof<IDxcUtils>(), utils.GetVoidAddressOf()));
|
||||
|
||||
//includeHandler.Get()->LoadSource();
|
||||
utils.Get()->CreateDefaultIncludeHandler(includeHandler.GetAddressOf());
|
||||
|
||||
// Create source blob
|
||||
using ComPtr<IDxcBlobEncoding> sourceBlob = default;
|
||||
//var sourceBytes = System.Text.Encoding.UTF8.GetBytes(shaderPath);
|
||||
|
||||
fixed (char* pShaderPath = shaderPath.AsSpan())
|
||||
if (utils.Get()->LoadFile(config.shaderPath.AsSpan().GetUnsafePtr(), null, sourceBlob.GetAddressOf()).FAILED)
|
||||
{
|
||||
utils.Get()->LoadFile(pShaderPath, null, sourceBlob.GetAddressOf());
|
||||
//utils.Get()->CreateBlob(sourceBytesPtr, (uint)sourceBytes.Length, DXC_CP_UTF8, sourceBlob.GetAddressOf());
|
||||
return Result<CompileResult>.Fail($"Failed to load shader file: {config.shaderPath}");
|
||||
}
|
||||
|
||||
// Prepare compilation arguments - NOTE: NO -Qstrip_reflect to keep reflection data
|
||||
var argsArray = new string[]
|
||||
var argsArray = GetCompilerArguments(in config);
|
||||
var argPtrs = stackalloc char*[argsArray.Count];
|
||||
for (var i = 0; i < argsArray.Count; i++)
|
||||
{
|
||||
"-T", GetProfileString(stage, version), // Target profile (vs_6_6, ps_6_6)
|
||||
"-E", GetEntryPoint(stage), // Entry point
|
||||
"-HV", "2021", // HLSL version 2021 (required for SM 6.6)
|
||||
"-enable-16bit-types", // Enable 16-bit types
|
||||
"-O3", // Optimization level
|
||||
"-Qstrip_debug" // Strip debug info but KEEP reflection
|
||||
};
|
||||
|
||||
// Convert to wide strings (DXC expects LPCWSTR)
|
||||
var wideArgs = new nuint[argsArray.Length];
|
||||
var argPointers = new IntPtr[argsArray.Length];
|
||||
|
||||
for (var i = 0; i < argsArray.Length; i++)
|
||||
{
|
||||
argPointers[i] = Marshal.StringToHGlobalUni(argsArray[i]);
|
||||
wideArgs[i] = (nuint)argPointers[i];
|
||||
argPtrs[i] = (char*)Marshal.StringToHGlobalUni(argsArray[i]);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Compile shader
|
||||
using ComPtr<IDxcResult> result = default;
|
||||
fixed (nuint* argsPtr = wideArgs)
|
||||
var buffer = new DxcBuffer
|
||||
{
|
||||
var buffer = new DxcBuffer
|
||||
{
|
||||
Ptr = sourceBlob.Get()->GetBufferPointer(),
|
||||
Size = sourceBlob.Get()->GetBufferSize(),
|
||||
Encoding = DXC.DXC_CP_UTF8
|
||||
};
|
||||
Ptr = sourceBlob.Get()->GetBufferPointer(),
|
||||
Size = sourceBlob.Get()->GetBufferSize(),
|
||||
Encoding = DXC.DXC_CP_UTF8
|
||||
};
|
||||
|
||||
compiler.Get()->Compile(&buffer, (char**)argsPtr, (uint)argsArray.Length, includeHandler.Get(), __uuidof<IDxcResult>(), result.GetVoidAddressOf());
|
||||
}
|
||||
ThrowIfFailed(compiler.Get()->Compile(&buffer, argPtrs, (uint)argsArray.Count, includeHandler.Get(), __uuidof<IDxcResult>(), result.GetVoidAddressOf()));
|
||||
|
||||
// Check compilation result
|
||||
HRESULT hrStatus;
|
||||
@@ -139,11 +252,11 @@ internal unsafe static class D3D12ShaderCompiler
|
||||
if (errorBlob.Get() != null)
|
||||
{
|
||||
var errorMessage = Marshal.PtrToStringUni((IntPtr)errorBlob.Get()->GetBufferPointer());
|
||||
throw new Exception($"DXC shader compilation failed: {errorMessage}");
|
||||
return Result<CompileResult>.Fail($"DXC shader compilation failed:\n{errorMessage}");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("DXC shader compilation failed with unknown error");
|
||||
return Result<CompileResult>.Fail("DXC shader compilation failed with unknown error.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,47 +265,46 @@ internal unsafe static class D3D12ShaderCompiler
|
||||
ThrowIfFailed(result.Get()->GetResult(bytecodeBlob.GetAddressOf()));
|
||||
|
||||
// Get reflection data using DXC API
|
||||
using ComPtr<IDxcBlob> reflectionBlob = default;
|
||||
ThrowIfFailed(result.Get()->GetOutput(DXC_OUT_KIND.DXC_OUT_REFLECTION, __uuidof<IDxcBlob>(), reflectionBlob.GetVoidAddressOf(), null));
|
||||
if (ppReflectionBlob != null)
|
||||
{
|
||||
ThrowIfFailed(result.Get()->GetOutput(DXC_OUT_KIND.DXC_OUT_REFLECTION, __uuidof<IDxcBlob>(), (void**)ppReflectionBlob, null));
|
||||
}
|
||||
|
||||
var bytecodeSize = bytecodeBlob.Get()->GetBufferSize();
|
||||
var bytecode = new UnsafeArray<byte>((int)bytecodeSize, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent);
|
||||
var bytecode = new UnsafeArray<byte>((int)bytecodeSize, allocator);
|
||||
|
||||
NativeMemory.Copy(bytecodeBlob.Get()->GetBufferPointer(), bytecode.GetUnsafePtr(), bytecodeSize);
|
||||
|
||||
return new CompileResult
|
||||
{
|
||||
bytecode = bytecode,
|
||||
reflection = reflectionBlob.Move()
|
||||
};
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Free allocated wide strings
|
||||
for (var i = 0; i < argPointers.Length; i++)
|
||||
for (var i = 0; i < argsArray.Count; i++)
|
||||
{
|
||||
Marshal.FreeHGlobal(argPointers[i]);
|
||||
Marshal.FreeHGlobal((nint)argPtrs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddProperty(ref Shader shader, string name, PropertyInfo propertyInfo)
|
||||
{
|
||||
var id = shader.Properties.Count;
|
||||
shader.Properties.Add(propertyInfo);
|
||||
shader.PropertyNameToIdMap[name] = id;
|
||||
}
|
||||
|
||||
// TODO: Since we are using fixed root signature layout, the reflection pass should only validate the layout, not generate it.
|
||||
public static void PerformDXCReflection(ref Shader shader, IDxcBlob* reflectionBlob)
|
||||
// TODO: Ideally this should return a structured reflection data instead of populating raw lists/dictionaries.
|
||||
public static Result<ShaderReflectionData> PerformDXCReflection(IDxcBlob* reflectionBlob)
|
||||
{
|
||||
if (reflectionBlob == null)
|
||||
{
|
||||
return Result<ShaderReflectionData>.Fail("Reflection blob is null.");
|
||||
}
|
||||
|
||||
// Create DXC utils to parse reflection data
|
||||
var pDxcUtils = (Guid*)Unsafe.AsPointer(in CLSID.CLSID_DxcUtils);
|
||||
using ComPtr<IDxcUtils> utils = default;
|
||||
DxcCreateInstance(pDxcUtils, __uuidof<IDxcUtils>(), utils.GetVoidAddressOf());
|
||||
ThrowIfFailed(DxcCreateInstance(pDxcUtils, __uuidof<IDxcUtils>(), utils.GetVoidAddressOf()));
|
||||
|
||||
// Create reflection interface from blob
|
||||
var reflectionData = new DxcBuffer
|
||||
var reflectionBuffer = new DxcBuffer
|
||||
{
|
||||
Ptr = reflectionBlob->GetBufferPointer(),
|
||||
Size = reflectionBlob->GetBufferSize(),
|
||||
@@ -200,94 +312,84 @@ internal unsafe static class D3D12ShaderCompiler
|
||||
};
|
||||
|
||||
using ComPtr<ID3D12ShaderReflection> reflection = default;
|
||||
ThrowIfFailed(utils.Get()->CreateReflection(&reflectionData, __uuidof<ID3D12ShaderReflection>(), reflection.GetVoidAddressOf()));
|
||||
ThrowIfFailed(utils.Get()->CreateReflection(&reflectionBuffer, __uuidof<ID3D12ShaderReflection>(), reflection.GetVoidAddressOf()));
|
||||
|
||||
D3D12_SHADER_DESC shaderDesc;
|
||||
reflection.Get()->GetDesc(&shaderDesc);
|
||||
ThrowIfFailed(reflection.Get()->GetDesc(&shaderDesc));
|
||||
|
||||
var cbufferRegistry = new Dictionary<string, CBufferInfo>();
|
||||
var textureRegistry = new Dictionary<string, TextureInfo>();
|
||||
var reflectionData = new ShaderReflectionData();
|
||||
|
||||
for (uint i = 0; i < shaderDesc.BoundResources; i++)
|
||||
{
|
||||
D3D12_SHADER_INPUT_BIND_DESC bindDesc;
|
||||
reflection.Get()->GetResourceBindingDesc(i, &bindDesc);
|
||||
ThrowIfFailed(reflection.Get()->GetResourceBindingDesc(i, &bindDesc));
|
||||
|
||||
var resourceName = Marshal.PtrToStringUTF8((IntPtr)bindDesc.Name);
|
||||
if (resourceName == null)
|
||||
{
|
||||
return Result<ShaderReflectionData>.Fail("Failed to get resource name from reflection data.");
|
||||
}
|
||||
|
||||
switch (bindDesc.Type)
|
||||
{
|
||||
case D3D_SHADER_INPUT_TYPE.D3D_SIT_CBUFFER:
|
||||
{
|
||||
var cbufferName = Marshal.PtrToStringAnsi((IntPtr)bindDesc.Name);
|
||||
if (cbufferName == null || cbufferRegistry.ContainsKey(cbufferName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var cbuffer = reflection.Get()->GetConstantBufferByName(bindDesc.Name);
|
||||
D3D12_SHADER_BUFFER_DESC cbufferDesc;
|
||||
cbuffer->GetDesc(&cbufferDesc);
|
||||
ThrowIfFailed(cbuffer->GetDesc(&cbufferDesc));
|
||||
|
||||
var cbufferInfo = new CBufferInfo
|
||||
{
|
||||
Size = cbufferDesc.Size,
|
||||
RegisterSlot = bindDesc.BindPoint
|
||||
};
|
||||
cbufferRegistry.Add(cbufferName, cbufferInfo);
|
||||
var variables = new List<CBufferVariableInfo>((int)cbufferDesc.Variables);
|
||||
|
||||
// Now we iterate all variables for *every* cbuffer, not just b3
|
||||
for (uint j = 0; j < cbufferDesc.Variables; j++)
|
||||
{
|
||||
var variable = cbuffer->GetVariableByIndex(j);
|
||||
D3D12_SHADER_VARIABLE_DESC varDesc;
|
||||
variable->GetDesc(&varDesc);
|
||||
|
||||
var variableName = Marshal.PtrToStringAnsi((IntPtr)varDesc.Name);
|
||||
if (variableName == null || shader.PropertyNameToIdMap.ContainsKey(variableName))
|
||||
var variableName = Marshal.PtrToStringUTF8((IntPtr)varDesc.Name);
|
||||
if (variableName == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var propInfo = new PropertyInfo
|
||||
variables.Add(new CBufferVariableInfo
|
||||
{
|
||||
CBufferIndex = cbufferInfo.RegisterSlot,
|
||||
ByteOffset = varDesc.StartOffset,
|
||||
Name = variableName,
|
||||
StartOffset = varDesc.StartOffset,
|
||||
Size = varDesc.Size
|
||||
};
|
||||
|
||||
AddProperty(ref shader, variableName, propInfo);
|
||||
});
|
||||
}
|
||||
|
||||
reflectionData.ConstantBuffers.Add(new CBufferInfo
|
||||
{
|
||||
Name = resourceName,
|
||||
RegisterSlot = bindDesc.BindPoint,
|
||||
RegisterSpace = bindDesc.Space,
|
||||
SizeInBytes = cbufferDesc.Size,
|
||||
Variables = variables
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case D3D_SHADER_INPUT_TYPE.D3D_SIT_TEXTURE:
|
||||
// NOTE: Currently we are not support resource bindings yet, everything access through bindless heaps.
|
||||
default:
|
||||
{
|
||||
#if SUPPORT_TEXTURE_BINDING
|
||||
var textureName = Marshal.PtrToStringAnsi((IntPtr)bindDesc.Name);
|
||||
if (textureName == null || textureRegistry.ContainsKey(textureName))
|
||||
reflectionData.OtherResources.Add(new ResourceBindingInfo
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Name = resourceName,
|
||||
Type = bindDesc.Type,
|
||||
BindPoint = bindDesc.BindPoint,
|
||||
BindCount = bindDesc.BindCount,
|
||||
Space = bindDesc.Space
|
||||
});
|
||||
|
||||
// ALL texture input slots are regular textures!
|
||||
// Bindless textures don't use explicit texture inputs - they use ResourceDescriptorHeap[index]
|
||||
var textureInfo = new TextureInfo
|
||||
{
|
||||
RegisterSlot = bindDesc.BindPoint,
|
||||
RootParameterIndex = (uint)shader.ConstantBuffers.Count // Descriptor table comes after CBVs
|
||||
};
|
||||
|
||||
textureRegistry.Add(textureName, textureInfo);
|
||||
break;
|
||||
#endif
|
||||
throw new NotSupportedException("Texture bindings are not supported in current version. Please use bindless textures.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shader.PerMaterialBufferInfo.Clear();
|
||||
foreach (var cbuf in cbufferRegistry.Values)
|
||||
{
|
||||
shader.PerMaterialBufferInfo.Add(cbuf);
|
||||
}
|
||||
return reflectionData;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Core.Utilities;
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Ghost.Graphics.Data;
|
||||
|
||||
@@ -63,6 +63,8 @@ internal static class D3D12_BLEND_DESC_Extensions
|
||||
public static D3D12_BLEND_DESC OPAQUE => Create(D3D12_BLEND_ONE, D3D12_BLEND_ZERO);
|
||||
public static D3D12_BLEND_DESC ALPHA_BLEND => Create(D3D12_BLEND_SRC_ALPHA, D3D12_BLEND_INV_SRC_ALPHA);
|
||||
public static D3D12_BLEND_DESC ADDITIVE => Create(D3D12_BLEND_SRC_ALPHA, D3D12_BLEND_ONE);
|
||||
public static D3D12_BLEND_DESC MULTIPLY => Create(D3D12_BLEND_DEST_COLOR, D3D12_BLEND_ZERO);
|
||||
public static D3D12_BLEND_DESC PREMULTIPLIED => Create(D3D12_BLEND_ONE, D3D12_BLEND_INV_SRC_ALPHA);
|
||||
public static D3D12_BLEND_DESC NON_PREMULTIPLIED => Create(D3D12_BLEND_SRC_ALPHA, D3D12_BLEND_INV_SRC_ALPHA);
|
||||
|
||||
public static D3D12_BLEND_DESC Create(D3D12_BLEND srcBlend, D3D12_BLEND destBlend)
|
||||
@@ -148,16 +150,4 @@ internal static class D3D12_DEPTH_STENCILOP_DESC_Extensions
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal unsafe static class D3D12MA_Allocation_Extensions
|
||||
{
|
||||
extension(ref readonly D3D12MA_Allocation allocation)
|
||||
{
|
||||
public bool IsNull => allocation.GetResource() == null
|
||||
&& allocation.GetHeap() == null
|
||||
&& allocation.GetSize() == 0;
|
||||
|
||||
public bool IsNotNull => !allocation.IsNull;
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Ghost.Graphics.D3D12.Utilities;
|
||||
|
||||
internal unsafe static class Win32Utility
|
||||
{
|
||||
public static void ThrowIfFailed(this HRESULT hr)
|
||||
{
|
||||
Windows.ThrowIfFailed(hr);
|
||||
}
|
||||
|
||||
public static void** GetVoidAddressOf<T>(this ComPtr<T> comPtr)
|
||||
where T : unmanaged, IUnknown.Interface
|
||||
{
|
||||
return (void**)comPtr.GetAddressOf();
|
||||
}
|
||||
|
||||
public static void** ReleaseAndGetVoidAddressOf<T>(this ComPtr<T> comPtr)
|
||||
where T : unmanaged, IUnknown.Interface
|
||||
{
|
||||
return (void**)comPtr.ReleaseAndGetAddressOf();
|
||||
}
|
||||
|
||||
public static ComPtr<T> Move<T>(this ComPtr<T> comPtr)
|
||||
where T : unmanaged, IUnknown.Interface
|
||||
{
|
||||
ComPtr<T> copy = default;
|
||||
Unsafe.AsRef(in comPtr).Swap(ref copy);
|
||||
return copy;
|
||||
}
|
||||
|
||||
public static bool HasFlag<T>(this uint flags, T flag)
|
||||
where T : Enum
|
||||
{
|
||||
return (flags & Unsafe.As<T, uint>(ref flag)) != 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user