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,7 +1,7 @@
parser grammar GhostComputeParser;
parser grammar GhostComputeShaderParser;
options {
tokenVocab = GhostComputeLexer;
tokenVocab = GhostShaderLexer;
}
// Top-level rule
@@ -13,7 +13,10 @@ compute:
RBRACE;
computeBody:
(definesBlock | includesBlock | keywordsBlock | hlslBlock | computeEntry)*;
shaderModel | (definesBlock | includesBlock | keywordsBlock | hlslBlock | computeEntry)*;
shaderModel:
SM IDENTIFIER SEMICOLON;
scope:
GLOBAL | LOCAL;

View File

@@ -11,6 +11,7 @@ INCLUDES: 'includes';
GLOBAL: 'global';
LOCAL: 'local';
HLSL: 'hlsl';
SM: 'sm';
// Punctuation
LBRACE: '{';

View File

@@ -13,7 +13,10 @@ shader:
RBRACE;
shaderBody:
(pipelineBlock | passBlock | functionCall)*;
shaderModel | (pipelineBlock | passBlock | functionCall)*;
shaderModel:
SM IDENTIFIER SEMICOLON;
scope:
GLOBAL | LOCAL;

View File

@@ -45,14 +45,14 @@ internal static class DSLShaderCompiler
// TODO: Implement shader inheritance resolution, including property and pass merging.
// Currently, we just ignore inheritance.
public static Result<ShaderDescriptor> ResolveShader(DSLShaderSemantics semantics)
public static Result<GraphicsShaderDescriptor> ResolveShader(DSLShaderSemantics semantics)
{
var descriptor = new ShaderDescriptor
var descriptor = new GraphicsShaderDescriptor
{
name = semantics.name,
};
if (!ShaderPropertiesRegistry.TryGetCode(semantics.name, out var info))
if (!ShaderPropertiesRegistry.TryGetInfo(semantics.name, out var info))
{
info = default;
}
@@ -60,6 +60,8 @@ internal static class DSLShaderCompiler
descriptor.propertiesCode = info.code ?? string.Empty;
descriptor.propertyBufferSize = info.size;
descriptor.shaderModel = semantics.shaderModel;
if (semantics.passes != null)
{
descriptor.passes = new PassDescriptor[semantics.passes.Count];
@@ -91,7 +93,7 @@ internal static class DSLShaderCompiler
return descriptor;
}
public static Result<ShaderDescriptor> CompileShader(string shaderPath, string generatedOutputDirectory)
public static Result<GraphicsShaderDescriptor> CompileGraphicsShader(string shaderPath)
{
try
{
@@ -143,4 +145,82 @@ internal static class DSLShaderCompiler
return Result.Failure("Failed to compile shader: " + ex.Message);
}
}
public static Result<ComputeShaderDescriptor> CompileComputeShader(string shaderPath)
{
try
{
var source = File.ReadAllText(shaderPath);
var shaderModels = AntlrShaderCompiler.ParseComputeShaders(source, out var parseErrors);
if (parseErrors.Count != 0)
{
var errorMessages = new StringBuilder();
foreach (var error in parseErrors)
{
errorMessages.AppendLine(error.ToString());
}
return Result.Failure("Failed to parse compute shader due to errors:\n" + errorMessages.ToString());
}
if (shaderModels.Count == 0)
{
return Result.Failure("No compute shader found in the provided file.");
}
var model = AntlrShaderCompiler.ConvertToComputeSemantics(shaderModels[0], out var errors);
if (errors.Count != 0 || model == null)
{
var errorMessages = new StringBuilder();
foreach (var error in errors)
{
errorMessages.AppendLine(error.ToString());
}
return Result.Failure("Failed to compile compute shader due to errors:\n" + errorMessages.ToString());
}
var result = ResolveComputeShader(model);
if (result.IsFailure)
{
return result;
}
return result.Value;
}
catch (Exception ex)
{
return Result.Failure("Failed to compile compute shader: " + ex.Message);
}
}
public static Result<ComputeShaderDescriptor> ResolveComputeShader(DSLComputeShaderSemantics semantics)
{
var descriptor = new ComputeShaderDescriptor
{
identifier = XxHash64.HashToUInt64(MemoryMarshal.AsBytes(semantics.name.AsSpan())),
name = semantics.name,
};
if (!ShaderPropertiesRegistry.TryGetInfo(semantics.name, out var info))
{
info = default;
}
descriptor.propertiesCode = info.code ?? string.Empty;
descriptor.propertyBufferSize = info.size;
descriptor.shaderModel = semantics.shaderModel;
descriptor.hlsl = semantics.hlsl;
descriptor.defines = semantics.defines?.ToArray() ?? Array.Empty<string>();
descriptor.includes = semantics.includes?.ToArray() ?? Array.Empty<string>();
descriptor.keywords = semantics.keywords?.ToArray() ?? Array.Empty<KeywordsGroup>();
descriptor.entryPoints = semantics.entryPoints?.ToArray() ?? Array.Empty<ShaderEntryPoint>();
return descriptor;
}
}

View File

@@ -33,6 +33,18 @@ public class PassSemantic
public class DSLShaderSemantics
{
public string name = string.Empty;
public ShaderModel shaderModel;
public PipelineSemantic? pipeline;
public List<PassSemantic>? passes;
}
public class DSLComputeShaderSemantics
{
public string name = string.Empty;
public string? hlsl;
public ShaderModel shaderModel;
public List<string>? defines;
public List<string>? includes;
public List<KeywordsGroup>? keywords;
public List<ShaderEntryPoint>? entryPoints;
}

View File

@@ -7,7 +7,7 @@ namespace Ghost.DSL.ShaderParser;
public class AntlrShaderCompiler
{
public static List<ShaderModel> ParseShaders(string source, out List<DSLShaderError> errors)
public static List<GraphicsShaderModel> ParseShaders(string source, out List<DSLShaderError> errors)
{
errors = new List<DSLShaderError>();
@@ -33,7 +33,7 @@ public class AntlrShaderCompiler
if (errors.Count > 0)
{
return new List<ShaderModel>();
return new List<GraphicsShaderModel>();
}
var visitor = new ShaderVisitor();
@@ -49,11 +49,165 @@ public class AntlrShaderCompiler
line = -1,
column = -1
});
return new List<ShaderModel>();
return new List<GraphicsShaderModel>();
}
}
public static DSLShaderSemantics? ConvertToSemantics(ShaderModel model, out List<DSLShaderError> errors)
public static List<ComputeShaderModel> ParseComputeShaders(string source, out List<DSLShaderError> errors)
{
errors = new List<DSLShaderError>();
try
{
var inputStream = new AntlrInputStream(source);
var lexer = new GhostShaderLexer(inputStream);
// Capture lexer errors
lexer.RemoveErrorListeners();
var lexerErrorListener = new ErrorListener(errors);
lexer.AddErrorListener(lexerErrorListener);
var tokenStream = new CommonTokenStream(lexer);
var parser = new GhostComputeShaderParser(tokenStream);
// Capture parser errors
parser.RemoveErrorListeners();
var parserErrorListener = new ErrorListener(errors);
parser.AddErrorListener(parserErrorListener);
var tree = parser.computeFile();
if (errors.Count > 0)
{
return new List<ComputeShaderModel>();
}
var visitor = new ComputeShaderVisitor();
visitor.Visit(tree);
return visitor.ComputeShaders;
}
catch (Exception ex)
{
errors.Add(new DSLShaderError
{
message = $"Unexpected error during parsing: {ex.Message}",
line = -1,
column = -1
});
return new List<ComputeShaderModel>();
}
}
public static DSLComputeShaderSemantics? ConvertToComputeSemantics(ComputeShaderModel model, out List<DSLShaderError> errors)
{
errors = new List<DSLShaderError>();
if (string.IsNullOrWhiteSpace(model.Name))
{
errors.Add(new DSLShaderError
{
message = "Compute shader name cannot be empty.",
line = 0,
column = 0
});
return null;
}
var semantics = new DSLComputeShaderSemantics
{
name = model.Name,
defines = model.Defines?.Defines,
includes = model.Includes?.Includes,
hlsl = model.Hlsl?.Code
};
if (string.IsNullOrEmpty(model.SM))
{
semantics.shaderModel = ShaderModel.SM_6_8; // Default to highest supported shader model
}
else
{
semantics.shaderModel = model.SM.ToLower() switch
{
"6_6" => ShaderModel.SM_6_6,
"6_7" => ShaderModel.SM_6_7,
"6_8" => ShaderModel.SM_6_8,
_ => ShaderModel.Invalid
};
if (semantics.shaderModel == ShaderModel.Invalid)
{
errors.Add(new DSLShaderError
{
message = $"Unknown shader model '{model.SM}'.",
line = 0,
column = 0
});
}
}
if (model.Keywords != null)
{
semantics.keywords = new List<KeywordsGroup>();
foreach (var group in model.Keywords.Groups)
{
var keywordGroup = new KeywordsGroup
{
space = group.Scope?.ToLower() == "global" ? KeywordSpace.Global : KeywordSpace.Local,
keywords = group.Keywords
};
semantics.keywords.Add(keywordGroup);
}
}
foreach (var entry in model.ShaderEntries)
{
var entryType = entry.EntryType.ToLower();
if (entryType == "cs")
{
semantics.entryPoints ??= new List<ShaderEntryPoint>();
semantics.entryPoints.Add(new ShaderEntryPoint
{
shader = entry.ShaderPath,
entry = entry.EntryPoint
});
}
else
{
errors.Add(new DSLShaderError
{
message = $"Unknown compute shader entry type '{entry.EntryType}'. Expected 'compute' or 'cs'.",
line = 0,
column = 0
});
}
}
if (semantics.entryPoints == null)
{
errors.Add(new DSLShaderError
{
message = $"Compute shader '{model.Name}' must contain a compute/cs entry declaration.",
line = 0,
column = 0
});
}
if (semantics.entryPoints != null && semantics.entryPoints.Count > 8)
{
errors.Add(new DSLShaderError
{
message = $"Compute shader '{model.Name}' cannot have more than 8 entry points.",
line = 0,
column = 0
});
}
return semantics;
}
public static DSLShaderSemantics? ConvertToSemantics(GraphicsShaderModel model, out List<DSLShaderError> errors)
{
errors = new List<DSLShaderError>();
@@ -74,6 +228,31 @@ public class AntlrShaderCompiler
pipeline = ConvertPipeline(model.Pipeline, errors)
};
if (string.IsNullOrEmpty(model.SM))
{
semantics.shaderModel = ShaderModel.SM_6_8; // Default to highest supported shader model
}
else
{
semantics.shaderModel = model.SM.ToLower() switch
{
"6_6" => ShaderModel.SM_6_6,
"6_7" => ShaderModel.SM_6_7,
"6_8" => ShaderModel.SM_6_8,
_ => ShaderModel.Invalid
};
if (semantics.shaderModel == ShaderModel.Invalid)
{
errors.Add(new DSLShaderError
{
message = $"Unknown shader model '{model.SM}'.",
line = 0,
column = 0
});
}
}
foreach (var pass in model.Passes)
{
var passSemantic = ConvertPass(pass, errors);
@@ -87,99 +266,6 @@ public class AntlrShaderCompiler
return semantics;
}
private static ShaderPropertyType ParsePropertyType(string type, List<DSLShaderError> errors)
{
return type.ToLower() switch
{
"float" => ShaderPropertyType.Float,
"float2" => ShaderPropertyType.Float2,
"float3" => ShaderPropertyType.Float3,
"float4" => ShaderPropertyType.Float4,
"float4x4" => ShaderPropertyType.Float4x4,
"int" => ShaderPropertyType.Int,
"int2" => ShaderPropertyType.Int2,
"int3" => ShaderPropertyType.Int3,
"int4" => ShaderPropertyType.Int4,
"uint" => ShaderPropertyType.UInt,
"uint2" => ShaderPropertyType.UInt2,
"uint3" => ShaderPropertyType.UInt3,
"uint4" => ShaderPropertyType.UInt4,
"bool" => ShaderPropertyType.Bool,
"bool2" => ShaderPropertyType.Bool2,
"bool3" => ShaderPropertyType.Bool3,
"bool4" => ShaderPropertyType.Bool4,
"tex2d" => ShaderPropertyType.Texture2D,
"tex3d" => ShaderPropertyType.Texture3D,
"texcube" => ShaderPropertyType.TextureCube,
"texcube_arr" => ShaderPropertyType.TextureCubeArray,
"tex2d_arr" => ShaderPropertyType.Texture2DArray,
"sampler" => ShaderPropertyType.Sampler,
_ => ShaderPropertyType.None
};
}
private static object? ParsePropertyValue(ShaderPropertyType type, List<string> values, List<DSLShaderError> errors)
{
// For textures, the value is an identifier (e.g., "white", "black")
if (type is ShaderPropertyType.Texture2D or ShaderPropertyType.Texture3D or ShaderPropertyType.TextureCube)
{
return values.Count > 0 ? values[0] : null;
}
// For samplers, no default value
if (type == ShaderPropertyType.Sampler)
{
return null;
}
// For numeric types, parse the values
try
{
return type switch
{
ShaderPropertyType.Float => values.Count > 0 ? float.Parse(values[0], System.Globalization.CultureInfo.InvariantCulture) : 0f,
ShaderPropertyType.Float2 => values.Count >= 2 ? new Misaki.HighPerformance.Mathematics.float2(
float.Parse(values[0], System.Globalization.CultureInfo.InvariantCulture),
float.Parse(values[1], System.Globalization.CultureInfo.InvariantCulture)) : default,
ShaderPropertyType.Float3 => values.Count >= 3 ? new Misaki.HighPerformance.Mathematics.float3(
float.Parse(values[0], System.Globalization.CultureInfo.InvariantCulture),
float.Parse(values[1], System.Globalization.CultureInfo.InvariantCulture),
float.Parse(values[2], System.Globalization.CultureInfo.InvariantCulture)) : default,
ShaderPropertyType.Float4 => values.Count >= 4 ? new Misaki.HighPerformance.Mathematics.float4(
float.Parse(values[0], System.Globalization.CultureInfo.InvariantCulture),
float.Parse(values[1], System.Globalization.CultureInfo.InvariantCulture),
float.Parse(values[2], System.Globalization.CultureInfo.InvariantCulture),
float.Parse(values[3], System.Globalization.CultureInfo.InvariantCulture)) : default,
ShaderPropertyType.Int => values.Count > 0 ? int.Parse(values[0], System.Globalization.CultureInfo.InvariantCulture) : 0,
ShaderPropertyType.Int2 => values.Count >= 2 ? new Misaki.HighPerformance.Mathematics.int2(
int.Parse(values[0], System.Globalization.CultureInfo.InvariantCulture),
int.Parse(values[1], System.Globalization.CultureInfo.InvariantCulture)) : default,
ShaderPropertyType.Int3 => values.Count >= 3 ? new Misaki.HighPerformance.Mathematics.int3(
int.Parse(values[0], System.Globalization.CultureInfo.InvariantCulture),
int.Parse(values[1], System.Globalization.CultureInfo.InvariantCulture),
int.Parse(values[2], System.Globalization.CultureInfo.InvariantCulture)) : default,
ShaderPropertyType.Int4 => values.Count >= 4 ? new Misaki.HighPerformance.Mathematics.int4(
int.Parse(values[0], System.Globalization.CultureInfo.InvariantCulture),
int.Parse(values[1], System.Globalization.CultureInfo.InvariantCulture),
int.Parse(values[2], System.Globalization.CultureInfo.InvariantCulture),
int.Parse(values[3], System.Globalization.CultureInfo.InvariantCulture)) : default,
ShaderPropertyType.UInt => values.Count > 0 ? uint.Parse(values[0], System.Globalization.CultureInfo.InvariantCulture) : 0u,
ShaderPropertyType.Bool => values.Count > 0 && (values[0] == "1" || values[0].ToLower() == "true"),
_ => null
};
}
catch (Exception ex)
{
errors.Add(new DSLShaderError
{
message = $"Failed to parse property value: {ex.Message}",
line = 0,
column = 0
});
return null;
}
}
private static PipelineSemantic? ConvertPipeline(PipelineBlockModel? pipeline, List<DSLShaderError> errors)
{
if (pipeline == null || pipeline.Statements.Count == 0)
@@ -275,13 +361,13 @@ public class AntlrShaderCompiler
switch (entryType)
{
case "mesh" or "ms":
case "ms":
semantic.meshShader = shaderEntry;
break;
case "pixel" or "ps":
case "ps":
semantic.pixelShader = shaderEntry;
break;
case "task" or "ts":
case "as":
semantic.taskShader = shaderEntry;
break;
default:

View File

@@ -0,0 +1,149 @@
using Antlr4.Runtime.Misc;
using Ghost.DSL.ShaderParser.Model;
using TerraFX.Interop.Windows;
namespace Ghost.DSL.ShaderParser;
internal class ComputeShaderVisitor : GhostComputeShaderParserBaseVisitor<object>
{
public List<ComputeShaderModel> ComputeShaders { get; } = new();
public override object VisitComputeFile([NotNull] GhostComputeShaderParser.ComputeFileContext context)
{
foreach (var shaderContext in context.compute())
{
var shader = (ComputeShaderModel)VisitCompute(shaderContext);
ComputeShaders.Add(shader);
}
return ComputeShaders;
}
private static string StripQuotes(string text)
{
if (text.Length >= 2 && text.StartsWith('"') && text.EndsWith('"'))
{
return text.Substring(1, text.Length - 2);
}
return text;
}
public override object VisitCompute([NotNull] GhostComputeShaderParser.ComputeContext context)
{
var compute = new ComputeShaderModel
{
Name = StripQuotes(context.STRING_LITERAL().GetText())
};
var computeBody = context.computeBody();
if (computeBody != null)
{
compute.SM = computeBody.shaderModel()?.GetText() ?? string.Empty;
foreach (var definesBlock in computeBody.definesBlock())
{
compute.Defines = (DefinesBlockModel)VisitDefinesBlock(definesBlock);
}
foreach (var includesBlock in computeBody.includesBlock())
{
compute.Includes = (IncludesBlockModel)VisitIncludesBlock(includesBlock);
}
foreach (var keywordsBlock in computeBody.keywordsBlock())
{
compute.Keywords = (KeywordsBlockModel)VisitKeywordsBlock(keywordsBlock);
}
var hlslBlock = computeBody.hlslBlock().FirstOrDefault();
if (hlslBlock != null)
{
compute.Hlsl = (HlslBlockModel)VisitHlslBlock(hlslBlock);
}
foreach (var computeEntry in computeBody.computeEntry())
{
compute.ShaderEntries.Add((ShaderEntryModel)VisitComputeEntry(computeEntry));
}
}
return compute;
}
public override object VisitDefinesBlock([NotNull] GhostComputeShaderParser.DefinesBlockContext context)
{
var defines = new DefinesBlockModel();
foreach (var defineStmt in context.defineStatement())
{
defines.Defines.Add(defineStmt.IDENTIFIER().GetText());
}
return defines;
}
public override object VisitIncludesBlock([NotNull] GhostComputeShaderParser.IncludesBlockContext context)
{
var includes = new IncludesBlockModel();
foreach (var includeStmt in context.includeStatement())
{
includes.Includes.Add(StripQuotes(includeStmt.STRING_LITERAL().GetText()));
}
return includes;
}
public override object VisitKeywordsBlock([NotNull] GhostComputeShaderParser.KeywordsBlockContext context)
{
var keywords = new KeywordsBlockModel();
foreach (var keywordStmt in context.keywordStatement())
{
var group = new KeywordGroupModel();
if (keywordStmt.scope() != null)
{
group.Scope = keywordStmt.scope().GetText();
}
foreach (var identifier in keywordStmt.IDENTIFIER())
{
group.Keywords.Add(identifier.GetText());
}
keywords.Groups.Add(group);
}
return keywords;
}
public override object VisitHlslBlock([NotNull] GhostComputeShaderParser.HlslBlockContext context)
{
var hlsl = new HlslBlockModel();
// Get the text between the braces
var start = context.LBRACE().Symbol.StopIndex + 1;
var stop = context.RBRACE().Symbol.StartIndex - 1;
if (stop >= start)
{
var input = context.Start.InputStream;
hlsl.Code = input.GetText(new Interval(start, stop));
}
return hlsl;
}
public override object VisitComputeEntry([NotNull] GhostComputeShaderParser.ComputeEntryContext context)
{
var entry = new ShaderEntryModel
{
EntryType = context.IDENTIFIER().GetText(),
ShaderPath = StripQuotes(context.STRING_LITERAL(0).GetText()),
EntryPoint = StripQuotes(context.STRING_LITERAL(1).GetText())
};
return entry;
}
}

View File

@@ -1,25 +1,24 @@
namespace Ghost.DSL.ShaderParser.Model;
public class ShaderModel
public class GraphicsShaderModel
{
public string Name { get; set; } = string.Empty;
public PropertiesBlockModel? Properties { get; set; }
public string SM { get; set; } = string.Empty;
public PipelineBlockModel? Pipeline { get; set; }
public List<PassBlockModel> Passes { get; set; } = new();
public List<FunctionCallModel> FunctionCalls { get; set; } = new();
}
public class PropertiesBlockModel
public class ComputeShaderModel
{
public List<PropertyDeclarationModel> Properties { get; set; } = new();
}
public class PropertyDeclarationModel
{
public string? Scope { get; set; }
public string Type { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public List<string> Initializer { get; set; } = new();
public string SM { get; set; } = string.Empty;
public DefinesBlockModel? Defines { get; set; }
public IncludesBlockModel? Includes { get; set; }
public KeywordsBlockModel? Keywords { get; set; }
public HlslBlockModel? Hlsl { get; set; }
public List<FunctionCallModel> FunctionCalls { get; set; } = new();
public List<ShaderEntryModel> ShaderEntries { get; set; } = new();
}
public class PipelineBlockModel

View File

@@ -5,13 +5,13 @@ namespace Ghost.DSL.ShaderParser;
public class ShaderVisitor : GhostShaderParserBaseVisitor<object>
{
public List<ShaderModel> Shaders { get; } = new();
public List<GraphicsShaderModel> Shaders { get; } = new();
public override object VisitShaderFile([NotNull] GhostShaderParser.ShaderFileContext context)
{
foreach (var shaderContext in context.shader())
{
var shader = (ShaderModel)VisitShader(shaderContext);
var shader = (GraphicsShaderModel)VisitShader(shaderContext);
Shaders.Add(shader);
}
return Shaders;
@@ -19,7 +19,7 @@ public class ShaderVisitor : GhostShaderParserBaseVisitor<object>
public override object VisitShader([NotNull] GhostShaderParser.ShaderContext context)
{
var shader = new ShaderModel
var shader = new GraphicsShaderModel
{
Name = StripQuotes(context.STRING_LITERAL().GetText())
};
@@ -27,6 +27,8 @@ public class ShaderVisitor : GhostShaderParserBaseVisitor<object>
var shaderBody = context.shaderBody();
if (shaderBody != null)
{
shader.SM = shaderBody.shaderModel()?.GetText() ?? string.Empty;
foreach (var pipelineBlock in shaderBody.pipelineBlock())
{
shader.Pipeline = (PipelineBlockModel)VisitPipelineBlock(pipelineBlock);

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

View File

@@ -1,4 +1,5 @@
using Ghost.Core;
using Ghost.Core.Graphics;
using Ghost.DSL.ShaderCompiler;
using Ghost.Graphics.Core;
using Ghost.Graphics.RenderGraphModule;
@@ -43,13 +44,13 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
renderSystem.GraphicsEngine.PipelineLibrary,
renderSystem.GraphicsEngine.ShaderCompiler);
var shaderDescriptor = DSLShaderCompiler.CompileShader("F:/csharp/GhostEngine/src/Runtime/Ghost.Graphics/test.gshdr", "C:/Users/Misaki/Downloads/Archive").GetValueOrThrow();
var shaderDescriptor = DSLShaderCompiler.CompileGraphicsShader("F:/csharp/GhostEngine/src/Runtime/Ghost.Graphics/test.gshdr").GetValueOrThrow();
var config = new ShaderCompilationConfig
{
optimizeLevel = CompilerOptimizeLevel.O3,
options = CompilerOption.KeepReflections,
tier = CompilerTier.Tier2
model = shaderDescriptor.shaderModel
};
ref readonly var pass = ref shaderDescriptor.passes[0];
@@ -281,14 +282,17 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
HeapType = HeapType.Upload,
};
//var frameBufferHandle = resourceManager.CreateTransientBuffer(in frameBufferDesc, "Frame Buffer");
//var frameBufferResource = frameBufferHandle.AsResource();
var frameBufferHandle = resourceManager.CreateTransientBuffer(in frameBufferDesc, "Frame Buffer");
var frameBufferResource = frameBufferHandle.AsResource();
//var frameData = new FrameData();
var frameData = new FrameData
{
instanceBuffer = resourceDatabase.GetBindlessIndex(instanceBufferResource)
};
//ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(frameBufferResource, BarrierSync.Copy, BarrierAccess.CopyDest));
//ctx.UploadBuffer(frameBufferHandle, new ReadOnlySpan<FrameData>(in frameData));
//ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(frameBufferResource, BarrierSync.AllShading, BarrierAccess.ShaderResource));
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(frameBufferResource, BarrierSync.Copy, BarrierAccess.CopyDest));
ctx.UploadBuffer(frameBufferHandle, new ReadOnlySpan<FrameData>(in frameData));
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(frameBufferResource, BarrierSync.AllShading, BarrierAccess.ShaderResource));
_renderGraph.Reset();
@@ -296,8 +300,7 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
MeshletDebugPass(backBuffer, request.opaqueRenderList,
uint.MaxValue,
resourceDatabase.GetBindlessIndex(viewBufferResource),
resourceDatabase.GetBindlessIndex(instanceBufferResource));
resourceDatabase.GetBindlessIndex(viewBufferResource));
var viewState = new ViewState(rtSize.x, rtSize.y, rtSize.x, rtSize.y);
_renderGraph.Compile(viewState);
@@ -313,7 +316,7 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
}
}
private void MeshletDebugPass(Identifier<RGTexture> backbuffer, RenderList renderList, uint globalIndex, uint viewIndex, uint instanceBuffer)
private void MeshletDebugPass(Identifier<RGTexture> backbuffer, RenderList renderList, uint globalIndex, uint viewIndex)
{
using (var builder = _renderGraph.AddRasterRenderPass<MeshletDebugPassData>("Meshlet Debug Pass", out var passData))
{
@@ -322,7 +325,6 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
passData.renderList = renderList;
passData.globalIndex = globalIndex;
passData.viewIndex = viewIndex;
passData.instanceIndex = instanceBuffer;
passData.material = _meshletMaterial;
builder.SetColorAttachment(backbuffer, 0);
@@ -331,7 +333,6 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
builder.SetRenderFunc<MeshletDebugPassData>(static (data, ctx) =>
{
ctx.SetGlobalData(data.globalIndex, data.viewIndex);
ctx.SetInstanceData(data.instanceIndex);
ctx.SetActiveMaterial(data.material);
var instanceIndex = 0u;

View File

@@ -1,38 +1,19 @@
using Ghost.DSL.Generator;
using Ghost.DSL.ShaderCompiler;
using Misaki.HighPerformance.Mathematics;
using System.Numerics;
ShaderStructGenerator.GenerateHLSL([typeof(TestStruct), typeof(TestEnum), typeof(TestEnumFlags)], PackingRules.Exact, "C:/Users/Misaki/Downloads/Archive/Test.cs.hlsl");
//ShaderStructGenerator.GenerateHLSL([typeof(TestStruct), typeof(TestEnum), typeof(TestEnumFlags)], PackingRules.Exact, "C:/Users/Misaki/Downloads/Archive/Test.cs.hlsl");
//return;
#if false
var source = File.ReadAllText("F:/csharp/GhostEngine/Ghost.Graphics/test.gshader");
var lexer = new Lexer(source);
var stream = new TokenStream(lexer.Tokenize());
var shaderInfo = SDLCompiler.ParseShaders(stream);
var model = SDLCompiler.SemanticAnalysis(shaderInfo[0], out var errors);
foreach (var error in errors)
{
Console.WriteLine(error);
}
if (errors.Count != 0)
#if true
var result = DSLShaderCompiler.CompileComputeShader("F:\\csharp\\GhostEngine\\src\\Runtime\\Ghost.Graphics\\TestCompute.gcomp");
if (result.IsFailure)
{
Console.WriteLine(result.Message);
return;
}
if (model == null)
{
Console.WriteLine("Failed to compile shader due to errors.");
return;
}
var descriptor = SDLCompiler.ResolveShader(model);
SDLCompiler.GenerateShaderCode(descriptor, "C:/Users/Misaki/Downloads/Archive");
Console.WriteLine("Shader compiled successfully:");
#endif
public struct TestStruct