feat(shader): add compute shader support and refactor pipeline
Refactored shader system to support both graphics and compute shaders. - Updated ANTLR grammars and parser logic for explicit shader model and compute shader entry points. - Split shader models and descriptors for graphics and compute. - Refactored pipeline key generation and D3D12 pipeline library for compute support. - Updated push constant layouts and HLSL includes for both shader types. - Improved error handling and test coverage with new example files. BREAKING CHANGE: Shader model, descriptor, and pipeline APIs have changed. Existing shader and pipeline code must be updated to use the new types and conventions.
This commit is contained in:
@@ -1,24 +1,19 @@
|
||||
namespace Ghost.Core.Graphics;
|
||||
|
||||
public enum ShaderModel
|
||||
{
|
||||
Invalid,
|
||||
SM_6_6,
|
||||
SM_6_7,
|
||||
SM_6_8
|
||||
}
|
||||
|
||||
public enum KeywordSpace
|
||||
{
|
||||
Local,
|
||||
Global,
|
||||
}
|
||||
|
||||
public enum ShaderPropertyType
|
||||
{
|
||||
None,
|
||||
Float, Float2, Float3, Float4,
|
||||
Float4x4,
|
||||
Int, Int2, Int3, Int4,
|
||||
UInt, UInt2, UInt3, UInt4,
|
||||
Bool, Bool2, Bool3, Bool4,
|
||||
Texture2D, Texture3D, TextureCube,
|
||||
Texture2DArray, TextureCubeArray,
|
||||
Sampler
|
||||
}
|
||||
|
||||
public struct ShaderEntryPoint
|
||||
{
|
||||
public string entry;
|
||||
@@ -35,7 +30,7 @@ public struct KeywordsGroup
|
||||
|
||||
public struct PassDescriptor
|
||||
{
|
||||
public ShaderDescriptor shader;
|
||||
public GraphicsShaderDescriptor shader;
|
||||
|
||||
public ulong identifier;
|
||||
public string name;
|
||||
@@ -50,21 +45,25 @@ public struct PassDescriptor
|
||||
public PipelineState localPipeline;
|
||||
}
|
||||
|
||||
public class ShaderDescriptor
|
||||
public class GraphicsShaderDescriptor
|
||||
{
|
||||
public string name = string.Empty;
|
||||
public string propertiesCode = string.Empty;
|
||||
public uint propertyBufferSize;
|
||||
public ShaderModel shaderModel;
|
||||
public PassDescriptor[] passes = Array.Empty<PassDescriptor>();
|
||||
}
|
||||
|
||||
public class ComputeShaderDescriptor
|
||||
{
|
||||
public ulong identifier;
|
||||
public string name = string.Empty;
|
||||
public string propertiesCode = string.Empty;
|
||||
public uint propertyBufferSize;
|
||||
public ShaderEntryPoint entryPoint;
|
||||
public string? hlsl;
|
||||
public ShaderModel shaderModel;
|
||||
public string[] defines = Array.Empty<string>();
|
||||
public string[] includes = Array.Empty<string>();
|
||||
public KeywordsGroup[] keywords = Array.Empty<KeywordsGroup>();
|
||||
public ShaderEntryPoint[] entryPoints = Array.Empty<ShaderEntryPoint>();
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ public static class ShaderPropertiesRegistry
|
||||
s_nameToCode[name] = new ShaderPropertyInfo { shaderName = name, code = code, size = size };
|
||||
}
|
||||
|
||||
public static bool TryGetCode(string name, out ShaderPropertyInfo info)
|
||||
public static bool TryGetInfo(string name, out ShaderPropertyInfo info)
|
||||
{
|
||||
return s_nameToCode.TryGetValue(name, out info);
|
||||
}
|
||||
|
||||
@@ -650,7 +650,7 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
|
||||
#endif
|
||||
IncrementCommandCount();
|
||||
|
||||
var psor = _pipelineLibrary.GetGraphicsPSO(pipelineKey);
|
||||
var psor = _pipelineLibrary.GetPipelineStateObject(pipelineKey);
|
||||
if (psor.Error != Error.None)
|
||||
{
|
||||
RecordError(nameof(SetPipelineState), psor.Error);
|
||||
|
||||
@@ -16,7 +16,6 @@ namespace Ghost.Graphics.D3D12;
|
||||
|
||||
internal struct D3D12PipelineState : IDisposable
|
||||
{
|
||||
public D3DX12_MESH_SHADER_PIPELINE_STATE_DESC psoDesc;
|
||||
public UniquePtr<ID3D12PipelineState> pso;
|
||||
public Key64<ShaderVariant> shaderVariant;
|
||||
|
||||
@@ -33,7 +32,7 @@ internal unsafe class D3D12PipelineLibrary : D3D12Object<ID3D12PipelineLibrary1>
|
||||
|
||||
private UniquePtr<ID3D12RootSignature> _defaultRootSignature;
|
||||
|
||||
private UnsafeHashMap<Key128<GraphicsPipeline>, D3D12PipelineState> _pipelineCache;
|
||||
private UnsafeHashMap<UInt128, D3D12PipelineState> _pipelineCache;
|
||||
|
||||
public ID3D12RootSignature* DefaultRootSignature => _defaultRootSignature.Get();
|
||||
|
||||
@@ -58,12 +57,12 @@ internal unsafe class D3D12PipelineLibrary : D3D12Object<ID3D12PipelineLibrary1>
|
||||
}
|
||||
|
||||
public D3D12PipelineLibrary(D3D12RenderDevice device, D3D12ResourceDatabase resourceDatabase)
|
||||
:base(CreateLibrary(device, null)) // TODO: we need to path to load the existing library from disk.
|
||||
: base(CreateLibrary(device, null)) // TODO: we need to path to load the existing library from disk.
|
||||
{
|
||||
_device = device;
|
||||
_resourceDatabase = resourceDatabase;
|
||||
|
||||
_pipelineCache = new UnsafeHashMap<Key128<GraphicsPipeline>, D3D12PipelineState>(32, Allocator.Persistent);
|
||||
_pipelineCache = new UnsafeHashMap<UInt128, D3D12PipelineState>(32, Allocator.Persistent);
|
||||
|
||||
CreateDefaultRootSignature().ThrowIfFailed();
|
||||
}
|
||||
@@ -174,7 +173,38 @@ internal unsafe class D3D12PipelineLibrary : D3D12Object<ID3D12PipelineLibrary1>
|
||||
return D3D12Utility.D3D12_DEPTH_STENCIL_DESC_CREATE(depthEnabled, writeEnabled, cmp);
|
||||
}
|
||||
|
||||
public Result<Key128<GraphicsPipeline>> CreatePSO(ref readonly GraphicsPSODescriptor descriptor, ref readonly GraphicsCompiledResult compiled)
|
||||
private Result CreatePSO(Key64<ShaderVariant> shaderVariantKey, UInt128 pipelineKey, D3D12_PIPELINE_STATE_STREAM_DESC* pStreamDesc)
|
||||
{
|
||||
ID3D12PipelineState* pPipelineState = default;
|
||||
|
||||
var pKeyStr = stackalloc char[33]; // 32 for 128 bits key + 1 for null terminator
|
||||
var keySpan = new Span<char>(pKeyStr, 33);
|
||||
if (!RHIUtility.TryGetStringFromHash(pipelineKey, keySpan))
|
||||
{
|
||||
return Result.Failure("Failed to convert pipeline key to string.");
|
||||
}
|
||||
|
||||
var hr = pNativeObject->LoadPipeline(pKeyStr, pStreamDesc, __uuidof(pPipelineState), (void**)&pPipelineState);
|
||||
if (hr == E.E_INVALIDARG)
|
||||
{
|
||||
// Pipeline not found in the library, create a new one.
|
||||
ThrowIfFailed(_device.NativeObject.Get()->CreatePipelineState(pStreamDesc, __uuidof(pPipelineState), (void**)&pPipelineState));
|
||||
ThrowIfFailed(pNativeObject->StorePipeline(pKeyStr, pPipelineState));
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowIfFailed(hr);
|
||||
}
|
||||
|
||||
D3D12PipelineState pso = default;
|
||||
pso.shaderVariant = shaderVariantKey;
|
||||
pso.pso.Attach(pPipelineState);
|
||||
|
||||
_pipelineCache[pipelineKey] = pso;
|
||||
return Result.Success();
|
||||
}
|
||||
|
||||
public Result<Key128<GraphicsPipeline>> CreateGraphicsPipeline(ref readonly GraphicsPSODescriptor descriptor, ref readonly GraphicsCompiledResult compiled)
|
||||
{
|
||||
static Result ValidatePassReflectionData(ref readonly GraphicsCompiledResult compiled)
|
||||
{
|
||||
@@ -270,45 +300,55 @@ internal unsafe class D3D12PipelineLibrary : D3D12Object<ID3D12PipelineLibrary1>
|
||||
SizeInBytes = (nuint)sizeof(CD3DX12_PIPELINE_MESH_STATE_STREAM)
|
||||
};
|
||||
|
||||
ID3D12PipelineState* pPipelineState = default;
|
||||
|
||||
var pKeyStr = stackalloc char[33]; // 32 for 128 bits key + 1 for null terminator
|
||||
var keySpan = new Span<char>(pKeyStr, 33);
|
||||
if (!pipelineKey.TryGetString(keySpan))
|
||||
result = CreatePSO(descriptor.VariantKey, pipelineKey, &streamDesc);
|
||||
if (result.IsFailure)
|
||||
{
|
||||
return Result.Failure("Failed to convert pipeline key to string.");
|
||||
return result;
|
||||
}
|
||||
|
||||
var hr = pNativeObject->LoadPipeline(pKeyStr, &streamDesc, __uuidof(pPipelineState), (void**)&pPipelineState);
|
||||
if (hr == E.E_INVALIDARG)
|
||||
{
|
||||
// Pipeline not found in the library, create a new one.
|
||||
ThrowIfFailed(_device.NativeObject.Get()->CreatePipelineState(&streamDesc, __uuidof(pPipelineState), (void**)&pPipelineState));
|
||||
ThrowIfFailed(pNativeObject->StorePipeline(pKeyStr, pPipelineState));
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowIfFailed(hr);
|
||||
}
|
||||
|
||||
D3D12PipelineState pso = default;
|
||||
pso.shaderVariant = descriptor.VariantKey;
|
||||
pso.psoDesc = desc;
|
||||
pso.pso.Attach(pPipelineState);
|
||||
|
||||
_pipelineCache[pipelineKey] = pso;
|
||||
}
|
||||
|
||||
return pipelineKey;
|
||||
}
|
||||
|
||||
public bool HasPipeline(Key128<GraphicsPipeline> key)
|
||||
public Result<Key128<ComputePipeline>> CreateComputePipeline(ref readonly ComputePSODescriptor descriptor, ref readonly ShaderCompileResult compiled)
|
||||
{
|
||||
AssertNotDisposed();
|
||||
|
||||
var pipelineKey = RHIUtility.CreateComputePipelineKey(descriptor.VariantKey, compiled.hashCode);
|
||||
if (!_pipelineCache.ContainsKey(pipelineKey))
|
||||
{
|
||||
var result = ValidateReflectionData(compiled.reflectionData);
|
||||
if (result.IsFailure)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
var byteCode = new D3D12_SHADER_BYTECODE(compiled.bytecode.GetUnsafePtr(), (nuint)compiled.bytecode.Length);
|
||||
var desc = new CD3DX12_PIPELINE_STATE_STREAM_CS(in byteCode);
|
||||
|
||||
var streamDesc = new D3D12_PIPELINE_STATE_STREAM_DESC
|
||||
{
|
||||
pPipelineStateSubobjectStream = &desc,
|
||||
SizeInBytes = (nuint)sizeof(CD3DX12_PIPELINE_STATE_STREAM_CS)
|
||||
};
|
||||
|
||||
result = CreatePSO(descriptor.VariantKey, pipelineKey, &streamDesc);
|
||||
if (result.IsFailure)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return pipelineKey;
|
||||
}
|
||||
|
||||
public bool HasPipelineStateObject(UInt128 key)
|
||||
{
|
||||
AssertNotDisposed();
|
||||
return _pipelineCache.ContainsKey(key);
|
||||
}
|
||||
|
||||
public Result<SharedPtr<ID3D12PipelineState>, Error> GetGraphicsPSO(Key128<GraphicsPipeline> key)
|
||||
public Result<SharedPtr<ID3D12PipelineState>, Error> GetPipelineStateObject(UInt128 key)
|
||||
{
|
||||
AssertNotDisposed();
|
||||
if (_pipelineCache.TryGetValue(key, out var cacheEntry))
|
||||
|
||||
@@ -18,22 +18,22 @@ namespace Ghost.Graphics.Core;
|
||||
|
||||
internal sealed partial class DXCShaderCompiler
|
||||
{
|
||||
private static string GetProfileString(ShaderStage stage, CompilerTier version)
|
||||
private static string GetProfileString(ShaderStage stage, ShaderModel version)
|
||||
{
|
||||
return (stage, version) switch
|
||||
{
|
||||
(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",
|
||||
(ShaderStage.TaskShader, ShaderModel.SM_6_6) => "as_6_6",
|
||||
(ShaderStage.PixelShader, ShaderModel.SM_6_6) => "ps_6_6",
|
||||
(ShaderStage.MeshShader, ShaderModel.SM_6_6) => "ms_6_6",
|
||||
(ShaderStage.ComputeShader, ShaderModel.SM_6_6) => "cs_6_6",
|
||||
(ShaderStage.TaskShader, ShaderModel.SM_6_7) => "as_6_7",
|
||||
(ShaderStage.PixelShader, ShaderModel.SM_6_7) => "ps_6_7",
|
||||
(ShaderStage.MeshShader, ShaderModel.SM_6_7) => "ms_6_7",
|
||||
(ShaderStage.ComputeShader, ShaderModel.SM_6_7) => "cs_6_7",
|
||||
(ShaderStage.TaskShader, ShaderModel.SM_6_8) => "as_6_8",
|
||||
(ShaderStage.PixelShader, ShaderModel.SM_6_8) => "ps_6_8",
|
||||
(ShaderStage.MeshShader, ShaderModel.SM_6_8) => "ms_6_8",
|
||||
(ShaderStage.ComputeShader, ShaderModel.SM_6_8) => "cs_6_8",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(stage), "Unsupported shader stage or compiler version")
|
||||
};
|
||||
}
|
||||
@@ -54,7 +54,7 @@ internal sealed partial class DXCShaderCompiler
|
||||
{
|
||||
var argsArray = new List<string>
|
||||
{
|
||||
"-T", GetProfileString(config.stage, config.tier), // Target profile (ms_6_6, ps_6_6)
|
||||
"-T", GetProfileString(config.stage, config.model), // 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
|
||||
@@ -67,6 +67,19 @@ internal sealed partial class DXCShaderCompiler
|
||||
argsArray.Add(define);
|
||||
}
|
||||
|
||||
if (config.stage == ShaderStage.TaskShader
|
||||
|| config.stage == ShaderStage.MeshShader
|
||||
|| config.stage == ShaderStage.PixelShader)
|
||||
{
|
||||
argsArray.Add("-D");
|
||||
argsArray.Add("__GRAPHICS__");
|
||||
}
|
||||
else if (config.stage == ShaderStage.ComputeShader)
|
||||
{
|
||||
argsArray.Add("-D");
|
||||
argsArray.Add("__COMPUTE__");
|
||||
}
|
||||
|
||||
if (!config.options.HasFlag(CompilerOption.KeepDebugInfo))
|
||||
{
|
||||
argsArray.Add("-Qstrip_debug");
|
||||
@@ -378,16 +391,22 @@ internal sealed unsafe partial class DXCShaderCompiler : IShaderCompiler
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This should be shader variant specific compile instead of pass specific.
|
||||
// TODO: Build final shader code in memory before compiling.
|
||||
public Result<GraphicsCompiledResult> CompilePass(ref readonly PassDescriptor descriptor, ref readonly ShaderCompilationConfig additionalConfig, ref readonly LocalKeywordSet keywords)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var defineCountInDescriptor = descriptor.defines?.Length ?? 0;
|
||||
var fullDefines = new string[defineCountInDescriptor + additionalConfig.defines.Length];
|
||||
descriptor.defines?.CopyTo(fullDefines);
|
||||
additionalConfig.defines.CopyTo(fullDefines.AsSpan(defineCountInDescriptor));
|
||||
string[] fullDefines;
|
||||
var totalDefineCount = descriptor.defines.Length + additionalConfig.defines.Length;
|
||||
if (totalDefineCount == 0)
|
||||
{
|
||||
fullDefines = Array.Empty<string>();
|
||||
}
|
||||
else
|
||||
{
|
||||
fullDefines = new string[totalDefineCount];
|
||||
descriptor.defines.CopyTo(fullDefines);
|
||||
additionalConfig.defines.CopyTo(fullDefines.AsSpan(descriptor.defines.Length));
|
||||
}
|
||||
|
||||
var injectedCodeBuilder = new StringBuilder();
|
||||
injectedCodeBuilder.AppendLine(descriptor.shader.propertiesCode);
|
||||
@@ -402,13 +421,13 @@ internal sealed unsafe partial class DXCShaderCompiler : IShaderCompiler
|
||||
{
|
||||
var config = new ShaderCompilationConfig
|
||||
{
|
||||
defines = fullDefines.AsSpan(),
|
||||
includes = descriptor.includes.AsSpan(),
|
||||
defines = fullDefines,
|
||||
includes = descriptor.includes,
|
||||
shaderPath = tsEntry.shader,
|
||||
entryPoint = tsEntry.entry,
|
||||
injectedCode = injectedCode,
|
||||
stage = ShaderStage.TaskShader,
|
||||
tier = additionalConfig.tier,
|
||||
model = additionalConfig.model,
|
||||
optimizeLevel = additionalConfig.optimizeLevel,
|
||||
options = additionalConfig.options,
|
||||
};
|
||||
@@ -428,13 +447,13 @@ internal sealed unsafe partial class DXCShaderCompiler : IShaderCompiler
|
||||
{
|
||||
var config = new ShaderCompilationConfig
|
||||
{
|
||||
defines = fullDefines.AsSpan(),
|
||||
includes = descriptor.includes.AsSpan(),
|
||||
defines = fullDefines,
|
||||
includes = descriptor.includes,
|
||||
shaderPath = msEntry.shader,
|
||||
entryPoint = msEntry.entry,
|
||||
injectedCode = injectedCode,
|
||||
stage = ShaderStage.MeshShader,
|
||||
tier = additionalConfig.tier,
|
||||
model = additionalConfig.model,
|
||||
optimizeLevel = additionalConfig.optimizeLevel,
|
||||
options = additionalConfig.options,
|
||||
};
|
||||
@@ -458,13 +477,13 @@ internal sealed unsafe partial class DXCShaderCompiler : IShaderCompiler
|
||||
{
|
||||
var config = new ShaderCompilationConfig
|
||||
{
|
||||
defines = fullDefines.AsSpan(),
|
||||
includes = descriptor.includes.AsSpan(),
|
||||
defines = fullDefines,
|
||||
includes = descriptor.includes,
|
||||
shaderPath = psEntry.shader,
|
||||
entryPoint = psEntry.entry,
|
||||
injectedCode = injectedCode,
|
||||
stage = ShaderStage.PixelShader,
|
||||
tier = additionalConfig.tier,
|
||||
model = additionalConfig.model,
|
||||
optimizeLevel = additionalConfig.optimizeLevel,
|
||||
options = additionalConfig.options,
|
||||
};
|
||||
|
||||
@@ -167,6 +167,7 @@ public struct ResourceRange
|
||||
|
||||
public readonly struct ShaderVariant;
|
||||
public readonly struct GraphicsPipeline;
|
||||
public readonly struct ComputePipeline;
|
||||
|
||||
public readonly struct ShaderPass
|
||||
{
|
||||
@@ -241,6 +242,14 @@ public ref struct GraphicsPSODescriptor
|
||||
}
|
||||
}
|
||||
|
||||
public ref struct ComputePSODescriptor
|
||||
{
|
||||
public Key64<ShaderVariant> VariantKey
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct CBufferPropertyInfo
|
||||
{
|
||||
public string Name
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace Ghost.Graphics.RHI;
|
||||
public interface IPipelineLibrary : IDisposable
|
||||
{
|
||||
void SaveLibraryToDisk(string filePath);
|
||||
bool HasPipeline(Key128<GraphicsPipeline> key);
|
||||
Result<Key128<GraphicsPipeline>> CreatePSO(ref readonly GraphicsPSODescriptor descriptor, ref readonly GraphicsCompiledResult compiled);
|
||||
bool HasPipelineStateObject(UInt128 key);
|
||||
Result<Key128<GraphicsPipeline>> CreateGraphicsPipeline(ref readonly GraphicsPSODescriptor descriptor, ref readonly GraphicsCompiledResult compiled);
|
||||
Result<Key128<ComputePipeline>> CreateComputePipeline(ref readonly ComputePSODescriptor descriptor, ref readonly ShaderCompileResult compiled);
|
||||
}
|
||||
|
||||
@@ -57,18 +57,11 @@ public ref struct ShaderCompilationConfig
|
||||
public string entryPoint;
|
||||
public string? injectedCode;
|
||||
public ShaderStage stage;
|
||||
public CompilerTier tier;
|
||||
public ShaderModel model;
|
||||
public CompilerOptimizeLevel optimizeLevel;
|
||||
public CompilerOption options;
|
||||
}
|
||||
|
||||
public enum CompilerTier
|
||||
{
|
||||
Tier0,
|
||||
Tier1,
|
||||
Tier2
|
||||
}
|
||||
|
||||
public enum CompilerOptimizeLevel
|
||||
{
|
||||
O0,
|
||||
@@ -92,7 +85,8 @@ public enum ShaderStage
|
||||
TaskShader,
|
||||
MeshShader,
|
||||
PixelShader,
|
||||
ComputeShader
|
||||
ComputeShader,
|
||||
Library // For ray tracing shaders or work graph shaders that don't fit into the traditional shader stages
|
||||
}
|
||||
|
||||
public enum ShaderInputType
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
public interface IWorkGraphPipeline
|
||||
{
|
||||
}
|
||||
@@ -10,6 +10,10 @@ namespace Ghost.Graphics.RHI;
|
||||
public static class RHIUtility
|
||||
{
|
||||
public const int MAX_RENDER_TARGETS = 8;
|
||||
public const ulong PIPELINE_KEY_MASK = 0xFFFFFFFFFFFFFFF0ul;
|
||||
public const ulong GRAPHICS_PIPELINE_KEY_FLAG = 0x1ul;
|
||||
public const ulong COMPUTE_PIPELINE_KEY_FLAG = 0x2ul;
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint GetBytesPerPixel(this TextureFormat format)
|
||||
@@ -180,15 +184,29 @@ public static class RHIUtility
|
||||
var pHi = pPasskey[1];
|
||||
|
||||
// Distinct constants + cross-feeding to reduce structural collisions.
|
||||
var lo = Mix64(mLo ^ (pLo + 0x9E3779B97F4A7C15ul) ^ (mHi * 0xD6E8FEB86659FD93ul));
|
||||
var hi = Mix64(mHi ^ (pHi + 0xC2B2AE3D27D4EB4Ful) ^ (pLo * 0x165667B19E3779F9ul));
|
||||
var lo = Mix64(mLo ^ (pLo + 0x9E3779B97F4A7C15ul) ^ (mHi * 0xD6E8FEB86659FD93ul));
|
||||
|
||||
return new Key128<GraphicsPipeline>(new UInt128(lo, hi));
|
||||
lo = lo & PIPELINE_KEY_MASK | GRAPHICS_PIPELINE_KEY_FLAG; // Ensure graphics pipeline keys are distinguishable from compute pipeline keys.
|
||||
|
||||
return new Key128<GraphicsPipeline>(new UInt128(hi, lo));
|
||||
}
|
||||
|
||||
public static Key128<ComputePipeline> CreateComputePipelineKey(Key64<ShaderVariant> shaderVariantKey, ulong compiledHash)
|
||||
{
|
||||
var shaderHash = shaderVariantKey.Value;
|
||||
var stateHash = compiledHash;
|
||||
// Simple XOR mix. Not as robust as the graphics pipeline key, but sufficient for compute shaders which have fewer variants.
|
||||
var hi = shaderHash ^ (stateHash + 0x9E3779B97F4A7C15ul) ^ (shaderHash * 0xD6E8FEB86659FD93ul);
|
||||
var lo = stateHash ^ (shaderHash + 0xC2B2AE3D27D4EB4Ful) ^ (stateHash * 0x165667B19E3779F9ul);
|
||||
lo = lo & PIPELINE_KEY_MASK | COMPUTE_PIPELINE_KEY_FLAG; // Ensure compute pipeline keys are distinguishable from graphics pipeline keys.
|
||||
|
||||
return new Key128<ComputePipeline>(new UInt128(hi, lo));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool TryGetString(this Key128<GraphicsPipeline> key, Span<char> destination)
|
||||
public static bool TryGetStringFromHash(UInt128 key, Span<char> destination)
|
||||
{
|
||||
return key.Value.TryFormat(destination, out var _, "X16");
|
||||
return key.TryFormat(destination, out var _, "X16");
|
||||
}
|
||||
}
|
||||
@@ -10,20 +10,25 @@ public static class RootSignatureLayout
|
||||
public const int ROOT_PARAMETER_COUNT = 1;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 16)]
|
||||
[StructLayout(LayoutKind.Explicit, Size = 12)]
|
||||
public struct PushConstantsData
|
||||
{
|
||||
public const uint NUM_32BITS_VALUE = 16u / sizeof(uint);
|
||||
|
||||
public const uint NUM_32BITS_VALUE = 12u / sizeof(uint);
|
||||
|
||||
[FieldOffset(0)]
|
||||
public uint frameBuffer;
|
||||
[FieldOffset(4)]
|
||||
public uint viewBuffer;
|
||||
public uint instanceBuffer;
|
||||
[FieldOffset(8)]
|
||||
public uint instanceIndex;
|
||||
[FieldOffset(8)]
|
||||
public uint propertyBuffer;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct FrameData
|
||||
{
|
||||
public uint instanceBuffer;
|
||||
public uint userBuffer;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ using Ghost.Core.Utilities;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
@@ -75,7 +76,7 @@ public partial struct Shader
|
||||
/// </summary>
|
||||
public partial struct Shader : IResourceReleasable
|
||||
{
|
||||
private readonly uint _cbufferSize;
|
||||
private readonly uint _propertyBufferSize;
|
||||
private UnsafeArray<ShaderPass> _shaderPasses;
|
||||
private UnsafeHashMap<int, int> _passIDToLocal;
|
||||
private UnsafeHashMap<int, int> _keywordIDToLocal;
|
||||
@@ -84,11 +85,11 @@ public partial struct Shader : IResourceReleasable
|
||||
// We can use a int array since the number and index of tags are fixed at compile time.
|
||||
|
||||
public readonly int PassCount => _shaderPasses.Count;
|
||||
public readonly uint PropertyBufferSize => _cbufferSize;
|
||||
public readonly uint PropertyBufferSize => _propertyBufferSize;
|
||||
|
||||
internal Shader(ShaderDescriptor descriptor, ref readonly GraphicsCompiledResult compiledResult)
|
||||
internal Shader(GraphicsShaderDescriptor descriptor, ref readonly GraphicsCompiledResult compiledResult)
|
||||
{
|
||||
_cbufferSize = descriptor.propertyBufferSize;
|
||||
_propertyBufferSize = descriptor.propertyBufferSize;
|
||||
_shaderPasses = new UnsafeArray<ShaderPass>(descriptor.passes.Length, Allocator.Persistent);
|
||||
_passIDToLocal = new UnsafeHashMap<int, int>(descriptor.passes.Length, Allocator.Persistent);
|
||||
_keywordIDToLocal = new UnsafeHashMap<int, int>(32, Allocator.Persistent);
|
||||
@@ -198,3 +199,31 @@ public partial struct Shader : IResourceReleasable
|
||||
_passIDToLocal.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public unsafe partial struct ComputeShader
|
||||
{
|
||||
private fixed ulong _entryHash[8];
|
||||
private readonly int _entryPointCount;
|
||||
private readonly uint _propertyBufferSize;
|
||||
|
||||
public readonly uint PropertyBufferSize => _propertyBufferSize;
|
||||
|
||||
internal ComputeShader(ComputeShaderDescriptor descriptor, ReadOnlySpan<ShaderCompileResult> compiledResults)
|
||||
{
|
||||
Debug.Assert(descriptor.entryPoints.Length == compiledResults.Length);
|
||||
|
||||
_propertyBufferSize = descriptor.propertyBufferSize;
|
||||
_entryPointCount = descriptor.entryPoints.Length;
|
||||
for (var i = 0; i < descriptor.entryPoints.Length; i++)
|
||||
{
|
||||
_entryHash[i] = Hash.Combine64(descriptor.identifier, compiledResults[i].hashCode);
|
||||
}
|
||||
}
|
||||
|
||||
public ulong GetEntryHash(int entryPointIndex)
|
||||
{
|
||||
Debug.Assert(entryPointIndex >= 0 && entryPointIndex < _entryPointCount);
|
||||
return _entryHash[entryPointIndex];
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,10 @@
|
||||
<IsTrimmable>True</IsTrimmable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="TestCompute.gcomp" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Misaki.HighPerformance.Analyzer" Version="1.1.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
||||
@@ -25,7 +25,6 @@ public interface IRasterRenderContext : IRenderGraphContext
|
||||
void SetScissorRect(ScissorRectDesc desc);
|
||||
|
||||
void SetGlobalData(uint globalIndex, uint viewIndex);
|
||||
void SetInstanceData(uint instanceBuffer);
|
||||
void SetInstanceIndex(uint instanceIndex);
|
||||
|
||||
void SetActiveMaterial(Handle<Material> material);
|
||||
@@ -64,7 +63,6 @@ internal sealed class RenderGraphContext : IUnsafeRenderContext
|
||||
|
||||
private uint _activeFrameBuffer;
|
||||
private uint _activeViewBuffer;
|
||||
private uint _activeInstanceBuffer;
|
||||
private uint _activeInstanceIndex;
|
||||
|
||||
public ResourceManager ResourceManager => _resourceManager;
|
||||
@@ -189,7 +187,7 @@ internal sealed class RenderGraphContext : IUnsafeRenderContext
|
||||
};
|
||||
|
||||
var compiled = compiledCacheResult.Value;
|
||||
_pipelineLibrary.CreatePSO(in psoDes, in compiled).GetValueOrThrow();
|
||||
_pipelineLibrary.CreateGraphicsPipeline(in psoDes, in compiled).GetValueOrThrow();
|
||||
}
|
||||
|
||||
_activePerMaterialData = material._cBufferCache.GpuResource;
|
||||
@@ -222,11 +220,6 @@ internal sealed class RenderGraphContext : IUnsafeRenderContext
|
||||
_activeViewBuffer = viewBuffer;
|
||||
}
|
||||
|
||||
public void SetInstanceData(uint instanceBuffer)
|
||||
{
|
||||
_activeInstanceBuffer = instanceBuffer;
|
||||
}
|
||||
|
||||
public void SetInstanceIndex(uint instanceIndex)
|
||||
{
|
||||
_activeInstanceIndex = instanceIndex;
|
||||
@@ -238,7 +231,6 @@ internal sealed class RenderGraphContext : IUnsafeRenderContext
|
||||
{
|
||||
frameBuffer = _activeFrameBuffer,
|
||||
viewBuffer = _activeViewBuffer,
|
||||
instanceBuffer = _activeInstanceBuffer,
|
||||
instanceIndex = _activeInstanceIndex,
|
||||
};
|
||||
|
||||
|
||||
@@ -142,7 +142,7 @@ public sealed partial class ResourceManager : IDisposable
|
||||
/// </summary>
|
||||
/// <returns>An <see cref="Identifier{Shader}"/> representing the newly created shader.</returns>
|
||||
/// <param name="descriptor">The viewGroup containing the shader's properties and passes.</param>
|
||||
public Identifier<Shader> CreateGraphicsShader(ShaderDescriptor descriptor, ref readonly GraphicsCompiledResult compiledResult)
|
||||
public Identifier<Shader> CreateGraphicsShader(GraphicsShaderDescriptor descriptor, ref readonly GraphicsCompiledResult compiledResult)
|
||||
{
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
|
||||
@@ -5,16 +5,23 @@
|
||||
|
||||
// TODO: This should be auto generated to match the c# side.
|
||||
|
||||
struct PushConstantData
|
||||
struct GraphicsPushConstantData
|
||||
{
|
||||
BYTE_ADDRESS_BUFFER frameBuffer;
|
||||
BYTE_ADDRESS_BUFFER viewBuffer;
|
||||
BYTE_ADDRESS_BUFFER instanceBuffer;
|
||||
uint instanceIndex;
|
||||
};
|
||||
|
||||
struct ComputePushConstantData
|
||||
{
|
||||
BYTE_ADDRESS_BUFFER frameBuffer;
|
||||
BYTE_ADDRESS_BUFFER viewBuffer;
|
||||
BYTE_ADDRESS_BUFFER propertiesBuffer;
|
||||
};
|
||||
|
||||
struct FrameData
|
||||
{
|
||||
BYTE_ADDRESS_BUFFER instanceBuffer;
|
||||
BYTE_ADDRESS_BUFFER userBuffer;
|
||||
};
|
||||
|
||||
@@ -48,6 +55,10 @@ struct MeshData
|
||||
BYTE_ADDRESS_BUFFER meshletTrianglesBuffer;
|
||||
};
|
||||
|
||||
PushConstantData g_PushConstantData : register(b0);
|
||||
#if define(__GRAPHICS__)
|
||||
GraphicsPushConstantData g_PushConstantData : register(b0);
|
||||
#elif defined(__COMPUTE__)
|
||||
ComputePushConstantData g_PushConstantData : register(b0);
|
||||
#endif
|
||||
|
||||
#endif // GHOST_PROPERTIES_HLSL
|
||||
|
||||
20
src/Runtime/Ghost.Graphics/TestCompute.gcomp
Normal file
20
src/Runtime/Ghost.Graphics/TestCompute.gcomp
Normal file
@@ -0,0 +1,20 @@
|
||||
compute "TestComputeShader"
|
||||
{
|
||||
hlsl
|
||||
{
|
||||
RWTexture2D<float4> OutputTexture : register(u0);
|
||||
[numthreads(8, 8, 1)]
|
||||
void CSMain(uint3 DTid : SV_DispatchThreadID)
|
||||
{
|
||||
OutputTexture[DTid.xy] = float4(DTid.x / 512.0f, DTid.y / 512.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
void CSMain2(uint3 DTid : SV_DispatchThreadID)
|
||||
{
|
||||
OutputTexture[DTid.xy] = float4(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
cs "hlsl_block" : "CSMain";
|
||||
cs "hlsl_block" : "CSMain2";
|
||||
}
|
||||
@@ -46,7 +46,8 @@ shader "MyShader/Standard"
|
||||
[numthreads(1, 1, 1)]
|
||||
void ASMain(uint3 groupID : SV_GroupID)
|
||||
{
|
||||
InstanceData instanceData = LoadData<InstanceData>(g_PushConstantData.instanceBuffer, g_PushConstantData.instanceIndex);
|
||||
FrameData frameData = LoadData<FrameData>(g_PushConstantData.frameBuffer, 0);
|
||||
InstanceData instanceData = LoadData<InstanceData>(frameData.instanceBuffer, g_PushConstantData.instanceIndex);
|
||||
MeshData meshData = LoadData<MeshData>(instanceData.meshBuffer, 0);
|
||||
|
||||
uint meshletIndex = groupID.x;
|
||||
@@ -100,7 +101,8 @@ shader "MyShader/Standard"
|
||||
out vertices PixelInput outVerts[64],
|
||||
out indices uint3 outTris[124])
|
||||
{
|
||||
InstanceData instanceData = LoadData<InstanceData>(g_PushConstantData.instanceBuffer, g_PushConstantData.instanceIndex);
|
||||
FrameData frameData = LoadData<FrameData>(g_PushConstantData.frameBuffer, 0);
|
||||
InstanceData instanceData = LoadData<InstanceData>(frameData.instanceBuffer, g_PushConstantData.instanceIndex);
|
||||
MeshData meshData = LoadData<MeshData>(instanceData.meshBuffer, 0);
|
||||
|
||||
ByteAddressBuffer meshletBuffer = GET_BUFFER(meshData.meshletBuffer);
|
||||
@@ -171,13 +173,14 @@ shader "MyShader/Standard"
|
||||
|
||||
return float4(r, g, b, 1.0);
|
||||
|
||||
// InstanceData instanceData = LoadData<InstanceData>(g_PushConstantData.instanceBuffer, g_PushConstantData.instanceIndex);
|
||||
// FrameData frameData = LoadData<FrameData>(g_PushConstantData.frameBuffer, 0);
|
||||
// InstanceData instanceData = LoadData<InstanceData>(frameData.instanceBuffer, g_PushConstantData.instanceIndex);
|
||||
// return mul(instanceData.localToWorld, float4(input.normal, 1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
task "hlsl_block" : "ASMain";
|
||||
mesh "hlsl_block" : "MSMain";
|
||||
pixel "hlsl_block" : "PSMain";
|
||||
as "hlsl_block" : "ASMain";
|
||||
ms "hlsl_block" : "MSMain";
|
||||
ps "hlsl_block" : "PSMain";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user