From 4e680c747987a2be28378a436d8ac74e78e4bf32 Mon Sep 17 00:00:00 2001 From: Adrien GIVRY Date: Wed, 16 Apr 2025 19:32:49 -0400 Subject: [PATCH 01/12] Added shader variant (multi-compile) system --- .../src/OvEditor/Panels/AssetBrowser.cpp | 2 + .../Resources/Loaders/ShaderLoader.h | 7 + .../include/OvRendering/Resources/Shader.h | 38 ++- .../Resources/Loaders/ShaderLoader.cpp | 279 ++++++++++++++---- .../src/OvRendering/Resources/Shader.cpp | 55 +++- 5 files changed, 315 insertions(+), 66 deletions(-) diff --git a/Sources/Overload/OvEditor/src/OvEditor/Panels/AssetBrowser.cpp b/Sources/Overload/OvEditor/src/OvEditor/Panels/AssetBrowser.cpp index 00592a91c..9b98e5f26 100644 --- a/Sources/Overload/OvEditor/src/OvEditor/Panels/AssetBrowser.cpp +++ b/Sources/Overload/OvEditor/src/OvEditor/Panels/AssetBrowser.cpp @@ -622,6 +622,7 @@ namespace { auto& shaderManager = OVSERVICE(OvCore::ResourceManagement::ShaderManager); const std::string resourcePath = EDITOR_EXEC(GetResourcePath(filePath, m_protected)); + OvRendering::Resources::Loaders::ShaderLoader::SetLoggingSettings(true, true); if (shaderManager.IsResourceRegistered(resourcePath)) { // Trying to recompile @@ -636,6 +637,7 @@ namespace OVLOG_INFO("[COMPILE] \"" + filePath + "\": Success!"); } } + OvRendering::Resources::Loaders::ShaderLoader::SetLoggingSettings(false, false); }; } }; diff --git a/Sources/Overload/OvRendering/include/OvRendering/Resources/Loaders/ShaderLoader.h b/Sources/Overload/OvRendering/include/OvRendering/Resources/Loaders/ShaderLoader.h index 713a389d4..fb8432388 100644 --- a/Sources/Overload/OvRendering/include/OvRendering/Resources/Loaders/ShaderLoader.h +++ b/Sources/Overload/OvRendering/include/OvRendering/Resources/Loaders/ShaderLoader.h @@ -25,6 +25,13 @@ namespace OvRendering::Resources::Loaders */ ShaderLoader() = delete; + /** + * Enable or disable logging for errors and success + * @param p_logErrors + * @param p_logSuccess + */ + static void SetLoggingSettings(bool p_logErrors, bool p_logSuccess); + /** * Create a shader * @param p_filePath diff --git a/Sources/Overload/OvRendering/include/OvRendering/Resources/Shader.h b/Sources/Overload/OvRendering/include/OvRendering/Resources/Shader.h index f1a257eb3..983742c26 100644 --- a/Sources/Overload/OvRendering/include/OvRendering/Resources/Shader.h +++ b/Sources/Overload/OvRendering/include/OvRendering/Resources/Shader.h @@ -8,6 +8,8 @@ #include #include +#include +#include #include @@ -24,20 +26,46 @@ namespace OvRendering::Resources friend class Loaders::ShaderLoader; public: + using FeatureSet = std::unordered_set; + + struct FeatureSetHash + { + size_t operator()(const FeatureSet& fs) const; + }; + + struct FeatureSetEqual + { + bool operator()(const FeatureSet& lhs, const FeatureSet& rhs) const; + }; + + using ProgramVariants = std::unordered_map< + FeatureSet, + std::unique_ptr, + FeatureSetHash, + FeatureSetEqual + >; + /** - * Returns the associated shader program + * Returns the associated shader program for a given feature set + * @param p_featureSet (optional) The feature set to use. If not provided, the default program will be used. */ - HAL::ShaderProgram& GetProgram() const; + HAL::ShaderProgram& GetProgram(const FeatureSet& p_featureSet = {}); private: - Shader(const std::string p_path, std::unique_ptr&& p_program); + Shader( + const std::string p_path, + const FeatureSet& p_features, + ProgramVariants&& p_programs + ); + ~Shader() = default; - void SetProgram(std::unique_ptr&& p_program); + void SetPrograms(const FeatureSet& p_features, ProgramVariants&& p_programs); public: const std::string path; private: - std::unique_ptr m_program; + FeatureSet m_features; + ProgramVariants m_programs; }; } \ No newline at end of file diff --git a/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp b/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp index f330af1b1..20f1c49c3 100644 --- a/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp +++ b/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp @@ -6,10 +6,12 @@ #include #include +#include #include #include #include #include +#include #include #include @@ -23,6 +25,20 @@ namespace { std::string __FILE_TRACE; + bool __LOG_ERRORS = false; + bool __LOG_SUCCESS = false; + + struct ShaderLoadResult + { + const std::string source; + }; + + struct ShaderParseResult + { + const std::string vertexShader; + const std::string fragmentShader; + const OvRendering::Resources::Shader::FeatureSet features; + }; struct ShaderStageDesc { @@ -36,9 +52,70 @@ namespace std::optional compilationResult; }; + std::string FeautreSetToString(const OvRendering::Resources::Shader::FeatureSet& features) + { + std::string result = " CreateProgram( - std::span p_stages, - bool p_verbose = false + std::span p_stages, + const OvRendering::Resources::Shader::FeatureSet& p_features = {} ) { using namespace OvRendering::HAL; @@ -53,15 +130,43 @@ namespace for (auto& stageInput : p_stages) { const auto& processedStage = processedStages.emplace_back(stageInput.type); - processedStage.stage.Upload(stageInput.source); + const auto source = EnableFeaturesInShaderCode(stageInput.source, p_features); + processedStage.stage.Upload(source); const auto compilationResult = processedStage.stage.Compile(); if (!compilationResult.success) { std::string shaderTypeStr = OvRendering::Utils::GetShaderTypeName(stageInput.type); - std::transform(shaderTypeStr.begin(), shaderTypeStr.end(), shaderTypeStr.begin(), std::toupper); - OVLOG_ERROR("[" + shaderTypeStr + " COMPILE] \"" + __FILE_TRACE + "\": " + compilationResult.message); + + for (char& c : shaderTypeStr) + { + c = std::toupper(c); + } + + if (__LOG_ERRORS) + { + OVLOG_ERROR(std::format( + "[{} COMPILE] \"{}\" {}: {}", + shaderTypeStr, + __FILE_TRACE, + FeautreSetToString(p_features), + compilationResult.message) + ); + } + ++errorCount; } + else + { + if (__LOG_SUCCESS) + { + OVLOG_INFO(std::format( + "[{} COMPILE] \"{}\" {}: Success!", + OvRendering::Utils::GetShaderTypeName(stageInput.type), + __FILE_TRACE, + FeautreSetToString(p_features)) + ); + } + } } if (errorCount == 0) @@ -82,15 +187,22 @@ namespace if (linkResult.success) { - if (p_verbose) + if (__LOG_SUCCESS) { - OVLOG_INFO("[COMPILE] \"" + __FILE_TRACE + "\": Success!"); + OVLOG_INFO(std::format( + "[LINK] \"{}\": Success!", + __FILE_TRACE + )); } return program; } - else + else if (__LOG_ERRORS) { - OVLOG_ERROR("[LINK] \"" + __FILE_TRACE + "\": Failed: " + linkResult.message); + OVLOG_ERROR(std::format( + "[LINK] \"{}\": Failed: {}", + __FILE_TRACE, + linkResult.message + )); } } @@ -126,7 +238,7 @@ void main() ShaderStageDesc{fragment, OvRendering::Settings::EShaderType::FRAGMENT} }; - auto program = CreateProgram(shaders); + auto program = CreateProgram(shaders, {}); OVASSERT(program != nullptr, "Failed to create default shader program"); return std::move(program); } @@ -150,14 +262,14 @@ void main() } } - std::string LoadShader(const std::string& p_filePath, OvRendering::Resources::Loaders::ShaderLoader::FilePathParserCallback p_pathParser) + ShaderLoadResult LoadShader(const std::string& p_filePath, OvRendering::Resources::Loaders::ShaderLoader::FilePathParserCallback p_pathParser) { std::ifstream file(p_filePath); if (!file.is_open()) { - OVLOG_ERROR("Error: Could not open shader file - " + p_filePath); - return ""; + OVLOG_ERROR(std::format("Error: Could not open shader file: \"{}\"", p_filePath)); + return {}; } std::stringstream buffer; @@ -173,12 +285,12 @@ void main() { // Recursively load the included file const std::string realIncludeFilePath = p_pathParser ? p_pathParser(includeFilePath) : includeFilePath; - std::string includedShader = LoadShader(realIncludeFilePath, p_pathParser); - buffer << includedShader << std::endl; + const auto result = LoadShader(realIncludeFilePath, p_pathParser); + buffer << result.source << std::endl; } else { - OVLOG_ERROR("Error: Invalid #include directive in file - " + p_filePath); + OVLOG_ERROR(std::format("Error: Invalid #include directive in file: \"{}\"", p_filePath)); } } else { @@ -187,22 +299,30 @@ void main() } } - return buffer.str(); + return { + buffer.str() + }; } - std::pair ParseShader(const std::string& p_filePath, OvRendering::Resources::Loaders::ShaderLoader::FilePathParserCallback p_pathParser) + ShaderParseResult ParseShader(const std::string& p_filePath, OvRendering::Resources::Loaders::ShaderLoader::FilePathParserCallback p_pathParser) { - const std::string shaderCode = LoadShader(p_filePath, p_pathParser); + const auto shaderLoadResult = LoadShader(p_filePath, p_pathParser); - std::istringstream stream(shaderCode); // Add this line to create a stringstream from shaderCode + std::istringstream stream(shaderLoadResult.source); // Add this line to create a stringstream from shaderCode std::string line; std::unordered_map ss; + OvRendering::Resources::Shader::FeatureSet features; auto type = OvRendering::Settings::EShaderType::NONE; while (std::getline(stream, line)) { - if (line.find("#shader") != std::string::npos) + if (line.starts_with("#feature")) + { + const std::string featureName = line.substr(line.find(' ') + 1); + features.insert(featureName); + } + else if (line.find("#shader") != std::string::npos) { if (line.find("vertex") != std::string::npos) type = OvRendering::Settings::EShaderType::VERTEX; @@ -218,30 +338,87 @@ void main() return { ss[OvRendering::Settings::EShaderType::VERTEX].str(), - ss[OvRendering::Settings::EShaderType::FRAGMENT].str() + ss[OvRendering::Settings::EShaderType::FRAGMENT].str(), + features }; } + + OvRendering::Resources::Shader::ProgramVariants CreatePrograms(const ShaderParseResult& p_parseResult) + { + const auto variantCount = (size_t{ 1UL } << p_parseResult.features.size()); + + OvRendering::Resources::Shader::ProgramVariants variants; + + // We create a shader program (AKA shader variant) for each combination of features. + // The number of combinations is 2^n, where n is the number of features. + for (size_t i = 0; i < variantCount; ++i) + { + OvRendering::Resources::Shader::FeatureSet featureSet; + for (size_t j = 0; j < p_parseResult.features.size(); ++j) + { + if (i & (size_t{ 1UL } << j)) + { + featureSet.insert(*std::next(p_parseResult.features.begin(), j)); + } + } + + const auto stages = std::to_array({ + { p_parseResult.vertexShader, OvRendering::Settings::EShaderType::VERTEX }, + { p_parseResult.fragmentShader, OvRendering::Settings::EShaderType::FRAGMENT } + }); + + auto program = CreateProgram( + stages, + featureSet + ); + + if (program) + { + variants.emplace(featureSet, std::move(program)); + } + } + + if (variants.empty()) + { + if (__LOG_ERRORS) + { + OVLOG_ERROR(std::format( + "[COMPILE] \"{}\" Failed! Previous shader version keept", + __FILE_TRACE + )); + } + + auto defaultProgram = CreateDefaultProgram(); + variants.emplace(OvRendering::Resources::Shader::FeatureSet{}, std::move(defaultProgram)); + } + else if (__LOG_SUCCESS) + { + OVLOG_INFO(std::format("[COMPILE] \"{}\" Compiled ({} variants)", __FILE_TRACE, variantCount)); + } + + return variants; + } } namespace OvRendering::Resources::Loaders { + void ShaderLoader::SetLoggingSettings(bool p_logErrors, bool p_logSuccess) + { + __LOG_ERRORS = p_logErrors; + __LOG_SUCCESS = p_logSuccess; + } + Shader* ShaderLoader::Create(const std::string& p_filePath, FilePathParserCallback p_pathParser) { __FILE_TRACE = p_filePath; - auto [vertex, fragment] = ParseShader(p_filePath, p_pathParser); - - auto shaders = std::array{ - ShaderStageDesc{vertex, Settings::EShaderType::VERTEX}, - ShaderStageDesc{fragment, Settings::EShaderType::FRAGMENT} - }; - - if (auto program = CreateProgram(shaders)) - { - return new Shader(p_filePath, std::move(program)); - } + const auto shaderParseResult = ParseShader(p_filePath, p_pathParser); - return new Shader(p_filePath, std::move(CreateDefaultProgram())); + return new Shader( + p_filePath, + shaderParseResult.features, + std::move(CreatePrograms(shaderParseResult)) + ); } Shader* ShaderLoader::CreateFromSource(const std::string& p_vertexShader, const std::string& p_fragmentShader) @@ -253,33 +430,29 @@ namespace OvRendering::Resources::Loaders ShaderStageDesc{p_fragmentShader, Settings::EShaderType::FRAGMENT} }; - if (auto program = CreateProgram(shaders)) - { - return new Shader("", std::move(program)); - } + const ShaderParseResult shaderParseResult{ + .vertexShader = p_vertexShader, + .fragmentShader = p_fragmentShader, + .features = {} + }; - return new Shader("", std::move(CreateDefaultProgram())); + return new Shader( + "", + shaderParseResult.features, + std::move(CreatePrograms(shaderParseResult)) + ); } void ShaderLoader::Recompile(Shader& p_shader, const std::string& p_filePath, FilePathParserCallback p_pathParser) { __FILE_TRACE = p_filePath; - auto [vertex, fragment] = ParseShader(p_filePath, p_pathParser); - - auto shaders = std::array{ - ShaderStageDesc{vertex, Settings::EShaderType::VERTEX}, - ShaderStageDesc{fragment, Settings::EShaderType::FRAGMENT} - }; + const auto shaderParseResult = ParseShader(p_filePath, p_pathParser); - if (auto program = CreateProgram(shaders, true)) // verbose = true - { - p_shader.SetProgram(std::move(program)); - } - else - { - OVLOG_ERROR("[COMPILE] \"" + __FILE_TRACE + "\": Failed! Previous shader version keept"); - } + p_shader.SetPrograms( + shaderParseResult.features, + std::move(CreatePrograms(shaderParseResult)) + ); } bool ShaderLoader::Destroy(Shader*& p_shader) diff --git a/Sources/Overload/OvRendering/src/OvRendering/Resources/Shader.cpp b/Sources/Overload/OvRendering/src/OvRendering/Resources/Shader.cpp index 764389df6..b6026ca54 100644 --- a/Sources/Overload/OvRendering/src/OvRendering/Resources/Shader.cpp +++ b/Sources/Overload/OvRendering/src/OvRendering/Resources/Shader.cpp @@ -7,19 +7,58 @@ #include #include -OvRendering::HAL::ShaderProgram& OvRendering::Resources::Shader::GetProgram() const +namespace { - OVASSERT(m_program != nullptr, "Trying to access a null ShaderProgram"); - return *m_program; + void ValidateProgramRegistry(const OvRendering::Resources::Shader::ProgramVariants& p_programs) + { + OVASSERT(p_programs.size() > 0 && p_programs.contains({}), + "Shader program registry must contain at least a default program" + ); + } } -OvRendering::Resources::Shader::Shader(const std::string p_path, std::unique_ptr&& p_program) : path(p_path) +size_t OvRendering::Resources::Shader::FeatureSetHash::operator()(const FeatureSet& fs) const { - SetProgram(std::move(p_program)); + size_t hash = 0; + + for (const auto& feature : fs) + { + hash ^= std::hash{}(feature)+0x9e3779b9 + (hash << 6) + (hash >> 2); + } + + return hash; +} + +bool OvRendering::Resources::Shader::FeatureSetEqual::operator()(const FeatureSet& lhs, const FeatureSet& rhs) const +{ + return lhs == rhs; +}; + +OvRendering::HAL::ShaderProgram& OvRendering::Resources::Shader::GetProgram(const FeatureSet& p_featureSet) +{ + if (m_programs.contains(p_featureSet)) + { + return *m_programs[p_featureSet]; + } + else + { + OVASSERT(m_programs.contains({}), "No default program found for this shader"); + return *m_programs[{}]; + } +} + +OvRendering::Resources::Shader::Shader( + const std::string p_path, + const FeatureSet& p_features, + ProgramVariants&& p_program +) : path(p_path) +{ + SetPrograms(p_features, std::move(p_program)); } -void OvRendering::Resources::Shader::SetProgram(std::unique_ptr&& p_program) +void OvRendering::Resources::Shader::SetPrograms(const FeatureSet& p_features, ProgramVariants&& p_programs) { - OVASSERT(p_program != nullptr, "Cannot assign an invalid program!"); - m_program = std::move(p_program); + ValidateProgramRegistry(p_programs); + m_features = p_features; + m_programs = std::move(p_programs); } From 0d4e25e367af854c2dda1c6dcaca6b75235ba40d Mon Sep 17 00:00:00 2001 From: Adrien GIVRY Date: Thu, 17 Apr 2025 11:09:25 -0400 Subject: [PATCH 02/12] Improved logging (more granual control) and function names --- .../src/OvEditor/Panels/AssetBrowser.cpp | 24 ++- .../Resources/Loaders/ShaderLoader.h | 22 +- .../Resources/Loaders/ShaderLoader.cpp | 190 ++++++++++++------ 3 files changed, 166 insertions(+), 70 deletions(-) diff --git a/Sources/Overload/OvEditor/src/OvEditor/Panels/AssetBrowser.cpp b/Sources/Overload/OvEditor/src/OvEditor/Panels/AssetBrowser.cpp index 9b98e5f26..9b6159223 100644 --- a/Sources/Overload/OvEditor/src/OvEditor/Panels/AssetBrowser.cpp +++ b/Sources/Overload/OvEditor/src/OvEditor/Panels/AssetBrowser.cpp @@ -618,11 +618,20 @@ namespace auto& compileAction = CreateWidget("Compile"); - compileAction.ClickedEvent += [this] - { + compileAction.ClickedEvent += [this] { + using namespace OvRendering::Resources::Loaders; auto& shaderManager = OVSERVICE(OvCore::ResourceManagement::ShaderManager); const std::string resourcePath = EDITOR_EXEC(GetResourcePath(filePath, m_protected)); - OvRendering::Resources::Loaders::ShaderLoader::SetLoggingSettings(true, true); + const auto previousLoggingSettings = ShaderLoader::GetLoggingSettings(); + + ShaderLoader::SetLoggingSettings(ShaderLoader::LoggingSettings{ + .summary = true, + .linkingErrors = true, + .linkingSuccess = false, + .compilationErrors = true, + .compilationSuccess = false + }); + if (shaderManager.IsResourceRegistered(resourcePath)) { // Trying to recompile @@ -631,13 +640,10 @@ namespace else { // Trying to compile - auto shader = OVSERVICE(OvCore::ResourceManagement::ShaderManager)[resourcePath]; - if (shader) - { - OVLOG_INFO("[COMPILE] \"" + filePath + "\": Success!"); - } + OVSERVICE(OvCore::ResourceManagement::ShaderManager).LoadResource(resourcePath); } - OvRendering::Resources::Loaders::ShaderLoader::SetLoggingSettings(false, false); + + ShaderLoader::SetLoggingSettings(previousLoggingSettings); }; } }; diff --git a/Sources/Overload/OvRendering/include/OvRendering/Resources/Loaders/ShaderLoader.h b/Sources/Overload/OvRendering/include/OvRendering/Resources/Loaders/ShaderLoader.h index fb8432388..956972bdc 100644 --- a/Sources/Overload/OvRendering/include/OvRendering/Resources/Loaders/ShaderLoader.h +++ b/Sources/Overload/OvRendering/include/OvRendering/Resources/Loaders/ShaderLoader.h @@ -18,6 +18,18 @@ namespace OvRendering::Resources::Loaders class ShaderLoader { public: + /** + * Logging settings for the ShaderLoader + */ + struct LoggingSettings + { + bool summary : 1; + bool linkingErrors : 1; + bool linkingSuccess : 1; + bool compilationErrors : 1; + bool compilationSuccess : 1; + }; + using FilePathParserCallback = std::function; /** @@ -25,12 +37,16 @@ namespace OvRendering::Resources::Loaders */ ShaderLoader() = delete; + /** + * Returns the current logging settings + */ + static LoggingSettings GetLoggingSettings(); + /** * Enable or disable logging for errors and success - * @param p_logErrors - * @param p_logSuccess + * @param p_settings */ - static void SetLoggingSettings(bool p_logErrors, bool p_logSuccess); + static void SetLoggingSettings(LoggingSettings p_settings); /** * Create a shader diff --git a/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp b/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp index 20f1c49c3..a2f589426 100644 --- a/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp +++ b/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -24,15 +25,23 @@ namespace { - std::string __FILE_TRACE; - bool __LOG_ERRORS = false; - bool __LOG_SUCCESS = false; + std::filesystem::path __FILE_TRACE; + + OvRendering::Resources::Loaders::ShaderLoader::LoggingSettings __LOGGING_SETTINGS{ + .summary = false, + .linkingErrors = false, + .linkingSuccess = false, + .compilationErrors = false, + .compilationSuccess = false, + }; + // Load a shader, including any included files struct ShaderLoadResult { const std::string source; }; + // Parse the shader source code and extract the vertex and fragment shaders, as well as features struct ShaderParseResult { const std::string vertexShader; @@ -40,6 +49,14 @@ namespace const OvRendering::Resources::Shader::FeatureSet features; }; + // Compile the shader and create a program for each combination of features + struct ShaderAssembleResult + { + const uint32_t failures; // How many variants failed to compile + const OvRendering::Resources::Shader::FeatureSet features; + OvRendering::Resources::Shader::ProgramVariants variants; + }; + struct ShaderStageDesc { const std::string source; @@ -52,9 +69,14 @@ namespace std::optional compilationResult; }; - std::string FeautreSetToString(const OvRendering::Resources::Shader::FeatureSet& features) + inline std::string GetCurrentFileName() + { + return __FILE_TRACE.stem().string(); + } + + std::string FeatureSetToString(const OvRendering::Resources::Shader::FeatureSet& features) { - std::string result = " CreateProgram( std::span p_stages, - const OvRendering::Resources::Shader::FeatureSet& p_features = {} + const OvRendering::Resources::Shader::FeatureSet& p_features = {}, + bool p_noLogging = false ) { using namespace OvRendering::HAL; @@ -142,13 +165,13 @@ namespace c = std::toupper(c); } - if (__LOG_ERRORS) + if (!p_noLogging && __LOGGING_SETTINGS.compilationErrors) { OVLOG_ERROR(std::format( "[{} COMPILE] \"{}\" {}: {}", shaderTypeStr, - __FILE_TRACE, - FeautreSetToString(p_features), + GetCurrentFileName(), + FeatureSetToString(p_features), compilationResult.message) ); } @@ -157,13 +180,13 @@ namespace } else { - if (__LOG_SUCCESS) + if (!p_noLogging && __LOGGING_SETTINGS.compilationSuccess) { OVLOG_INFO(std::format( "[{} COMPILE] \"{}\" {}: Success!", OvRendering::Utils::GetShaderTypeName(stageInput.type), - __FILE_TRACE, - FeautreSetToString(p_features)) + GetCurrentFileName(), + FeatureSetToString(p_features)) ); } } @@ -187,20 +210,22 @@ namespace if (linkResult.success) { - if (__LOG_SUCCESS) + if (!p_noLogging && __LOGGING_SETTINGS.linkingSuccess) { OVLOG_INFO(std::format( - "[LINK] \"{}\": Success!", - __FILE_TRACE + "[LINK] \"{}\" {}: Success!", + GetCurrentFileName(), + FeatureSetToString(p_features) )); } return program; } - else if (__LOG_ERRORS) + else if (!p_noLogging && __LOGGING_SETTINGS.linkingErrors) { OVLOG_ERROR(std::format( - "[LINK] \"{}\": Failed: {}", - __FILE_TRACE, + "[LINK] \"{}\" {}: Failed: {}", + GetCurrentFileName(), + FeatureSetToString(p_features), linkResult.message )); } @@ -238,7 +263,12 @@ void main() ShaderStageDesc{fragment, OvRendering::Settings::EShaderType::FRAGMENT} }; - auto program = CreateProgram(shaders, {}); + auto program = CreateProgram( + shaders, + {}, + true // Force no logging for default program (we expect it to succeed, otherwise we have a problem) + ); + OVASSERT(program != nullptr, "Failed to create default shader program"); return std::move(program); } @@ -304,11 +334,9 @@ void main() }; } - ShaderParseResult ParseShader(const std::string& p_filePath, OvRendering::Resources::Loaders::ShaderLoader::FilePathParserCallback p_pathParser) + ShaderParseResult ParseShader(const ShaderLoadResult& p_shaderLoadResult) { - const auto shaderLoadResult = LoadShader(p_filePath, p_pathParser); - - std::istringstream stream(shaderLoadResult.source); // Add this line to create a stringstream from shaderCode + std::istringstream stream(p_shaderLoadResult.source); // Add this line to create a stringstream from shaderCode std::string line; std::unordered_map ss; OvRendering::Resources::Shader::FeatureSet features; @@ -343,10 +371,11 @@ void main() }; } - OvRendering::Resources::Shader::ProgramVariants CreatePrograms(const ShaderParseResult& p_parseResult) + ShaderAssembleResult AssembleShader(const ShaderParseResult& p_parseResult) { const auto variantCount = (size_t{ 1UL } << p_parseResult.features.size()); + uint32_t failures = 0; OvRendering::Resources::Shader::ProgramVariants variants; // We create a shader program (AKA shader variant) for each combination of features. @@ -376,48 +405,91 @@ void main() { variants.emplace(featureSet, std::move(program)); } + else + { + ++failures; + } } - if (variants.empty()) + // If no default program was created, we create a default one + if (!variants.contains({})) { - if (__LOG_ERRORS) + variants.emplace( + OvRendering::Resources::Shader::FeatureSet{}, + std::move(CreateDefaultProgram()) + ); + } + + if (failures > 0) + { + if (__LOGGING_SETTINGS.summary) { OVLOG_ERROR(std::format( - "[COMPILE] \"{}\" Failed! Previous shader version keept", - __FILE_TRACE + "[COMPILE] \"{}\" Failed! {} variants failed to compile", + GetCurrentFileName(), + failures )); } - - auto defaultProgram = CreateDefaultProgram(); - variants.emplace(OvRendering::Resources::Shader::FeatureSet{}, std::move(defaultProgram)); } - else if (__LOG_SUCCESS) + else if (__LOGGING_SETTINGS.summary) { - OVLOG_INFO(std::format("[COMPILE] \"{}\" Compiled ({} variants)", __FILE_TRACE, variantCount)); + OVLOG_INFO(std::format("[COMPILE] \"{}\" Compiled ({} variants)", GetCurrentFileName(), variantCount)); } - return variants; + return ShaderAssembleResult{ + failures, + p_parseResult.features, // TODO: Exclude features that failed to compile + std::move(variants) + }; + } + + ShaderAssembleResult CompileShaderFromFile( + const std::string& p_filePath, + OvRendering::Resources::Loaders::ShaderLoader::FilePathParserCallback p_pathParser + ) + { + const auto shaderLoadResult = LoadShader(p_filePath, p_pathParser); + const auto shaderParseResult = ParseShader(shaderLoadResult); + return AssembleShader(shaderParseResult); + } + + ShaderAssembleResult CompileShaderFromSources( + const std::string& p_vertexShader, + const std::string& p_fragmentShader + ) + { + const ShaderParseResult shaderParseResult{ + .vertexShader = p_vertexShader, + .fragmentShader = p_fragmentShader, + .features = {} // No support for features in embedded shaders + }; + + return AssembleShader(shaderParseResult); } } namespace OvRendering::Resources::Loaders { - void ShaderLoader::SetLoggingSettings(bool p_logErrors, bool p_logSuccess) + ShaderLoader::LoggingSettings ShaderLoader::GetLoggingSettings() + { + return __LOGGING_SETTINGS; + } + + void ShaderLoader::SetLoggingSettings(LoggingSettings p_settings) { - __LOG_ERRORS = p_logErrors; - __LOG_SUCCESS = p_logSuccess; + __LOGGING_SETTINGS = p_settings; } Shader* ShaderLoader::Create(const std::string& p_filePath, FilePathParserCallback p_pathParser) { __FILE_TRACE = p_filePath; - const auto shaderParseResult = ParseShader(p_filePath, p_pathParser); + auto result = CompileShaderFromFile(p_filePath, p_pathParser); return new Shader( p_filePath, - shaderParseResult.features, - std::move(CreatePrograms(shaderParseResult)) + result.features, + std::move(result.variants) ); } @@ -425,21 +497,12 @@ namespace OvRendering::Resources::Loaders { __FILE_TRACE = "{C++ embedded shader}"; - auto shaders = std::array{ - ShaderStageDesc{p_vertexShader, Settings::EShaderType::VERTEX}, - ShaderStageDesc{p_fragmentShader, Settings::EShaderType::FRAGMENT} - }; - - const ShaderParseResult shaderParseResult{ - .vertexShader = p_vertexShader, - .fragmentShader = p_fragmentShader, - .features = {} - }; + auto result = CompileShaderFromSources(p_vertexShader, p_fragmentShader); return new Shader( "", - shaderParseResult.features, - std::move(CreatePrograms(shaderParseResult)) + result.features, + std::move(result.variants) ); } @@ -447,12 +510,23 @@ namespace OvRendering::Resources::Loaders { __FILE_TRACE = p_filePath; - const auto shaderParseResult = ParseShader(p_filePath, p_pathParser); + auto result = CompileShaderFromFile(p_filePath, p_pathParser); - p_shader.SetPrograms( - shaderParseResult.features, - std::move(CreatePrograms(shaderParseResult)) - ); + if (result.failures == 0) + { + p_shader.SetPrograms( + result.features, + std::move(result.variants) + ); + } + else + { + OVLOG_ERROR(std::format( + "[RECOMPILE] \"{}\" Failed! Previous shader kept.", + GetCurrentFileName(), + result.failures + )); + } } bool ShaderLoader::Destroy(Shader*& p_shader) From da14ce34c07b1b0f6d4b8a37cb2e8736d47e67a7 Mon Sep 17 00:00:00 2001 From: Adrien GIVRY Date: Thu, 17 Apr 2025 11:49:19 -0400 Subject: [PATCH 03/12] Simplified shader creation, auto-deduced FeatureSet from ProgramVariants --- .../include/OvRendering/Resources/Shader.h | 8 ++++++-- .../Resources/Loaders/ShaderLoader.cpp | 19 +++---------------- .../src/OvRendering/Resources/Shader.cpp | 19 +++++++++++++++---- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/Sources/Overload/OvRendering/include/OvRendering/Resources/Shader.h b/Sources/Overload/OvRendering/include/OvRendering/Resources/Shader.h index 983742c26..b674a4c88 100644 --- a/Sources/Overload/OvRendering/include/OvRendering/Resources/Shader.h +++ b/Sources/Overload/OvRendering/include/OvRendering/Resources/Shader.h @@ -51,15 +51,19 @@ namespace OvRendering::Resources */ HAL::ShaderProgram& GetProgram(const FeatureSet& p_featureSet = {}); + /** + * Returns supported features + */ + const FeatureSet& GetFeatures() const; + private: Shader( const std::string p_path, - const FeatureSet& p_features, ProgramVariants&& p_programs ); ~Shader() = default; - void SetPrograms(const FeatureSet& p_features, ProgramVariants&& p_programs); + void SetPrograms(ProgramVariants&& p_programs); public: const std::string path; diff --git a/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp b/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp index a2f589426..5e188f19d 100644 --- a/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp +++ b/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp @@ -53,7 +53,6 @@ namespace struct ShaderAssembleResult { const uint32_t failures; // How many variants failed to compile - const OvRendering::Resources::Shader::FeatureSet features; OvRendering::Resources::Shader::ProgramVariants variants; }; @@ -438,7 +437,6 @@ void main() return ShaderAssembleResult{ failures, - p_parseResult.features, // TODO: Exclude features that failed to compile std::move(variants) }; } @@ -486,11 +484,7 @@ namespace OvRendering::Resources::Loaders auto result = CompileShaderFromFile(p_filePath, p_pathParser); - return new Shader( - p_filePath, - result.features, - std::move(result.variants) - ); + return new Shader(p_filePath, std::move(result.variants)); } Shader* ShaderLoader::CreateFromSource(const std::string& p_vertexShader, const std::string& p_fragmentShader) @@ -499,11 +493,7 @@ namespace OvRendering::Resources::Loaders auto result = CompileShaderFromSources(p_vertexShader, p_fragmentShader); - return new Shader( - "", - result.features, - std::move(result.variants) - ); + return new Shader({}, std::move(result.variants)); } void ShaderLoader::Recompile(Shader& p_shader, const std::string& p_filePath, FilePathParserCallback p_pathParser) @@ -514,10 +504,7 @@ namespace OvRendering::Resources::Loaders if (result.failures == 0) { - p_shader.SetPrograms( - result.features, - std::move(result.variants) - ); + p_shader.SetPrograms(std::move(result.variants)); } else { diff --git a/Sources/Overload/OvRendering/src/OvRendering/Resources/Shader.cpp b/Sources/Overload/OvRendering/src/OvRendering/Resources/Shader.cpp index b6026ca54..59bfcef6f 100644 --- a/Sources/Overload/OvRendering/src/OvRendering/Resources/Shader.cpp +++ b/Sources/Overload/OvRendering/src/OvRendering/Resources/Shader.cpp @@ -47,18 +47,29 @@ OvRendering::HAL::ShaderProgram& OvRendering::Resources::Shader::GetProgram(cons } } +const OvRendering::Resources::Shader::FeatureSet& OvRendering::Resources::Shader::GetFeatures() const +{ + return m_features; +} + OvRendering::Resources::Shader::Shader( const std::string p_path, - const FeatureSet& p_features, ProgramVariants&& p_program ) : path(p_path) { - SetPrograms(p_features, std::move(p_program)); + SetPrograms(std::move(p_program)); } -void OvRendering::Resources::Shader::SetPrograms(const FeatureSet& p_features, ProgramVariants&& p_programs) +void OvRendering::Resources::Shader::SetPrograms(ProgramVariants&& p_programs) { ValidateProgramRegistry(p_programs); - m_features = p_features; m_programs = std::move(p_programs); + + m_features.clear(); + + // Find all features based on the compiled programs + for (const auto& [key, _] : m_programs) + { + m_features.insert(key.begin(), key.end()); + } } From c3a540cd26bab949df1c8a7cf7a552013a67fb6e Mon Sep 17 00:00:00 2001 From: Adrien GIVRY Date: Thu, 17 Apr 2025 12:15:36 -0400 Subject: [PATCH 04/12] Updated shader loader documentation for CreateFromSource --- .../include/OvRendering/Resources/Loaders/ShaderLoader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Overload/OvRendering/include/OvRendering/Resources/Loaders/ShaderLoader.h b/Sources/Overload/OvRendering/include/OvRendering/Resources/Loaders/ShaderLoader.h index 956972bdc..ee5ee4f40 100644 --- a/Sources/Overload/OvRendering/include/OvRendering/Resources/Loaders/ShaderLoader.h +++ b/Sources/Overload/OvRendering/include/OvRendering/Resources/Loaders/ShaderLoader.h @@ -59,7 +59,7 @@ namespace OvRendering::Resources::Loaders * Create a shader from source * @param p_vertexShader * @param p_fragmentShader - * @note Doesn't support path parsing/resolving + * @note Doesn't support parsing (no include, no features) */ static Shader* CreateFromSource(const std::string& p_vertexShader, const std::string& p_fragmentShader); From 88ebc465a60fc948854c527427daef8fb27390ff Mon Sep 17 00:00:00 2001 From: Adrien GIVRY Date: Thu, 17 Apr 2025 12:21:28 -0400 Subject: [PATCH 05/12] Cleaned up `ShaderLoader.h` --- .../Resources/Loaders/ShaderLoader.h | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/Sources/Overload/OvRendering/include/OvRendering/Resources/Loaders/ShaderLoader.h b/Sources/Overload/OvRendering/include/OvRendering/Resources/Loaders/ShaderLoader.h index ee5ee4f40..70cc52ce9 100644 --- a/Sources/Overload/OvRendering/include/OvRendering/Resources/Loaders/ShaderLoader.h +++ b/Sources/Overload/OvRendering/include/OvRendering/Resources/Loaders/ShaderLoader.h @@ -8,7 +8,7 @@ #include -#include "OvRendering/Resources/Shader.h" +#include namespace OvRendering::Resources::Loaders { @@ -23,11 +23,11 @@ namespace OvRendering::Resources::Loaders */ struct LoggingSettings { - bool summary : 1; - bool linkingErrors : 1; - bool linkingSuccess : 1; - bool compilationErrors : 1; - bool compilationSuccess : 1; + const bool summary : 1; + const bool linkingErrors : 1; + const bool linkingSuccess : 1; + const bool compilationErrors : 1; + const bool compilationSuccess : 1; }; using FilePathParserCallback = std::function; @@ -43,20 +43,20 @@ namespace OvRendering::Resources::Loaders static LoggingSettings GetLoggingSettings(); /** - * Enable or disable logging for errors and success + * Sets logging settings for the ShaderLoader * @param p_settings */ static void SetLoggingSettings(LoggingSettings p_settings); /** - * Create a shader + * Creates a shader from a file * @param p_filePath * @param p_pathParser */ static Shader* Create(const std::string& p_filePath, FilePathParserCallback p_pathParser = nullptr); /** - * Create a shader from source + * Creates a shader from vertex and fragment source code * @param p_vertexShader * @param p_fragmentShader * @note Doesn't support parsing (no include, no features) @@ -64,16 +64,17 @@ namespace OvRendering::Resources::Loaders static Shader* CreateFromSource(const std::string& p_vertexShader, const std::string& p_fragmentShader); /** - * Recompile a shader + * Recompiles a shader * @param p_shader * @param p_filePath + * @param p_pathParser */ static void Recompile(Shader& p_shader, const std::string& p_filePath, FilePathParserCallback p_pathParser = nullptr); /** - * Destroy a shader + * Destroys a shader * @param p_shader */ static bool Destroy(Shader*& p_shader); }; -} \ No newline at end of file +} From 4df10779e5af529187a0d188b77ba4120a1e1d46 Mon Sep 17 00:00:00 2001 From: Adrien GIVRY Date: Thu, 17 Apr 2025 12:35:51 -0400 Subject: [PATCH 06/12] Code cleanup --- .../Resources/Loaders/ShaderLoader.h | 10 +- .../Resources/Loaders/ShaderLoader.cpp | 190 ++++++++---------- 2 files changed, 91 insertions(+), 109 deletions(-) diff --git a/Sources/Overload/OvRendering/include/OvRendering/Resources/Loaders/ShaderLoader.h b/Sources/Overload/OvRendering/include/OvRendering/Resources/Loaders/ShaderLoader.h index 70cc52ce9..e7ebd719e 100644 --- a/Sources/Overload/OvRendering/include/OvRendering/Resources/Loaders/ShaderLoader.h +++ b/Sources/Overload/OvRendering/include/OvRendering/Resources/Loaders/ShaderLoader.h @@ -23,11 +23,11 @@ namespace OvRendering::Resources::Loaders */ struct LoggingSettings { - const bool summary : 1; - const bool linkingErrors : 1; - const bool linkingSuccess : 1; - const bool compilationErrors : 1; - const bool compilationSuccess : 1; + bool summary : 1; + bool linkingErrors : 1; + bool linkingSuccess : 1; + bool compilationErrors : 1; + bool compilationSuccess : 1; }; using FilePathParserCallback = std::function; diff --git a/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp b/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp index 5e188f19d..40c1224d4 100644 --- a/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp +++ b/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp @@ -75,7 +75,7 @@ namespace std::string FeatureSetToString(const OvRendering::Resources::Shader::FeatureSet& features) { - std::string result = std::format(" CreateProgram( @@ -140,97 +134,85 @@ namespace bool p_noLogging = false ) { - using namespace OvRendering::HAL; - using namespace OvRendering::Resources; - using namespace OvRendering::Settings; + // Lambda for logging messages if enabled + auto log = [&p_noLogging, &p_features](bool condition, const std::string& type, const std::string& message = "Success!") { + if (!p_noLogging && condition) + { + OVLOG_INFO(std::format( + "[{}] \"{}\" {}: {}", + type, + GetCurrentFileName(), + FeatureSetToString(p_features), + message + )); + } + }; + // Process and compile all shader stages std::vector processedStages; processedStages.reserve(p_stages.size()); - uint32_t errorCount = 0; + bool allCompiled = true; - for (auto& stageInput : p_stages) + for (const auto& stageInput : p_stages) { const auto& processedStage = processedStages.emplace_back(stageInput.type); const auto source = EnableFeaturesInShaderCode(stageInput.source, p_features); processedStage.stage.Upload(source); - const auto compilationResult = processedStage.stage.Compile(); - if (!compilationResult.success) - { - std::string shaderTypeStr = OvRendering::Utils::GetShaderTypeName(stageInput.type); - - for (char& c : shaderTypeStr) - { - c = std::toupper(c); - } - - if (!p_noLogging && __LOGGING_SETTINGS.compilationErrors) - { - OVLOG_ERROR(std::format( - "[{} COMPILE] \"{}\" {}: {}", - shaderTypeStr, - GetCurrentFileName(), - FeatureSetToString(p_features), - compilationResult.message) - ); - } - ++errorCount; + if (const auto result = processedStage.stage.Compile(); !result.success) + { + const std::string shaderTypeStr = OvRendering::Utils::GetShaderTypeName(stageInput.type); + + log( + __LOGGING_SETTINGS.compilationErrors, + shaderTypeStr + " Shader Compile", + result.message + ); + allCompiled = false; } else { - if (!p_noLogging && __LOGGING_SETTINGS.compilationSuccess) - { - OVLOG_INFO(std::format( - "[{} COMPILE] \"{}\" {}: Success!", - OvRendering::Utils::GetShaderTypeName(stageInput.type), - GetCurrentFileName(), - FeatureSetToString(p_features)) - ); - } + log( + __LOGGING_SETTINGS.compilationSuccess, + OvRendering::Utils::GetShaderTypeName(stageInput.type) + " Shader Compile" + ); } } - if (errorCount == 0) + if (!allCompiled) { - auto program = std::make_unique(); + return nullptr; + } - for (const auto& processedStage : processedStages) - { - program->Attach(processedStage.stage); - } + // Link the program + auto program = std::make_unique(); - const auto linkResult = program->Link(); + // Attach all stages + for (const auto& processedStage : processedStages) + { + program->Attach(processedStage.stage); + } - for (const auto& processedStage : processedStages) - { - program->Detach(processedStage.stage); - } + // Link and detach regardless of result + const auto linkResult = program->Link(); - if (linkResult.success) - { - if (!p_noLogging && __LOGGING_SETTINGS.linkingSuccess) - { - OVLOG_INFO(std::format( - "[LINK] \"{}\" {}: Success!", - GetCurrentFileName(), - FeatureSetToString(p_features) - )); - } - return program; - } - else if (!p_noLogging && __LOGGING_SETTINGS.linkingErrors) - { - OVLOG_ERROR(std::format( - "[LINK] \"{}\" {}: Failed: {}", - GetCurrentFileName(), - FeatureSetToString(p_features), - linkResult.message - )); - } + // Detach all stages + for (const auto& processedStage : processedStages) + { + program->Detach(processedStage.stage); } - return nullptr; + if (linkResult.success) + { + log(__LOGGING_SETTINGS.linkingSuccess, "Shader Linking"); + return program; + } + else + { + log(__LOGGING_SETTINGS.linkingErrors, "Shader Linking", linkResult.message); + return nullptr; + } } std::unique_ptr CreateDefaultProgram() @@ -319,7 +301,7 @@ void main() } else { - OVLOG_ERROR(std::format("Error: Invalid #include directive in file: \"{}\"", p_filePath)); + OVLOG_ERROR(std::format("[Shader Loading] Invalid #include directive in file: \"{}\"", p_filePath)); } } else { @@ -424,7 +406,7 @@ void main() if (__LOGGING_SETTINGS.summary) { OVLOG_ERROR(std::format( - "[COMPILE] \"{}\" Failed! {} variants failed to compile", + "[Shader Compilation] \"{}\" Failed! {} variants failed to compile", GetCurrentFileName(), failures )); @@ -432,7 +414,7 @@ void main() } else if (__LOGGING_SETTINGS.summary) { - OVLOG_INFO(std::format("[COMPILE] \"{}\" Compiled ({} variants)", GetCurrentFileName(), variantCount)); + OVLOG_INFO(std::format("[Shader Compilation] \"{}\" Compiled ({} variants)", GetCurrentFileName(), variantCount)); } return ShaderAssembleResult{ From 6ac26da163316a842b4b5f48c8e8290f911dd8ef Mon Sep 17 00:00:00 2001 From: Adrien GIVRY Date: Thu, 17 Apr 2025 13:59:20 -0400 Subject: [PATCH 07/12] Cleaned up log output and simplified code --- .../src/OvEditor/Panels/AssetBrowser.cpp | 11 +- .../Resources/Loaders/ShaderLoader.cpp | 150 +++++++++++------- 2 files changed, 96 insertions(+), 65 deletions(-) diff --git a/Sources/Overload/OvEditor/src/OvEditor/Panels/AssetBrowser.cpp b/Sources/Overload/OvEditor/src/OvEditor/Panels/AssetBrowser.cpp index 9b6159223..7b2319c22 100644 --- a/Sources/Overload/OvEditor/src/OvEditor/Panels/AssetBrowser.cpp +++ b/Sources/Overload/OvEditor/src/OvEditor/Panels/AssetBrowser.cpp @@ -623,14 +623,9 @@ namespace auto& shaderManager = OVSERVICE(OvCore::ResourceManagement::ShaderManager); const std::string resourcePath = EDITOR_EXEC(GetResourcePath(filePath, m_protected)); const auto previousLoggingSettings = ShaderLoader::GetLoggingSettings(); - - ShaderLoader::SetLoggingSettings(ShaderLoader::LoggingSettings{ - .summary = true, - .linkingErrors = true, - .linkingSuccess = false, - .compilationErrors = true, - .compilationSuccess = false - }); + auto newLoggingSettings = previousLoggingSettings; + newLoggingSettings.summary = true; // Force enable summary logging + ShaderLoader::SetLoggingSettings(newLoggingSettings); if (shaderManager.IsResourceRegistered(resourcePath)) { diff --git a/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp b/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp index 40c1224d4..191e59f57 100644 --- a/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp +++ b/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp @@ -25,25 +25,31 @@ namespace { - std::filesystem::path __FILE_TRACE; + struct ShaderInputInfo + { + std::string path; + std::string name; + }; OvRendering::Resources::Loaders::ShaderLoader::LoggingSettings __LOGGING_SETTINGS{ .summary = false, - .linkingErrors = false, + .linkingErrors = true, .linkingSuccess = false, - .compilationErrors = false, + .compilationErrors = true, .compilationSuccess = false, }; // Load a shader, including any included files struct ShaderLoadResult { + const ShaderInputInfo inputInfo; const std::string source; }; // Parse the shader source code and extract the vertex and fragment shaders, as well as features struct ShaderParseResult { + const ShaderInputInfo inputInfo; const std::string vertexShader; const std::string fragmentShader; const OvRendering::Resources::Shader::FeatureSet features; @@ -52,6 +58,7 @@ namespace // Compile the shader and create a program for each combination of features struct ShaderAssembleResult { + const ShaderInputInfo inputInfo; const uint32_t failures; // How many variants failed to compile OvRendering::Resources::Shader::ProgramVariants variants; }; @@ -68,27 +75,32 @@ namespace std::optional compilationResult; }; - inline std::string GetCurrentFileName() + std::string GetShaderNameFromPath(const std::string& p_path) { - return __FILE_TRACE.stem().string(); + return std::filesystem::path{ p_path }.stem().string(); } std::string FeatureSetToString(const OvRendering::Resources::Shader::FeatureSet& features) { - std::string result = std::format("(features[{}] {{", features.size()); + if (features.size() == 0) + { + return {}; + } + + std::string result = " ("; bool first = true; for (const auto& feature : features) { if (!first) { - result += ", "; + result += "|"; } result += feature; first = false; } - result += "})"; + result += ")"; return result; } @@ -129,25 +141,12 @@ namespace } std::unique_ptr CreateProgram( + const ShaderInputInfo& p_shaderInputInfo, std::span p_stages, const OvRendering::Resources::Shader::FeatureSet& p_features = {}, bool p_noLogging = false ) { - // Lambda for logging messages if enabled - auto log = [&p_noLogging, &p_features](bool condition, const std::string& type, const std::string& message = "Success!") { - if (!p_noLogging && condition) - { - OVLOG_INFO(std::format( - "[{}] \"{}\" {}: {}", - type, - GetCurrentFileName(), - FeatureSetToString(p_features), - message - )); - } - }; - // Process and compile all shader stages std::vector processedStages; processedStages.reserve(p_stages.size()); @@ -162,21 +161,31 @@ namespace if (const auto result = processedStage.stage.Compile(); !result.success) { - const std::string shaderTypeStr = OvRendering::Utils::GetShaderTypeName(stageInput.type); + if (!p_noLogging && __LOGGING_SETTINGS.compilationErrors) + { + OVLOG_ERROR(std::format( + "[Shader Compile] {}{}: {}", + std::format("{}/{}", + p_shaderInputInfo.name, + OvRendering::Utils::GetShaderTypeName(stageInput.type) + ), + FeatureSetToString(p_features), + result.message + )); + } - log( - __LOGGING_SETTINGS.compilationErrors, - shaderTypeStr + " Shader Compile", - result.message - ); allCompiled = false; } - else + else if (!p_noLogging && __LOGGING_SETTINGS.compilationSuccess) { - log( - __LOGGING_SETTINGS.compilationSuccess, - OvRendering::Utils::GetShaderTypeName(stageInput.type) + " Shader Compile" - ); + OVLOG_INFO(std::format( + "[Shader Compile] {}{}: Compilation successful.", + std::format("{}/{}", + p_shaderInputInfo.name, + OvRendering::Utils::GetShaderTypeName(stageInput.type) + ), + FeatureSetToString(p_features) + )); } } @@ -205,19 +214,35 @@ namespace if (linkResult.success) { - log(__LOGGING_SETTINGS.linkingSuccess, "Shader Linking"); + if (!p_noLogging && __LOGGING_SETTINGS.linkingSuccess) + { + OVLOG_INFO(std::format( + "[Shader Linking] {}{}: {}", + p_shaderInputInfo.name, + FeatureSetToString(p_features), + "Linking successful." + )); + } return program; } else { - log(__LOGGING_SETTINGS.linkingErrors, "Shader Linking", linkResult.message); + if (!p_noLogging && __LOGGING_SETTINGS.linkingErrors) + { + OVLOG_INFO(std::format( + "[Shader Linking] {}{}: {}", + p_shaderInputInfo.name, + FeatureSetToString(p_features), + linkResult.message + )); + } return nullptr; } } std::unique_ptr CreateDefaultProgram() { - const std::string vertex =R"( + const std::string vertex = R"( #version 450 core layout(location = 0) in vec3 geo_Pos; @@ -245,6 +270,10 @@ void main() }; auto program = CreateProgram( + ShaderInputInfo{ + .path = "N/A", + .name = "DefaultProgram" + }, shaders, {}, true // Force no logging for default program (we expect it to succeed, otherwise we have a problem) @@ -273,13 +302,13 @@ void main() } } - ShaderLoadResult LoadShader(const std::string& p_filePath, OvRendering::Resources::Loaders::ShaderLoader::FilePathParserCallback p_pathParser) + ShaderLoadResult LoadShader(const ShaderInputInfo& p_shaderInputInfo, const std::string& p_filePath, OvRendering::Resources::Loaders::ShaderLoader::FilePathParserCallback p_pathParser) { std::ifstream file(p_filePath); if (!file.is_open()) { - OVLOG_ERROR(std::format("Error: Could not open shader file: \"{}\"", p_filePath)); + OVLOG_ERROR(std::format("[Shader Loading] Could not open shader file: \"{}\"", p_filePath)); return {}; } @@ -296,7 +325,7 @@ void main() { // Recursively load the included file const std::string realIncludeFilePath = p_pathParser ? p_pathParser(includeFilePath) : includeFilePath; - const auto result = LoadShader(realIncludeFilePath, p_pathParser); + const auto result = LoadShader(p_shaderInputInfo, realIncludeFilePath, p_pathParser); buffer << result.source << std::endl; } else @@ -311,7 +340,8 @@ void main() } return { - buffer.str() + .inputInfo = p_shaderInputInfo, + .source = buffer.str() }; } @@ -344,8 +374,8 @@ void main() } } - return - { + return { + p_shaderLoadResult.inputInfo, ss[OvRendering::Settings::EShaderType::VERTEX].str(), ss[OvRendering::Settings::EShaderType::FRAGMENT].str(), features @@ -378,6 +408,7 @@ void main() }); auto program = CreateProgram( + p_parseResult.inputInfo, stages, featureSet ); @@ -406,18 +437,23 @@ void main() if (__LOGGING_SETTINGS.summary) { OVLOG_ERROR(std::format( - "[Shader Compilation] \"{}\" Failed! {} variants failed to compile", - GetCurrentFileName(), + "[Shader Assembling] {}: {} variant(s) failed to compile.", + p_parseResult.inputInfo.name, failures )); } } else if (__LOGGING_SETTINGS.summary) { - OVLOG_INFO(std::format("[Shader Compilation] \"{}\" Compiled ({} variants)", GetCurrentFileName(), variantCount)); + OVLOG_INFO(std::format( + "[Shader Assembling] {}: {} variant(s) assembled.", + p_parseResult.inputInfo.name, + variantCount + )); } return ShaderAssembleResult{ + p_parseResult.inputInfo, failures, std::move(variants) }; @@ -428,7 +464,12 @@ void main() OvRendering::Resources::Loaders::ShaderLoader::FilePathParserCallback p_pathParser ) { - const auto shaderLoadResult = LoadShader(p_filePath, p_pathParser); + const auto shaderInputInfo = ShaderInputInfo{ + .path = p_filePath, + .name = GetShaderNameFromPath(p_filePath) + }; + + const auto shaderLoadResult = LoadShader(shaderInputInfo, p_filePath, p_pathParser); const auto shaderParseResult = ParseShader(shaderLoadResult); return AssembleShader(shaderParseResult); } @@ -439,6 +480,10 @@ void main() ) { const ShaderParseResult shaderParseResult{ + .inputInfo = { + .path = "N/A", + .name = "{C++ embedded shader}" + }, .vertexShader = p_vertexShader, .fragmentShader = p_fragmentShader, .features = {} // No support for features in embedded shaders @@ -462,26 +507,18 @@ namespace OvRendering::Resources::Loaders Shader* ShaderLoader::Create(const std::string& p_filePath, FilePathParserCallback p_pathParser) { - __FILE_TRACE = p_filePath; - auto result = CompileShaderFromFile(p_filePath, p_pathParser); - return new Shader(p_filePath, std::move(result.variants)); } Shader* ShaderLoader::CreateFromSource(const std::string& p_vertexShader, const std::string& p_fragmentShader) { - __FILE_TRACE = "{C++ embedded shader}"; - auto result = CompileShaderFromSources(p_vertexShader, p_fragmentShader); - return new Shader({}, std::move(result.variants)); } void ShaderLoader::Recompile(Shader& p_shader, const std::string& p_filePath, FilePathParserCallback p_pathParser) { - __FILE_TRACE = p_filePath; - auto result = CompileShaderFromFile(p_filePath, p_pathParser); if (result.failures == 0) @@ -491,9 +528,8 @@ namespace OvRendering::Resources::Loaders else { OVLOG_ERROR(std::format( - "[RECOMPILE] \"{}\" Failed! Previous shader kept.", - GetCurrentFileName(), - result.failures + "[Shader Reload] {}: reloading failed, previous shader programs kept.", + result.inputInfo.name )); } } From 071b877562f57d3845dcb606f594b84adccceab6 Mon Sep 17 00:00:00 2001 From: Adrien GIVRY Date: Thu, 17 Apr 2025 16:14:57 -0400 Subject: [PATCH 08/12] Added support for spaces & tabs before and after the feature name --- .../Resources/Loaders/ShaderLoader.cpp | 58 ++++++++++++++----- 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp b/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp index 191e59f57..7ccd20100 100644 --- a/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp +++ b/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -347,38 +348,63 @@ void main() ShaderParseResult ParseShader(const ShaderLoadResult& p_shaderLoadResult) { + using namespace OvRendering::Settings; + std::istringstream stream(p_shaderLoadResult.source); // Add this line to create a stringstream from shaderCode std::string line; - std::unordered_map ss; + std::unordered_map shaderSources; OvRendering::Resources::Shader::FeatureSet features; - auto type = OvRendering::Settings::EShaderType::NONE; + auto currentType = EShaderType::NONE; while (std::getline(stream, line)) { - if (line.starts_with("#feature")) + // Pratical so we can use "starts_with" to check for custom directives + auto view = + line | + std::views::drop_while(isspace) | + std::views::reverse | + std::views::drop_while(isspace) | + std::views::reverse; + + const auto trimmedLine = std::string{ view.begin(), view.end()}; + + constexpr std::string_view kFeatureToken = "#feature"; + + if (trimmedLine.starts_with(kFeatureToken)) { - const std::string featureName = line.substr(line.find(' ') + 1); + std::string featureName; + featureName.reserve(16); // Reserve some arbitrary space for the feature name + + for (auto& c : trimmedLine | + std::views::drop(kFeatureToken.size()) | + std::views::drop_while(isspace) | + std::views::take_while([](char c) { return !isspace(c); })) + { + featureName += c; + } + features.insert(featureName); } - else if (line.find("#shader") != std::string::npos) + else if (trimmedLine.starts_with("#shader vertex")) { - if (line.find("vertex") != std::string::npos) - type = OvRendering::Settings::EShaderType::VERTEX; - else if (line.find("fragment") != std::string::npos) - type = OvRendering::Settings::EShaderType::FRAGMENT; + currentType = EShaderType::VERTEX; } - else if (type != OvRendering::Settings::EShaderType::NONE) + else if (trimmedLine.starts_with("#shader fragment")) { - ss[type] << line << '\n'; + currentType = EShaderType::FRAGMENT; + } + else if (currentType != EShaderType::NONE) + { + shaderSources[currentType] << line << '\n'; } } - return { - p_shaderLoadResult.inputInfo, - ss[OvRendering::Settings::EShaderType::VERTEX].str(), - ss[OvRendering::Settings::EShaderType::FRAGMENT].str(), - features + return ShaderParseResult{ + .inputInfo = p_shaderLoadResult.inputInfo, + .vertexShader = shaderSources[EShaderType::VERTEX].str(), + .fragmentShader = shaderSources[EShaderType::FRAGMENT].str(), + .features = features }; } From 58ac250512a6ee034e12adfc8dd536a96f0ac331 Mon Sep 17 00:00:00 2001 From: Adrien GIVRY Date: Thu, 17 Apr 2025 17:01:24 -0400 Subject: [PATCH 09/12] Code cleanup, added Grammar namespace for shader tokens --- .../Resources/Loaders/ShaderLoader.cpp | 124 +++++++++--------- 1 file changed, 63 insertions(+), 61 deletions(-) diff --git a/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp b/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp index 7ccd20100..9265ccaf1 100644 --- a/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp +++ b/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,16 @@ namespace { + namespace Grammar + { + constexpr std::string_view kFeatureToken = "#feature"; + constexpr std::string_view kVertexShaderToken = "#shader vertex"; + constexpr std::string_view kFragmentShaderToken = "#shader fragment"; + constexpr std::string_view kIncludeToken = "#include"; + constexpr std::string_view kDefineToken = "#define"; + constexpr std::string_view kVersionToken = "#version"; + } + struct ShaderInputInfo { std::string path; @@ -81,28 +92,31 @@ namespace return std::filesystem::path{ p_path }.stem().string(); } - std::string FeatureSetToString(const OvRendering::Resources::Shader::FeatureSet& features) + std::string Trim(const std::string_view p_str) { - if (features.size() == 0) - { - return {}; - } - - std::string result = " ("; + auto view = + p_str | + std::views::drop_while(isspace) | + std::views::reverse | + std::views::drop_while(isspace) | + std::views::reverse; + + return std::string{ view.begin(), view.end() }; + } - bool first = true; - for (const auto& feature : features) + std::string FeatureSetToString(const OvRendering::Resources::Shader::FeatureSet& features) + { + if (!features.empty()) { - if (!first) - { - result += "|"; - } - result += feature; - first = false; + return std::format("({})", std::accumulate( + std::next(features.begin()), features.end(), *features.begin(), + [](const auto& a, const auto& b) { + return std::format("{}|{}", a, b); + } + )); } - result += ")"; - return result; + return std::string{}; } std::string EnableFeaturesInShaderCode(const std::string& shaderCode, const OvRendering::Resources::Shader::FeatureSet& features) @@ -116,11 +130,11 @@ namespace std::string definesStr; for (const auto& feature : features) { - definesStr += "#define " + feature + "\n"; + definesStr += std::format("{} {}\n", Grammar::kDefineToken, feature); } // Find insertion point after version directive if it exists - const size_t versionPos = shaderCode.find("#version"); + const size_t versionPos = shaderCode.find(Grammar::kVersionToken); if (versionPos == std::string::npos) { // No version directive, prepend defines @@ -144,15 +158,15 @@ namespace std::unique_ptr CreateProgram( const ShaderInputInfo& p_shaderInputInfo, std::span p_stages, - const OvRendering::Resources::Shader::FeatureSet& p_features = {}, - bool p_noLogging = false + const OvRendering::Resources::Shader::FeatureSet& p_features, + bool p_disableLogging = false ) { // Process and compile all shader stages std::vector processedStages; processedStages.reserve(p_stages.size()); - bool allCompiled = true; + bool compilationFailed = false; for (const auto& stageInput : p_stages) { @@ -162,7 +176,7 @@ namespace if (const auto result = processedStage.stage.Compile(); !result.success) { - if (!p_noLogging && __LOGGING_SETTINGS.compilationErrors) + if (!p_disableLogging && __LOGGING_SETTINGS.compilationErrors) { OVLOG_ERROR(std::format( "[Shader Compile] {}{}: {}", @@ -175,9 +189,9 @@ namespace )); } - allCompiled = false; + compilationFailed = true; } - else if (!p_noLogging && __LOGGING_SETTINGS.compilationSuccess) + else if (!p_disableLogging && __LOGGING_SETTINGS.compilationSuccess) { OVLOG_INFO(std::format( "[Shader Compile] {}{}: Compilation successful.", @@ -190,7 +204,7 @@ namespace } } - if (!allCompiled) + if (compilationFailed) { return nullptr; } @@ -215,7 +229,7 @@ namespace if (linkResult.success) { - if (!p_noLogging && __LOGGING_SETTINGS.linkingSuccess) + if (!p_disableLogging && __LOGGING_SETTINGS.linkingSuccess) { OVLOG_INFO(std::format( "[Shader Linking] {}{}: {}", @@ -224,11 +238,12 @@ namespace "Linking successful." )); } + return program; } else { - if (!p_noLogging && __LOGGING_SETTINGS.linkingErrors) + if (!p_disableLogging && __LOGGING_SETTINGS.linkingErrors) { OVLOG_INFO(std::format( "[Shader Linking] {}{}: {}", @@ -237,6 +252,7 @@ namespace linkResult.message )); } + return nullptr; } } @@ -284,23 +300,17 @@ void main() return std::move(program); } - bool ParseIncludeDirective(const std::string& line, std::string& includeFilePath) + std::optional ParseIncludeDirective(const std::string_view line) { - // Find the position of the opening and closing quotes - size_t start = line.find("\""); - size_t end = line.find("\"", start + 1); + const auto start = line.find('"'); + const auto end = line.find('"', start + 1); - // Check if both quotes are found - if (start != std::string::npos && end != std::string::npos && end > start) - { - // Extract the included file path - includeFilePath = line.substr(start + 1, end - start - 1); - return true; - } - else + if (start != std::string_view::npos && end != std::string_view::npos && end > start) { - return false; + return std::make_optional(line.substr(start + 1, end - start - 1)); } + + return std::nullopt; } ShaderLoadResult LoadShader(const ShaderInputInfo& p_shaderInputInfo, const std::string& p_filePath, OvRendering::Resources::Loaders::ShaderLoader::FilePathParserCallback p_pathParser) @@ -318,14 +328,15 @@ void main() while (std::getline(file, line)) { - if (line.find("#include") != std::string::npos) + const std::string trimmedLine = Trim(line); + + if (trimmedLine.starts_with(Grammar::kIncludeToken)) { // If the line contains #include, process the included file - std::string includeFilePath; - if (ParseIncludeDirective(line, includeFilePath)) + if (const auto includeFilePath = ParseIncludeDirective(line)) { // Recursively load the included file - const std::string realIncludeFilePath = p_pathParser ? p_pathParser(includeFilePath) : includeFilePath; + const std::string realIncludeFilePath = p_pathParser ? p_pathParser(includeFilePath.value()) : includeFilePath.value(); const auto result = LoadShader(p_shaderInputInfo, realIncludeFilePath, p_pathParser); buffer << result.source << std::endl; } @@ -334,7 +345,8 @@ void main() OVLOG_ERROR(std::format("[Shader Loading] Invalid #include directive in file: \"{}\"", p_filePath)); } } - else { + else + { // If the line does not contain #include, just append it to the buffer buffer << line << std::endl; } @@ -359,25 +371,15 @@ void main() while (std::getline(stream, line)) { - // Pratical so we can use "starts_with" to check for custom directives - auto view = - line | - std::views::drop_while(isspace) | - std::views::reverse | - std::views::drop_while(isspace) | - std::views::reverse; - - const auto trimmedLine = std::string{ view.begin(), view.end()}; - - constexpr std::string_view kFeatureToken = "#feature"; + const std::string trimmedLine = Trim(line); - if (trimmedLine.starts_with(kFeatureToken)) + if (trimmedLine.starts_with(Grammar::kFeatureToken)) { std::string featureName; featureName.reserve(16); // Reserve some arbitrary space for the feature name for (auto& c : trimmedLine | - std::views::drop(kFeatureToken.size()) | + std::views::drop(Grammar::kFeatureToken.size()) | std::views::drop_while(isspace) | std::views::take_while([](char c) { return !isspace(c); })) { @@ -386,11 +388,11 @@ void main() features.insert(featureName); } - else if (trimmedLine.starts_with("#shader vertex")) + else if (trimmedLine.starts_with(Grammar::kVertexShaderToken)) { currentType = EShaderType::VERTEX; } - else if (trimmedLine.starts_with("#shader fragment")) + else if (trimmedLine.starts_with(Grammar::kFragmentShaderToken)) { currentType = EShaderType::FRAGMENT; } From 565099a43e54173e65ba5617be3c4e3377fefde4 Mon Sep 17 00:00:00 2001 From: Adrien GIVRY Date: Thu, 17 Apr 2025 17:36:22 -0400 Subject: [PATCH 10/12] More code cleanup --- .../src/OvRendering/Resources/Loaders/ShaderLoader.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp b/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp index 9265ccaf1..ef51dc583 100644 --- a/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp +++ b/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp @@ -87,11 +87,6 @@ namespace std::optional compilationResult; }; - std::string GetShaderNameFromPath(const std::string& p_path) - { - return std::filesystem::path{ p_path }.stem().string(); - } - std::string Trim(const std::string_view p_str) { auto view = @@ -494,7 +489,7 @@ void main() { const auto shaderInputInfo = ShaderInputInfo{ .path = p_filePath, - .name = GetShaderNameFromPath(p_filePath) + .name = std::filesystem::path{ p_filePath }.stem().string() }; const auto shaderLoadResult = LoadShader(shaderInputInfo, p_filePath, p_pathParser); From 405e52a7deb61cd885cc4ba62b7808c5bb6329bb Mon Sep 17 00:00:00 2001 From: Adrien GIVRY Date: Thu, 17 Apr 2025 18:01:42 -0400 Subject: [PATCH 11/12] Re-ordered includes --- .../src/OvRendering/Resources/Loaders/ShaderLoader.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp b/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp index ef51dc583..f40d304e8 100644 --- a/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp +++ b/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp @@ -16,13 +16,13 @@ #include #include -#include #include +#include -#include -#include #include #include +#include +#include #include namespace From e1860179aa8ce8f18705020673abe3ddf1826678 Mon Sep 17 00:00:00 2001 From: Adrien GIVRY Date: Thu, 17 Apr 2025 18:07:10 -0400 Subject: [PATCH 12/12] Added more documentation to important functions --- .../Resources/Loaders/ShaderLoader.cpp | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp b/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp index f40d304e8..5ba1fc843 100644 --- a/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp +++ b/Sources/Overload/OvRendering/src/OvRendering/Resources/Loaders/ShaderLoader.cpp @@ -51,14 +51,12 @@ namespace .compilationSuccess = false, }; - // Load a shader, including any included files struct ShaderLoadResult { const ShaderInputInfo inputInfo; const std::string source; }; - // Parse the shader source code and extract the vertex and fragment shaders, as well as features struct ShaderParseResult { const ShaderInputInfo inputInfo; @@ -67,7 +65,6 @@ namespace const OvRendering::Resources::Shader::FeatureSet features; }; - // Compile the shader and create a program for each combination of features struct ShaderAssembleResult { const ShaderInputInfo inputInfo; @@ -308,7 +305,14 @@ void main() return std::nullopt; } - ShaderLoadResult LoadShader(const ShaderInputInfo& p_shaderInputInfo, const std::string& p_filePath, OvRendering::Resources::Loaders::ShaderLoader::FilePathParserCallback p_pathParser) + /** + * Loads a shader file (ovfx) and its included files (ovfxh) recursively. + */ + ShaderLoadResult LoadShader( + const ShaderInputInfo& p_shaderInputInfo, + const std::string& p_filePath, + OvRendering::Resources::Loaders::ShaderLoader::FilePathParserCallback p_pathParser + ) { std::ifstream file(p_filePath); @@ -353,6 +357,9 @@ void main() }; } + /** + * Parse the laoded shader code and extract the vertex and fragment shaders, as well as features + */ ShaderParseResult ParseShader(const ShaderLoadResult& p_shaderLoadResult) { using namespace OvRendering::Settings; @@ -405,6 +412,9 @@ void main() }; } + /** + * Compile and create programs for each shader variant, and assemble them for a shader to use. + */ ShaderAssembleResult AssembleShader(const ShaderParseResult& p_parseResult) { const auto variantCount = (size_t{ 1UL } << p_parseResult.features.size()); @@ -446,7 +456,7 @@ void main() } } - // If no default program was created, we create a default one + // If no default program was created, we create a default one (fallback) if (!variants.contains({})) { variants.emplace(