forked from Misaki/GhostEngine
Refactor render graph & DSL; remove material system
- Major optimization of Ghost.RenderGraph.Concept: pooled resources, zero-allocation hot paths, explicit queue types, and batch barrier APIs. - Migrated Ghost.DSL shader compiler to ANTLR4-based parser; removed hand-written parser, added grammar files and semantic model conversion. - Added CollectionPool/ListPool for pooled list management. - Updated documentation for new architecture and performance. - Removed Ghost.Shader.Concept (material/material system) from repo and solution. - README.md replaced with a brief project statement.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Core.Graphics;
|
||||
using Ghost.DSL.ShaderCompiler.Parser;
|
||||
using Ghost.DSL.ShaderParser;
|
||||
using System.Text;
|
||||
|
||||
namespace Ghost.DSL.ShaderCompiler;
|
||||
@@ -22,107 +22,6 @@ 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 struct ShaderInheritance
|
||||
// {
|
||||
// public DSLShaderSemantics? parent;
|
||||
// public List<ShaderInheritance>? children;
|
||||
// }
|
||||
|
||||
public static List<DSLShaderSyntax> ParseShaders(TokenStream stream)
|
||||
{
|
||||
var shaders = new List<DSLShaderSyntax>();
|
||||
|
||||
while (stream.TryPeek(out var nextToken))
|
||||
{
|
||||
if (ShaderBlock.ShouldEnter(nextToken))
|
||||
{
|
||||
var shader = ShaderBlock.Parse(stream.SliceNextBlock());
|
||||
shaders.Add(shader);
|
||||
}
|
||||
else if (nextToken.Match(TokenType.EndOfFile))
|
||||
{
|
||||
stream.Consume();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Unexpected token '{nextToken.lexeme}' at top level. Expected 'shader' declaration.");
|
||||
}
|
||||
}
|
||||
|
||||
return shaders;
|
||||
}
|
||||
|
||||
public static DSLShaderSemantics? SemanticAnalysis(DSLShaderSyntax syntax, out List<DSLShaderError> errors)
|
||||
{
|
||||
errors = new List<DSLShaderError>();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(syntax.name.lexeme))
|
||||
{
|
||||
errors.Add(new DSLShaderError
|
||||
{
|
||||
message = "Shader name cannot be empty.",
|
||||
line = syntax.name.line,
|
||||
column = syntax.name.column
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
var shaderModel = ShaderBlock.SemanticAnalysis(syntax, errors);
|
||||
return shaderModel;
|
||||
}
|
||||
|
||||
private static List<DSLShaderSemantics>? TopologicalSort(ReadOnlySpan<DSLShaderSemantics> semantics)
|
||||
{
|
||||
var inDegrees = new Dictionary<string, int>();
|
||||
var childrenMap = new Dictionary<string, List<string>>();
|
||||
var semanticsMap = new Dictionary<string, DSLShaderSemantics>();
|
||||
|
||||
foreach (var s in semantics)
|
||||
{
|
||||
inDegrees[s.name] = 0;
|
||||
childrenMap[s.name] = new List<string>();
|
||||
semanticsMap[s.name] = s;
|
||||
}
|
||||
|
||||
foreach (var s in semantics)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(s.fallback) && semanticsMap.ContainsKey(s.fallback))
|
||||
{
|
||||
childrenMap[s.fallback].Add(s.name);
|
||||
inDegrees[s.name]++;
|
||||
}
|
||||
}
|
||||
|
||||
var queue = new Queue<DSLShaderSemantics>();
|
||||
foreach (var s in semantics)
|
||||
{
|
||||
if (inDegrees[s.name] == 0)
|
||||
{
|
||||
queue.Enqueue(s);
|
||||
}
|
||||
}
|
||||
|
||||
var sortedList = new List<DSLShaderSemantics>();
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
var current = queue.Dequeue();
|
||||
sortedList.Add(current);
|
||||
|
||||
foreach (var childName in childrenMap[current.name])
|
||||
{
|
||||
inDegrees[childName]--;
|
||||
if (inDegrees[childName] == 0)
|
||||
{
|
||||
queue.Enqueue(semanticsMap[childName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there's a cycle, the graph will not be fully traversed.
|
||||
return sortedList.Count == semantics.Length ? sortedList : null;
|
||||
}
|
||||
|
||||
private static string GetPassUniqueId(DSLShaderSemantics shader, PassSemantic pass)
|
||||
{
|
||||
return $"{shader.name}_{pass.name}";
|
||||
@@ -175,7 +74,8 @@ internal static class DSLShaderCompiler
|
||||
{
|
||||
var descriptor = new ShaderDescriptor
|
||||
{
|
||||
name = semantics.name
|
||||
name = semantics.name,
|
||||
hlsl = semantics.hlsl
|
||||
};
|
||||
|
||||
var shaderGlobalProperties = semantics.properties?
|
||||
@@ -217,7 +117,8 @@ internal static class DSLShaderCompiler
|
||||
localPipeline = localPipeline,
|
||||
defines = pass.defines?.ToArray() ?? Array.Empty<string>(),
|
||||
includes = pass.includes?.ToArray() ?? Array.Empty<string>(),
|
||||
keywords = pass.keywords?.ToArray() ?? Array.Empty<KeywordsGroup>()
|
||||
keywords = pass.keywords?.ToArray() ?? Array.Empty<KeywordsGroup>(),
|
||||
hlsl = pass.hlsl
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -235,15 +136,27 @@ internal static class DSLShaderCompiler
|
||||
{
|
||||
var source = File.ReadAllText(shaderPath);
|
||||
|
||||
var lexer = new Lexer(source);
|
||||
var stream = new TokenStream(lexer.Tokenize());
|
||||
var shaderInfo = ParseShaders(stream);
|
||||
if (shaderInfo.Count == 0)
|
||||
// Use ANTLR4 parser
|
||||
var shaderModels = AntlrShaderCompiler.ParseShaders(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 shader due to errors:\n" + errorMessages.ToString());
|
||||
}
|
||||
|
||||
if (shaderModels.Count == 0)
|
||||
{
|
||||
return Result.Failure("No shader found in the provided file.");
|
||||
}
|
||||
|
||||
var model = SemanticAnalysis(shaderInfo[0], out var errors);
|
||||
// Convert to semantics
|
||||
var model = AntlrShaderCompiler.ConvertToSemantics(shaderModels[0], out var errors);
|
||||
|
||||
if (errors.Count != 0 || model == null)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user