// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. #include "ShaderProcessing.h" #if COMPILE_WITH_SHADER_COMPILER #include "Engine/Core/Collections/Array.h" #include "Engine/Utilities/TextProcessing.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Core/Log.h" #include "ShaderFunctionReader.CB.h" #include "ShaderFunctionReader.VS.h" #include "ShaderFunctionReader.HS.h" #include "ShaderFunctionReader.DS.h" #include "ShaderFunctionReader.GS.h" #include "ShaderFunctionReader.PS.h" #include "ShaderFunctionReader.CS.h" #include "Config.h" ShaderProcessing::Parser::Parser(const String& targetName, const char* source, int32 sourceLength, ParserMacros macros, FeatureLevel featureLevel) : failed(false) , targetName(targetName) , text(source, sourceLength) , _macros(macros) , _featureLevel(featureLevel) { } ShaderProcessing::Parser::~Parser() { } bool ShaderProcessing::Parser::Process(const String& targetName, const char* source, int32 sourceLength, ParserMacros macros, FeatureLevel featureLevel, ShaderMeta* result) { PROFILE_CPU_NAMED("Shader.Parse"); Parser parser(targetName, source, sourceLength, macros, featureLevel); parser.Process(result); return parser.Failed(); } void ShaderProcessing::Parser::Process(ShaderMeta* result) { init(); failed = process(); if (!failed) failed = collectResults(result); } void ShaderProcessing::Parser::init() { // Init text processing tokens for hlsl language text.Setup_HLSL(); // Init shader functions readers _childReaders.Add(New()); _childReaders.Add(New()); _childReaders.Add(New()); _childReaders.Add(New()); _childReaders.Add(New()); _childReaders.Add(New()); _childReaders.Add(New()); } bool ShaderProcessing::Parser::process() { const Token defineToken("#define"); const Separator singleLineCommentSeparator('/', '/'); const Separator multiLineCommentSeparator('/', '*'); // TODO: split parsing into two phrases: comments preprocessing and parsing // Read whole source code Token token; while (text.CanRead()) { text.ReadToken(&token); // Single line comment if (token.Separator == singleLineCommentSeparator) { // Read whole line text.ReadLine(); } // Multi line comment else if (token.Separator == multiLineCommentSeparator) { // Read tokens until end sequence char prev = ' '; char c; while (text.CanRead()) { c = text.ReadChar(); if (prev == '*' && c == '/') { break; } prev = c; } // Check if comment is valid (has end before file end) if (!text.CanRead()) { OnWarning(TEXT("Missing multiline comment ending")); } } // Preprocessor definition else if (token == defineToken) { // Skip text.ReadLine(); } else { // Call children ProcessChildren(token, this); } } return false; } bool ShaderProcessing::Parser::collectResults(ShaderMeta* result) { // Collect results from all the readers for (int32 i = 0; i < _childReaders.Count(); i++) _childReaders[i]->CollectResults(this, result); return false; } void ShaderProcessing::Parser::OnError(const String& message) { // Set flag failed = true; // Send event LOG(Error, "Processing shader '{0}' error at line {1}. {2}", targetName, text.GetLine(), message); } void ShaderProcessing::Parser::OnWarning(const String& message) { // Send event LOG(Warning, "Processing shader '{0}' warning at line {1}. {2}", targetName, text.GetLine(), message); } #endif