From 7b7a3dc6455fd0ccddd130a6bf2722dc7acbc8fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Sun, 18 Aug 2024 23:30:47 +0200 Subject: [PATCH] Started rewriting shader compilation/processing --- .../src/Assets/Importers/ShaderImporter.bf | 51 ++ .../src/Assets/Processors/ShaderCompiler.bf | 475 ++++++++++++++++++ .../src/Assets/Processors/ShaderProcessor.bf | 440 ++++++++++++++++ GlitchyEditor/src/EditorApp.bf | 4 + GlitchyEngine/src/Content/AssetType.bf | 3 +- 5 files changed, 972 insertions(+), 1 deletion(-) create mode 100644 GlitchyEditor/src/Assets/Importers/ShaderImporter.bf create mode 100644 GlitchyEditor/src/Assets/Processors/ShaderCompiler.bf create mode 100644 GlitchyEditor/src/Assets/Processors/ShaderProcessor.bf diff --git a/GlitchyEditor/src/Assets/Importers/ShaderImporter.bf b/GlitchyEditor/src/Assets/Importers/ShaderImporter.bf new file mode 100644 index 0000000..763e569 --- /dev/null +++ b/GlitchyEditor/src/Assets/Importers/ShaderImporter.bf @@ -0,0 +1,51 @@ +using Bon; +using System; +using System.Collections; +using GlitchyEngine.Content; +using System.IO; + +namespace GlitchyEditor.Assets.Importers; + +class ImportedShader : ImportedResource +{ + private String _hlslCode = new .() ~ delete _; + + public StringView HlslCode + { + get => _hlslCode; + } + + public this(AssetIdentifier ownAssetIdentifier) : base(ownAssetIdentifier) + { + } +} + +[BonTarget, BonPolyRegister] +class ShaderImporterConfig : AssetImporterConfig +{ + +} + +class ShaderImporter : IAssetImporter +{ + private static readonly List _fileExtensions = new .(){".hlsl"} ~ delete _; + + public static List FileExtensions => _fileExtensions; + + public static Type ProcessedAssetType => typeof(ImportedShader); + + public AssetImporterConfig CreateDefaultConfig() + { + return new AssetImporterConfig(); + } + + public Result Import(StringView fullFileName, AssetIdentifier assetIdentifier, AssetConfig config) + { + ImportedShader shader = new ImportedShader(new AssetIdentifier(assetIdentifier)); + + Try!(File.ReadAllText(fullFileName, shader.[Friend]_hlslCode, true)); + shader.[Friend]_hlslCode.Append('\n'); + + return shader; + } +} diff --git a/GlitchyEditor/src/Assets/Processors/ShaderCompiler.bf b/GlitchyEditor/src/Assets/Processors/ShaderCompiler.bf new file mode 100644 index 0000000..dfc3371 --- /dev/null +++ b/GlitchyEditor/src/Assets/Processors/ShaderCompiler.bf @@ -0,0 +1,475 @@ +using System; +using DirectX.Common; +using GlitchyEngine; +using System.IO; +using DirectX.D3DCompiler; +using GlitchyEngine.Renderer; +using DirectX.D3D11Shader; +using System.Collections; +using GlitchyEngine.Content; + +namespace GlitchyEditor.Assets.Processors; + +using internal GlitchyEditor.Assets.Processors; + +class ShaderDefineValue +{ + private String _name ~ delete _; + private String _definition ~ delete _; + + public StringView Name + { + get => _name; + set => String.NewOrSet!(_name, value); + } + + public StringView Definition + { + get => _definition; + set => String.NewOrSet!(_definition, value); + } +} + +class CompiledShader +{ + private Dictionary _buffers = new .() ~ DeleteDictionaryAndValues!(_); + private Dictionary _textures = new .() ~ DeleteDictionaryAndValues!(_); + + public void AddConstantBuffer(ReflectedConstantBuffer buffer) + { + _buffers.Add(buffer.Name, buffer); + } + + public void AddTexture(ReflectedTexture texture) + { + _textures.Add(texture.Name, texture); + } +} + +class ReflectedConstantBuffer +{ + private String _name ~ delete _; + + public int Size; + + public StringView Name + { + get => _name; + set => String.NewOrSet!(_name, value); + } + + private Dictionary _variables = new .() ~ DeleteDictionaryAndValues!(_); + + public void AddVariable(ReflectedConstantBufferVariable vaiable) + { + _variables.Add(vaiable.Name, vaiable); + } + + public uint8[] RawData ~ delete _; +} + +class ReflectedTexture +{ + private String _name ~ delete _; + + public uint32 BindPoint; + + public TextureDimension TextureDimension; + + public StringView Name + { + get => _name; + set => String.NewOrSet!(_name, value); + } + + public this(StringView name, uint32 bindPoint, TextureDimension textureDimension) + { + Name = name; + BindPoint = bindPoint; + TextureDimension = textureDimension; + } +} + +class ReflectedConstantBufferVariable +{ + private String _name ~ delete _; + + public StringView Name + { + get => _name; + set => String.NewOrSet!(_name, value); + } + + public int Offset; + public int SizeInBytes; + public bool IsUsed; + + public GlitchyEngine.Renderer.ShaderVariableType ElementType; + public int Rows; + public int Columns; + + public int ArraySize; +} + +// TODO: Dx11 +extension CompiledShader +{ + internal ID3DBlob* _shaderBlob; +} + +class ShaderCompiler +{ + public static Result CompileAndReflectShader(StringView code, AssetIdentifier assetIdentifier, StringView entryPoint, StringView compileTarget, Span defines) + { + defer { + if (@return case .Err) + delete shader; + } + + CompiledShader shader = new CompiledShader(); + + Try!(PlatformCompileShaderFromSource(code, assetIdentifier, entryPoint, compileTarget, defines, shader)); + + Try!(PlatformReflectShader(shader)); + + return shader; + } + + protected static extern Result PlatformCompileShaderFromSource(StringView code, AssetIdentifier fileName, StringView entryPoint, StringView compileTarget, Span defines, CompiledShader outShader); + + protected static extern Result PlatformReflectShader(CompiledShader shader); +} + +// TODO: Dx11 +extension ShaderCompiler +{ + // TODO: This should be a setting per shader, really! + protected const ShaderCompileFlags DefaultCompileFlags = .EnableStrictness | +#if DEBUG + .Debug; +#else + .OptimizationLevel3; +#endif + + protected static override Result PlatformCompileShaderFromSource(StringView code, AssetIdentifier fileName, StringView entryPoint, StringView compileTarget, Span defines, CompiledShader outShader) + { + Debug.Profiler.ProfileResourceFunction!(); + + Log.EngineLogger.AssertDebug(outShader._shaderBlob == null); + + ShaderMacro* nativeMacros = defines.Length == 0 ? null : new:ScopedAlloc! ShaderMacro[defines.Length]*; + + for(int i < defines.Length) + { + nativeMacros[i].Name = defines[i].Name.ToScopedNativeWChar!::(); + nativeMacros[i].Definition = defines[i].Definition.ToScopedNativeWChar!::(); + } + + // Todo: sourceName, includes, + // Todo: variable shader target? + + ID3DBlob* errorBlob = null; + + String directory = scope .(); + + Path.GetDirectoryPath(fileName, directory); + + using (let includer = Includer(directory)) + { + 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); + + if(result.Failed) + { + StringView str = StringView((char8*)errorBlob.GetBufferPointer(), (int)errorBlob.GetBufferSize()); + Log.EngineLogger.Error($"Failed to compile Shader: Error Code({(int)result}): {result} | Error Message: {str}"); + } + } + + Log.EngineLogger.Assert(outShader._shaderBlob != null, "Shader compilation failed."); + + return .Ok; + } + + protected override static Result PlatformReflectShader(CompiledShader shader) + { + Debug.Profiler.ProfileResourceFunction!(); + + ID3D11ShaderReflection* reflection = null; + defer { reflection?.Release(); } + + var reflectionResult = D3DCompiler.D3DReflect(shader._shaderBlob.GetBufferPointer(), shader._shaderBlob.GetBufferSize(), &reflection); + if (reflectionResult.Failed) + { + Log.EngineLogger.Error($"Failed to reflect shader: ({(int)reflectionResult}) {reflectionResult}"); + return .Err; + } + + var getDescResult = reflection.GetDescription(let desc); + if (getDescResult.Failed) + { + Log.EngineLogger.Error($"GetDescription failed: ({(int)getDescResult}) {getDescResult}"); + return .Err; + } + + uint32 resourceCount = desc.BoundResources; + for (uint32 i < resourceCount) + { + var res = reflection.GetResourceBindingDescription(i, let bindDesc); + + if (res.Failed) + { + Log.EngineLogger.Error($"Error({(int)res}) {res}: Failed to get resource binding desc for resource {i}"); + return .Err; + } + + switch(bindDesc.Type) + { + case .ConstantBuffer: + var bufferReflection = reflection.GetConstantBufferByName(bindDesc.Name); + + bufferReflection.GetDescription(let bufferDesc); + + // ConstantBuffer + if(bufferDesc.Type == .D3D11_CT_CBUFFER) + { + ReflectedConstantBuffer cbuffer = Try!(ReflectConstantBuffer(bufferReflection)); + shader.AddConstantBuffer(cbuffer); + } + case .Texture: + TextureDimension textureDimension; + + switch (bindDesc.Dimension) + { + case .Texture1D: + textureDimension = .Texture1D; + case .Texture1DArray: + textureDimension = .Texture1DArray; + case .Texture2D: + textureDimension = .Texture2D; + case .Texture2DArray: + textureDimension = .Texture2DArray; + case .Texture3D: + textureDimension = .Texture3D; + case .TextureCube: + textureDimension = .TextureCube; + case .TextureCubeArray: + textureDimension = .TextureCubeArray; + default: + textureDimension = .Unknown; + } + + shader.AddTexture(new ReflectedTexture(StringView(bindDesc.Name), bindDesc.BindPoint, textureDimension)); + case .Sampler: + // TODO: do we have to do something for samplers? + // i.e. can we get default values? + default: + Log.EngineLogger.Warning($"Unhandled shader resource type: \"{bindDesc.Type}\""); + } + } + + return .Ok; + } + + private static Result ReflectConstantBuffer(ID3D11ShaderReflectionConstantBuffer* bufferReflection) + { + Debug.Profiler.ProfileResourceFunction!(); + + var buffer = new ReflectedConstantBuffer(); + + HResult result = bufferReflection.GetDescription(let bufferDescription); + Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to get buffer description. Error({(int)result}): {result}"); + + Log.EngineLogger.AssertDebug(bufferDescription.Type == .D3D_CT_CBUFFER, "The buffer is not of type \"D3D_CT_CBUFFER\""); + + buffer.Name = StringView(bufferDescription.Name); + buffer.Size = bufferDescription.Size; + buffer.RawData = new uint8[buffer.Size]; + + for(uint32 v = 0; v < bufferDescription.Variables; v++) + { + ID3D11ShaderReflectionVariable* variableReflection = bufferReflection.GetVariableByIndex(v); + + if (ReflectConstantBufferVariable(buffer, variableReflection) case .Err) + { + delete buffer; + return .Err; + } + } + + return buffer; + } + + private static Result ReflectConstantBufferVariable(ReflectedConstantBuffer buffer, ID3D11ShaderReflectionVariable* variableReflection) + { + Debug.Profiler.ProfileResourceFunction!(); + + HResult getDescResult = variableReflection.GetDescription(let variableDescription); + if (getDescResult.Failed) + { + Log.EngineLogger.Error($"Failed to get variable description. Error({(int)getDescResult}): {getDescResult}"); + return .Err; + } + + let variableType = variableReflection.GetVariableType(); + let getTypeDescResult = variableType.GetDescription(let shaderTypeDescription); + if (getTypeDescResult.Failed) + { + Log.EngineLogger.Error($"Failed to get variable description. Error({(int)getDescResult}): {getDescResult}"); + return .Ok; + } + + ReflectedConstantBufferVariable variable = new ReflectedConstantBufferVariable(); + variable.Name = StringView(variableDescription.Name); + + variable.Offset = variableDescription.StartOffset; + variable.SizeInBytes = variableDescription.Size; + variable.IsUsed = variableDescription.uFlags.HasFlag(.Used); + + variable.Columns = shaderTypeDescription.Columns; + variable.Rows = shaderTypeDescription.Rows; + variable.ArraySize = shaderTypeDescription.Elements; + + switch(shaderTypeDescription.Type) + { + case .Bool: + variable.ElementType = .Bool; + case .Float: + variable.ElementType = .Float; + case .Int: + variable.ElementType = .Int; + case .UInt: + variable.ElementType = .UInt; + default: + Log.EngineLogger.Error($"Unhandled shader variable type: {shaderTypeDescription.Type}."); + delete variable; + return .Err; + } + + if (variableDescription.DefaultValue == null) + { + Internal.MemSet(&buffer.RawData[variable.Offset], 0, variable.SizeInBytes); + } + else + { + Internal.MemCpy(&buffer.RawData[variable.Offset], variableDescription.DefaultValue, variable.SizeInBytes); + } + + buffer.AddVariable(variable); + + return .Ok; + } +} + +struct Includer : ID3DInclude, IDisposable +{ + private VTable _vTable; + + private Dictionary _loadedFiles; + + private String _parentFileDirectory; + + public this(String parentFileDirectory) + { + _parentFileDirectory = parentFileDirectory; + _loadedFiles = new Dictionary(); + + _vTable.Open = => Open; + _vTable.Close = => Close; + + _vt = &_vTable; + } + + public void Dispose() + { + for (let (key, value) in _loadedFiles) + { + delete value.FileName; + delete value.FileContent; + } + + delete _loadedFiles; + } + + public static HResult Open(ID3DInclude* self, IncludeType includeType, char8* fileNamePtr, void* parentData, void** data, uint32* bytes) + { + Includer* includer = (.)self; + + StringView fileName = StringView(fileNamePtr); + + if (includer._loadedFiles.TryGetValue(fileName, let value)) + { + *data = (void*)value.FileContent.Ptr; + *bytes = (uint32)value.FileContent.Length; + + return .S_OK; + } + + String fullPath = scope .(); + + Path.Combine(fullPath, includer._parentFileDirectory, fileName); + + if (!File.Exists(fullPath)) + { + let treeResult = Editor.Instance.ContentManager.AssetHierarchy.GetNodeFromIdentifier(scope AssetIdentifier(fileName)); + + if (var assetNode = treeResult) + { + fullPath.Set(assetNode->Path); + } + else + { + Log.EngineLogger.Error($"Failed to include shader file \"{fileName}\""); + return .E_FILENOTFOUND; + } + } + + Stream fileStream = Application.Instance.ContentManager.GetStream(fullPath); + + if (fileStream == null) + { + fileStream = Application.Instance.ContentManager.GetStream(StringView(fileName)); + } + + if (fileStream == null) + { + } + + String fileContent = new String(); + + { + StreamReader reader = scope .(fileStream); + + reader.ReadToEnd(fileContent); + + String fileNameStr = new String(fileName); + + includer._loadedFiles.Add(fileNameStr, (fileNameStr, fileContent)); + } + + delete fileStream; + + *data = (void*)fileContent.Ptr; + *bytes = (uint32)fileContent.Length; + + return .S_OK; + } + + public static HResult Close(ID3DInclude* self, void** data) + { + Includer* includer = (.)self; + + for (var v in includer._loadedFiles) + { + if (v.value.FileContent.Ptr == data) + { + includer._loadedFiles.Remove(v.key); + delete v.value.FileContent; + delete v.value.FileName; + break; + } + } + + return .S_OK; + } +} diff --git a/GlitchyEditor/src/Assets/Processors/ShaderProcessor.bf b/GlitchyEditor/src/Assets/Processors/ShaderProcessor.bf new file mode 100644 index 0000000..ae015f5 --- /dev/null +++ b/GlitchyEditor/src/Assets/Processors/ShaderProcessor.bf @@ -0,0 +1,440 @@ +using GlitchyEditor.Assets.Importers; +using System; +using GlitchyEngine.Content; +using System.Collections; +using GlitchyEngine; +using GlitchyEngine.Math; +using GlitchyEngine.Renderer; + +namespace GlitchyEditor.Assets.Processors; + +class ProcessedShader : ProcessedResource +{ + public override AssetType AssetType => .Shader; + + public CompiledShader VertexShader ~ delete _; + public CompiledShader PixelShader ~ delete _; + + public this(AssetIdentifier ownAssetIdentifier, AssetHandle assetHandle) : base(ownAssetIdentifier, assetHandle) + { + + } +} + +class ShaderVariable +{ + private String _name ~ delete _; + + private Dictionary _parameters = new .() ~ { + for (var (entryKey, entry) in _) + { + delete entryKey; + entry.Dispose(); + } + delete _; + }; + + public StringView Name + { + get => _name; + set => String.NewOrSet!(_name, value); + } + + public Dictionary Parameters => _parameters; + + public void AddParameter(StringView name, Variant value) + { + _parameters.Add(new String(name), value); + } +} + +class ShaderProcessor : IAssetProcessor +{ + public AssetProcessorConfig CreateDefaultConfig() + { + return new AssetProcessorConfig(); + } + + public static Type ProcessedAssetType => typeof(ImportedShader); + + public Result Process(ImportedResource importedResource, AssetConfig config, List outProcessedResources) + { + Log.EngineLogger.AssertDebug(importedResource is ImportedShader); + + Try!(ProcessShader(importedResource as ImportedShader, config, outProcessedResources)); + + return default; + } + + private static Result ProcessShader(ImportedShader importedShader, AssetConfig config, List outProcessedResources) + { + String vsName = scope String(); + String psName = scope String(); + + Dictionary variables = scope .(); + Dictionary engineBuffers = scope .(); + + defer + { + ClearDictionaryAndDeleteValues!(variables); + + for (let (key, value) in engineBuffers) + { + delete key; + delete value; + } + } + + String code = new String(importedShader.HlslCode); + defer { delete code; } + + Try!(ProcessFileContent(code, vsName, psName, variables, engineBuffers)); + + if (String.IsNullOrWhiteSpace(vsName) && String.IsNullOrWhiteSpace(psName)) + { + // this is not an effect -> we don't need to compile it + return .Ok; + } + + ProcessedShader processedShader = new ProcessedShader(new AssetIdentifier(importedShader.AssetIdentifier), config.AssetHandle); + + Try!(CompileAndReflect(vsName, psName, importedShader, code, processedShader)); + + Try!(MergeResources(processedShader)); + + outProcessedResources.Add(processedShader); + + return .Ok; + } + + private static Result MergeResources(ProcessedShader processedShader) + { + + + return .Ok; + } + + private static Result CompileAndReflect(StringView vsName, StringView psName, ImportedShader shader, StringView code, ProcessedShader processedShader) + { + if (!vsName.IsWhiteSpace) + { + processedShader.VertexShader = Try!(CompileAndReflectVertexShader(vsName, shader, code)); + } + + if (!psName.IsWhiteSpace) + { + processedShader.PixelShader = Try!(CompileAndReflectPixelShader(psName, shader, code)); + } + + return .Ok; + } + + private static Result CompileAndReflectVertexShader(StringView vsName, ImportedShader importedShader, StringView code) + { + Debug.Profiler.ProfileResourceFunction!(); + + return ShaderCompiler.CompileAndReflectShader(code, importedShader.AssetIdentifier, vsName, "vs_5_0", .()); + } + + private static Result CompileAndReflectPixelShader(StringView vsName, ImportedShader importedShader, StringView code) + { + Debug.Profiler.ProfileResourceFunction!(); + + return ShaderCompiler.CompileAndReflectShader(code, importedShader.AssetIdentifier, vsName, "ps_5_0", .()); + } + + private static Result ProcessFileContent(String fileContent, String outVsName, String outPsName, Dictionary outVarDescs, Dictionary outEngineBuffers) + { + Debug.Profiler.ProfileResourceFunction!(); + + Dictionary 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, 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 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 ProcessEngineBuffer(Dictionary arguments, Dictionary 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; + } + + outEngineBuffers.Add(nameInEngine, nameInShader); + + return .Ok; + } + + private static Result ProcessEditorVariables(Dictionary arguments, Dictionary 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 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; + } + } +} diff --git a/GlitchyEditor/src/EditorApp.bf b/GlitchyEditor/src/EditorApp.bf index fc4a13d..ec9aa5e 100644 --- a/GlitchyEditor/src/EditorApp.bf +++ b/GlitchyEditor/src/EditorApp.bf @@ -42,6 +42,10 @@ namespace GlitchyEditor _contentManager.RegisterAssetExporter(); _contentManager.RegisterAssetExporter(); + + _contentManager.RegisterAssetImporter(); + _contentManager.RegisterAssetProcessor(); + //_contentManager.RegisterAssetExporter(); _contentManager.SetGlobalAssetCacheDirectory(".cache"); _contentManager.SetResourcesDirectory("Resources"); diff --git a/GlitchyEngine/src/Content/AssetType.bf b/GlitchyEngine/src/Content/AssetType.bf index c069050..465c9ec 100644 --- a/GlitchyEngine/src/Content/AssetType.bf +++ b/GlitchyEngine/src/Content/AssetType.bf @@ -4,5 +4,6 @@ enum AssetType : uint16 { Unknown, Texture, - Sprite + Sprite, + Shader } \ No newline at end of file