Refactor shader pipeline and improve modularity

- Added `generatedCodePath` to `FullPassDescriptor` for better shader code organization.
- Removed redundant `IID_PPV_ARGS` method and unused `Misaki.HighPerformance.Unsafe` reference.
- Refactored `Material` and `MaterialAccessor` to use `CBuffer` and updated buffer size handling.
- Renamed command buffer variables in `RenderingContext` for consistency.
- Updated `D3D12PipelineLibrary` to cache compiled shader results and added `ShaderPassKey`.
- Refactored `D3D12GraphicsEngine` to integrate `_copyCommandBuffer` lifecycle.
- Enhanced `D3D12ResourceAllocator` with shader pass creation using constant buffer info.
- Simplified `D3D12ShaderCompiler` with `GENERATED_CODE_PATH` support and improved reflection handling.
- Introduced `CBufferPropertyInfo` and `CBufferInfo` structs for better encapsulation.
- Updated HLSL shaders to use `g_PerMaterialData` and dynamic includes.
- Improved error handling in `SDLCompiler` with try-catch blocks and better messages.
- Refactored `test.gshader` to use dynamically generated includes.
- Fixed typos, improved code readability, and removed unused code.
This commit is contained in:
2025-11-14 19:41:36 +09:00
parent 708b8cd065
commit d91d6f6e57
20 changed files with 325 additions and 321 deletions

View File

@@ -77,6 +77,7 @@ public class FullPassDescriptor : IPassDescriptor
public ShaderEntryPoint taskShader; public ShaderEntryPoint taskShader;
public ShaderEntryPoint meshShader; public ShaderEntryPoint meshShader;
public ShaderEntryPoint pixelShader; public ShaderEntryPoint pixelShader;
public string? generatedCodePath;
public List<string>? defines; public List<string>? defines;
public List<string>? includes; public List<string>? includes;
public List<KeywordsGroup>? keywords; public List<KeywordsGroup>? keywords;

View File

@@ -41,13 +41,6 @@ internal static unsafe partial class Win32Utility
return new IID_PPV(Windows.__uuidof<T>(), comPtr.PPV()); return new IID_PPV(Windows.__uuidof<T>(), comPtr.PPV());
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IID_PPV IID_PPV_ARGS<T>(T** ppv)
where T : unmanaged, IUnknown.Interface
{
return new IID_PPV(Windows.__uuidof<T>(), (void**)ppv);
}
[Conditional("DEBUG")] [Conditional("DEBUG")]
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Assert(this HRESULT hr) public static void Assert(this HRESULT hr)

View File

@@ -38,12 +38,6 @@
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Reference Include="Misaki.HighPerformance.Unsafe">
<HintPath>..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.LowLevel\bin\Release\net9.0\Misaki.HighPerformance.LowLevel.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup> <ItemGroup>
<None Update="Template\ForEach.tt"> <None Update="Template\ForEach.tt">
<Generator>TextTemplatingFileGenerator</Generator> <Generator>TextTemplatingFileGenerator</Generator>

View File

@@ -63,17 +63,17 @@ public struct Material : IResourceReleasable, IHandleType
for (var i = 0; i < shader.PassCount; i++) for (var i = 0; i < shader.PassCount; i++)
{ {
var pass = database.GetShaderPass(shader.GetPassKey(i)); var pass = database.GetShaderPass(shader.GetPassKey(i));
var cbufferInfo = pass.PassPropertyInfo; var cbufferInfo = pass.CBuffer;
var desc = new BufferDesc var desc = new BufferDesc
{ {
Size = cbufferInfo.Size, Size = cbufferInfo.SizeInBytes,
Usage = BufferUsage.Constant, Usage = BufferUsage.Constant,
MemoryType = ResourceMemoryType.Default, MemoryType = ResourceMemoryType.Default,
}; };
var buffer = allocator.CreateBuffer(ref desc); var buffer = allocator.CreateBuffer(ref desc);
_materialPropertiesCache[i] = new CBufferCache(buffer, cbufferInfo.Size); _materialPropertiesCache[i] = new CBufferCache(buffer, cbufferInfo.SizeInBytes);
} }
} }
@@ -118,7 +118,7 @@ public ref struct MaterialAccessor
} }
ref var cache = ref _materialData.GetPassCache(index); ref var cache = ref _materialData.GetPassCache(index);
Unsafe.WriteUnaligned(ref cache.CpuData[propertyInfo.ByteOffset], value); Unsafe.WriteUnaligned(ref cache.CpuData[propertyInfo.StartOffset], value);
} }
} }

View File

@@ -10,13 +10,13 @@ namespace Ghost.Graphics.Core;
public unsafe readonly ref struct RenderingContext public unsafe readonly ref struct RenderingContext
{ {
private readonly IGraphicsEngine _engine; private readonly IGraphicsEngine _engine;
private readonly ICommandBuffer _directCmb; private readonly ICommandBuffer _directCmd;
private readonly ICommandBuffer _copyCmb; private readonly ICommandBuffer _copyCmd;
private readonly ICommandBuffer _computeCmb; private readonly ICommandBuffer _computeCmd;
public ICommandBuffer DirectCommandBuffer => _directCmb; public ICommandBuffer DirectCommandBuffer => _directCmd;
public ICommandBuffer CopyCommandBuffer => _copyCmb; public ICommandBuffer CopyCommandBuffer => _copyCmd;
public ICommandBuffer ComputeCommandBuffer => _computeCmb; public ICommandBuffer ComputeCommandBuffer => _computeCmd;
public IResourceAllocator ResourceAllocator => _engine.ResourceAllocator; public IResourceAllocator ResourceAllocator => _engine.ResourceAllocator;
public IResourceDatabase ResourceDatabase => _engine.ResourceDatabase; public IResourceDatabase ResourceDatabase => _engine.ResourceDatabase;
@@ -29,9 +29,9 @@ public unsafe readonly ref struct RenderingContext
ICommandBuffer computeCmd) ICommandBuffer computeCmd)
{ {
_engine = engine; _engine = engine;
_directCmb = directCmd; _directCmd = directCmd;
_copyCmb = copyCmd; _copyCmd = copyCmd;
_computeCmb = computeCmd; _computeCmd = computeCmd;
} }
public ICommandBuffer CrearteCommandBuffer(CommandBufferType type) public ICommandBuffer CrearteCommandBuffer(CommandBufferType type)
@@ -88,29 +88,25 @@ public unsafe readonly ref struct RenderingContext
if (needVertexTransition) if (needVertexTransition)
{ {
_copyCmb.ResourceBarrier(meshData.vertexBuffer.AsResource(), vertexState, ResourceState.CopyDest); _directCmd.ResourceBarrier(meshData.vertexBuffer.AsResource(), vertexState, ResourceState.CopyDest);
ResourceDatabase.SetResourceState(meshData.vertexBuffer.AsResource(), ResourceState.CopyDest);
} }
if (needIndexTransition) if (needIndexTransition)
{ {
_copyCmb.ResourceBarrier(meshData.indexBuffer.AsResource(), indexState, ResourceState.CopyDest); _directCmd.ResourceBarrier(meshData.indexBuffer.AsResource(), indexState, ResourceState.CopyDest);
ResourceDatabase.SetResourceState(meshData.indexBuffer.AsResource(), ResourceState.CopyDest);
} }
_copyCmb.UploadBuffer<Vertex>(meshData.vertexBuffer, meshData.vertices.AsSpan()); _directCmd.UploadBuffer(meshData.vertexBuffer, meshData.vertices.AsSpan());
_copyCmb.UploadBuffer<uint>(meshData.indexBuffer, meshData.indices.AsSpan()); _directCmd.UploadBuffer(meshData.indexBuffer, meshData.indices.AsSpan());
if (needVertexTransition) if (needVertexTransition)
{ {
_copyCmb.ResourceBarrier(meshData.vertexBuffer.AsResource(), ResourceState.CopyDest, vertexState); _directCmd.ResourceBarrier(meshData.vertexBuffer.AsResource(), ResourceState.CopyDest, vertexState);
ResourceDatabase.SetResourceState(meshData.vertexBuffer.AsResource(), vertexState);
} }
if (needIndexTransition) if (needIndexTransition)
{ {
ResourceDatabase.SetResourceState(meshData.indexBuffer.AsResource(), indexState); _directCmd.ResourceBarrier(meshData.indexBuffer.AsResource(), ResourceState.CopyDest, indexState);
_copyCmb.ResourceBarrier(meshData.indexBuffer.AsResource(), ResourceState.CopyDest, indexState);
} }
if (markMeshStatic) if (markMeshStatic)
@@ -141,20 +137,18 @@ public unsafe readonly ref struct RenderingContext
if (needTransition) if (needTransition)
{ {
_copyCmb.ResourceBarrier(texture.AsResource(), sateBefore, ResourceState.CopyDest); _directCmd.ResourceBarrier(texture.AsResource(), sateBefore, ResourceState.CopyDest);
ResourceDatabase.SetResourceState(texture.AsResource(), ResourceState.CopyDest);
} }
_copyCmb.UploadTexture(texture, subresourceData); _directCmd.UploadTexture(texture, subresourceData);
if (needTransition) if (needTransition)
{ {
_copyCmb.ResourceBarrier(texture.AsResource(), ResourceState.CopyDest, sateBefore); _directCmd.ResourceBarrier(texture.AsResource(), ResourceState.CopyDest, sateBefore);
ResourceDatabase.SetResourceState(texture.AsResource(), sateBefore);
} }
} }
// TODO: Ideally we should queue the draw call to our rendering system, and render it in the full rendering pipeline. // TODO: Ideally we should queue the draw call to our rendering system, and render it in a full rendering pipeline.
// This is just a place holder for now for testing purpose. // This is just a place holder for now for testing purpose.
public void DispatchMesh(Handle<Mesh> mesh, Handle<Material> material, string passName, uint numThreadsX) public void DispatchMesh(Handle<Mesh> mesh, Handle<Material> material, string passName, uint numThreadsX)
{ {
@@ -172,20 +166,20 @@ public unsafe readonly ref struct RenderingContext
hash.rtvFormats[0] = TextureFormat.B8G8R8A8_UNorm; hash.rtvFormats[0] = TextureFormat.B8G8R8A8_UNorm;
var pipelineKey = hash.GetKey(); var pipelineKey = hash.GetKey();
_directCmb.SetPipelineState(pipelineKey); _directCmd.SetPipelineState(pipelineKey);
// NOTE: We use fixed root signature layout for bindless rendering. // NOTE: We use fixed root signature layout for bindless rendering.
ref var cache = ref materialRef.GetPassCache(passIndex); ref var cache = ref materialRef.GetPassCache(passIndex);
_directCmb.SetConstantBufferView(RootSignatureLayout.PER_MATERIAL_BUFFER_SLOT, cache.GpuResource); _directCmd.SetConstantBufferView(RootSignatureLayout.PER_MATERIAL_BUFFER_SLOT, cache.GpuResource);
// NOTE: Since we are using true bindless resources, we only need to set the descriptor heaps, not individual tables. // NOTE: Since we are using true bindless resources, we only need to set the descriptor heaps, not individual tables.
// TODO: Matbe handle the transitional bindless model? // TODO: Maybe handle the traditional bindless model?
#if false #if false
var samplerGpuHandle = _descriptorAllocator.GetSamplerHeap()->GetGPUDescriptorHandleForHeapStart(); var samplerGpuHandle = _descriptorAllocator.GetSamplerHeap()->GetGPUDescriptorHandleForHeapStart();
_commandList.Get()->SetGraphicsRootDescriptorTable(rootParamIndex, samplerGpuHandle); _commandList.Get()->SetGraphicsRootDescriptorTable(rootParamIndex, samplerGpuHandle);
#endif #endif
var threadGroupCountX = ((uint)meshRef.indices.Count + numThreadsX - 1) / numThreadsX; var threadGroupCountX = ((uint)meshRef.indices.Count + numThreadsX - 1) / numThreadsX;
_directCmb.DispatchMesh(threadGroupCountX, 1, 1); _directCmd.DispatchMesh(threadGroupCountX, 1, 1);
} }
} }

View File

@@ -7,66 +7,23 @@ using System.Runtime.InteropServices;
namespace Ghost.Graphics.Core; namespace Ghost.Graphics.Core;
public readonly struct TextureInfo public class ShaderPass : IResourceReleasable
{
public uint RegisterSlot
{
get; init;
}
public uint RootParameterIndex
{
get; init;
}
}
public readonly struct PropertyInfo
{
public uint CBufferIndex
{
get; init;
}
public uint ByteOffset
{
get; init;
}
public uint Size
{
get; init;
}
}
public readonly struct CBufferInfo
{
public uint Size
{
get; init;
}
public uint RegisterSlot
{
get; init;
}
}
public unsafe class ShaderPass : IResourceReleasable
{ {
private CBufferInfo _cbufferInfo;
// NOTE: This is for per pass cbuffer only. Global, per view, and per mesh cbuffers are fixed. // NOTE: This is for per pass cbuffer only. Global, per view, and per mesh cbuffers are fixed.
private readonly Dictionary<string, int> _propertyLookup; private readonly Dictionary<string, int> _propertyLookup;
private readonly UnsafeList<PropertyInfo> _properties;
internal CBufferInfo PassPropertyInfo public CBufferInfo CBuffer => _cbufferInfo;
public ShaderPass(CBufferInfo info)
{ {
get; _cbufferInfo = info;
_propertyLookup = new Dictionary<string, int>(info.Properties.Count);
for (var i = 0; i < info.Properties.Count; i++)
{
_propertyLookup[info.Properties[i].Name] = i;
} }
public ShaderPass(CBufferInfo info, UnsafeList<PropertyInfo> properties, Dictionary<string, int> propertyNameToIdMap)
{
PassPropertyInfo = info;
_properties = properties;
_propertyLookup = propertyNameToIdMap;
} }
public int GetPropertyId(string propertyName) public int GetPropertyId(string propertyName)
@@ -74,19 +31,18 @@ public unsafe class ShaderPass : IResourceReleasable
return _propertyLookup.TryGetValue(propertyName, out var id) ? id : -1; return _propertyLookup.TryGetValue(propertyName, out var id) ? id : -1;
} }
public PropertyInfo GetPropertyInfo(int id) public CBufferPropertyInfo GetPropertyInfo(int id)
{ {
return _properties[id]; return _cbufferInfo.Properties[id];
} }
public PropertyInfo GetPropertyInfo(string propertyName) public CBufferPropertyInfo GetPropertyInfo(string propertyName)
{ {
return _properties[GetPropertyId(propertyName)]; return _cbufferInfo.Properties[GetPropertyId(propertyName)];
} }
void IResourceReleasable.ReleaseResource(IResourceDatabase database) void IResourceReleasable.ReleaseResource(IResourceDatabase database)
{ {
_properties.Dispose();
} }
} }

View File

@@ -307,7 +307,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
ThrowIfNotRecording(); ThrowIfNotRecording();
IncrementCommandCount(); IncrementCommandCount();
var shaderPipeline = _pipelineLibrary.LoadGraphicsPSO(pipelineKey).GetValueOrThrow(); var shaderPipeline = _pipelineLibrary.GetGraphicsPSO(pipelineKey).GetValueOrThrow();
_commandList.Get()->SetPipelineState(shaderPipeline.value); _commandList.Get()->SetPipelineState(shaderPipeline.value);
} }

View File

@@ -12,9 +12,9 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
#endif #endif
private readonly D3D12RenderDevice _device; private readonly D3D12RenderDevice _device;
private readonly D3D12PipelineLibrary _pipelineLibrary;
private readonly D3D12DescriptorAllocator _descriptorAllocator; private readonly D3D12DescriptorAllocator _descriptorAllocator;
private readonly D3D12ResourceDatabase _resourceDatabase; private readonly D3D12ResourceDatabase _resourceDatabase;
private readonly D3D12PipelineLibrary _pipelineLibrary;
private readonly D3D12ResourceAllocator _resourceAllocator; private readonly D3D12ResourceAllocator _resourceAllocator;
private readonly D3D12CommandBuffer _copyCommandBuffer; private readonly D3D12CommandBuffer _copyCommandBuffer;
@@ -37,9 +37,9 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
_descriptorAllocator = new(_device); _descriptorAllocator = new(_device);
_resourceDatabase = new(_descriptorAllocator); _resourceDatabase = new(_descriptorAllocator);
_resourceAllocator = new(renderSystem, _device, _descriptorAllocator, _resourceDatabase);
_pipelineLibrary = new(_device, _resourceDatabase); _pipelineLibrary = new(_device, _resourceDatabase);
_resourceAllocator = new(renderSystem, _device, _descriptorAllocator, _resourceDatabase, _pipelineLibrary);
_copyCommandBuffer = new( _copyCommandBuffer = new(
_device, _device,
_pipelineLibrary, _pipelineLibrary,
@@ -112,6 +112,8 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
{ {
renderer.ExecutePendingResize(); renderer.ExecutePendingResize();
} }
_copyCommandBuffer.Begin();
} }
public void RenderFrame() public void RenderFrame()
@@ -127,6 +129,8 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
public void EndFrame() public void EndFrame()
{ {
ThrowIfDisposed(); ThrowIfDisposed();
_copyCommandBuffer.End();
_resourceAllocator.ReleaseTempResources(); _resourceAllocator.ReleaseTempResources();
} }
@@ -138,9 +142,9 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine
} }
_copyCommandBuffer.Dispose(); _copyCommandBuffer.Dispose();
_pipelineLibrary.Dispose();
_resourceAllocator.Dispose(); _resourceAllocator.Dispose();
_pipelineLibrary.Dispose();
_resourceDatabase.Dispose(); _resourceDatabase.Dispose();
_descriptorAllocator.Dispose(); _descriptorAllocator.Dispose();

View File

@@ -7,7 +7,6 @@ using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Collections;
using Misaki.HighPerformance.LowLevel.Utilities; using Misaki.HighPerformance.LowLevel.Utilities;
using Misaki.HighPerformance.Utilities; using Misaki.HighPerformance.Utilities;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using TerraFX.Interop.DirectX; using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows; using TerraFX.Interop.Windows;
@@ -22,6 +21,7 @@ internal struct D3D12GraphicsCompiledResult : IDisposable
public CompileResult tsResult; public CompileResult tsResult;
public CompileResult msResult; public CompileResult msResult;
public CompileResult psResult; public CompileResult psResult;
public CBufferInfo cbufferInfo;
public void Dispose() public void Dispose()
{ {
@@ -33,14 +33,12 @@ internal struct D3D12GraphicsCompiledResult : IDisposable
internal struct D3D12PipelineState : IDisposable 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 D3DX12_MESH_SHADER_PIPELINE_STATE_DESC psoDesc;
public ComPtr<ID3D12PipelineState> pso; public ComPtr<ID3D12PipelineState> pso;
public ShaderPassKey shaderPass;
public void Dispose() public void Dispose()
{ {
compileResult.Dispose();
pso.Dispose(); pso.Dispose();
} }
} }
@@ -62,6 +60,8 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
private ComPtr<ID3D12RootSignature> _defaultRootSignature; private ComPtr<ID3D12RootSignature> _defaultRootSignature;
private readonly Dictionary<GraphicsPipelineKey, D3D12PipelineState> _pipelineCache; private readonly Dictionary<GraphicsPipelineKey, D3D12PipelineState> _pipelineCache;
// NOTE: This is just a temporary cache for compiled shader code. We will implement a proper disk cache later.
private readonly Dictionary<ShaderPassKey, D3D12GraphicsCompiledResult> _compiledResults;
public ID3D12RootSignature* DefaultRootSignature => _defaultRootSignature.Get(); public ID3D12RootSignature* DefaultRootSignature => _defaultRootSignature.Get();
@@ -71,6 +71,7 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
_resourceDatabase = resourceDatabase; _resourceDatabase = resourceDatabase;
_pipelineCache = new(); _pipelineCache = new();
_compiledResults = new();
CreateDefaultRootSignature(); CreateDefaultRootSignature();
} }
@@ -214,7 +215,7 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
fs.Write(buffer.AsSpan()); fs.Write(buffer.AsSpan());
} }
private static void ValidateReflectionData(ShaderReflectionData reflectionData) private static CBufferInfo ValidateReflectionData(FullPassDescriptor descriptor, ShaderReflectionData reflectionData)
{ {
if (reflectionData.ConstantBuffers.Count != rootParamCount) if (reflectionData.ConstantBuffers.Count != rootParamCount)
{ {
@@ -226,27 +227,27 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
throw new NotSupportedException("Shader reflection data contains unsupported resource types. Only constant buffers are supported in the current root signature."); throw new NotSupportedException("Shader reflection data contains unsupported resource types. Only constant buffers are supported in the current root signature.");
} }
return reflectionData.ConstantBuffers[RootSignatureLayout.PER_MATERIAL_BUFFER_SLOT];
// TODO: Validate Cbuffer sizes and bindings. // TODO: Validate Cbuffer sizes and bindings.
} }
private static Result<D3D12GraphicsCompiledResult> CompileAndValidateFullPass(FullPassDescriptor descriptor) private static D3D12GraphicsCompiledResult CompileAndValidateFullPass(FullPassDescriptor descriptor)
{ {
static CompileResult CompileAndValidate(ref CompilerConfig config) static CompileResult CompileAndValidate(ref CompilerConfig config, FullPassDescriptor descriptor)
{ {
IDxcBlob* reflectionBlob = default; IDxcBlob* reflectionBlob = default;
CBufferInfo cbufferInfo = default;
try try
{ {
// TODO: This does not include generated code. This will cause a root signature mismatch. // TODO: This does not include generated code. This will cause a root signature mismatch.
var result = D3D12ShaderCompiler.Compile(ref config, Allocator.Persistent, &reflectionBlob).GetValueOrThrow(); var result = D3D12ShaderCompiler.Compile(ref config, Allocator.Persistent, &reflectionBlob).GetValueOrThrow();
#if false
if (reflectionBlob != null) if (reflectionBlob != null)
{ {
var reflection = D3D12ShaderCompiler.PerformDXCReflection(reflectionBlob).GetValueOrThrow(); var reflection = D3D12ShaderCompiler.PerformDXCReflection(reflectionBlob).GetValueOrThrow();
ValidateReflectionData(reflection); cbufferInfo = ValidateReflectionData(descriptor, reflection);
} }
#endif
return result; return result;
} }
@@ -266,7 +267,7 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
var config = new CompilerConfig var config = new CompilerConfig
{ {
defines = descriptor.defines.AsSpan(), defines = descriptor.defines.AsSpan(),
includes = descriptor.includes.AsSpan(), include = descriptor.generatedCodePath,
shaderPath = tsEntry.shader, shaderPath = tsEntry.shader,
entryPoint = tsEntry.entry, entryPoint = tsEntry.entry,
stage = ShaderStage.TaskShader, stage = ShaderStage.TaskShader,
@@ -275,7 +276,7 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
options = CompilerOption.KeepReflections, options = CompilerOption.KeepReflections,
}; };
tsResult = CompileAndValidate(ref config); tsResult = CompileAndValidate(ref config, descriptor);
} }
CompileResult msResult; CompileResult msResult;
@@ -285,7 +286,7 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
var config = new CompilerConfig var config = new CompilerConfig
{ {
defines = descriptor.defines.AsSpan(), defines = descriptor.defines.AsSpan(),
includes = descriptor.includes.AsSpan(), include = descriptor.generatedCodePath,
shaderPath = msEntry.shader, shaderPath = msEntry.shader,
entryPoint = msEntry.entry, entryPoint = msEntry.entry,
stage = ShaderStage.MeshShader, stage = ShaderStage.MeshShader,
@@ -294,11 +295,11 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
options = CompilerOption.KeepReflections, options = CompilerOption.KeepReflections,
}; };
msResult = CompileAndValidate(ref config); msResult = CompileAndValidate(ref config, descriptor);
} }
else else
{ {
return Result<D3D12GraphicsCompiledResult>.Fail("Mesh shader expected."); throw new InvalidOperationException("Mesh shader expected.");
} }
CompileResult psResult; CompileResult psResult;
@@ -308,7 +309,7 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
var config = new CompilerConfig var config = new CompilerConfig
{ {
defines = descriptor.defines.AsSpan(), defines = descriptor.defines.AsSpan(),
includes = descriptor.includes.AsSpan(), include = descriptor.generatedCodePath,
shaderPath = psEntry.shader, shaderPath = psEntry.shader,
entryPoint = psEntry.entry, entryPoint = psEntry.entry,
stage = ShaderStage.PixelShader, stage = ShaderStage.PixelShader,
@@ -317,11 +318,11 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
options = CompilerOption.KeepReflections, options = CompilerOption.KeepReflections,
}; };
psResult = CompileAndValidate(ref config); psResult = CompileAndValidate(ref config, descriptor);
} }
else else
{ {
return Result<D3D12GraphicsCompiledResult>.Fail("Pixel shader expected."); throw new InvalidOperationException("Pixel shader expected.");
} }
return new D3D12GraphicsCompiledResult return new D3D12GraphicsCompiledResult
@@ -353,6 +354,11 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
return D3D12Utility.D3D12_DEPTH_STENCIL_DESC_CREATE(depthEnabled, writeEnabled, cmp); return D3D12Utility.D3D12_DEPTH_STENCIL_DESC_CREATE(depthEnabled, writeEnabled, cmp);
} }
private bool TryGetCompiledCache(ShaderPassKey passKey, out D3D12GraphicsCompiledResult compiled)
{
return _compiledResults.TryGetValue(passKey, out compiled);
}
private GraphicsPipelineKey CompilePSO(ref readonly GraphicsPSODescriptor descriptor, ref readonly D3D12GraphicsCompiledResult compiled) private GraphicsPipelineKey CompilePSO(ref readonly GraphicsPSODescriptor descriptor, ref readonly D3D12GraphicsCompiledResult compiled)
{ {
var rtvCount = (uint)Math.Min(descriptor.rtvFormats.Length, D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT); var rtvCount = (uint)Math.Min(descriptor.rtvFormats.Length, D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT);
@@ -412,7 +418,6 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
ref var existing = ref CollectionsMarshal.GetValueRefOrAddDefault(_pipelineCache, key, out var exists); ref var existing = ref CollectionsMarshal.GetValueRefOrAddDefault(_pipelineCache, key, out var exists);
if (!exists) if (!exists)
{ {
existing.compileResult = compiled;
existing.psoDesc = desc; existing.psoDesc = desc;
var meshStream = new CD3DX12_PIPELINE_MESH_STATE_STREAM(in desc); var meshStream = new CD3DX12_PIPELINE_MESH_STATE_STREAM(in desc);
@@ -448,14 +453,22 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
public GraphicsPipelineKey CompilePassPSO(IPassDescriptor descriptor, ReadOnlySpan<TextureFormat> rtvs, TextureFormat dsv) public GraphicsPipelineKey CompilePassPSO(IPassDescriptor descriptor, ReadOnlySpan<TextureFormat> rtvs, TextureFormat dsv)
{ {
var key = default(GraphicsPipelineKey); GraphicsPipelineKey key = default;
var passKey = new ShaderPassKey(descriptor.Identifier);
var hasCompiledCache = TryGetCompiledCache(passKey, out var compiled);
switch (descriptor) switch (descriptor)
{ {
case FullPassDescriptor fullPass: case FullPassDescriptor fullPass:
var result = CompileAndValidateFullPass(fullPass).GetValueOrThrow(); if (!hasCompiledCache)
{
compiled = CompileAndValidateFullPass(fullPass);
}
var psoDes = new GraphicsPSODescriptor var psoDes = new GraphicsPSODescriptor
{ {
passId = new(fullPass.Identifier), passId = new ShaderPassKey(fullPass.Identifier),
zTest = fullPass.localPipeline.zTest, zTest = fullPass.localPipeline.zTest,
zWrite = fullPass.localPipeline.zWrite, zWrite = fullPass.localPipeline.zWrite,
cull = fullPass.localPipeline.cull, cull = fullPass.localPipeline.cull,
@@ -466,10 +479,17 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
dsvFormat = dsv, dsvFormat = dsv,
}; };
key = CompilePSO(in psoDes, in result); key = CompilePSO(in psoDes, in compiled);
break; break;
// Do we need to support other pass types? // Do we need to support other pass types?
case FallbackPassDescriptor:
if (!hasCompiledCache)
{
throw new ArgumentException("FallbackPassDescriptor is not supported for PSO compilation. There may be some inheritance dependency issues.");
}
break;
default: default:
break; break;
@@ -478,15 +498,24 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable
return key; return key;
} }
public Result<Ptr<ID3D12PipelineState>> LoadGraphicsPSO(GraphicsPipelineKey key) public Result<Ptr<ID3D12PipelineState>> GetGraphicsPSO(GraphicsPipelineKey key)
{ {
ref var cacheEntry = ref CollectionsMarshal.GetValueRefOrNullRef(_pipelineCache, key); if (_pipelineCache.TryGetValue(key, out var cacheEntry))
if (Unsafe.IsNullRef(ref cacheEntry))
{ {
return new Ptr<ID3D12PipelineState>(cacheEntry.pso.Get());
}
return Result.Fail("Pipeline state not found in cache."); return Result.Fail("Pipeline state not found in cache.");
} }
return new Ptr<ID3D12PipelineState>(cacheEntry.pso.Get()); public Result<CBufferInfo> GetCBufferInfo(ShaderPassKey key)
{
if (_compiledResults.TryGetValue(key, out var compiled))
{
return compiled.cbufferInfo;
}
return Result.Fail("Compiled shader not found in cache.");
} }
public void Dispose() public void Dispose()

View File

@@ -210,6 +210,13 @@ internal unsafe class D3D12Renderer : IRenderer
clearStencil = 0, clearStencil = 0,
}; };
// NOTE: Testing only.
var ctx = new RenderingContext(_graphicsEngine, cmd, _graphicsEngine.CopyCommandBuffer, null!);
if (_frameIndex == 0)
{
_pass.Initialize(ref ctx);
}
cmd.BeginRenderPass(rtDesc, depthDesc, false); cmd.BeginRenderPass(rtDesc, depthDesc, false);
var viewport = new ViewportDesc { width = _currentSize.x, height = _currentSize.y, minDepth = 0, maxDepth = 1 }; var viewport = new ViewportDesc { width = _currentSize.x, height = _currentSize.y, minDepth = 0, maxDepth = 1 };
@@ -219,12 +226,6 @@ internal unsafe class D3D12Renderer : IRenderer
cmd.SetScissorRect(scissor); cmd.SetScissorRect(scissor);
// NOTE: Testing only. // NOTE: Testing only.
var ctx = new RenderingContext(_graphicsEngine, cmd, _graphicsEngine.CopyCommandBuffer, null!);
if (_frameIndex == 0)
{
_pass.Initialize(ref ctx);
}
_pass.Execute(ref ctx); _pass.Execute(ref ctx);
cmd.EndRenderPass(); cmd.EndRenderPass();

View File

@@ -597,13 +597,19 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
private readonly D3D12RenderDevice _device; private readonly D3D12RenderDevice _device;
private readonly D3D12DescriptorAllocator _descriptorAllocator; private readonly D3D12DescriptorAllocator _descriptorAllocator;
private readonly D3D12ResourceDatabase _resourceDatabase; private readonly D3D12ResourceDatabase _resourceDatabase;
private readonly D3D12PipelineLibrary _pipelineLibrary;
private ComPtr<D3D12MA_Allocator> _allocator; private ComPtr<D3D12MA_Allocator> _allocator;
private UnsafeQueue<Handle<GPUResource>> _temResources; private UnsafeQueue<Handle<GPUResource>> _temResources;
private bool _disposed; private bool _disposed;
public D3D12ResourceAllocator(IFenceSynchronizer fenceSynchronizer, D3D12RenderDevice device, D3D12DescriptorAllocator descriptorAllocator, D3D12ResourceDatabase resourceDatabase) public D3D12ResourceAllocator(
IFenceSynchronizer fenceSynchronizer,
D3D12RenderDevice device,
D3D12DescriptorAllocator descriptorAllocator,
D3D12ResourceDatabase resourceDatabase,
D3D12PipelineLibrary pipelineLibrary)
{ {
var desc = new D3D12MA_ALLOCATOR_DESC var desc = new D3D12MA_ALLOCATOR_DESC
{ {
@@ -616,10 +622,11 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
ThrowIfFailed(D3D12MA_CreateAllocator(&desc, &pAllocator)); ThrowIfFailed(D3D12MA_CreateAllocator(&desc, &pAllocator));
_allocator.Attach(pAllocator); _allocator.Attach(pAllocator);
_device = device;
_fenceSynchronizer = fenceSynchronizer; _fenceSynchronizer = fenceSynchronizer;
_device = device;
_descriptorAllocator = descriptorAllocator; _descriptorAllocator = descriptorAllocator;
_resourceDatabase = resourceDatabase; _resourceDatabase = resourceDatabase;
_pipelineLibrary = pipelineLibrary;
_temResources = new(64, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent); _temResources = new(64, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent);
} }
@@ -869,6 +876,18 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
public Identifier<Shader> CreateShader(ShaderDescriptor descriptor) public Identifier<Shader> CreateShader(ShaderDescriptor descriptor)
{ {
var shader = new Shader(descriptor); var shader = new Shader(descriptor);
foreach (var pass in descriptor.passes)
{
if (pass is not FullPassDescriptor fullPass)
{
continue;
}
var passKey = new ShaderPassKey(fullPass.uniqueIdentifier);
var cbufferInfo = _pipelineLibrary.GetCBufferInfo(passKey).GetValueOrThrow();
_resourceDatabase.AddShaderPass(new ShaderPassKey(fullPass.uniqueIdentifier), new ShaderPass(cbufferInfo));
}
return _resourceDatabase.AddShader(shader); return _resourceDatabase.AddShader(shader);
} }

View File

@@ -1,13 +1,13 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Core.Utilities;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Collections;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using TerraFX.Interop.DirectX; using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows; using TerraFX.Interop.Windows;
using static TerraFX.Interop.DirectX.DXC;
namespace Ghost.Graphics.D3D12; namespace Ghost.Graphics.D3D12;
internal struct CompileResult : IDisposable internal struct CompileResult : IDisposable
@@ -22,52 +22,6 @@ internal struct CompileResult : IDisposable
} }
} }
internal readonly struct CBufferVariableInfo
{
public string Name
{
get; init;
}
public uint StartOffset
{
get; init;
}
public uint Size
{
get; init;
}
}
internal readonly struct CBufferInfo
{
public string Name
{
get; init;
}
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 internal readonly struct ResourceBindingInfo
{ {
public string Name public string Name
@@ -145,10 +99,10 @@ internal static unsafe class D3D12ShaderCompiler
{ {
return level switch return level switch
{ {
CompilerOptimizeLevel.O0 => "-O0", CompilerOptimizeLevel.O0 => DXC_ARG_OPTIMIZATION_LEVEL0,
CompilerOptimizeLevel.O1 => "-O1", CompilerOptimizeLevel.O1 => DXC_ARG_OPTIMIZATION_LEVEL1,
CompilerOptimizeLevel.O2 => "-O2", CompilerOptimizeLevel.O2 => DXC_ARG_OPTIMIZATION_LEVEL2,
CompilerOptimizeLevel.O3 => "-O3", CompilerOptimizeLevel.O3 => DXC_ARG_OPTIMIZATION_LEVEL3,
_ => throw new ArgumentOutOfRangeException(nameof(level), "Unsupported optimization level") _ => throw new ArgumentOutOfRangeException(nameof(level), "Unsupported optimization level")
}; };
} }
@@ -164,18 +118,20 @@ internal static unsafe class D3D12ShaderCompiler
GetOptimizeLevelString(config.optimizeLevel), // Optimization level GetOptimizeLevelString(config.optimizeLevel), // Optimization level
}; };
foreach (var include in config.includes)
{
argsArray.Add("-I");
argsArray.Add(include);
}
foreach (var define in config.defines) foreach (var define in config.defines)
{ {
argsArray.Add("-D"); argsArray.Add("-D");
argsArray.Add(define); argsArray.Add(define);
} }
// HACK: Currently DXC does not support force include, we have to use GENERATED_CODE_PATH define as a workaround.
// User must to write '#include GENERATED_CODE_PATH' in their shader code manually.
if (File.Exists(config.include))
{
argsArray.Add("-D");
argsArray.Add($"GENERATED_CODE_PATH={'"' + config.include.Replace("\\", "/") + '"'}");
}
if (!config.options.HasFlag(CompilerOption.KeepDebugInfo)) if (!config.options.HasFlag(CompilerOption.KeepDebugInfo))
{ {
argsArray.Add("-Qstrip_debug"); argsArray.Add("-Qstrip_debug");
@@ -188,7 +144,7 @@ internal static unsafe class D3D12ShaderCompiler
if (config.options.HasFlag(CompilerOption.WarnAsError)) if (config.options.HasFlag(CompilerOption.WarnAsError))
{ {
argsArray.Add("-WX"); argsArray.Add(DXC_ARG_WARNINGS_ARE_ERRORS);
} }
return argsArray; return argsArray;
@@ -210,8 +166,7 @@ internal static unsafe class D3D12ShaderCompiler
ThrowIfFailed(DxcCreateInstance(&dxccID, __uuidof(pCompiler), (void**)&pCompiler)); ThrowIfFailed(DxcCreateInstance(&dxccID, __uuidof(pCompiler), (void**)&pCompiler));
ThrowIfFailed(DxcCreateInstance(&dxcuID, __uuidof(pUtils), (void**)&pUtils)); ThrowIfFailed(DxcCreateInstance(&dxcuID, __uuidof(pUtils), (void**)&pUtils));
//pIncludeHandler.Get()->LoadSource(); ThrowIfFailed(pUtils->CreateDefaultIncludeHandler(&pIncludeHandler));
pUtils->CreateDefaultIncludeHandler(&pIncludeHandler);
// Create source blob // Create source blob
using ComPtr<IDxcBlobEncoding> sourceBlob = default; using ComPtr<IDxcBlobEncoding> sourceBlob = default;
@@ -239,7 +194,7 @@ internal static unsafe class D3D12ShaderCompiler
{ {
Ptr = sourceBlob.Get()->GetBufferPointer(), Ptr = sourceBlob.Get()->GetBufferPointer(),
Size = sourceBlob.Get()->GetBufferSize(), Size = sourceBlob.Get()->GetBufferSize(),
Encoding = DXC.DXC_CP_UTF8 Encoding = DXC_CP_UTF8
}; };
ThrowIfFailed(pCompiler->Compile(&buffer, argPtrs, (uint)argsArray.Count, pIncludeHandler, __uuidof(pResult), (void**)&pResult)); ThrowIfFailed(pCompiler->Compile(&buffer, argPtrs, (uint)argsArray.Count, pIncludeHandler, __uuidof(pResult), (void**)&pResult));
@@ -268,7 +223,7 @@ internal static unsafe class D3D12ShaderCompiler
using ComPtr<IDxcBlob> bytecodeBlob = default; using ComPtr<IDxcBlob> bytecodeBlob = default;
ThrowIfFailed(pResult->GetResult(bytecodeBlob.GetAddressOf())); ThrowIfFailed(pResult->GetResult(bytecodeBlob.GetAddressOf()));
// Get reflection data using DXC API // Get pReflection data using DXC API
if (ppReflectionBlob != null) if (ppReflectionBlob != null)
{ {
ThrowIfFailed(pResult->GetOutput(DXC_OUT_KIND.DXC_OUT_REFLECTION, __uuidof<IDxcBlob>(), (void**)ppReflectionBlob, null)); ThrowIfFailed(pResult->GetOutput(DXC_OUT_KIND.DXC_OUT_REFLECTION, __uuidof<IDxcBlob>(), (void**)ppReflectionBlob, null));
@@ -302,8 +257,8 @@ internal static unsafe class D3D12ShaderCompiler
} }
} }
// TODO: Since we are using fixed root signature layout, the reflection pass should only validate the layout, not generate it. // TODO: Since we are using fixed root signature layout, the pReflection pass should only validate the layout, not generate it.
// TODO: Ideally this should return a structured reflection data instead of populating raw lists/dictionaries. // TODO: Ideally this should return a structured pReflection data instead of populating raw lists/dictionaries.
public static Result<ShaderReflectionData> PerformDXCReflection(IDxcBlob* reflectionBlob) public static Result<ShaderReflectionData> PerformDXCReflection(IDxcBlob* reflectionBlob)
{ {
if (reflectionBlob == null) if (reflectionBlob == null)
@@ -311,34 +266,34 @@ internal static unsafe class D3D12ShaderCompiler
return Result<ShaderReflectionData>.Fail("Reflection blob is null."); return Result<ShaderReflectionData>.Fail("Reflection blob is null.");
} }
ComPtr<IDxcUtils> utils = default; IDxcUtils* pUtils = default;
ComPtr<ID3D12ShaderReflection> reflection = default; ID3D12ShaderReflection* pReflection = default;
try try
{ {
// Create DXC pUtils to parse reflection data // Create DXC pUtils to parse pReflection data
var dxcuID = CLSID.CLSID_DxcUtils; var dxcuID = CLSID.CLSID_DxcUtils;
ThrowIfFailed(DxcCreateInstance(&dxcuID, utils.IID(), utils.PPV())); ThrowIfFailed(DxcCreateInstance(&dxcuID, __uuidof(pUtils), (void**)&pUtils));
// Create reflection interface from blob // Create pReflection interface from blob
var reflectionBuffer = new DxcBuffer var reflectionBuffer = new DxcBuffer
{ {
Ptr = reflectionBlob->GetBufferPointer(), Ptr = reflectionBlob->GetBufferPointer(),
Size = reflectionBlob->GetBufferSize(), Size = reflectionBlob->GetBufferSize(),
Encoding = DXC.DXC_CP_ACP Encoding = DXC_CP_ACP
}; };
ThrowIfFailed(utils.Get()->CreateReflection(&reflectionBuffer, reflection.IID(), reflection.PPV())); ThrowIfFailed(pUtils->CreateReflection(&reflectionBuffer, __uuidof(pReflection), (void**)&pReflection));
D3D12_SHADER_DESC shaderDesc; D3D12_SHADER_DESC shaderDesc;
ThrowIfFailed(reflection.Get()->GetDesc(&shaderDesc)); ThrowIfFailed(pReflection->GetDesc(&shaderDesc));
var reflectionData = new ShaderReflectionData(); var reflectionData = new ShaderReflectionData();
for (uint i = 0; i < shaderDesc.BoundResources; i++) for (uint i = 0; i < shaderDesc.BoundResources; i++)
{ {
D3D12_SHADER_INPUT_BIND_DESC bindDesc; D3D12_SHADER_INPUT_BIND_DESC bindDesc;
ThrowIfFailed(reflection.Get()->GetResourceBindingDesc(i, &bindDesc)); ThrowIfFailed(pReflection->GetResourceBindingDesc(i, &bindDesc));
var resourceName = Marshal.PtrToStringUTF8((IntPtr)bindDesc.Name); var resourceName = Marshal.PtrToStringUTF8((IntPtr)bindDesc.Name);
if (resourceName == null) if (resourceName == null)
@@ -350,11 +305,11 @@ internal static unsafe class D3D12ShaderCompiler
{ {
case D3D_SHADER_INPUT_TYPE.D3D_SIT_CBUFFER: case D3D_SHADER_INPUT_TYPE.D3D_SIT_CBUFFER:
{ {
var cbuffer = reflection.Get()->GetConstantBufferByName(bindDesc.Name); var cbuffer = pReflection->GetConstantBufferByName(bindDesc.Name);
D3D12_SHADER_BUFFER_DESC cbufferDesc; D3D12_SHADER_BUFFER_DESC cbufferDesc;
ThrowIfFailed(cbuffer->GetDesc(&cbufferDesc)); ThrowIfFailed(cbuffer->GetDesc(&cbufferDesc));
var variables = new List<CBufferVariableInfo>((int)cbufferDesc.Variables); var variables = new List<CBufferPropertyInfo>((int)cbufferDesc.Variables);
// Now we iterate all variables for *every* cbuffer, not just b3 // Now we iterate all variables for *every* cbuffer, not just b3
for (uint j = 0; j < cbufferDesc.Variables; j++) for (uint j = 0; j < cbufferDesc.Variables; j++)
@@ -369,7 +324,7 @@ internal static unsafe class D3D12ShaderCompiler
continue; continue;
} }
variables.Add(new CBufferVariableInfo variables.Add(new CBufferPropertyInfo
{ {
Name = variableName, Name = variableName,
StartOffset = varDesc.StartOffset, StartOffset = varDesc.StartOffset,
@@ -383,7 +338,7 @@ internal static unsafe class D3D12ShaderCompiler
RegisterSlot = bindDesc.BindPoint, RegisterSlot = bindDesc.BindPoint,
RegisterSpace = bindDesc.Space, RegisterSpace = bindDesc.Space,
SizeInBytes = cbufferDesc.Size, SizeInBytes = cbufferDesc.Size,
Variables = variables Properties = variables
}); });
break; break;
@@ -411,8 +366,8 @@ internal static unsafe class D3D12ShaderCompiler
} }
finally finally
{ {
utils.Dispose(); pUtils->Release();
reflection.Dispose(); pReflection->Release();
} }
} }
} }

View File

@@ -1,12 +1,12 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Core.Graphics; using Ghost.Core.Graphics;
using Ghost.Graphics.Core;
using Ghost.Graphics.D3D12.Utilities; using Ghost.Graphics.D3D12.Utilities;
using Misaki.HighPerformance.Utilities; using Misaki.HighPerformance.Utilities;
using System.IO.Hashing; using System.IO.Hashing;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using TerraFX.Interop.DirectX; using TerraFX.Interop.DirectX;
using Ghost.Graphics.Core;
namespace Ghost.Graphics.RHI; namespace Ghost.Graphics.RHI;
@@ -122,6 +122,51 @@ public ref struct GraphicsPSODescriptor
public TextureFormat dsvFormat; public TextureFormat dsvFormat;
} }
public readonly struct CBufferPropertyInfo
{
public string Name
{
get; init;
}
public uint StartOffset
{
get; init;
}
public uint Size
{
get; init;
}
}
public readonly struct CBufferInfo
{
public string Name
{
get; init;
}
public uint RegisterSlot
{
get; init;
}
public uint RegisterSpace
{
get; init;
}
public uint SizeInBytes
{
get; init;
}
public IReadOnlyList<CBufferPropertyInfo> Properties
{
get; init;
}
}
public struct ViewportDesc public struct ViewportDesc
{ {
@@ -685,8 +730,8 @@ public enum PrimitiveTopology
internal ref struct CompilerConfig internal ref struct CompilerConfig
{ {
public ReadOnlySpan<string> includes;
public ReadOnlySpan<string> defines; public ReadOnlySpan<string> defines;
public string? include;
public string shaderPath; public string shaderPath;
public string entryPoint; public string entryPoint;
public ShaderStage stage; public ShaderStage stage;

View File

@@ -28,7 +28,7 @@ internal unsafe class MeshRenderPass : IRenderPass
public void Initialize(ref readonly RenderingContext ctx) public void Initialize(ref readonly RenderingContext ctx)
{ {
var shaderDescriptor = SDLCompiler.CompileShader("F:\\csharp\\GhostEngine\\Ghost.Graphics\\test.gshader").GetValueOrThrow(); var shaderDescriptor = SDLCompiler.CompileShader("F:/csharp/GhostEngine/Ghost.Graphics/test.gshader", "C:/Users/Misaki/Downloads/Archive").GetValueOrThrow();
var key = ctx.PipelineLibrary.CompilePassPSO(shaderDescriptor.passes[0], [TextureFormat.B8G8R8A8_UNorm], TextureFormat.Unknown); var key = ctx.PipelineLibrary.CompilePassPSO(shaderDescriptor.passes[0], [TextureFormat.B8G8R8A8_UNorm], TextureFormat.Unknown);

View File

@@ -1,13 +1,6 @@
cbuffer ConstantBuffer : register(b0)
{ #include GENERATED_CODE_PATH
float4 _Color; #include "F:/csharp/GhostEngine/Ghost.Shader/BuiltIn/Properties.hlsl"
uint _TextureIndex1;
uint _TextureIndex2;
uint _TextureIndex3;
uint _TextureIndex4;
uint _VertexBufferIndex;
uint _IndexBufferIndex;
};
struct Vertex struct Vertex
{ {
@@ -34,8 +27,8 @@ void MSMain(
out indices uint3 outTris[1]) out indices uint3 outTris[1])
{ {
// Fetch bindless buffers // Fetch bindless buffers
ByteAddressBuffer vertexBuffer = ResourceDescriptorHeap[_VertexBufferIndex]; ByteAddressBuffer vertexBuffer = ResourceDescriptorHeap[g_PerMaterialData.vertexBufferIndex];
ByteAddressBuffer indexBuffer = ResourceDescriptorHeap[_IndexBufferIndex]; ByteAddressBuffer indexBuffer = ResourceDescriptorHeap[g_PerMaterialData.indexBufferIndex];
// Compute the triangles vertex indices // Compute the triangles vertex indices
uint vertexId = groupThreadID.x; uint vertexId = groupThreadID.x;
@@ -67,11 +60,11 @@ void MSMain(
float4 PSMain(PixelInput input) : SV_TARGET float4 PSMain(PixelInput input) : SV_TARGET
{ {
float4 color1 = SAMPLE_TEXTURE2D_BINDLESS(_TextureIndex1, 0, input.uv.xy); float4 color1 = SAMPLE_TEXTURE2D_BINDLESS(g_PerMaterialData.texture1, 0, input.uv.xy);
float4 color2 = SAMPLE_TEXTURE2D_BINDLESS(_TextureIndex2, 0, input.uv.xy); float4 color2 = SAMPLE_TEXTURE2D_BINDLESS(g_PerMaterialData.texture2, 0, input.uv.xy);
float4 color3 = SAMPLE_TEXTURE2D_BINDLESS(_TextureIndex3, 0, input.uv.xy); float4 color3 = SAMPLE_TEXTURE2D_BINDLESS(g_PerMaterialData.texture3, 0, input.uv.xy);
float4 color4 = SAMPLE_TEXTURE2D_BINDLESS(_TextureIndex4, 0, input.uv.xy); float4 color4 = SAMPLE_TEXTURE2D_BINDLESS(g_PerMaterialData.texture4, 0, input.uv.xy);
float4 blendedColor = (color1 + color2 + color3 + color4) * 0.25f; float4 blendedColor = (color1 + color2 + color3 + color4) * 0.25f;
return blendedColor * _Color; return blendedColor * g_PerMaterialData.color;
} }

View File

@@ -7,6 +7,8 @@ shader "MyShader/Standard"
tex2d_b texture2 = tex2d_b(white); tex2d_b texture2 = tex2d_b(white);
tex2d_b texture3 = tex2d_b(grey); tex2d_b texture3 = tex2d_b(grey);
tex2d_b texture4 = tex2d_b(normal); tex2d_b texture4 = tex2d_b(normal);
uint vertexBufferIndex;
uint indexBufferIndex;
} }
pipeline pipeline
@@ -22,10 +24,5 @@ shader "MyShader/Standard"
{ {
ms("F:/csharp/GhostEngine/Ghost.Graphics/RenderPasses/ShaderCode.hlsl", "MSMain"); ms("F:/csharp/GhostEngine/Ghost.Graphics/RenderPasses/ShaderCode.hlsl", "MSMain");
ps("F:/csharp/GhostEngine/Ghost.Graphics/RenderPasses/ShaderCode.hlsl", "PSMain"); ps("F:/csharp/GhostEngine/Ghost.Graphics/RenderPasses/ShaderCode.hlsl", "PSMain");
includes
{
"F:/csharp/GhostEngine/Ghost.Shader/BuiltIn/Common.hlsl";
}
} }
} }

View File

@@ -1,4 +1,4 @@
#ifndef COMMON_HLSL #ifndef COMMON_HLSL
#define COMMON_HLSL #define COMMON_HLSL
#undef USE_TRADITIONAL_BINDLESS // Just for testing, this should be handled by engine feature level. #undef USE_TRADITIONAL_BINDLESS // Just for testing, this should be handled by engine feature level.
@@ -43,11 +43,11 @@
#define STRUCT_BUFFER_BINDLESS uint #define STRUCT_BUFFER_BINDLESS uint
#define BYTE_ADDRESS_BUFFER_BINDLESS uint #define BYTE_ADDRESS_BUFFER_BINDLESS uint
#define TEXTURE2D Texture2D<float4> #define TEXTURE2D Texture2D
#define TEXTURE3D Texture3D<float4> #define TEXTURE3D Texture3D
#define TEXTURECUBE TextureCube<float4> #define TEXTURECUBE TextureCube
#define TEXTURE2D_ARRAY Texture2DArray<float4> #define TEXTURE2D_ARRAY Texture2DArray
#define TEXTURECUBE_ARRAY TextureCubeArray<float4> #define TEXTURECUBE_ARRAY TextureCubeArray
#define SAMPLER SamplerState #define SAMPLER SamplerState
@@ -69,11 +69,26 @@
#define SAMPLE_TEXTURE2D(tex, samp, uv) tex.Sample(samp, uv) #define SAMPLE_TEXTURE2D(tex, samp, uv) tex.Sample(samp, uv)
#define SAMPLE_TEXTURE2D_LEVEL(tex, samp, uv, level) tex.SampleLevel(samp, uv, level) #define SAMPLE_TEXTURE2D_LEVEL(tex, samp, uv, level) tex.SampleLevel(samp, uv, level)
#define SAMPLE_TEXTURE2D_BINDLESS(texId, sampId, uv) GET_TEXTURE2D_BINDLESS(texId).Sample(GET_BINDLESS_SAMPLER(sampId), uv) #define SAMPLE_TEXTURE2D_BINDLESS(texId, sampId, uv) SampleTexture2DBindless(texId, sampId, uv)
#define SAMPLE_TEXTURE2D_LEVEL_BINDLESS(texId, sampId, uv, level) GET_TEXTURE2D_BINDLESS(texId).SampleLevel(GET_BINDLESS_SAMPLER(sampId), uv, level) #define SAMPLE_TEXTURE2D_LEVEL_BINDLESS(texId, sampId, uv, level) SampleTexture2DLevelBindless(texId, sampId, uv, level)
#define SAMPLE_TEXTURE2D_ARRAY(tex, samp, uv, index) tex.Sample(samp, uv, index) #define SAMPLE_TEXTURE2D_ARRAY(tex, samp, uv, index) tex.Sample(samp, uv, index)
#define SAMPLE_TEXTURE2D_ARRAY_BINDLESS(texId, sampId, uv, index) GET_TEXTURE2D_ARRAY_BINDLESS(texId).Sample(GET_BINDLESS_SAMPLER(sampId), uv, index) #define SAMPLE_TEXTURE2D_ARRAY_BINDLESS(texId, sampId, uv, index) GET_TEXTURE2D_ARRAY_BINDLESS(texId).Sample(GET_SAMPLER_BINDLESS(sampId), uv, index)
static inline float4 SampleTexture2DBindless(uint texId, uint sampId, float2 uv)
{
Texture2D tex = GET_TEXTURE2D_BINDLESS(texId);
SamplerState samp = GET_SAMPLER_BINDLESS(sampId);
return tex.Sample(samp, uv);
}
static inline float4 SampleTexture2DLevelBindless(uint texId, uint sampId, float2 uv, float level)
{
Texture2D tex = GET_TEXTURE2D_BINDLESS(texId);
SamplerState samp = GET_SAMPLER_BINDLESS(sampId);
return tex.SampleLevel(samp, uv, level);
}

View File

@@ -1,4 +1,4 @@
#ifndef PROPERTIES_HLSL #ifndef PROPERTIES_HLSL
#define PROPERTIES_HLSL #define PROPERTIES_HLSL
#include "F:/csharp/GhostEngine/Ghost.Shader/BuiltIn/Common.hlsl" #include "F:/csharp/GhostEngine/Ghost.Shader/BuiltIn/Common.hlsl"

View File

@@ -11,7 +11,7 @@ public struct SDLError
public int line; public int line;
public int column; public int column;
public readonly override string ToString() public override readonly string ToString()
{ {
return $"Error at {line}:{column} - {message}"; return $"Error at {line}:{column} - {message}";
} }
@@ -125,24 +125,6 @@ internal static class SDLCompiler
private static string GetPassUniqueId(SDLSemantics shader, PassSemantic pass) private static string GetPassUniqueId(SDLSemantics shader, PassSemantic pass)
{ {
//static ulong Fnv1a64(ReadOnlySpan<char> data)
//{
// const ulong offset = 14695981039346656037;
// const ulong prime = 1099511628211;
// var hash = offset;
// foreach (var b in data)
// {
// hash ^= b;
// hash *= prime;
// }
// return hash;
//}
//return $"{Fnv1a64(shader.name)}_{pass.name}";
return $"{shader.name}_{pass.name}"; return $"{shader.name}_{pass.name}";
} }
@@ -215,6 +197,7 @@ internal static class SDLCompiler
if (shaderGlobalProperties != null) if (shaderGlobalProperties != null)
{ {
descriptor.globalProperties ??= new List<PropertyDescriptor>();
descriptor.globalProperties.AddRange(shaderGlobalProperties); descriptor.globalProperties.AddRange(shaderGlobalProperties);
} }
@@ -245,7 +228,9 @@ internal static class SDLCompiler
return descriptor; return descriptor;
} }
public static Result<ShaderDescriptor> CompileShader(string shaderPath) public static Result<ShaderDescriptor> CompileShader(string shaderPath, string generatedOutputDirectory)
{
try
{ {
var source = File.ReadAllText(shaderPath); var source = File.ReadAllText(shaderPath);
@@ -262,10 +247,30 @@ internal static class SDLCompiler
errorMessages.AppendLine(error.ToString()); errorMessages.AppendLine(error.ToString());
} }
return Result<ShaderDescriptor>.Fail("Failed to compile shader due to errors:\n" + errorMessages.ToString()); return Result.Fail("Failed to compile shader due to errors:\n" + errorMessages.ToString());
} }
return ResolveShader(model); var desc = ResolveShader(model);
var globalPropPath = GenerateGlobalProperties(desc.globalProperties, generatedOutputDirectory);
foreach (var pass in desc.passes)
{
if (pass is not FullPassDescriptor fullPass)
{
continue;
}
fullPass.includes ??= new List<string>();
fullPass.includes.Add(globalPropPath);
fullPass.generatedCodePath = GeneratePass(fullPass, generatedOutputDirectory);
}
return desc;
}
catch (Exception ex)
{
return Result.Fail("Failed to generate shader files: " + ex.Message);
}
} }
private static string ShaderPropertyTypeToHLSLType(ShaderPropertyType type) private static string ShaderPropertyTypeToHLSLType(ShaderPropertyType type)
@@ -330,6 +335,16 @@ internal static class SDLCompiler
#define {fileDefine} #define {fileDefine}
#include ""F:/csharp/GhostEngine/Ghost.Shader/BuiltIn/Common.hlsl"""); #include ""F:/csharp/GhostEngine/Ghost.Shader/BuiltIn/Common.hlsl""");
if (fullPass.includes != null)
{
foreach (var include in fullPass.includes)
{
sb.Append($@"
#include ""{include}""");
}
sb.AppendLine();
}
if (fullPass.properties != null) if (fullPass.properties != null)
{ {
@@ -354,16 +369,13 @@ struct PerMaterialData
return outputFilePath; return outputFilePath;
} }
public static void GenerateShader(ShaderDescriptor descriptor, string targetDirectory) public static string GenerateGlobalProperties(List<PropertyDescriptor> globalProperties, string targetDirectory)
{ {
if (!Directory.Exists(targetDirectory)) if (!Directory.Exists(targetDirectory))
{ {
throw new ArgumentException("Target directory does not exist.", nameof(targetDirectory)); throw new ArgumentException("Target directory does not exist.", nameof(targetDirectory));
} }
// Generate global property file.
if (descriptor.globalProperties.Count > 0)
{
var globalFilePath = Path.Combine(targetDirectory, _GLOBAL_PROPERTY_FILE_NAME); var globalFilePath = Path.Combine(targetDirectory, _GLOBAL_PROPERTY_FILE_NAME);
using var globalFileStream = File.CreateText(globalFilePath); using var globalFileStream = File.CreateText(globalFilePath);
@@ -378,22 +390,17 @@ struct PerMaterialData
struct GlobalData struct GlobalData
{"); {");
foreach (var prop in descriptor.globalProperties) foreach (var prop in globalProperties)
{ {
sb.Append($@" sb.Append($@"
{ShaderPropertyTypeToHLSLType(prop.type)} {prop.name};"); {ShaderPropertyTypeToHLSLType(prop.type)} {prop.name};");
} }
sb.AppendLine(@" sb.AppendLine(@"
}; };
#endif // GLOBALDATA_G_HLSL"); #endif // GLOBALDATA_G_HLSL");
globalFileStream.Write(sb.ToString()); globalFileStream.Write(sb.ToString());
}
// Compile each pass. return globalFilePath;
foreach (var pass in descriptor.passes)
{
GeneratePass(pass, targetDirectory);
}
} }
} }

View File

@@ -274,6 +274,7 @@ struct {structName}
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.AppendLine(@$"// Auto-generated HLSL code, please do not edit this file directly. sb.AppendLine(@$"// Auto-generated HLSL code, please do not edit this file directly.
#ifndef {hlslDefine} #ifndef {hlslDefine}
#define {hlslDefine}"); #define {hlslDefine}");