From ffd72ee5989969a2446b19897d42f5abac9580df Mon Sep 17 00:00:00 2001 From: PancakeTAS Date: Mon, 14 Jul 2025 00:06:36 +0200 Subject: [PATCH] groundwork for a configuration file --- .gitmodules | 10 +- CMakeLists.txt | 5 +- include/config/config.hpp | 51 ++++++++++ include/utils/benchmark.hpp | 10 ++ pe-parse | 1 - src/config/config.cpp | 15 +++ src/main.cpp | 96 +++++++++++++++++++ src/utils/benchmark.cpp | 182 +++++++++++++++++------------------- dxbc => thirdparty/dxbc | 0 thirdparty/pe-parse | 1 + 10 files changed, 265 insertions(+), 106 deletions(-) create mode 100644 include/config/config.hpp create mode 100644 include/utils/benchmark.hpp delete mode 160000 pe-parse create mode 100644 src/config/config.cpp create mode 100644 src/main.cpp rename dxbc => thirdparty/dxbc (100%) create mode 160000 thirdparty/pe-parse diff --git a/.gitmodules b/.gitmodules index 57f075d..a01239c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ -[submodule "dxbc"] - path = dxbc - url = https://github.com/PancakeTAS/dxbc.git -[submodule "pe-parse"] - path = pe-parse +[submodule "thirdparty/pe-parse"] + path = thirdparty/pe-parse url = https://github.com/trailofbits/pe-parse +[submodule "thirdparty/dxbc"] + path = thirdparty/dxbc + url = https://github.com/PancakeTAS/dxbc.git diff --git a/CMakeLists.txt b/CMakeLists.txt index dab32fe..cef0e3b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,8 +14,8 @@ add_compile_options(-fPIC -Wno-deprecated-declarations -Wno-unused-template) -add_subdirectory(dxbc EXCLUDE_FROM_ALL) -add_subdirectory(pe-parse/pe-parser-library EXCLUDE_FROM_ALL) +add_subdirectory(thirdparty/dxbc EXCLUDE_FROM_ALL) +add_subdirectory(thirdparty/pe-parse/pe-parser-library EXCLUDE_FROM_ALL) add_subdirectory(lsfg-vk-common) add_subdirectory(lsfg-vk-v3.1) add_subdirectory(lsfg-vk-v3.1p) @@ -27,6 +27,7 @@ project(lsfg-vk LANGUAGES CXX) file(GLOB SOURCES + "src/config/*.cpp" "src/extract/*.cpp" "src/mini/*.cpp" "src/utils/*.cpp" diff --git a/include/config/config.hpp b/include/config/config.hpp new file mode 100644 index 0000000..733330b --- /dev/null +++ b/include/config/config.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace Config { + + /// lsfg-vk configuration. + struct Configuration { + /// Whether lsfg-vk should be loaded in the first place. + bool enable{false}; + /// Path to Lossless.dll. + std::string dll; + + /// The frame generation muliplier + size_t multiplier{2}; + /// The internal flow scale factor + float flowScale{1.0F}; + /// Whether performance mode is enabled + bool performance{false}; + /// Whether HDR is enabled + bool hdr{false}; + + /// Atomic property to check if the configuration is valid or outdated. + std::shared_ptr valid; + }; + + /// + /// Load the config file and create a file watcher. + /// + /// @param file The path to the configuration file. + /// @return Whether a configuration exists or not. + /// + /// @throws std::runtime_error if an error occurs while loading the configuration file. + /// + bool loadAndWatchConfig(const std::string& file); + + /// + /// Get the configuration for a game. + /// + /// @param name The name of the executable to fetch. + /// @return The configuration for the game or global configuration. + /// + /// @throws std::runtime_error if the configuration is invalid. + /// + Configuration getConfig(std::string_view name); + +} diff --git a/include/utils/benchmark.hpp b/include/utils/benchmark.hpp new file mode 100644 index 0000000..f940872 --- /dev/null +++ b/include/utils/benchmark.hpp @@ -0,0 +1,10 @@ +#pragma once + +namespace Benchmark { + + /// + /// Run the benchmark. + /// + void run(); + +} diff --git a/pe-parse b/pe-parse deleted file mode 160000 index 7888f1f..0000000 --- a/pe-parse +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7888f1f8de2f6bc302c291a5b4519fad926c0133 diff --git a/src/config/config.cpp b/src/config/config.cpp new file mode 100644 index 0000000..c6b856f --- /dev/null +++ b/src/config/config.cpp @@ -0,0 +1,15 @@ +#include "config/config.hpp" + +using namespace Config; + +const Configuration defaultConf{ + .enable = false +}; + +bool Config::loadAndWatchConfig(const std::string& file) { + return false; +} + +Configuration Config::getConfig(std::string_view name) { + return defaultConf; +} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..d35d624 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,96 @@ +#include "config/config.hpp" + +#include +#include +#include +#include +#include +#include + +#include // NOLINT +#include + +namespace { + /// Check if the library is preloaded or Vulkan loaded. + bool isPreload() { + const char* preload = std::getenv("LD_PRELOAD"); + if (!preload || *preload == '\0') + return false; + const std::string preload_str(preload); + return preload_str.find("liblsfg-vk.so") != std::string::npos; + } + + /// Check if a benchmark is requested. + bool requestedBenchmark() { + const char* benchmark = std::getenv("LSFG_BENCHMARK"); + if (!benchmark || *benchmark == '\0') + return false; + const std::string benchmark_str(benchmark); + return benchmark_str == "1"; + } + + /// Get the process name + std::string getProcessName() { + std::array exe{}; + const ssize_t exe_len = readlink("/proc/self/exe", exe.data(), exe.size() - 1); + if (exe_len <= 0) + return "Unknown Process"; + exe.at(static_cast(exe_len)) = '\0'; + return{basename(exe.data())}; + } + + /// Get the config file + std::string getConfigFile() { + const char* configFile = std::getenv("LSFG_CONFIG"); + if (configFile && *configFile != '\0') + return{configFile}; + const char* homePath = std::getenv("HOME"); + if (homePath && *homePath != '\0') + return std::string(homePath) + "/.config/lsfg-vk.conf"; + return "/etc/lsfg-vk.conf"; + } + + __attribute__((constructor)) void lsfgvk_init() { + if (isPreload()) { + if (requestedBenchmark()) { + // TODO: Call benchmark function. + exit(0); + } + + std::cerr << "lsfg-vk: This library is not meant to be preloaded, unless you are running a benchmark.\n"; + exit(1); + } + + // read configuration (might block) + const std::string file = getConfigFile(); + try { + Config::loadAndWatchConfig(file); + } catch (const std::exception& e) { + std::cerr << "lsfg-vk: Unable to read configuration file, exiting." << '\n'; + std::cerr << e.what() << '\n'; + exit(0); + } + + const std::string name = getProcessName(); + Config::Configuration conf{}; + try { + conf = Config::getConfig(name); + } catch (const std::exception& e) { + std::cerr << "lsfg-vk: The configuration for " << name << " is invalid, exiting." << '\n'; + std::cerr << e.what() << '\n'; + exit(0); + } + + // exit silently if not enabled + if (!conf.enable) + return; + + // print config + std::cerr << "lsfg-vk: Loaded configuration for " << name << ":\n"; + std::cerr << "lsfg-vk: Using DLL from: " << conf.dll << '\n'; + std::cerr << "lsfg-vk: Multiplier: " << conf.multiplier << '\n'; + std::cerr << "lsfg-vk: Flow Scale: " << conf.flowScale << '\n'; + std::cerr << "lsfg-vk: Performance Mode: " << (conf.performance ? "Enabled" : "Disabled") << '\n'; + std::cerr << "lsfg-vk: HDR: " << (conf.hdr ? "Enabled" : "Disabled") << '\n'; + } +} diff --git a/src/utils/benchmark.cpp b/src/utils/benchmark.cpp index 47b94fc..eb86702 100644 --- a/src/utils/benchmark.cpp +++ b/src/utils/benchmark.cpp @@ -1,3 +1,4 @@ +#include "utils/benchmark.hpp" #include "extract/extract.hpp" #include "extract/trans.hpp" #include "utils/log.hpp" @@ -12,106 +13,91 @@ #include #include -namespace { - void __attribute__((constructor)) benchmark_init() { - // continue if preloaded - const char* preload = std::getenv("LD_PRELOAD"); - if (!preload || *preload == '\0') - return; - const std::string preload_str(preload); - if (preload_str.find("liblsfg-vk.so") == std::string::npos) - return; - // continue if benchmark requested - const char* benchmark = std::getenv("LSFG_BENCHMARK"); - if (!benchmark || *benchmark == '\0') - return; - const std::string benchmark_str(benchmark); - if (benchmark_str != "1") - return; +using namespace Benchmark; - // fetch benchmark parameters - const char* lsfgFlowScale = std::getenv("LSFG_FLOW_SCALE"); - const char* lsfgHdr = std::getenv("LSFG_HDR"); - const char* lsfgMultiplier = std::getenv("LSFG_MULTIPLIER"); - const char* lsfgExtentWidth = std::getenv("LSFG_EXTENT_WIDTH"); - const char* lsfgExtentHeight = std::getenv("LSFG_EXTENT_HEIGHT"); - const char* lsfgPerfMode = std::getenv("LSFG_PERF_MODE"); +void Benchmark::run() { + // fetch benchmark parameters + const char* lsfgFlowScale = std::getenv("LSFG_FLOW_SCALE"); + const char* lsfgHdr = std::getenv("LSFG_HDR"); + const char* lsfgMultiplier = std::getenv("LSFG_MULTIPLIER"); + const char* lsfgExtentWidth = std::getenv("LSFG_EXTENT_WIDTH"); + const char* lsfgExtentHeight = std::getenv("LSFG_EXTENT_HEIGHT"); + const char* lsfgPerfMode = std::getenv("LSFG_PERF_MODE"); - const float flowScale = lsfgFlowScale - ? std::stof(lsfgFlowScale) : 1.0F; - const bool isHdr = lsfgHdr - ? *lsfgHdr == '1' : false; - const uint64_t multiplier = lsfgMultiplier - ? std::stoull(std::string(lsfgMultiplier)) : 2; - const uint32_t width = lsfgExtentWidth - ? static_cast(std::stoul(lsfgExtentWidth)) : 1920; - const uint32_t height = lsfgExtentHeight - ? static_cast(std::stoul(lsfgExtentHeight)) : 1080; - const bool perfMode = lsfgPerfMode - ? *lsfgPerfMode == '1' : false; + const float flowScale = lsfgFlowScale + ? std::stof(lsfgFlowScale) : 1.0F; + const bool isHdr = lsfgHdr + ? *lsfgHdr == '1' : false; + const uint64_t multiplier = lsfgMultiplier + ? std::stoull(std::string(lsfgMultiplier)) : 2; + const uint32_t width = lsfgExtentWidth + ? static_cast(std::stoul(lsfgExtentWidth)) : 1920; + const uint32_t height = lsfgExtentHeight + ? static_cast(std::stoul(lsfgExtentHeight)) : 1080; + const bool perfMode = lsfgPerfMode + ? *lsfgPerfMode == '1' : false; - auto* lsfgInitialize = LSFG_3_1::initialize; - auto* lsfgCreateContext = LSFG_3_1::createContext; - auto* lsfgPresentContext = LSFG_3_1::presentContext; - if (perfMode) { - lsfgInitialize = LSFG_3_1P::initialize; - lsfgCreateContext = LSFG_3_1P::createContext; - lsfgPresentContext = LSFG_3_1P::presentContext; - } - - Log::info("bench", "Running {}x benchmark with {}x{} extent and flow scale of {} {} HDR", - multiplier, width, height, flowScale, isHdr ? "with" : "without"); - - // create the benchmark context - const char* lsfgDeviceUUID = std::getenv("LSFG_DEVICE_UUID"); - const uint64_t deviceUUID = lsfgDeviceUUID - ? std::stoull(std::string(lsfgDeviceUUID), nullptr, 16) : 0x1463ABAC; - - Extract::extractShaders(); - lsfgInitialize( - deviceUUID, // some magic number if not given - isHdr, 1.0F / flowScale, multiplier - 1, - [](const std::string& name) -> std::vector { - auto dxbc = Extract::getShader(name); - auto spirv = Extract::translateShader(dxbc); - return spirv; - } - ); - const int32_t ctx = lsfgCreateContext(-1, -1, {}, - { .width = width, .height = height }, - isHdr ? VK_FORMAT_R16G16B16A16_SFLOAT : VK_FORMAT_R8G8B8A8_UNORM - ); - - Log::info("bench", "Benchmark context created, ready to run"); - - // run the benchmark (run 8*n + 1 so the fences are waited on) - const auto now = std::chrono::high_resolution_clock::now(); - const uint64_t iterations = (8 * 500) + 1; - for (uint64_t count = 0; count < iterations; count++) { - lsfgPresentContext(ctx, -1, {}); - - if (count % 500 == 0) - Log::info("bench", "{:.2f}% done ({}/{})", - static_cast(count) / static_cast(iterations) * 100.0F, - count + 1, iterations); - } - const auto then = std::chrono::high_resolution_clock::now(); - - // print results - const auto ms = std::chrono::duration_cast(then - now).count(); - - const auto perIteration = static_cast(ms) / static_cast(iterations); - - const uint64_t totalGen = (multiplier - 1) * iterations; - const auto genFps = static_cast(totalGen) / (static_cast(ms) / 1000.0F); - - const uint64_t totalFrames = iterations * multiplier; - const auto totalFps = static_cast(totalFrames) / (static_cast(ms) / 1000.0F); - - Log::info("bench", "Benchmark completed in {} ms", ms); - Log::info("bench", "Time per iteration: {:.2f} ms", perIteration); - Log::info("bench", "Generation FPS: {:.2f}", genFps); - Log::info("bench", "Final FPS: {:.2f}", totalFps); - Log::info("bench", "Benchmark finished, exiting"); + auto* lsfgInitialize = LSFG_3_1::initialize; + auto* lsfgCreateContext = LSFG_3_1::createContext; + auto* lsfgPresentContext = LSFG_3_1::presentContext; + if (perfMode) { + lsfgInitialize = LSFG_3_1P::initialize; + lsfgCreateContext = LSFG_3_1P::createContext; + lsfgPresentContext = LSFG_3_1P::presentContext; } + + Log::info("bench", "Running {}x benchmark with {}x{} extent and flow scale of {} {} HDR", + multiplier, width, height, flowScale, isHdr ? "with" : "without"); + + // create the benchmark context + const char* lsfgDeviceUUID = std::getenv("LSFG_DEVICE_UUID"); + const uint64_t deviceUUID = lsfgDeviceUUID + ? std::stoull(std::string(lsfgDeviceUUID), nullptr, 16) : 0x1463ABAC; + + Extract::extractShaders(); + lsfgInitialize( + deviceUUID, // some magic number if not given + isHdr, 1.0F / flowScale, multiplier - 1, + [](const std::string& name) -> std::vector { + auto dxbc = Extract::getShader(name); + auto spirv = Extract::translateShader(dxbc); + return spirv; + } + ); + const int32_t ctx = lsfgCreateContext(-1, -1, {}, + { .width = width, .height = height }, + isHdr ? VK_FORMAT_R16G16B16A16_SFLOAT : VK_FORMAT_R8G8B8A8_UNORM + ); + + Log::info("bench", "Benchmark context created, ready to run"); + + // run the benchmark (run 8*n + 1 so the fences are waited on) + const auto now = std::chrono::high_resolution_clock::now(); + const uint64_t iterations = (8 * 500) + 1; + for (uint64_t count = 0; count < iterations; count++) { + lsfgPresentContext(ctx, -1, {}); + + if (count % 500 == 0) + Log::info("bench", "{:.2f}% done ({}/{})", + static_cast(count) / static_cast(iterations) * 100.0F, + count + 1, iterations); + } + const auto then = std::chrono::high_resolution_clock::now(); + + // print results + const auto ms = std::chrono::duration_cast(then - now).count(); + + const auto perIteration = static_cast(ms) / static_cast(iterations); + + const uint64_t totalGen = (multiplier - 1) * iterations; + const auto genFps = static_cast(totalGen) / (static_cast(ms) / 1000.0F); + + const uint64_t totalFrames = iterations * multiplier; + const auto totalFps = static_cast(totalFrames) / (static_cast(ms) / 1000.0F); + + Log::info("bench", "Benchmark completed in {} ms", ms); + Log::info("bench", "Time per iteration: {:.2f} ms", perIteration); + Log::info("bench", "Generation FPS: {:.2f}", genFps); + Log::info("bench", "Final FPS: {:.2f}", totalFps); + Log::info("bench", "Benchmark finished, exiting"); } diff --git a/dxbc b/thirdparty/dxbc similarity index 100% rename from dxbc rename to thirdparty/dxbc diff --git a/thirdparty/pe-parse b/thirdparty/pe-parse new file mode 160000 index 0000000..31ac596 --- /dev/null +++ b/thirdparty/pe-parse @@ -0,0 +1 @@ +Subproject commit 31ac5966503689d5693cd9fb520bd525a8710e17