From be4b47894d2c9c96bc34f4f37ba98e3c5cfd2dd0 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Sun, 27 Aug 2023 02:40:17 -0700 Subject: [PATCH] feat: add Shader Defines advanced option This can set Shader Compiler defines for advanced debugging --- CMakeLists.txt | 22 ++++++++++++---------- src/Menu.cpp | 13 +++++++++++++ src/ShaderCache.cpp | 11 ++++++++++- src/State.cpp | 36 ++++++++++++++++++++++++++++++++++++ src/State.h | 5 +++++ src/Util.cpp | 13 +++++++++++++ vcpkg.json | 1 + 7 files changed, 90 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e0668e9e3..9a835edf9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,24 +16,26 @@ find_package(imgui CONFIG REQUIRED) find_package(EASTL CONFIG REQUIRED) find_package(directxtk CONFIG REQUIRED) find_path(CLIB_UTIL_INCLUDE_DIRS "ClibUtil/utils.hpp") +find_package(pystring CONFIG REQUIRED) target_include_directories( ${PROJECT_NAME} PRIVATE - ${CLIB_UTIL_INCLUDE_DIRS} + ${CLIB_UTIL_INCLUDE_DIRS} ) target_link_libraries( - ${PROJECT_NAME} + ${PROJECT_NAME} PRIVATE - debug ${CMAKE_CURRENT_SOURCE_DIR}/include/detours/Debug/detours.lib - optimized ${CMAKE_CURRENT_SOURCE_DIR}/include/detours/Release/detours.lib - magic_enum::magic_enum - xbyak::xbyak - nlohmann_json::nlohmann_json - imgui::imgui - EASTL - Microsoft::DirectXTK + debug ${CMAKE_CURRENT_SOURCE_DIR}/include/detours/Debug/detours.lib + optimized ${CMAKE_CURRENT_SOURCE_DIR}/include/detours/Release/detours.lib + magic_enum::magic_enum + xbyak::xbyak + nlohmann_json::nlohmann_json + imgui::imgui + EASTL + Microsoft::DirectXTK + pystring::pystring ) # https://gitlab.kitware.com/cmake/cmake/-/issues/24922#note_1371990 diff --git a/src/Menu.cpp b/src/Menu.cpp index 34cdbd531..89002615a 100644 --- a/src/Menu.cpp +++ b/src/Menu.cpp @@ -1,6 +1,7 @@ #include "Menu.h" #include +#include #include #include "ShaderCache.h" @@ -395,6 +396,18 @@ void Menu::DrawSettings() ImGui::PopTextWrapPos(); ImGui::EndTooltip(); } + + auto& shaderDefines = State::GetSingleton()->shaderDefinesString; + if (ImGui::InputText("Shader Defines", &shaderDefines)) { + State::GetSingleton()->SetDefines(shaderDefines); + } + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); + ImGui::Text("Defines for Shader Compiler. Semicolon \";\" separated. Clear with space. Rebuild shaders after making change. Compute Shaders require a restart to recompile."); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } } if (ImGui::CollapsingHeader("General", ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)) { diff --git a/src/ShaderCache.cpp b/src/ShaderCache.cpp index ce415e4c4..9aa172631 100644 --- a/src/ShaderCache.cpp +++ b/src/ShaderCache.cpp @@ -902,6 +902,10 @@ namespace SIE for (const auto& def : defines) { if (def.Name != nullptr) { result += def.Name; + if (def.Definition != nullptr && !std::string(def.Definition).empty()) { + result += "="; + result += def.Definition; + } result += ' '; } else { break; @@ -1054,7 +1058,7 @@ namespace SIE const auto type = shader.shaderType.get(); const std::wstring path = GetShaderPath(shader.fxpFilename); - std::array defines; + std::array defines{}; auto lastIndex = 0; if (shaderClass == ShaderClass::Vertex) { defines[lastIndex++] = { "VSHADER", nullptr }; @@ -1067,6 +1071,11 @@ namespace SIE } if (REL::Module::IsVR()) defines[lastIndex++] = { "VR", nullptr }; + auto shaderDefines = State::GetSingleton()->GetDefines(); + if (!shaderDefines->empty()) { + for (unsigned int i = 0; i < shaderDefines->size(); i++) + defines[lastIndex++] = { shaderDefines->at(i).first.c_str(), shaderDefines->at(i).second.c_str() }; + } defines[lastIndex] = { nullptr, nullptr }; // do final entry GetShaderDefines(type, descriptor, &defines[lastIndex]); diff --git a/src/State.cpp b/src/State.cpp index 96b74a056..f9305081a 100644 --- a/src/State.cpp +++ b/src/State.cpp @@ -1,6 +1,7 @@ #include "State.h" #include +#include #include "Menu.h" #include "ShaderCache.h" @@ -79,6 +80,8 @@ void State::Load() if (advanced["Log Level"].is_number_integer()) { logLevel = static_cast((int)advanced["Log Level"]); //logLevel = static_cast(max(spdlog::level::trace, min(spdlog::level::off, (int)advanced["Log Level"]))); + if (advanced["Shader Defines"].is_string()) + SetDefines(advanced["Shader Defines"]); } } @@ -120,6 +123,7 @@ void State::Save() json advanced; advanced["Dump Shaders"] = shaderCache.IsDump(); advanced["Log Level"] = logLevel; + advanced["Shader Defines"] = shaderDefinesString; settings["Advanced"] = advanced; json general; @@ -170,6 +174,38 @@ spdlog::level::level_enum State::GetLogLevel() return logLevel; } +void State::SetDefines(std::string a_defines) +{ + shaderDefines.clear(); + shaderDefinesString = ""; + std::string name = ""; + std::string definition = ""; + auto defines = pystring::split(a_defines, ";"); + for (const auto& define : defines) { + auto cleanedDefine = pystring::strip(define); + auto token = pystring::split(cleanedDefine, "="); + if (token.empty() || token[0].empty()) + continue; + if (token.size() > 2) { + logger::warn("Define string has too many '='; ignoring {}", define); + continue; + } + name = pystring::strip(token[0]); + if (token.size() == 2) { + definition = pystring::strip(token[1]); + } + shaderDefinesString += pystring::strip(define) + ";"; + shaderDefines.push_back(std::pair(name, definition)); + } + shaderDefinesString = shaderDefinesString.substr(0, shaderDefinesString.size() - 1); + logger::debug("Shader Defines set to {}", shaderDefinesString); +} + +std::vector>* State::GetDefines() +{ + return &shaderDefines; +} + bool State::ShaderEnabled(const RE::BSShader::Type a_type) { auto index = static_cast(a_type) + 1; diff --git a/src/State.h b/src/State.h index 674386714..eee740dd8 100644 --- a/src/State.h +++ b/src/State.h @@ -18,6 +18,8 @@ class State uint32_t currentVertexDescriptor = 0; uint32_t currentPixelDescriptor = 0; spdlog::level::level_enum logLevel = spdlog::level::info; + std::string shaderDefinesString = ""; + std::vector> shaderDefines{}; // data structure to parse string into; needed to avoid dangling pointers void Draw(); void Reset(); @@ -32,6 +34,9 @@ class State void SetLogLevel(spdlog::level::level_enum a_level = spdlog::level::info); spdlog::level::level_enum GetLogLevel(); + void SetDefines(std::string defines); + std::vector>* GetDefines(); + /* * Whether a_type is currently enabled in Community Shaders * diff --git a/src/Util.cpp b/src/Util.cpp index d3542d03c..c7fd97994 100644 --- a/src/Util.cpp +++ b/src/Util.cpp @@ -125,6 +125,11 @@ namespace Util macros.push_back({ "D3DCOMPILE_SKIP_OPTIMIZATION", "" }); macros.push_back({ "D3DCOMPILE_DEBUG", "" }); } + auto shaderDefines = State::GetSingleton()->GetDefines(); + if (!shaderDefines->empty()) { + for (unsigned int i = 0; i < shaderDefines->size(); i++) + macros.push_back({ shaderDefines->at(i).first.c_str(), shaderDefines->at(i).second.c_str() }); + } if (!_stricmp(ProgramType, "ps_5_0")) macros.push_back({ "PIXELSHADER", "" }); else if (!_stricmp(ProgramType, "vs_5_0")) @@ -197,6 +202,10 @@ namespace Util for (const auto& def : defines) { if (def.first != nullptr) { result += def.first; + if (def.second != nullptr && !std::string(def.second).empty()) { + result += "="; + result += def.second; + } result += ' '; } else { break; @@ -210,6 +219,10 @@ namespace Util for (const auto& def : defines) { if (def.Name != nullptr) { result += def.Name; + if (def.Definition != nullptr && !std::string(def.Definition).empty()) { + result += "="; + result += def.Definition; + } result += ' '; } else { break; diff --git a/vcpkg.json b/vcpkg.json index 12f38287d..8870f8cd3 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -13,6 +13,7 @@ "catch2", "magic-enum", "nlohmann-json", + "pystring", { "name": "imgui", "features": [