feat(shader): refactor and enhance shader pipeline
Refactored the shader compilation pipeline to introduce modularity, improve performance, and enhance maintainability. Key changes include: - Added `ShaderCompilationConfig`, `CompilerOptimizeLevel`, and `ShaderStage` enums. - Replaced `SM` property with `ShaderModel` in shader models. - Introduced `ShaderLibrary` for in-memory and disk-based shader caching. - Refactored `DSLShaderCompiler` and `AntlrShaderCompiler` for better hashing and error handling. - Centralized shader compilation logic in `ShaderCompilerUtility`. - Removed legacy shader compilation logic from `IShaderCompiler`. - Updated `RenderGraph`, `ResourceManager`, and `Material` to integrate with the new caching system. - Improved memory management with `NativeMemoryManager<T>`. BREAKING CHANGE: Removed legacy shader compilation methods and replaced them with a new caching and compilation system.
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Core.Graphics;
|
||||
using Ghost.Core.Utilities;
|
||||
using Ghost.DSL.ShaderParser;
|
||||
using Misaki.HighPerformance.Utilities;
|
||||
using System.IO.Hashing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
@@ -22,9 +24,15 @@ public struct DSLShaderError
|
||||
|
||||
internal static class DSLShaderCompiler
|
||||
{
|
||||
private static ulong GetPassUniqueId(DSLShaderSemantics shader, PassSemantic pass)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static ulong GetUniqueId(string code)
|
||||
{
|
||||
return XxHash64.HashToUInt64(MemoryMarshal.AsBytes($"{shader.name}_{pass.name}".AsSpan()));
|
||||
if (string.IsNullOrEmpty(code))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return XxHash64.HashToUInt64(MemoryMarshal.AsBytes(code.AsSpan()));
|
||||
}
|
||||
|
||||
private static PipelineState MeragePipeline(PipelineSemantic? semantic, PipelineState parent)
|
||||
@@ -132,9 +140,13 @@ internal static class DSLShaderCompiler
|
||||
|
||||
var pixelShaderCode = new ShaderCode { code = result.Value, entryPoint = pass.pixelShader.entry };
|
||||
|
||||
var asHash = Hash.Combine64(GetUniqueId(amplificationShaderCode.code), GetUniqueId(amplificationShaderCode.entryPoint));
|
||||
var msHash = Hash.Combine64(GetUniqueId(meshShaderCode.code), GetUniqueId(meshShaderCode.entryPoint));
|
||||
var psHash = Hash.Combine64(GetUniqueId(pixelShaderCode.code), GetUniqueId(pixelShaderCode.entryPoint));
|
||||
|
||||
passes[i] = new PassDescriptor
|
||||
{
|
||||
identifier = GetPassUniqueId(semantics, pass),
|
||||
identifier = Hash.Combine64(GetUniqueId(semantics.name + pass.name), asHash, msHash, psHash),
|
||||
name = pass.name,
|
||||
|
||||
amplificationShaderCode = amplificationShaderCode,
|
||||
@@ -289,7 +301,6 @@ internal static class DSLShaderCompiler
|
||||
|
||||
return new ComputeShaderDescriptor
|
||||
{
|
||||
identifier = XxHash64.HashToUInt64(MemoryMarshal.AsBytes(semantics.name.AsSpan())),
|
||||
name = semantics.name,
|
||||
propertyBufferSize = propertyInfo.size,
|
||||
shaderModel = semantics.shaderModel,
|
||||
|
||||
@@ -99,6 +99,40 @@ public class AntlrShaderCompiler
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryGetShaderModel(string model, List<DSLShaderError> errors, out ShaderModel shaderModel)
|
||||
{
|
||||
if (string.IsNullOrEmpty(model))
|
||||
{
|
||||
shaderModel = ShaderModel.SM_6_6; // Default to lowest supported shader model for compute shaders
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (model)
|
||||
{
|
||||
case "6_6":
|
||||
shaderModel = ShaderModel.SM_6_6;
|
||||
break;
|
||||
case "6_7":
|
||||
shaderModel = ShaderModel.SM_6_7;
|
||||
break;
|
||||
case "6_8":
|
||||
shaderModel = ShaderModel.SM_6_8;
|
||||
break;
|
||||
default:
|
||||
shaderModel = default;
|
||||
errors.Add(new DSLShaderError
|
||||
{
|
||||
message = $"Unknown shader model '{model}'.",
|
||||
line = 0,
|
||||
column = 0
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static DSLComputeShaderSemantics? ConvertToComputeSemantics(ComputeShaderModel model, out List<DSLShaderError> errors)
|
||||
{
|
||||
errors = new List<DSLShaderError>();
|
||||
@@ -122,29 +156,9 @@ public class AntlrShaderCompiler
|
||||
hlsl = model.Hlsl?.Code
|
||||
};
|
||||
|
||||
if (string.IsNullOrEmpty(model.SM))
|
||||
if (TryGetShaderModel(model.ShaderModel, errors, out var shaderModel))
|
||||
{
|
||||
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
|
||||
});
|
||||
}
|
||||
semantics.shaderModel = shaderModel;
|
||||
}
|
||||
|
||||
if (model.Keywords != null)
|
||||
@@ -207,65 +221,6 @@ public class AntlrShaderCompiler
|
||||
return semantics;
|
||||
}
|
||||
|
||||
public static DSLShaderSemantics? ConvertToSemantics(GraphicsShaderModel model, out List<DSLShaderError> errors)
|
||||
{
|
||||
errors = new List<DSLShaderError>();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(model.Name))
|
||||
{
|
||||
errors.Add(new DSLShaderError
|
||||
{
|
||||
message = "Shader name cannot be empty.",
|
||||
line = 0,
|
||||
column = 0
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
var semantics = new DSLShaderSemantics
|
||||
{
|
||||
name = model.Name,
|
||||
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);
|
||||
if (passSemantic != null)
|
||||
{
|
||||
semantics.passes ??= new List<PassSemantic>();
|
||||
semantics.passes.Add(passSemantic);
|
||||
}
|
||||
}
|
||||
|
||||
return semantics;
|
||||
}
|
||||
|
||||
private static PipelineSemantic? ConvertPipeline(PipelineBlockModel? pipeline, List<DSLShaderError> errors)
|
||||
{
|
||||
if (pipeline == null || pipeline.Statements.Count == 0)
|
||||
@@ -394,6 +349,45 @@ public class AntlrShaderCompiler
|
||||
return semantic;
|
||||
}
|
||||
|
||||
public static DSLShaderSemantics? ConvertToSemantics(GraphicsShaderModel model, out List<DSLShaderError> errors)
|
||||
{
|
||||
errors = new List<DSLShaderError>();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(model.Name))
|
||||
{
|
||||
errors.Add(new DSLShaderError
|
||||
{
|
||||
message = "Shader name cannot be empty.",
|
||||
line = 0,
|
||||
column = 0
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
var semantics = new DSLShaderSemantics
|
||||
{
|
||||
name = model.Name,
|
||||
pipeline = ConvertPipeline(model.Pipeline, errors)
|
||||
};
|
||||
|
||||
if (TryGetShaderModel(model.ShaderModel, errors, out var shaderModel))
|
||||
{
|
||||
semantics.shaderModel = shaderModel;
|
||||
}
|
||||
|
||||
foreach (var pass in model.Passes)
|
||||
{
|
||||
var passSemantic = ConvertPass(pass, errors);
|
||||
if (passSemantic != null)
|
||||
{
|
||||
semantics.passes ??= new List<PassSemantic>();
|
||||
semantics.passes.Add(passSemantic);
|
||||
}
|
||||
}
|
||||
|
||||
return semantics;
|
||||
}
|
||||
|
||||
private class ErrorListener : BaseErrorListener, IAntlrErrorListener<int>, IAntlrErrorListener<IToken>
|
||||
{
|
||||
private readonly List<DSLShaderError> _errors;
|
||||
|
||||
@@ -38,7 +38,7 @@ internal class ComputeShaderVisitor : GhostComputeShaderParserBaseVisitor<object
|
||||
var computeBody = context.computeBody();
|
||||
if (computeBody != null)
|
||||
{
|
||||
compute.SM = computeBody.shaderModel()?.GetText() ?? string.Empty;
|
||||
compute.ShaderModel = computeBody.shaderModel()?.GetText() ?? string.Empty;
|
||||
|
||||
foreach (var definesBlock in computeBody.definesBlock())
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@ namespace Ghost.DSL.ShaderParser.Model;
|
||||
public class GraphicsShaderModel
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string SM { get; set; } = string.Empty;
|
||||
public string ShaderModel { get; set; } = string.Empty;
|
||||
public PipelineBlockModel? Pipeline { get; set; }
|
||||
public List<PassBlockModel> Passes { get; set; } = new();
|
||||
public List<FunctionCallModel> FunctionCalls { get; set; } = new();
|
||||
@@ -12,7 +12,7 @@ public class GraphicsShaderModel
|
||||
public class ComputeShaderModel
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string SM { get; set; } = string.Empty;
|
||||
public string ShaderModel { get; set; } = string.Empty;
|
||||
public DefinesBlockModel? Defines { get; set; }
|
||||
public IncludesBlockModel? Includes { get; set; }
|
||||
public KeywordsBlockModel? Keywords { get; set; }
|
||||
|
||||
@@ -27,7 +27,7 @@ public class ShaderVisitor : GhostShaderParserBaseVisitor<object>
|
||||
var shaderBody = context.shaderBody();
|
||||
if (shaderBody != null)
|
||||
{
|
||||
shader.SM = shaderBody.shaderModel()?.GetText() ?? string.Empty;
|
||||
shader.ShaderModel = shaderBody.shaderModel()?.GetText() ?? string.Empty;
|
||||
|
||||
foreach (var pipelineBlock in shaderBody.pipelineBlock())
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user