Refactor D3D12 Resource Management

Refactored and renamed components related to D3D12 graphics programming, replacing "descriptor" with "viewGroup" to improve resource grouping and management. Updated `D3D12CommandBuffer`, `D3D12DescriptorAllocator`, and `D3D12PipelineLibrary` to reflect these changes. Simplified material and shader creation in `D3D12ResourceAllocator`. Enhanced `D3D12ResourceDatabase` with resource naming for debugging and improved management. Refactored `Shader` and `ShaderPass` to use modern C# features and `IResourceReleasable` interface. Introduced `D3D12Utility` for centralized utility methods. Updated `Material` class for efficient buffer creation. Renamed `ShaderCompiler` to `SDLCompiler` with improved error handling. Updated `MeshRenderPass` to use new shader compilation process. Various improvements in error handling, code readability, and utility methods.
This commit is contained in:
2025-10-23 14:42:53 +09:00
parent d2d9f5feb7
commit 28c386b0bb
28 changed files with 393 additions and 306 deletions

View File

@@ -27,7 +27,7 @@ internal class DefinesBlock : IBlockParser<List<Token>, List<string>>
return defines;
}
public static List<string>? SemanticAnalysis(List<Token>? syntax, List<ShaderError> errors)
public static List<string>? SemanticAnalysis(List<Token>? syntax, List<SDLError> errors)
{
if (syntax == null)
{

View File

@@ -4,5 +4,5 @@ internal interface IBlockParser<T, U>
{
public static abstract bool ShouldEnter(Token token);
public static abstract T? Parse(TokenStreamSlice ts);
public static abstract U? SemanticAnalysis(T? syntax, List<ShaderError> errors);
public static abstract U? SemanticAnalysis(T? syntax, List<SDLError> errors);
}

View File

@@ -27,7 +27,7 @@ internal class IncludesBlock : IBlockParser<List<Token>, List<string>>
return includes;
}
public static List<string>? SemanticAnalysis(List<Token>? syntax, List<ShaderError> errors)
public static List<string>? SemanticAnalysis(List<Token>? syntax, List<SDLError> errors)
{
if (syntax == null || syntax.Count == 0)
{
@@ -44,7 +44,7 @@ internal class IncludesBlock : IBlockParser<List<Token>, List<string>>
}
else
{
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = $"Included file '{path}' not found.",
line = includeToken.line,

View File

@@ -30,7 +30,7 @@ internal class KeywordsBlock : IBlockParser<List<FunctionCallDeclaration>, List<
return keywords;
}
public static List<KeywordsGroup>? SemanticAnalysis(List<FunctionCallDeclaration>? syntax, List<ShaderError> errors)
public static List<KeywordsGroup>? SemanticAnalysis(List<FunctionCallDeclaration>? syntax, List<SDLError> errors)
{
if (syntax == null)
{
@@ -42,7 +42,7 @@ internal class KeywordsBlock : IBlockParser<List<FunctionCallDeclaration>, List<
{
if (keyword.arguments == null || keyword.arguments.Count == 0)
{
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = $"Function '{keyword.name.lexeme}' must have at least one argument.",
line = keyword.name.line,
@@ -61,7 +61,7 @@ internal class KeywordsBlock : IBlockParser<List<FunctionCallDeclaration>, List<
group.type = KeywordType.Static;
break;
default:
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = $"Unknown function name '{keyword.name.lexeme}'.",
line = keyword.name.line,

View File

@@ -61,7 +61,7 @@ internal class PassBlock : IBlockParser<PassSyntax, PassSemantic>
return pass;
}
public static PassSemantic? SemanticAnalysis(PassSyntax? syntax, List<ShaderError> errors)
public static PassSemantic? SemanticAnalysis(PassSyntax? syntax, List<SDLError> errors)
{
if (syntax == null)
{
@@ -81,7 +81,7 @@ internal class PassBlock : IBlockParser<PassSyntax, PassSemantic>
if (semantic.localProperties != null
&& semantic.localProperties.Any(p => p.scope == PropertyScope.Global))
{
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = "Global properties cannot be declared inside a pass. Move them to the shader properties block.",
line = syntax.name.line,
@@ -108,7 +108,7 @@ internal class PassBlock : IBlockParser<PassSyntax, PassSemantic>
break;
default:
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = $"Unknown function '{func.name.lexeme}' in pass {syntax.name.lexeme}.",
line = func.name.line,
@@ -123,7 +123,7 @@ internal class PassBlock : IBlockParser<PassSyntax, PassSemantic>
{
// TODO: Inheritance from base pass.
// TODO: Add mesh shader support.
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = $"Pass {syntax.name.lexeme} must contain a mesh shader (ms) and a pixel shader (ps) declaration.",
line = syntax.name.line,
@@ -134,11 +134,11 @@ internal class PassBlock : IBlockParser<PassSyntax, PassSemantic>
return semantic;
}
private static void AnalysisShaderEntry(List<ShaderError> errors, FunctionCallDeclaration func, ref ShaderEntryPoint shaderEntryPoint)
private static void AnalysisShaderEntry(List<SDLError> errors, FunctionCallDeclaration func, ref ShaderEntryPoint shaderEntryPoint)
{
if (func.arguments?.Count != 2)
{
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = "Shader declaration requires exactly two arguments: (shaderPath, entryPoint).",
line = func.name.line,

View File

@@ -38,7 +38,7 @@ internal class PipelineBlock : IBlockParser<PipelineSyntax, PipelineSemantic>
return pipeline;
}
public static PipelineSemantic? SemanticAnalysis(PipelineSyntax? syntax, List<ShaderError> errors)
public static PipelineSemantic? SemanticAnalysis(PipelineSyntax? syntax, List<SDLError> errors)
{
if (syntax == null)
{
@@ -80,7 +80,7 @@ internal class PipelineBlock : IBlockParser<PipelineSyntax, PipelineSemantic>
semantic.zTest = ZTestOptions.Always;
break;
default:
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = $"Invalid ZTest option: {valueDecl.value.lexeme}",
line = valueDecl.value.line,
@@ -100,7 +100,7 @@ internal class PipelineBlock : IBlockParser<PipelineSyntax, PipelineSemantic>
semantic.zWrite = ZWriteOptions.Off;
break;
default:
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = $"Invalid ZWrite option: {valueDecl.value.lexeme}",
line = valueDecl.value.line,
@@ -123,7 +123,7 @@ internal class PipelineBlock : IBlockParser<PipelineSyntax, PipelineSemantic>
semantic.cull = CullOptions.Back;
break;
default:
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = $"Invalid Cull option: {valueDecl.value.lexeme}",
line = valueDecl.value.line,
@@ -152,7 +152,7 @@ internal class PipelineBlock : IBlockParser<PipelineSyntax, PipelineSemantic>
semantic.blend = BlendOptions.PremultipliedAlpha;
break;
default:
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = $"Invalid Blend option: {valueDecl.value.lexeme}",
line = valueDecl.value.line,
@@ -169,7 +169,7 @@ internal class PipelineBlock : IBlockParser<PipelineSyntax, PipelineSemantic>
}
else
{
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = $"Invalid Color Mask value: {valueDecl.value.lexeme}",
line = valueDecl.value.line,

View File

@@ -6,7 +6,7 @@ namespace Ghost.Shader.Compiler.Parser;
internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySemantic>>
{
private delegate object? PropertyValueBuilder(List<Token> tokens, List<ShaderError> errors);
private delegate object? PropertyValueBuilder(List<Token> tokens, List<SDLError> errors);
private sealed record PropTypeInfo(int ArgCount, TokenType ArgTokenType, PropertyValueBuilder? Builder);
@@ -78,11 +78,11 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
[ShaderPropertyType.TextureCube] = new(1, TokenType.Identifier, (syntax, errors) => ParseTextureDefault(syntax[0], errors)),
};
private static float ParseFloatValue(Token token, List<ShaderError> errors)
private static float ParseFloatValue(Token token, List<SDLError> errors)
{
if (!float.TryParse(token.lexeme, CultureInfo.InvariantCulture, out var result))
{
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = $"Failed to parse float value '{token.lexeme}'.",
line = token.line,
@@ -93,11 +93,11 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
return result;
}
private static int ParseIntValue(Token token, List<ShaderError> errors)
private static int ParseIntValue(Token token, List<SDLError> errors)
{
if (!int.TryParse(token.lexeme, CultureInfo.InvariantCulture, out var result))
{
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = $"Failed to parse int value '{token.lexeme}'.",
line = token.line,
@@ -108,11 +108,11 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
return result;
}
private static uint ParseUIntValue(Token token, List<ShaderError> errors)
private static uint ParseUIntValue(Token token, List<SDLError> errors)
{
if (!uint.TryParse(token.lexeme, CultureInfo.InvariantCulture, out var result))
{
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = $"Failed to parse uint value '{token.lexeme}'.",
line = token.line,
@@ -123,11 +123,11 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
return result;
}
private static bool ParseBoolValue(Token token, List<ShaderError> errors)
private static bool ParseBoolValue(Token token, List<SDLError> errors)
{
if (!bool.TryParse(token.lexeme, out var result))
{
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = $"Failed to parse bool value '{token.lexeme}'.",
line = token.line,
@@ -138,11 +138,11 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
return result;
}
private static string ParseTextureDefault(Token token, List<ShaderError> errors)
private static string ParseTextureDefault(Token token, List<SDLError> errors)
{
if (!TokenLexicon.IsTextureDefaultValue(token.lexeme))
{
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = $"Texture default value '{token.lexeme}' is not valid.",
line = token.line,
@@ -242,7 +242,7 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
return syntax;
}
public static List<PropertySemantic>? SemanticAnalysis(PropertiesSyntax? syntax, List<ShaderError> errors)
public static List<PropertySemantic>? SemanticAnalysis(PropertiesSyntax? syntax, List<SDLError> errors)
{
if (syntax == null)
{
@@ -295,11 +295,11 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
return models;
}
private static bool ValidatePropertyType(List<ShaderError> errors, PropertyDeclaration property, PropertySemantic model)
private static bool ValidatePropertyType(List<SDLError> errors, PropertyDeclaration property, PropertySemantic model)
{
if (!TokenLexicon.IsType(property.type.lexeme))
{
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = $"Shader property type '{property.type.lexeme}' is not a valid type.",
line = property.type.line,
@@ -313,11 +313,11 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
return true;
}
private static bool ValidatePropertyName(List<ShaderError> errors, HashSet<string> usedPropertyNames, PropertyDeclaration property, PropertySemantic model)
private static bool ValidatePropertyName(List<SDLError> errors, HashSet<string> usedPropertyNames, PropertyDeclaration property, PropertySemantic model)
{
if (string.IsNullOrWhiteSpace(property.name.lexeme))
{
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = "Shader property has an empty name.",
line = property.name.line,
@@ -328,7 +328,7 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
}
else if (usedPropertyNames.Contains(property.name.lexeme))
{
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = $"Shader property name '{property.name.lexeme}' is duplicated.",
line = property.name.line,
@@ -342,12 +342,12 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
return true;
}
private static bool ValidatePropertyConstructor(List<ShaderError> errors, PropertyDeclaration property, PropertySemantic model)
private static bool ValidatePropertyConstructor(List<SDLError> errors, PropertyDeclaration property, PropertySemantic model)
{
var constructor = property.propertyConstructor;
if (!constructor.HasValue)
{
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = "Shader property constructor is null.",
line = property.name.line,
@@ -360,7 +360,7 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
var constructorValue = constructor.Value;
if (string.IsNullOrWhiteSpace(constructorValue.name.lexeme))
{
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = "Shader property constructor has an empty name.",
line = constructorValue.name.line,
@@ -372,7 +372,7 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
if (constructorValue.name.lexeme != property.type.lexeme)
{
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = $"Shader property constructor name '{constructorValue.name.lexeme}' does not match property type '{property.type.lexeme}'.",
line = constructorValue.name.line,
@@ -384,7 +384,7 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
if (!s_propTypeInfo.TryGetValue(model.type, out var info))
{
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = $"No constructor metadata registered for property type '{model.type}'.",
line = constructorValue.name.line,
@@ -397,7 +397,7 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
// Count check
if (constructorValue.arguments == null)
{
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = "Shader property constructor arguments are null.",
line = constructorValue.name.line,
@@ -409,7 +409,7 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
if (constructorValue.arguments.Count != info.ArgCount)
{
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = $"Shader property constructor for type '{property.type.lexeme}' expects {info.ArgCount} argument(s), but got {constructorValue.arguments.Count}.",
line = constructorValue.name.line,
@@ -426,7 +426,7 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
var arg = constructorValue.arguments[i];
if (!arg.Match(info.ArgTokenType))
{
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = $"Shader property constructor argument {i} expects token kind '{info.ArgTokenType}', but got '{arg.type}'.",
line = arg.line,
@@ -451,7 +451,7 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
}
catch (Exception ex)
{
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = $"Failed to construct default value for property '{property.name.lexeme}': {ex.Message}",
line = constructorValue.name.line,

View File

@@ -49,7 +49,7 @@ internal class ShaderBlock : IBlockParser<ShaderSyntax, ShaderSemantics>
return shader;
}
public static ShaderSemantics? SemanticAnalysis(ShaderSyntax? syntax, List<ShaderError> errors)
public static ShaderSemantics? SemanticAnalysis(ShaderSyntax? syntax, List<SDLError> errors)
{
if (syntax == null)
{
@@ -85,7 +85,7 @@ internal class ShaderBlock : IBlockParser<ShaderSyntax, ShaderSemantics>
case TokenLexicon.KnownFunctions.FALLBACK:
if (func.arguments == null || func.arguments.Count != 1)
{
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = "Fallback declaration requires exactly one arguments: (fallback shader name).",
line = func.name.line,
@@ -98,7 +98,7 @@ internal class ShaderBlock : IBlockParser<ShaderSyntax, ShaderSemantics>
shaderModel.fallback = func.arguments[0].lexeme;
break;
default:
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = $"Unknown function '{func.name.lexeme}' in shader.",
line = func.name.line,

View File

@@ -1,10 +1,11 @@
using Ghost.Core.Graphics;
using Ghost.Core;
using Ghost.Core.Graphics;
using Ghost.Shader.Compiler.Parser;
using System.Text;
namespace Ghost.Shader.Compiler;
public struct ShaderError
public struct SDLError
{
public string message;
public int line;
@@ -16,7 +17,7 @@ public struct ShaderError
}
}
internal static class ShaderCompiler
internal static class SDLCompiler
{
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.";
@@ -51,13 +52,13 @@ internal static class ShaderCompiler
return shaders;
}
public static ShaderSemantics? SemanticAnalysis(ShaderSyntax syntax, out List<ShaderError> errors)
public static ShaderSemantics? SemanticAnalysis(ShaderSyntax syntax, out List<SDLError> errors)
{
errors = new();
if (string.IsNullOrWhiteSpace(syntax.name.lexeme))
{
errors.Add(new ShaderError
errors.Add(new SDLError
{
message = "Shader name cannot be empty.",
line = syntax.name.line,
@@ -244,6 +245,29 @@ internal static class ShaderCompiler
return descriptor;
}
public static Result<ShaderDescriptor> CompileShader(string shaderPath)
{
var source = File.ReadAllText(shaderPath);
var lexer = new Lexer(source);
var stream = new TokenStream(lexer.Tokenize());
var shaderInfo = ParseShaders(stream);
var model = SemanticAnalysis(shaderInfo[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<ShaderDescriptor>.Fail("Failed to compile shader due to errors:\n" + errorMessages.ToString());
}
return ResolveShader(model);
}
private static string ShaderPropertyTypeToHLSLType(ShaderPropertyType type)
{
return type switch
@@ -274,7 +298,7 @@ internal static class ShaderCompiler
};
}
public static string CompilePass(IPassDescriptor descriptor, string targetDirectory)
public static string GeneratePass(IPassDescriptor descriptor, string targetDirectory)
{
if (descriptor is not FullPassDescriptor fullPass)
{
@@ -330,7 +354,7 @@ struct PerMaterialData
return outputFilePath;
}
public static void CompileShader(ShaderDescriptor descriptor, string targetDirectory)
public static void GenerateShader(ShaderDescriptor descriptor, string targetDirectory)
{
if (!Directory.Exists(targetDirectory))
{
@@ -369,7 +393,7 @@ struct GlobalData
// Compile each pass.
foreach (var pass in descriptor.passes)
{
CompilePass(pass, targetDirectory);
GeneratePass(pass, targetDirectory);
}
}
}