diff --git a/src/Editor/Ghost.DSL/Ghost.DSL.csproj b/src/Editor/Ghost.DSL/Ghost.DSL.csproj
index 4e8bf85..89bdeed 100644
--- a/src/Editor/Ghost.DSL/Ghost.DSL.csproj
+++ b/src/Editor/Ghost.DSL/Ghost.DSL.csproj
@@ -17,6 +17,11 @@
false
true
+
+ true
+ MSBuild:Compile
+ false
+
MSBuild:Compile
false
diff --git a/src/Editor/Ghost.DSL/Grammar/GhostComputeShaderParser.g4 b/src/Editor/Ghost.DSL/Grammar/GhostComputeShaderParser.g4
new file mode 100644
index 0000000..a7e2a06
--- /dev/null
+++ b/src/Editor/Ghost.DSL/Grammar/GhostComputeShaderParser.g4
@@ -0,0 +1,68 @@
+parser grammar GhostComputeParser;
+
+options {
+ tokenVocab = GhostComputeLexer;
+}
+
+// Top-level rule
+computeFile: compute+ EOF;
+
+compute:
+ COMPUTE STRING_LITERAL LBRACE
+ computeBody
+ RBRACE;
+
+computeBody:
+ (definesBlock | includesBlock | keywordsBlock | hlslBlock | computeEntry)*;
+
+scope:
+ GLOBAL | LOCAL;
+
+definesBlock:
+ DEFINES LBRACE
+ defineStatement*
+ RBRACE;
+
+defineStatement:
+ IDENTIFIER SEMICOLON;
+
+includesBlock:
+ INCLUDES LBRACE
+ includeStatement*
+ RBRACE;
+
+includeStatement:
+ STRING_LITERAL SEMICOLON;
+
+keywordsBlock:
+ KEYWORDS LBRACE
+ keywordStatement*
+ RBRACE;
+
+keywordStatement:
+ scope? IDENTIFIER (COMMA IDENTIFIER)* SEMICOLON;
+
+hlslBlock:
+ HLSL LBRACE
+ hlslBody
+ RBRACE;
+
+// Recursively matches content, ensuring braces are balanced.
+hlslBody:
+ (
+ ~(LBRACE | RBRACE) // Match ANY token except open/close braces
+ |
+ LBRACE hlslBody RBRACE // Or match a nested block recursively
+ )*;
+
+computeEntry:
+ IDENTIFIER STRING_LITERAL COLON STRING_LITERAL SEMICOLON;
+
+functionCall:
+ IDENTIFIER LPAREN functionArguments? RPAREN SEMICOLON;
+
+functionArguments:
+ functionArgument (COMMA functionArgument)*;
+
+functionArgument:
+ STRING_LITERAL | NUMBER | IDENTIFIER;
diff --git a/src/Editor/Ghost.DSL/Grammar/GhostShaderLexer.g4 b/src/Editor/Ghost.DSL/Grammar/GhostShaderLexer.g4
index f1568cd..0bf0cf0 100644
--- a/src/Editor/Ghost.DSL/Grammar/GhostShaderLexer.g4
+++ b/src/Editor/Ghost.DSL/Grammar/GhostShaderLexer.g4
@@ -2,7 +2,7 @@ lexer grammar GhostShaderLexer;
// Keywords
SHADER: 'shader';
-PROPERTIES: 'properties';
+COMPUTE: 'compute';
PIPELINE: 'pipeline';
PASS: 'pass';
DEFINES: 'defines';
diff --git a/src/Editor/Ghost.DSL/Grammar/GhostShaderParser.g4 b/src/Editor/Ghost.DSL/Grammar/GhostShaderParser.g4
index 607c834..7a0ecd5 100644
--- a/src/Editor/Ghost.DSL/Grammar/GhostShaderParser.g4
+++ b/src/Editor/Ghost.DSL/Grammar/GhostShaderParser.g4
@@ -13,23 +13,11 @@ shader:
RBRACE;
shaderBody:
- (propertiesBlock | pipelineBlock | passBlock | functionCall)*;
-
-// Properties block
-propertiesBlock:
- PROPERTIES LBRACE
- propertyDeclaration*
- RBRACE;
-
-propertyDeclaration:
- scope? IDENTIFIER IDENTIFIER (EQUALS LBRACE propertyInitializer RBRACE)? SEMICOLON;
+ (pipelineBlock | passBlock | functionCall)*;
scope:
GLOBAL | LOCAL;
-propertyInitializer:
- (NUMBER | IDENTIFIER) (COMMA (NUMBER | IDENTIFIER))*;
-
// Pipeline block
pipelineBlock:
PIPELINE LBRACE
diff --git a/src/Editor/Ghost.DSL/ShaderCompiler/DSLShaderCompiler.cs b/src/Editor/Ghost.DSL/ShaderCompiler/DSLShaderCompiler.cs
index cdf67a3..03e50b8 100644
--- a/src/Editor/Ghost.DSL/ShaderCompiler/DSLShaderCompiler.cs
+++ b/src/Editor/Ghost.DSL/ShaderCompiler/DSLShaderCompiler.cs
@@ -1,6 +1,8 @@
using Ghost.Core;
using Ghost.Core.Graphics;
using Ghost.DSL.ShaderParser;
+using System.IO.Hashing;
+using System.Runtime.InteropServices;
using System.Text;
namespace Ghost.DSL.ShaderCompiler;
@@ -19,12 +21,9 @@ public struct DSLShaderError
internal static class DSLShaderCompiler
{
- private const string _GLOBAL_PROPERTY_FILE_NAME = "GlobalData.g.hlsl";
- private const string _GENERATED_FILE_HEADER = "// Auto-generated shader file. Please do not edit this file directly.";
-
- private static string GetPassUniqueId(DSLShaderSemantics shader, PassSemantic pass)
+ private static ulong GetPassUniqueId(DSLShaderSemantics shader, PassSemantic pass)
{
- return $"{shader.name}_{pass.name}";
+ return XxHash64.HashToUInt64(MemoryMarshal.AsBytes($"{shader.name}_{pass.name}".AsSpan()));
}
private static PipelineState MeragePipeline(PipelineSemantic? semantic, PipelineState parent)
@@ -44,64 +43,22 @@ internal static class DSLShaderCompiler
};
}
- private static int LayoutCBufferProperties(Span properties)
- {
- if (properties.IsEmpty)
- {
- return 0;
- }
-
- var currentOffset = 0;
-
- foreach (ref var prop in properties)
- {
- var size = prop.type.GetSize();
-
- if ((currentOffset % 16) + size > 16)
- {
- currentOffset = (currentOffset + 15) & ~15;
- }
-
- prop.offset = currentOffset;
- prop.size = size;
-
- currentOffset += size;
- }
-
- return (currentOffset + 15) & ~15;
- }
-
// TODO: Implement shader inheritance resolution, including property and pass merging.
// Currently, we just ignore inheritance.
- public static ShaderDescriptor ResolveShader(DSLShaderSemantics semantics)
+ public static Result ResolveShader(DSLShaderSemantics semantics)
{
var descriptor = new ShaderDescriptor
{
name = semantics.name,
- hlsl = semantics.hlsl
};
- var shaderGlobalProperties = semantics.properties?
- .Where(p => p.scope == PropertyScope.Global)
- .Select(p => new PropertyDescriptor
- {
- name = p.name,
- type = p.type,
- defaultValue = p.defaultValue
- }).ToArray();
+ if (!ShaderPropertiesRegistry.TryGetCode(semantics.name, out var info))
+ {
+ info = default;
+ }
- var shaderLocalProperties = semantics.properties?
- .Where(p => p.scope == PropertyScope.Local)
- .Select(p => new PropertyDescriptor
- {
- name = p.name,
- type = p.type,
- defaultValue = p.defaultValue
- }).ToArray();
-
- descriptor.globalProperties = shaderGlobalProperties ?? Array.Empty();
- descriptor.properties = shaderLocalProperties ?? Array.Empty();
- descriptor.cbufferSize = LayoutCBufferProperties(descriptor.properties);
+ descriptor.propertiesCode = info.code ?? string.Empty;
+ descriptor.propertyBufferSize = info.size;
if (semantics.passes != null)
{
@@ -112,6 +69,7 @@ internal static class DSLShaderCompiler
var localPipeline = MeragePipeline(pass.localPipeline, PipelineState.Default);
descriptor.passes[i] = new PassDescriptor
{
+ shader = descriptor,
identifier = GetPassUniqueId(semantics, pass),
name = pass.name,
taskShader = pass.taskShader,
@@ -172,155 +130,17 @@ internal static class DSLShaderCompiler
return Result.Failure("Failed to compile shader due to errors:\n" + errorMessages.ToString());
}
- var desc = ResolveShader(model);
- var globalPropResult = GenerateGlobalProperties(desc.globalProperties, generatedOutputDirectory);
- if (globalPropResult.IsFailure)
+ var result = ResolveShader(model);
+ if (result.IsFailure)
{
- return Result.Failure("Failed to generate global properties: " + globalPropResult.Message);
+ return result;
}
- var generatedResult = GenerateShaderCode(desc, generatedOutputDirectory);
- if (generatedResult.IsFailure)
- {
- return Result.Failure("Failed to generate pass files: " + generatedResult.Message);
- }
-
- foreach (ref var pass in desc.passes.AsSpan())
- {
- if (pass.includes == null)
- {
- pass.includes = new string[2];
- }
- else
- {
- Array.Resize(ref pass.includes, pass.includes.Length + 2);
- // Shift existing includes to make room for the two new includes at the front.
- pass.includes.AsSpan(0, pass.includes.Length - 2).CopyTo(pass.includes.AsSpan(2));
- }
-
- pass.includes[0] = globalPropResult.Value;
- pass.includes[1] = generatedResult.Value;
- }
-
- return desc;
+ return result.Value;
}
catch (Exception ex)
{
return Result.Failure("Failed to compile shader: " + ex.Message);
}
}
-
- private static string ShaderPropertyTypeToHLSLType(ShaderPropertyType type)
- {
- return type switch
- {
- ShaderPropertyType.Float => "float",
- ShaderPropertyType.Float2 => "float2",
- ShaderPropertyType.Float3 => "float3",
- ShaderPropertyType.Float4 => "float4",
- ShaderPropertyType.Int => "int",
- ShaderPropertyType.Int2 => "int2",
- ShaderPropertyType.Int3 => "int3",
- ShaderPropertyType.Int4 => "int4",
- ShaderPropertyType.UInt => "uint",
- ShaderPropertyType.UInt2 => "uint2",
- ShaderPropertyType.UInt3 => "uint3",
- ShaderPropertyType.UInt4 => "uint4",
- ShaderPropertyType.Bool => "bool",
- ShaderPropertyType.Bool2 => "bool2",
- ShaderPropertyType.Bool3 => "bool3",
- ShaderPropertyType.Bool4 => "bool4",
- // NOTE: Textures here are bindless, represented as uint (descriptor index).
- ShaderPropertyType.Texture2D => "TEXTURE2D",
- ShaderPropertyType.Texture3D => "TEXTURE3D",
- ShaderPropertyType.TextureCube => "TEXTURECUBE",
- ShaderPropertyType.Texture2DArray => "TEXTURE2D_ARRAY",
- ShaderPropertyType.TextureCubeArray => "TEXTURECUBE_ARRAY",
- ShaderPropertyType.Sampler => "SAMPLER",
- _ => throw new ArgumentOutOfRangeException(nameof(type), $"Unsupported shader property type: {type}")
- };
- }
-
- public static Result GenerateShaderCode(ShaderDescriptor descriptor, string targetDirectory)
- {
- if (!Directory.Exists(targetDirectory))
- {
- return Result.Failure("Target directory does not exist.");
- }
-
- var outputFileName = descriptor.name.Replace('/', '_');
- var outputFilePath = Path.Combine(targetDirectory, outputFileName + ".g.hlsl");
- var outputDirectory = Path.GetDirectoryName(outputFilePath);
-
- if (!Directory.Exists(outputDirectory))
- {
- Directory.CreateDirectory(outputDirectory!);
- }
-
- using var fileStream = File.CreateText(outputFilePath);
- var fileDefine = outputFileName.Replace('/', '_').ToUpperInvariant() + "_G_HLSL";
-
- var sb = new StringBuilder();
-
- sb.AppendLine(_GENERATED_FILE_HEADER);
- sb.AppendLine(@$"
-#ifndef {fileDefine}
-#define {fileDefine}
-
-#include ""F:/csharp/GhostEngine/src/Runtime//Ghost.Graphics/Shaders/Includes/Common.hlsl""");
-
- sb.Append(@"
-struct PerMaterialData
-{");
- foreach (var prop in descriptor.properties)
- {
- sb.Append($@"
- {ShaderPropertyTypeToHLSLType(prop.type)} {prop.name};");
- }
- sb.Append(@"
-};");
-
- sb.AppendLine();
- sb.AppendLine(@$"
-#endif // {fileDefine}");
-
- fileStream.Write(sb.ToString());
-
- return outputFilePath;
- }
-
- public static Result GenerateGlobalProperties(ReadOnlySpan globalProperties, string targetDirectory)
- {
- if (!Directory.Exists(targetDirectory))
- {
- return Result.Failure("Target directory does not exist.");
- }
-
- var globalFilePath = Path.Combine(targetDirectory, _GLOBAL_PROPERTY_FILE_NAME);
- using var globalFileStream = File.CreateText(globalFilePath);
-
- var sb = new StringBuilder();
-
- sb.AppendLine(_GENERATED_FILE_HEADER);
- sb.Append(@"
-#ifndef GLOBALDATA_G_HLSL
-#define GLOBALDATA_G_HLSL
-
-#include ""F:/csharp/GhostEngine/src/Runtime//Ghost.Graphics/Shaders/Includes/Common.hlsl""
-
-struct GlobalData
-{");
- foreach (var prop in globalProperties)
- {
- sb.Append($@"
-{ShaderPropertyTypeToHLSLType(prop.type)} {prop.name};");
- }
- sb.AppendLine(@"
-};
-
-#endif // GLOBALDATA_G_HLSL");
- globalFileStream.Write(sb.ToString());
-
- return globalFilePath;
- }
}
diff --git a/src/Editor/Ghost.DSL/ShaderCompiler/DSLShaderSemantics.cs b/src/Editor/Ghost.DSL/ShaderCompiler/DSLShaderSemantics.cs
index d9883c0..014f1bd 100644
--- a/src/Editor/Ghost.DSL/ShaderCompiler/DSLShaderSemantics.cs
+++ b/src/Editor/Ghost.DSL/ShaderCompiler/DSLShaderSemantics.cs
@@ -8,14 +8,6 @@ public enum PropertyScope
Local,
}
-public class PropertySemantic
-{
- public PropertyScope scope;
- public ShaderPropertyType type;
- public string name = string.Empty;
- public object? defaultValue;
-}
-
public class PipelineSemantic
{
public ZTest? zTest;
@@ -41,8 +33,6 @@ public class PassSemantic
public class DSLShaderSemantics
{
public string name = string.Empty;
- public string? hlsl;
- public List? properties;
public PipelineSemantic? pipeline;
public List? passes;
}
\ No newline at end of file
diff --git a/src/Editor/Ghost.DSL/ShaderParser/AntlrShaderCompiler.cs b/src/Editor/Ghost.DSL/ShaderParser/AntlrShaderCompiler.cs
index 14141f7..950d5a6 100644
--- a/src/Editor/Ghost.DSL/ShaderParser/AntlrShaderCompiler.cs
+++ b/src/Editor/Ghost.DSL/ShaderParser/AntlrShaderCompiler.cs
@@ -71,7 +71,6 @@ public class AntlrShaderCompiler
var semantics = new DSLShaderSemantics
{
name = model.Name,
- properties = ConvertProperties(model.Properties, errors),
pipeline = ConvertPipeline(model.Pipeline, errors)
};
@@ -88,48 +87,6 @@ public class AntlrShaderCompiler
return semantics;
}
- private static List? ConvertProperties(PropertiesBlockModel? properties, List errors)
- {
- if (properties == null || properties.Properties.Count == 0)
- {
- return null;
- }
-
- var result = new List();
- var usedNames = new HashSet();
-
- foreach (var prop in properties.Properties)
- {
- if (usedNames.Contains(prop.Name))
- {
- errors.Add(new DSLShaderError
- {
- message = $"Duplicate property name '{prop.Name}'.",
- line = 0,
- column = 0
- });
- continue;
- }
-
- var semantic = new PropertySemantic
- {
- name = prop.Name,
- scope = prop.Scope?.ToLower() == "global" ? PropertyScope.Global : PropertyScope.Local,
- type = ParsePropertyType(prop.Type, errors)
- };
-
- if (prop.Initializer.Count > 0)
- {
- semantic.defaultValue = ParsePropertyValue(semantic.type, prop.Initializer, errors);
- }
-
- usedNames.Add(prop.Name);
- result.Add(semantic);
- }
-
- return result;
- }
-
private static ShaderPropertyType ParsePropertyType(string type, List errors)
{
return type.ToLower() switch
diff --git a/src/Editor/Ghost.DSL/ShaderParser/ShaderVisitor.cs b/src/Editor/Ghost.DSL/ShaderParser/ShaderVisitor.cs
index 601c526..5dea490 100644
--- a/src/Editor/Ghost.DSL/ShaderParser/ShaderVisitor.cs
+++ b/src/Editor/Ghost.DSL/ShaderParser/ShaderVisitor.cs
@@ -27,11 +27,6 @@ public class ShaderVisitor : GhostShaderParserBaseVisitor