Refactor and optimize rendering pipeline

- Added `<IsTrimmable>` property in project files for trimming.
- Replaced bindless texture types with non-bindless equivalents.
- Refactored `ShaderDescriptor` and `ShaderPass` for better modularity.
- Introduced `ShaderDescriptorExtensions` for property size calculations.
- Simplified constant buffer handling in `Material.cs`.
- Improved resource management in `D3D12` components.
- Added support for static meshes and optimized resource barriers.
- Refactored shader code generation and property merging in `SDLCompiler`.
- Removed unused or redundant code (e.g., `IncludesBlock` parser).
- Updated comments, documentation, and error handling for clarity.
This commit is contained in:
2025-11-28 18:58:50 +09:00
parent 0720444c2c
commit bd97d233cb
49 changed files with 842 additions and 1025 deletions

View File

@@ -1,59 +0,0 @@
namespace Ghost.SDL.Compiler.Parser;
internal class IncludesBlock : IBlockParser<List<Token>, List<string>>
{
public static bool ShouldEnter(Token token)
{
return token.Match(TokenType.Keyword, TokenLexicon.KnownKeywords.INCLUDES);
}
public static List<Token> Parse(TokenStreamSlice stream)
{
stream.Expect(TokenType.Keyword);
stream.Expect(TokenType.LBrace);
var includes = new List<Token>();
var bodyStream = stream.Slice(stream.Remaining - 1);
while (bodyStream.HasMore)
{
var includeToken = bodyStream.Expect(TokenType.StringLiteral);
includes.Add(includeToken);
bodyStream.Expect(TokenType.Semicolon);
}
stream.Expect(TokenType.RBrace);
return includes;
}
public static List<string>? SemanticAnalysis(List<Token>? syntax, List<SDLError> errors)
{
if (syntax == null || syntax.Count == 0)
{
return null;
}
var includes = new List<string>(syntax.Count);
foreach (var includeToken in syntax)
{
var path = includeToken.lexeme;
if (File.Exists(path))
{
includes.Add(path);
}
else
{
errors.Add(new SDLError
{
message = $"Included file '{path}' not found.",
line = includeToken.line,
column = includeToken.column
});
continue;
}
}
return includes;
}
}

View File

@@ -27,10 +27,6 @@ internal class PassBlock : IBlockParser<PassSyntax, PassSemantic>
{
pass.defines = DefinesBlock.Parse(bodyStream.SliceNextBlock());
}
else if (IncludesBlock.ShouldEnter(nextToken))
{
pass.includes = IncludesBlock.Parse(bodyStream.SliceNextBlock());
}
else if (KeywordsBlock.ShouldEnter(nextToken))
{
pass.keywords = KeywordsBlock.Parse(bodyStream.SliceNextBlock());
@@ -39,10 +35,6 @@ internal class PassBlock : IBlockParser<PassSyntax, PassSemantic>
{
pass.localPipeline = PipelineBlock.Parse(bodyStream.SliceNextBlock());
}
else if (PropertiesBlock.ShouldEnter(nextToken))
{
pass.localProperties = PropertiesBlock.Parse(bodyStream.SliceNextBlock());
}
else if (nextToken.Match(TokenType.Identifier))
{
var func = ParseUtility.ParseFunction(ref bodyStream, TokenType.StringLiteral);
@@ -72,23 +64,10 @@ internal class PassBlock : IBlockParser<PassSyntax, PassSemantic>
{
name = syntax.name.lexeme,
defines = DefinesBlock.SemanticAnalysis(syntax.defines, errors),
includes = IncludesBlock.SemanticAnalysis(syntax.includes, errors),
keywords = KeywordsBlock.SemanticAnalysis(syntax.keywords, errors),
localProperties = PropertiesBlock.SemanticAnalysis(syntax.localProperties, errors),
localPipeline = PipelineBlock.SemanticAnalysis(syntax.localPipeline, errors),
};
if (semantic.localProperties != null
&& semantic.localProperties.Any(p => p.scope == PropertyScope.Global))
{
errors.Add(new SDLError
{
message = "Global properties cannot be declared inside a pass. Move them to the shader properties block.",
line = syntax.name.line,
column = syntax.name.column
});
}
if (syntax.functionCalls != null)
{
foreach (var func in syntax.functionCalls)

View File

@@ -73,9 +73,9 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
ParseBoolValue(syntax[3], errors))),
// Textures (single identifier argument)
[ShaderPropertyType.Texture2DBindless] = new(1, TokenType.Identifier, (syntax, errors) => ParseTextureDefault(syntax[0], errors)),
[ShaderPropertyType.Texture3DBindless] = new(1, TokenType.Identifier, (syntax, errors) => ParseTextureDefault(syntax[0], errors)),
[ShaderPropertyType.TextureCubeBindless] = new(1, TokenType.Identifier, (syntax, errors) => ParseTextureDefault(syntax[0], errors)),
[ShaderPropertyType.Texture2D] = new(1, TokenType.Identifier, (syntax, errors) => ParseTextureDefault(syntax[0], errors)),
[ShaderPropertyType.Texture3D] = new(1, TokenType.Identifier, (syntax, errors) => ParseTextureDefault(syntax[0], errors)),
[ShaderPropertyType.TextureCube] = new(1, TokenType.Identifier, (syntax, errors) => ParseTextureDefault(syntax[0], errors)),
};
private static float ParseFloatValue(Token token, List<SDLError> errors)
@@ -166,6 +166,7 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
TokenLexicon.KnownTypes.FLOAT2 => ShaderPropertyType.Float2,
TokenLexicon.KnownTypes.FLOAT3 => ShaderPropertyType.Float3,
TokenLexicon.KnownTypes.FLOAT4 => ShaderPropertyType.Float4,
TokenLexicon.KnownTypes.FLOAT4X4 => ShaderPropertyType.Float4x4,
TokenLexicon.KnownTypes.INT => ShaderPropertyType.Int,
TokenLexicon.KnownTypes.INT2 => ShaderPropertyType.Int2,
TokenLexicon.KnownTypes.INT3 => ShaderPropertyType.Int3,
@@ -178,9 +179,9 @@ internal class PropertiesBlock : IBlockParser<PropertiesSyntax, List<PropertySem
TokenLexicon.KnownTypes.BOOL2 => ShaderPropertyType.Bool2,
TokenLexicon.KnownTypes.BOOL3 => ShaderPropertyType.Bool3,
TokenLexicon.KnownTypes.BOOL4 => ShaderPropertyType.Bool4,
TokenLexicon.KnownTypes.TEXTURE2D_BINDLESS => ShaderPropertyType.Texture2DBindless,
TokenLexicon.KnownTypes.TEXTURE3D_BINDLESS => ShaderPropertyType.Texture3DBindless,
TokenLexicon.KnownTypes.TEXTURECUBE_BINDLESS => ShaderPropertyType.TextureCubeBindless,
TokenLexicon.KnownTypes.TEXTURE2D => ShaderPropertyType.Texture2D,
TokenLexicon.KnownTypes.TEXTURE3D => ShaderPropertyType.Texture3D,
TokenLexicon.KnownTypes.TEXTURECUBE => ShaderPropertyType.TextureCube,
_ => ShaderPropertyType.None,
};
}

View File

@@ -54,7 +54,7 @@ internal static class SDLCompiler
public static SDLSemantics? SemanticAnalysis(SDLSyntax syntax, out List<SDLError> errors)
{
errors = new();
errors = new List<SDLError>();
if (string.IsNullOrWhiteSpace(syntax.name.lexeme))
{
@@ -145,31 +145,23 @@ internal static class SDLCompiler
};
}
private static List<PropertyDescriptor> MergeProperties(List<PropertySemantic>? semantics, List<PropertyDescriptor>? parent)
private static uint CalculateCBufferSize(List<PropertyDescriptor> properties)
{
var result = new List<PropertyDescriptor>();
if (parent != null)
{
result.AddRange(parent);
}
var currentOffset = 0u;
if (semantics != null)
foreach (var prop in properties)
{
foreach (var prop in semantics)
var size = prop.type.GetSize();
if ((currentOffset % 16) + size > 16)
{
if (prop.scope == PropertyScope.Local)
{
result.Add(new PropertyDescriptor
{
name = prop.name,
type = prop.type,
defaultValue = prop.defaultValue
});
}
currentOffset = (currentOffset + 15u) & ~15u;
}
currentOffset += size;
}
return result.DistinctBy(p => p.name).ToList();
return (currentOffset + 15u) & ~15u;
}
// TODO: Implement shader inheritance resolution, including property and pass merging.
@@ -181,19 +173,23 @@ internal static class SDLCompiler
name = semantics.name
};
var shaderGlobalProperties = semantics.properties?.Where(p => p.scope == PropertyScope.Global).Select(p => new PropertyDescriptor
{
name = p.name,
type = p.type,
defaultValue = p.defaultValue
}).ToList();
var shaderGlobalProperties = semantics.properties?
.Where(p => p.scope == PropertyScope.Global)
.Select(p => new PropertyDescriptor
{
name = p.name,
type = p.type,
defaultValue = p.defaultValue
}).ToList();
var shaderLocalProperties = semantics.properties?.Where(p => p.scope == PropertyScope.Local).Select(p => new PropertyDescriptor
{
name = p.name,
type = p.type,
defaultValue = p.defaultValue
}).ToList();
var shaderLocalProperties = semantics.properties?
.Where(p => p.scope == PropertyScope.Local)
.Select(p => new PropertyDescriptor
{
name = p.name,
type = p.type,
defaultValue = p.defaultValue
}).ToList();
if (shaderGlobalProperties != null)
{
@@ -201,13 +197,18 @@ internal static class SDLCompiler
descriptor.globalProperties.AddRange(shaderGlobalProperties);
}
if (shaderLocalProperties != null)
{
descriptor.properties ??= new List<PropertyDescriptor>();
descriptor.properties.AddRange(shaderLocalProperties);
descriptor.cbufferSize = CalculateCBufferSize(descriptor.properties);
}
if (semantics.passes != null)
{
foreach (var pass in semantics.passes)
{
var localPipeline = MeragePipeline(pass.localPipeline, PipelineDescriptor.Default);
var localProperties = MergeProperties(pass.localProperties, shaderLocalProperties); // TODO: Merge with base shader properties if inheritance is implemented.
var fullPass = new FullPassDescriptor
{
uniqueIdentifier = GetPassUniqueId(semantics, pass),
@@ -217,9 +218,7 @@ internal static class SDLCompiler
pixelShader = pass.pixelShader,
localPipeline = localPipeline,
defines = pass.defines,
includes = pass.includes,
keywords = pass.keywords,
properties = localProperties
};
descriptor.passes.Add(fullPass);
@@ -258,24 +257,14 @@ internal static class SDLCompiler
return Result.Failure("Failed to generate global properties: " + globalPropResult.Message);
}
foreach (var pass in desc.passes)
var generatedResult = GenerateShaderCode(desc, generatedOutputDirectory);
if (generatedResult.IsFailure)
{
if (pass is not FullPassDescriptor fullPass)
{
continue;
}
fullPass.includes ??= new List<string>();
fullPass.includes.Add(globalPropResult.Value);
var generatedResult = GeneratePass(fullPass, generatedOutputDirectory);
if (generatedResult.IsFailure)
{
return Result.Failure("Failed to generate pass files: " + generatedResult.Message);
}
fullPass.generatedCodePath = generatedResult.Value;
return Result.Failure("Failed to generate pass files: " + generatedResult.Message);
}
desc.generatedCodePath = generatedResult.Value;
return desc;
}
catch (Exception ex)
@@ -305,28 +294,23 @@ internal static class SDLCompiler
ShaderPropertyType.Bool3 => "bool3",
ShaderPropertyType.Bool4 => "bool4",
// NOTE: Textures here are bindless, represented as uint (descriptor index).
ShaderPropertyType.Texture2DBindless => "TEXTURE2D_BINDLESS",
ShaderPropertyType.Texture3DBindless => "TEXTURE3D_BINDLESS",
ShaderPropertyType.TextureCubeBindless => "TEXTURECUBE_BINDLESS",
ShaderPropertyType.Texture2DArrayBindless => "TEXTURE2D_ARRAY_BINDLESS",
ShaderPropertyType.TextureCubeArrayBindless => "TEXTURECUBE_ARRAY_BINDLESS",
ShaderPropertyType.Texture2D => "TEXTURE2D_BINDLESS",
ShaderPropertyType.Texture3D => "TEXTURE3D_BINDLESS",
ShaderPropertyType.TextureCube => "TEXTURECUBE_BINDLESS",
ShaderPropertyType.Texture2DArray => "TEXTURE2D_ARRAY_BINDLESS",
ShaderPropertyType.TextureCubeArray => "TEXTURECUBE_ARRAY_BINDLESS",
_ => throw new ArgumentOutOfRangeException(nameof(type), $"Unsupported shader property type: {type}")
};
}
public static Result<string> GeneratePass(IPassDescriptor descriptor, string targetDirectory)
public static Result<string> GenerateShaderCode(ShaderDescriptor descriptor, string targetDirectory)
{
if (descriptor is not FullPassDescriptor fullPass)
{
return Result.Failure("Only full pass descriptors are supported for compilation.");
}
if (!Directory.Exists(targetDirectory))
{
return Result.Failure("Target directory does not exist.");
}
var outputFileName = fullPass.uniqueIdentifier.Replace(' ', '_');
var outputFileName = descriptor.name.Replace('/', '_');
var outputFilePath = Path.Combine(targetDirectory, outputFileName + ".g.hlsl");
var outputDirectory = Path.GetDirectoryName(outputFilePath);
@@ -346,30 +330,17 @@ internal static class SDLCompiler
#define {fileDefine}
#include ""F:/csharp/GhostEngine/Ghost.Shader/BuiltIn/Common.hlsl""");
if (fullPass.includes != null)
{
foreach (var include in fullPass.includes)
{
sb.Append($@"
#include ""{include}""");
}
sb.AppendLine();
}
if (fullPass.properties != null)
{
sb.Append(@"
sb.Append(@"
struct PerMaterialData
{");
foreach (var prop in fullPass.properties)
{
sb.Append($@"
foreach (var prop in descriptor.properties)
{
sb.Append($@"
{ShaderPropertyTypeToHLSLType(prop.type)} {prop.name};");
}
sb.Append(@"
};");
}
sb.Append(@"
};");
sb.AppendLine();
sb.AppendLine(@$"
@@ -414,4 +385,4 @@ struct GlobalData
return globalFilePath;
}
}
}

View File

@@ -32,9 +32,7 @@ internal class PassSemantic
public ShaderEntryPoint meshShader;
public ShaderEntryPoint pixelShader;
public List<string>? defines;
public List<string>? includes;
public List<KeywordsGroup>? keywords;
public List<PropertySemantic>? localProperties;
public PipelineSemantic? localPipeline;
}

View File

@@ -36,9 +36,7 @@ internal class PassSyntax
{
public Token name;
public PipelineSyntax? localPipeline;
public PropertiesSyntax? localProperties;
public List<Token>? defines;
public List<Token>? includes;
public List<FunctionCallDeclaration>? keywords;
public List<FunctionCallDeclaration>? functionCalls;
}

View File

@@ -153,6 +153,7 @@ internal static class TokenLexicon
public const string FLOAT2 = "float2";
public const string FLOAT3 = "float3";
public const string FLOAT4 = "float4";
public const string FLOAT4X4 = "float4x4";
public const string INT = "int";
public const string INT2 = "int2";
@@ -170,11 +171,11 @@ internal static class TokenLexicon
public const string BOOL4 = "bool4";
// Texture types
public const string TEXTURE2D_BINDLESS = "tex2d_b";
public const string TEXTURE2D_ARRAY_BINDLESS = "tex2d_arr_b";
public const string TEXTURE3D_BINDLESS = "tex3d_b";
public const string TEXTURECUBE_BINDLESS = "texcube_b";
public const string TEXTURECUBE_ARRAY_BINDLESS = "texcube_arr_b";
public const string TEXTURE2D = "tex2d";
public const string TEXTURE2D_ARRAY = "tex2d_arr";
public const string TEXTURE3D = "tex3d";
public const string TEXTURECUBE = "texcube";
public const string TEXTURECUBE_ARRAY = "texcube_arr";
}
public static class KnownTextureValue
@@ -211,12 +212,12 @@ internal static class TokenLexicon
private static readonly HashSet<string> s_types = new()
{
KnownTypes.FLOAT, KnownTypes.FLOAT2, KnownTypes.FLOAT3, KnownTypes.FLOAT4,
KnownTypes.FLOAT, KnownTypes.FLOAT2, KnownTypes.FLOAT3, KnownTypes.FLOAT4, KnownTypes.FLOAT4X4,
KnownTypes.INT, KnownTypes.INT2, KnownTypes.INT3, KnownTypes.INT4,
KnownTypes.UINT, KnownTypes.UINT2, KnownTypes.UINT3, KnownTypes.UINT4,
KnownTypes.BOOL, KnownTypes.BOOL2, KnownTypes.BOOL3, KnownTypes.BOOL4,
KnownTypes.TEXTURE2D_BINDLESS, KnownTypes.TEXTURE2D_ARRAY_BINDLESS, KnownTypes.TEXTURE3D_BINDLESS,
KnownTypes.TEXTURECUBE_BINDLESS, KnownTypes.TEXTURECUBE_ARRAY_BINDLESS,
KnownTypes.TEXTURE2D, KnownTypes.TEXTURE2D_ARRAY, KnownTypes.TEXTURE3D,
KnownTypes.TEXTURECUBE, KnownTypes.TEXTURECUBE_ARRAY,
};
private static readonly HashSet<string> s_textureDefaultValues = new()