forked from Misaki/GhostEngine
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:
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user