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,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;
|
||||
|
||||
@@ -11,6 +11,7 @@ INCLUDES: 'includes';
|
||||
GLOBAL: 'global';
|
||||
LOCAL: 'local';
|
||||
HLSL: 'hlsl';
|
||||
SM: 'sm';
|
||||
|
||||
// Punctuation
|
||||
LBRACE: '{';
|
||||
|
||||
@@ -13,7 +13,10 @@ shader:
|
||||
RBRACE;
|
||||
|
||||
shaderBody:
|
||||
(pipelineBlock | passBlock | functionCall)*;
|
||||
shaderModel | (pipelineBlock | passBlock | functionCall)*;
|
||||
|
||||
shaderModel:
|
||||
SM IDENTIFIER SEMICOLON;
|
||||
|
||||
scope:
|
||||
GLOBAL | LOCAL;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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:
|
||||
|
||||
149
src/Editor/Ghost.DSL/ShaderParser/ComputeShaderVisitor.cs
Normal file
149
src/Editor/Ghost.DSL/ShaderParser/ComputeShaderVisitor.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user