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,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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user