mirror of
https://github.com/aharabada/glitchy-engine-beef.git
synced 2026-09-05 21:01:52 +00:00
Handle EngineBuffers, Preview Names and variable editor types for materials
This commit is contained in:
@@ -0,0 +1,331 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using GlitchyEngine;
|
||||
using GlitchyEngine.Math;
|
||||
|
||||
namespace GlitchyEditor.Assets.Processors;
|
||||
|
||||
static class ShaderCodePreprocessor
|
||||
{
|
||||
public static Result<void> ProcessFileContent(String fileContent, String outVsName, String outPsName,
|
||||
Dictionary<StringView, ShaderVariable> outVarDescs,
|
||||
List<String> outBufferNames, Dictionary<StringView, StringView> outEngineBuffers)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
Dictionary<StringView, StringView> arguments = scope .();
|
||||
|
||||
int index = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if ((int Start, int End) value = GetNextPreprocessor(fileContent, index, let name, arguments..Clear()))
|
||||
{
|
||||
index = value.End;
|
||||
|
||||
switch(name)
|
||||
{
|
||||
case "Effect":
|
||||
for (let (argName, argValue) in arguments)
|
||||
{
|
||||
switch(argName)
|
||||
{
|
||||
case "VS", "VertexShader":
|
||||
outVsName.Append(argValue);
|
||||
case "PS", "PixelShader":
|
||||
outPsName.Append(argValue);
|
||||
default:
|
||||
Log.EngineLogger.Error($"Unknown parameter name \"{name}\".");
|
||||
return .Err;
|
||||
}
|
||||
}
|
||||
case "EditorVariable":
|
||||
Try!(ProcessEditorVariables(arguments, outVarDescs));
|
||||
case "EngineBuffer":
|
||||
Try!(ProcessEngineBuffer(arguments, outBufferNames, outEngineBuffers));
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
CommentLine(fileContent, value.Start);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
private static void CommentLine(StringView code, int commentPosition)
|
||||
{
|
||||
code[commentPosition] = '/';
|
||||
code[commentPosition + 1] = '/';
|
||||
}
|
||||
|
||||
private static Result<(int Start, int End)> GetNextPreprocessor(StringView code, int startindex, out StringView name, Dictionary<StringView, StringView> arguments)
|
||||
{
|
||||
var startindex;
|
||||
|
||||
name = .();
|
||||
|
||||
int startOfLine = -1;
|
||||
int startOfPragma = -1;
|
||||
int endOfLine = startindex;
|
||||
do
|
||||
{
|
||||
repeat
|
||||
{
|
||||
startindex = endOfLine;
|
||||
|
||||
startOfPragma = code.IndexOf("#pragma", startindex);
|
||||
|
||||
if (startOfPragma == -1)
|
||||
return .Err;
|
||||
|
||||
endOfLine = code.IndexOf('\n', startOfPragma);
|
||||
|
||||
startOfLine = code[...startOfPragma].LastIndexOf('\n');
|
||||
|
||||
// If the line is commented out, look for the next
|
||||
} while (startOfLine != -1 && code[startOfLine...startOfPragma].Contains("//"));
|
||||
|
||||
StringView line = (endOfLine != -1) ? code.Substring(startOfPragma, endOfLine - startOfPragma) : code.Substring(startOfPragma);
|
||||
|
||||
// cut off the #pragma
|
||||
line = line.Substring(7);
|
||||
|
||||
int lBracketIndex = line.IndexOf('[');
|
||||
|
||||
if (lBracketIndex == -1)
|
||||
{
|
||||
name = line..Trim();
|
||||
break;
|
||||
}
|
||||
|
||||
name = line.Substring(0, lBracketIndex);
|
||||
name.Trim();
|
||||
|
||||
int rBracketIndex = line.IndexOf(']');
|
||||
|
||||
if (rBracketIndex == -1)
|
||||
{
|
||||
Log.EngineLogger.Error($"Pragma is missing closing Bracket (\"{line}\")");
|
||||
rBracketIndex = line.Length;
|
||||
}
|
||||
|
||||
StringView argumentText = line.Substring(lBracketIndex + 1, rBracketIndex - lBracketIndex - 1);
|
||||
|
||||
for (StringView argument in argumentText.Split(';'))
|
||||
{
|
||||
int equalsIndex = argument.IndexOf('=');
|
||||
|
||||
StringView argumentName = .();
|
||||
StringView argumentValue = .();
|
||||
|
||||
if (equalsIndex == -1)
|
||||
{
|
||||
argumentName = argument;
|
||||
argumentName.Trim();
|
||||
}
|
||||
else
|
||||
{
|
||||
argumentName = argument.Substring(0, equalsIndex);
|
||||
argumentName.Trim();
|
||||
|
||||
argumentValue = argument.Substring(equalsIndex + 1);
|
||||
argumentValue.Trim();
|
||||
}
|
||||
|
||||
if (arguments.ContainsKey(argumentName))
|
||||
{
|
||||
Log.EngineLogger.Error($"Arguments \"{argumentName}\" already exists.");
|
||||
continue;
|
||||
}
|
||||
|
||||
arguments.Add(argumentName, argumentValue);
|
||||
}
|
||||
}
|
||||
|
||||
return .Ok((startOfPragma, endOfLine));
|
||||
}
|
||||
|
||||
private static Result<void> ProcessEngineBuffer(Dictionary<StringView, StringView> arguments,
|
||||
List<String> outBufferNames, Dictionary<StringView, StringView> outEngineBuffers)
|
||||
{
|
||||
String nameInEngine = null;
|
||||
String nameInShader = null;
|
||||
|
||||
defer
|
||||
{
|
||||
if (@return case .Err)
|
||||
{
|
||||
delete nameInEngine;
|
||||
delete nameInShader;
|
||||
}
|
||||
}
|
||||
|
||||
for (var (argName, argValue) in arguments)
|
||||
{
|
||||
if (argValue.StartsWith('"') && argValue.EndsWith('"'))
|
||||
{
|
||||
argValue = argValue[1...^2];
|
||||
}
|
||||
switch (argName)
|
||||
{
|
||||
case "Name":
|
||||
nameInShader = new String(argValue);
|
||||
case "Binding":
|
||||
nameInEngine = new String(argValue);
|
||||
default:
|
||||
Log.EngineLogger.Error($"Unknown parameter for EngineBuffer: \"{argName}\".");
|
||||
return .Err;
|
||||
}
|
||||
}
|
||||
|
||||
if (String.IsNullOrWhiteSpace(nameInEngine) || String.IsNullOrWhiteSpace(nameInShader))
|
||||
{
|
||||
Log.EngineLogger.Error($"Name and Binding need to be defined.");
|
||||
return .Err;
|
||||
}
|
||||
|
||||
outBufferNames.Add(nameInShader);
|
||||
outBufferNames.Add(nameInEngine);
|
||||
outEngineBuffers.Add(nameInShader, nameInEngine);
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
private static Result<void> ProcessEditorVariables(Dictionary<StringView, StringView> arguments, Dictionary<StringView, ShaderVariable> outVarDescs)
|
||||
{
|
||||
ShaderVariable variable = new .();
|
||||
|
||||
defer
|
||||
{
|
||||
if (@return case .Err)
|
||||
delete variable;
|
||||
}
|
||||
|
||||
for (var (name, value) in arguments)
|
||||
{
|
||||
if (value.StartsWith('"') && value.EndsWith('"'))
|
||||
{
|
||||
value = value[1...^2];
|
||||
}
|
||||
|
||||
switch(name)
|
||||
{
|
||||
case "Name":
|
||||
variable.Name = value;
|
||||
case "Preview":
|
||||
variable.PreviewName = value;
|
||||
case "Type":
|
||||
variable.EditorType = value;
|
||||
case "Min":
|
||||
variable.MinValue = Try!(ParseVariableValue(value));
|
||||
case "Max":
|
||||
variable.MaxValue = Try!(ParseVariableValue(value));
|
||||
default:
|
||||
Variant paramValue = Variant.Create(new String(value), true);
|
||||
variable.AddParameter(name, paramValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (variable.Name.IsWhiteSpace)
|
||||
{
|
||||
Log.EngineLogger.Error("Failed to process shader: Missing argument \"Name\" int variable description.");
|
||||
return .Err;
|
||||
}
|
||||
|
||||
outVarDescs.Add(variable.Name, variable);
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
private static Result<Variant> ParseVariableValue(StringView valueString)
|
||||
{
|
||||
// TODO: Simply always use double?
|
||||
|
||||
if (valueString[0].IsDigit || valueString[0] == '-')
|
||||
{
|
||||
var valueString;
|
||||
|
||||
if (valueString.EndsWith('f'))
|
||||
valueString.Length--;
|
||||
|
||||
float value = Try!(float.Parse(valueString));
|
||||
|
||||
return Variant.Create(value);
|
||||
}
|
||||
else if (valueString.StartsWith("float"))
|
||||
{
|
||||
int index = 5;
|
||||
|
||||
int numComponents = valueString[index++] - '0';
|
||||
|
||||
while (valueString[index] != '(')
|
||||
{
|
||||
if (!valueString[index].IsWhiteSpace)
|
||||
{
|
||||
Log.EngineLogger.Error("Failed to process shader: Expected '('.");
|
||||
return .Err;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
if (numComponents < 2 || numComponents > 4)
|
||||
{
|
||||
Log.EngineLogger.Error($"Failed to process shader: Unsupported component count {numComponents}. Value must be between 2 and 4.");
|
||||
return .Err;
|
||||
}
|
||||
|
||||
float[] floats = scope float[numComponents];
|
||||
|
||||
for (int i < numComponents)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
char8 c = valueString[++index];
|
||||
|
||||
if (c.IsDigit || c == '.' || c == '-')
|
||||
break;
|
||||
}
|
||||
|
||||
int start = index;
|
||||
|
||||
while (true)
|
||||
{
|
||||
char8 c = valueString[++index];
|
||||
|
||||
if (!c.IsDigit && c != '.')
|
||||
break;
|
||||
}
|
||||
|
||||
int end = index;
|
||||
|
||||
StringView numberView = .(valueString, start, end - start);
|
||||
|
||||
var result = float.Parse(numberView);
|
||||
|
||||
if (result case .Ok(let value))
|
||||
{
|
||||
floats[i] = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (numComponents == 2)
|
||||
return Variant.Create(*(float2*)floats.Ptr);
|
||||
else if (numComponents == 3)
|
||||
return Variant.Create(*(float3*)floats.Ptr);
|
||||
else
|
||||
return Variant.Create(*(float4*)floats.Ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.EngineLogger.Error($"Failed to process shader: Unsupported variable value: \"{valueString}\".");
|
||||
return .Err;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -109,6 +109,8 @@ class ReflectedTexture
|
||||
class ReflectedConstantBufferVariable
|
||||
{
|
||||
private String _name ~ delete _;
|
||||
private String _previewName ~ delete _;
|
||||
private String _editorType ~ delete _;
|
||||
|
||||
public StringView Name
|
||||
{
|
||||
@@ -116,6 +118,18 @@ class ReflectedConstantBufferVariable
|
||||
set => String.NewOrSet!(_name, value);
|
||||
}
|
||||
|
||||
public StringView PreviewName
|
||||
{
|
||||
get => _previewName;
|
||||
set => String.NewOrSet!(_previewName, value);
|
||||
}
|
||||
|
||||
public StringView EditorType
|
||||
{
|
||||
get => _editorType;
|
||||
set => String.NewOrSet!(_editorType, value);
|
||||
}
|
||||
|
||||
public int Offset;
|
||||
public int SizeInBytes;
|
||||
public bool IsUsed;
|
||||
@@ -125,6 +139,9 @@ class ReflectedConstantBufferVariable
|
||||
public int Columns;
|
||||
|
||||
public int ArraySize;
|
||||
|
||||
public Variant MinValue;
|
||||
public Variant MaxValue;
|
||||
}
|
||||
|
||||
// TODO: Dx11
|
||||
@@ -146,30 +163,43 @@ class ShaderCompiler
|
||||
|
||||
CompiledShader shader = new CompiledShader();
|
||||
|
||||
Try!(PlatformCompileShaderFromSource(code, assetIdentifier, entryPoint, compileTarget, defines, shader));
|
||||
ShaderCompilationContext context = scope .();
|
||||
|
||||
Try!(PlatformReflectShader(shader));
|
||||
Try!(PlatformCompileShaderFromSource(code, assetIdentifier, entryPoint, compileTarget, defines, shader, context));
|
||||
|
||||
Try!(PlatformReflectShader(shader, context));
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
protected static extern Result<void> PlatformCompileShaderFromSource(StringView code, AssetIdentifier fileName, StringView entryPoint, StringView compileTarget, Span<ShaderDefineValue> defines, CompiledShader outShader);
|
||||
protected static extern Result<void> PlatformCompileShaderFromSource(StringView code, AssetIdentifier fileName, StringView entryPoint, StringView compileTarget, Span<ShaderDefineValue> defines, CompiledShader outShader, ShaderCompilationContext context);
|
||||
|
||||
protected static extern Result<void> PlatformReflectShader(CompiledShader shader);
|
||||
protected static extern Result<void> PlatformReflectShader(CompiledShader shader, ShaderCompilationContext context);
|
||||
}
|
||||
|
||||
class ShaderCompilationContext
|
||||
{
|
||||
public append String VsName = String();
|
||||
public append String PsName = String();
|
||||
|
||||
public append Dictionary<StringView, ShaderVariable> Variables = .() ~ ClearDictionaryAndDeleteValues!(_);
|
||||
public append List<String> BufferNames = .() ~ ClearAndDeleteItems!(_);
|
||||
// Name in Shader -> Name in Engine
|
||||
public append Dictionary<StringView, StringView> EngineBuffers = .();
|
||||
}
|
||||
|
||||
// TODO: Dx11
|
||||
extension ShaderCompiler
|
||||
{
|
||||
// TODO: This should be a setting per shader, really!
|
||||
protected const ShaderCompileFlags DefaultCompileFlags = .EnableStrictness |
|
||||
protected const ShaderCompileFlags DefaultCompileFlags = .EnableStrictness |
|
||||
#if DEBUG
|
||||
.Debug;
|
||||
#else
|
||||
.OptimizationLevel3;
|
||||
#endif
|
||||
|
||||
protected static override Result<void> PlatformCompileShaderFromSource(StringView code, AssetIdentifier fileName, StringView entryPoint, StringView compileTarget, Span<ShaderDefineValue> defines, CompiledShader outShader)
|
||||
protected static override Result<void> PlatformCompileShaderFromSource(StringView code, AssetIdentifier fileName, StringView entryPoint, StringView compileTarget, Span<ShaderDefineValue> defines, CompiledShader outShader, ShaderCompilationContext context)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
@@ -192,7 +222,11 @@ extension ShaderCompiler
|
||||
|
||||
Path.GetDirectoryPath(fileName, directory);
|
||||
|
||||
using (let includer = Includer(directory))
|
||||
String shaderCode = new:ScopedAlloc! String(code);
|
||||
|
||||
Try!(ShaderCodePreprocessor.ProcessFileContent(shaderCode, context.VsName, context.PsName, context.Variables, context.BufferNames, context.EngineBuffers));
|
||||
|
||||
using (let includer = PreprocessingIncluder(directory, context))
|
||||
{
|
||||
var result = D3DCompiler.D3DCompile(code.Ptr, (.)code.Length, fileName.FullIdentifier.ToScopeCStr!(), nativeMacros, &includer, entryPoint.ToScopeCStr!(),
|
||||
compileTarget.ToScopeCStr!(), DefaultCompileFlags /* Pass down compile flags? */, .None, &outShader._shaderBlob, &errorBlob);
|
||||
@@ -210,7 +244,7 @@ extension ShaderCompiler
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
protected override static Result<void> PlatformReflectShader(CompiledShader shader)
|
||||
protected override static Result<void> PlatformReflectShader(CompiledShader shader, ShaderCompilationContext context)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
@@ -253,7 +287,7 @@ extension ShaderCompiler
|
||||
// ConstantBuffer
|
||||
if(bufferDesc.Type == .D3D11_CT_CBUFFER)
|
||||
{
|
||||
ReflectedConstantBuffer cbuffer = Try!(ReflectConstantBuffer(bindDesc, bufferReflection));
|
||||
ReflectedConstantBuffer cbuffer = Try!(ReflectConstantBuffer(bindDesc, bufferReflection, context));
|
||||
shader.AddConstantBuffer(cbuffer);
|
||||
}
|
||||
case .Texture:
|
||||
@@ -290,7 +324,7 @@ extension ShaderCompiler
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
private static Result<ReflectedConstantBuffer> ReflectConstantBuffer(ShaderInputBindDescription bindDesc, ID3D11ShaderReflectionConstantBuffer* bufferReflection)
|
||||
private static Result<ReflectedConstantBuffer> ReflectConstantBuffer(ShaderInputBindDescription bindDesc, ID3D11ShaderReflectionConstantBuffer* bufferReflection, ShaderCompilationContext context)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
@@ -310,17 +344,22 @@ extension ShaderCompiler
|
||||
{
|
||||
ID3D11ShaderReflectionVariable* variableReflection = bufferReflection.GetVariableByIndex(v);
|
||||
|
||||
if (ReflectConstantBufferVariable(buffer, variableReflection) case .Err)
|
||||
if (ReflectConstantBufferVariable(buffer, variableReflection, context) case .Err)
|
||||
{
|
||||
delete buffer;
|
||||
return .Err;
|
||||
}
|
||||
}
|
||||
|
||||
if (context.EngineBuffers.TryGetValue(buffer.Name, let engineBufferName))
|
||||
{
|
||||
buffer.EngineBufferName = engineBufferName;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private static Result<void> ReflectConstantBufferVariable(ReflectedConstantBuffer buffer, ID3D11ShaderReflectionVariable* variableReflection)
|
||||
private static Result<void> ReflectConstantBufferVariable(ReflectedConstantBuffer buffer, ID3D11ShaderReflectionVariable* variableReflection, ShaderCompilationContext context)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
@@ -375,13 +414,23 @@ extension ShaderCompiler
|
||||
Internal.MemCpy(&buffer.RawData[variable.Offset], variableDescription.DefaultValue, variable.SizeInBytes);
|
||||
}
|
||||
|
||||
if (context.Variables.TryGetValue(variable.Name, let variableAnnotation))
|
||||
{
|
||||
variable.PreviewName = variableAnnotation.PreviewName;
|
||||
variable.EditorType = variableAnnotation.EditorType;
|
||||
|
||||
// TODO: We need to somehow validate against our variable type
|
||||
variable.MinValue = variableAnnotation.MinValue;
|
||||
variable.MaxValue = variableAnnotation.MaxValue;
|
||||
}
|
||||
|
||||
buffer.AddVariable(variable);
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
}
|
||||
|
||||
struct Includer : ID3DInclude, IDisposable
|
||||
struct PreprocessingIncluder : ID3DInclude, IDisposable
|
||||
{
|
||||
private VTable _vTable;
|
||||
|
||||
@@ -389,7 +438,9 @@ struct Includer : ID3DInclude, IDisposable
|
||||
|
||||
private String _parentFileDirectory;
|
||||
|
||||
public this(String parentFileDirectory)
|
||||
private ShaderCompilationContext _context;
|
||||
|
||||
public this(String parentFileDirectory, ShaderCompilationContext context)
|
||||
{
|
||||
_parentFileDirectory = parentFileDirectory;
|
||||
_loadedFiles = new Dictionary<StringView, (String FileName, String FileContent)>();
|
||||
@@ -398,6 +449,8 @@ struct Includer : ID3DInclude, IDisposable
|
||||
_vTable.Close = => Close;
|
||||
|
||||
_vt = &_vTable;
|
||||
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@@ -413,7 +466,7 @@ struct Includer : ID3DInclude, IDisposable
|
||||
|
||||
public static HResult Open(ID3DInclude* self, IncludeType includeType, char8* fileNamePtr, void* parentData, void** data, uint32* bytes)
|
||||
{
|
||||
Includer* includer = (.)self;
|
||||
PreprocessingIncluder* includer = (.)self;
|
||||
|
||||
StringView fileName = StringView(fileNamePtr);
|
||||
|
||||
@@ -424,7 +477,7 @@ struct Includer : ID3DInclude, IDisposable
|
||||
|
||||
return .S_OK;
|
||||
}
|
||||
|
||||
|
||||
String fullPath = scope .();
|
||||
|
||||
Path.Combine(fullPath, includer._parentFileDirectory, fileName);
|
||||
@@ -453,6 +506,7 @@ struct Includer : ID3DInclude, IDisposable
|
||||
|
||||
if (fileStream == null)
|
||||
{
|
||||
return .E_FILENOTFOUND;
|
||||
}
|
||||
|
||||
String fileContent = new String();
|
||||
@@ -469,6 +523,13 @@ struct Includer : ID3DInclude, IDisposable
|
||||
|
||||
delete fileStream;
|
||||
|
||||
String trashbin = scope String();
|
||||
|
||||
if (ShaderCodePreprocessor.ProcessFileContent(fileContent, trashbin, trashbin, includer._context.Variables, includer._context.BufferNames, includer._context.EngineBuffers) case .Err)
|
||||
{
|
||||
return .E_FAIL;
|
||||
}
|
||||
|
||||
*data = (void*)fileContent.Ptr;
|
||||
*bytes = (uint32)fileContent.Length;
|
||||
|
||||
@@ -477,7 +538,7 @@ struct Includer : ID3DInclude, IDisposable
|
||||
|
||||
public static HResult Close(ID3DInclude* self, void** data)
|
||||
{
|
||||
Includer* includer = (.)self;
|
||||
PreprocessingIncluder* includer = (.)self;
|
||||
|
||||
for (var v in includer._loadedFiles)
|
||||
{
|
||||
|
||||
@@ -69,6 +69,8 @@ class ProcessedShader : ProcessedResource
|
||||
class ShaderVariable
|
||||
{
|
||||
private String _name ~ delete _;
|
||||
private String _previewName ~ delete _;
|
||||
private String _editorType ~ delete _;
|
||||
|
||||
private Dictionary<String, Variant> _parameters = new .() ~ {
|
||||
for (var (entryKey, entry) in _)
|
||||
@@ -84,6 +86,21 @@ class ShaderVariable
|
||||
get => _name;
|
||||
set => String.NewOrSet!(_name, value);
|
||||
}
|
||||
|
||||
public StringView PreviewName
|
||||
{
|
||||
get => _previewName;
|
||||
set => String.NewOrSet!(_previewName, value);
|
||||
}
|
||||
|
||||
public StringView EditorType
|
||||
{
|
||||
get => _editorType;
|
||||
set => String.NewOrSet!(_editorType, value);
|
||||
}
|
||||
|
||||
public Variant MinValue ~ _.Dispose();
|
||||
public Variant MaxValue ~ _.Dispose();
|
||||
|
||||
public Dictionary<String, Variant> Parameters => _parameters;
|
||||
|
||||
@@ -134,10 +151,12 @@ class ShaderProcessor : IAssetProcessor
|
||||
}
|
||||
}
|
||||
|
||||
String code = new String(importedShader.HlslCode);
|
||||
defer { delete code; }
|
||||
String tmpCode = new String(importedShader.HlslCode);
|
||||
|
||||
Try!(ProcessFileContent(code, vsName, psName, variables, bufferNames, engineBuffers));
|
||||
// TODO: This is terrible. Currently we process too often... (here + each shader stage)
|
||||
Try!(ShaderCodePreprocessor.ProcessFileContent(tmpCode , vsName, psName, variables, bufferNames, engineBuffers));
|
||||
|
||||
delete tmpCode;
|
||||
|
||||
if (String.IsNullOrWhiteSpace(vsName) && String.IsNullOrWhiteSpace(psName))
|
||||
{
|
||||
@@ -147,28 +166,24 @@ class ShaderProcessor : IAssetProcessor
|
||||
|
||||
processedShader = new ProcessedShader(new AssetIdentifier(importedShader.AssetIdentifier), config.AssetHandle);
|
||||
|
||||
Try!(CompileAndReflect(vsName, psName, importedShader, code, processedShader));
|
||||
Try!(CompileAndReflect(vsName, psName, importedShader, importedShader.HlslCode, processedShader));
|
||||
|
||||
Try!(MergeResources(processedShader, variables, engineBuffers));
|
||||
Try!(MergeResources(processedShader));
|
||||
|
||||
outProcessedResources.Add(processedShader);
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
private static Result<void> MergeResources(ProcessedShader processedShader,
|
||||
Dictionary<StringView, ShaderVariable> variables,
|
||||
Dictionary<StringView, StringView> engineBuffers)
|
||||
private static Result<void> MergeResources(ProcessedShader processedShader)
|
||||
{
|
||||
Try!(MergeConstantBuffers(processedShader, variables, engineBuffers));
|
||||
Try!(MergeConstantBuffers(processedShader));
|
||||
Try!(MergeTextures(processedShader));
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
private static Result<void> MergeConstantBuffers(ProcessedShader processedShader,
|
||||
Dictionary<StringView, ShaderVariable> variables,
|
||||
Dictionary<StringView, StringView> engineBuffers)
|
||||
private static Result<void> MergeConstantBuffers(ProcessedShader processedShader)
|
||||
{
|
||||
HashSet<StringView> bufferNames = scope .();
|
||||
|
||||
@@ -211,11 +226,6 @@ class ShaderProcessor : IAssetProcessor
|
||||
Log.EngineLogger.Assert(constantBufferEntry.ConstantBuffer != null);
|
||||
Log.EngineLogger.Assert(constantBufferEntry.VertexShaderBindPoint > -1 || constantBufferEntry.PixelShaderBindPoint > -1);
|
||||
|
||||
if (engineBuffers.TryGetValue(constantBufferEntry.ConstantBuffer.Name, let engineBufferName))
|
||||
{
|
||||
constantBufferEntry.ConstantBuffer.EngineBufferName = engineBufferName;
|
||||
}
|
||||
|
||||
processedShader.AddConstantBuffer(constantBufferEntry);
|
||||
}
|
||||
|
||||
@@ -283,304 +293,4 @@ class ShaderProcessor : IAssetProcessor
|
||||
|
||||
return ShaderCompiler.CompileAndReflectShader(code, importedShader.AssetIdentifier, vsName, "ps_5_0", .());
|
||||
}
|
||||
|
||||
private static Result<void> ProcessFileContent(String fileContent, String outVsName, String outPsName,
|
||||
Dictionary<StringView, ShaderVariable> outVarDescs,
|
||||
List<String> outBufferNames, Dictionary<StringView, StringView> outEngineBuffers)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
Dictionary<StringView, StringView> arguments = scope .();
|
||||
|
||||
int index = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if ((int Start, int End) value = GetNextPreprocessor(fileContent, index, let name, arguments..Clear()))
|
||||
{
|
||||
index = value.End;
|
||||
|
||||
switch(name)
|
||||
{
|
||||
case "Effect":
|
||||
for (let (argName, argValue) in arguments)
|
||||
{
|
||||
switch(argName)
|
||||
{
|
||||
case "VS", "VertexShader":
|
||||
outVsName.Append(argValue);
|
||||
case "PS", "PixelShader":
|
||||
outPsName.Append(argValue);
|
||||
default:
|
||||
Log.EngineLogger.Error($"Unknown parameter name \"{name}\".");
|
||||
return .Err;
|
||||
}
|
||||
}
|
||||
case "EditorVariable":
|
||||
Try!(ProcessEditorVariables(arguments, outVarDescs));
|
||||
case "EngineBuffer":
|
||||
Try!(ProcessEngineBuffer(arguments, outBufferNames, outEngineBuffers));
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
CommentLine(fileContent, value.Start);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
private static void CommentLine(StringView code, int commentPosition)
|
||||
{
|
||||
code[commentPosition] = '/';
|
||||
code[commentPosition + 1] = '/';
|
||||
}
|
||||
|
||||
private static Result<(int Start, int End)> GetNextPreprocessor(StringView code, int startindex, out StringView name, Dictionary<StringView, StringView> arguments)
|
||||
{
|
||||
name = .();
|
||||
|
||||
int startOfLine;
|
||||
int endOfLine;
|
||||
do
|
||||
{
|
||||
startOfLine = code.IndexOf("#pragma", startindex);
|
||||
|
||||
if (startOfLine == -1)
|
||||
return .Err;
|
||||
|
||||
endOfLine = code.IndexOf('\n', startOfLine);
|
||||
|
||||
StringView line = (endOfLine != -1) ? code.Substring(startOfLine, endOfLine - startOfLine) : code.Substring(startOfLine);
|
||||
|
||||
// cut off the #pragma
|
||||
line = line.Substring(7);
|
||||
|
||||
int lBracketIndex = line.IndexOf('[');
|
||||
|
||||
if (lBracketIndex == -1)
|
||||
{
|
||||
name = line..Trim();
|
||||
break;
|
||||
}
|
||||
|
||||
name = line.Substring(0, lBracketIndex);
|
||||
name.Trim();
|
||||
|
||||
int rBracketIndex = line.IndexOf(']');
|
||||
|
||||
if (rBracketIndex == -1)
|
||||
{
|
||||
Log.EngineLogger.Error($"Pragma is missing closing Bracket (\"{line}\")");
|
||||
rBracketIndex = line.Length;
|
||||
}
|
||||
|
||||
StringView argumentText = line.Substring(lBracketIndex + 1, rBracketIndex - lBracketIndex - 1);
|
||||
|
||||
for (StringView argument in argumentText.Split(';'))
|
||||
{
|
||||
int equalsIndex = argument.IndexOf('=');
|
||||
|
||||
StringView argumentName = .();
|
||||
StringView argumentValue = .();
|
||||
|
||||
if (equalsIndex == -1)
|
||||
{
|
||||
argumentName = argument;
|
||||
argumentName.Trim();
|
||||
}
|
||||
else
|
||||
{
|
||||
argumentName = argument.Substring(0, equalsIndex);
|
||||
argumentName.Trim();
|
||||
|
||||
argumentValue = argument.Substring(equalsIndex + 1);
|
||||
argumentValue.Trim();
|
||||
}
|
||||
|
||||
if (arguments.ContainsKey(argumentName))
|
||||
{
|
||||
Log.EngineLogger.Error($"Arguments \"{argumentName}\" already exists.");
|
||||
continue;
|
||||
}
|
||||
|
||||
arguments.Add(argumentName, argumentValue);
|
||||
}
|
||||
}
|
||||
|
||||
return .Ok((startOfLine, endOfLine));
|
||||
}
|
||||
|
||||
private static Result<void> ProcessEngineBuffer(Dictionary<StringView, StringView> arguments,
|
||||
List<String> outBufferNames, Dictionary<StringView, StringView> outEngineBuffers)
|
||||
{
|
||||
String nameInEngine = null;
|
||||
String nameInShader = null;
|
||||
|
||||
for (var (argName, argValue) in arguments)
|
||||
{
|
||||
if (argValue.StartsWith('"') && argValue.EndsWith('"'))
|
||||
{
|
||||
argValue = argValue[1...^2];
|
||||
}
|
||||
switch (argName)
|
||||
{
|
||||
case "Name":
|
||||
nameInShader = new String(argValue);
|
||||
case "Binding":
|
||||
nameInEngine = new String(argValue);
|
||||
default:
|
||||
Log.EngineLogger.Error($"Unknown parameter for EngineBuffer: \"{argName}\".");
|
||||
delete nameInEngine;
|
||||
delete nameInShader;
|
||||
return .Err;
|
||||
}
|
||||
}
|
||||
|
||||
if (String.IsNullOrWhiteSpace(nameInEngine) || String.IsNullOrWhiteSpace(nameInShader))
|
||||
{
|
||||
Log.EngineLogger.Error($"Name and Binding need to be defined.");
|
||||
delete nameInEngine;
|
||||
delete nameInShader;
|
||||
return .Err;
|
||||
}
|
||||
|
||||
outBufferNames.Add(nameInShader);
|
||||
outBufferNames.Add(nameInEngine);
|
||||
outEngineBuffers.Add(nameInShader, nameInEngine);
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
private static Result<void> ProcessEditorVariables(Dictionary<StringView, StringView> arguments, Dictionary<StringView, ShaderVariable> outVarDescs)
|
||||
{
|
||||
ShaderVariable variable = new .();
|
||||
|
||||
for (var (name, value) in arguments)
|
||||
{
|
||||
if (value.StartsWith('"') && value.EndsWith('"'))
|
||||
{
|
||||
value = value[1...^2];
|
||||
}
|
||||
|
||||
switch(name)
|
||||
{
|
||||
case "Name":
|
||||
variable.Name = value;
|
||||
case "Min", "Max":
|
||||
if (Variant paramValue = ParseVariableValue(value))
|
||||
variable.AddParameter(name, paramValue);
|
||||
else
|
||||
{
|
||||
delete variable;
|
||||
return .Err;
|
||||
}
|
||||
default:
|
||||
Variant paramValue = Variant.Create(new String(value), true);
|
||||
variable.AddParameter(name, paramValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (variable.Name.IsWhiteSpace)
|
||||
{
|
||||
Log.EngineLogger.Error("Failed to process shader: Missing argument \"Name\" int variable description.");
|
||||
|
||||
delete variable;
|
||||
|
||||
return .Err;
|
||||
}
|
||||
|
||||
outVarDescs.Add(variable.Name, variable);
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
private static Result<Variant> ParseVariableValue(StringView valueString)
|
||||
{
|
||||
if (valueString[0].IsDigit || valueString[0] == '-')
|
||||
{
|
||||
var valueString;
|
||||
|
||||
if (valueString.EndsWith('f'))
|
||||
valueString.Length--;
|
||||
|
||||
float value = Try!(float.Parse(valueString));
|
||||
|
||||
return Variant.Create(value);
|
||||
}
|
||||
else if (valueString.StartsWith("float"))
|
||||
{
|
||||
int index = 5;
|
||||
|
||||
int numComponents = valueString[index++] - '0';
|
||||
|
||||
while (valueString[index] != '(')
|
||||
{
|
||||
if (!valueString[index].IsWhiteSpace)
|
||||
{
|
||||
Log.EngineLogger.Error("Failed to process shader: Expected '('.");
|
||||
return .Err;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
if (numComponents < 2 || numComponents > 4)
|
||||
{
|
||||
Log.EngineLogger.Error($"Failed to process shader: Unsupported component count {numComponents}. Value must be between 2 and 4.");
|
||||
return .Err;
|
||||
}
|
||||
|
||||
float[] floats = scope float[numComponents];
|
||||
|
||||
for (int i < numComponents)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
char8 c = valueString[++index];
|
||||
|
||||
if (c.IsDigit || c == '.' || c == '-')
|
||||
break;
|
||||
}
|
||||
|
||||
int start = index;
|
||||
|
||||
while (true)
|
||||
{
|
||||
char8 c = valueString[++index];
|
||||
|
||||
if (!c.IsDigit && c != '.')
|
||||
break;
|
||||
}
|
||||
|
||||
int end = index;
|
||||
|
||||
StringView numberView = .(valueString, start, end - start);
|
||||
|
||||
var result = float.Parse(numberView);
|
||||
|
||||
if (result case .Ok(let value))
|
||||
{
|
||||
floats[i] = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (numComponents == 2)
|
||||
return Variant.Create(*(float2*)floats.Ptr);
|
||||
else if (numComponents == 3)
|
||||
return Variant.Create(*(float3*)floats.Ptr);
|
||||
else
|
||||
return Variant.Create(*(float4*)floats.Ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.EngineLogger.Error($"Failed to process shader: Unsupported variable value: \"{valueString}\".");
|
||||
return .Err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user