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:
2026-04-10 02:53:40 +09:00
parent 68fda03aa9
commit 4ed5572ce7
29 changed files with 742 additions and 290 deletions

View File

@@ -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>();
}

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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))

View File

@@ -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,
};

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -1,9 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Ghost.Graphics.RHI;
public interface IWorkGraphPipeline
{
}

View File

@@ -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");
}
}

View File

@@ -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;
}

View File

@@ -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];
}
}

View File

@@ -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>

View File

@@ -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,
};

View File

@@ -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);

View File

@@ -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

View 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";
}

View File

@@ -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";
}
}