mirror of
https://github.com/PancakeTAS/lsfg-vk.git
synced 2025-12-17 05:22:15 +00:00
groundwork for a configuration file
This commit is contained in:
parent
05b8413dbc
commit
ffd72ee598
10 changed files with 265 additions and 106 deletions
10
.gitmodules
vendored
10
.gitmodules
vendored
|
|
@ -1,6 +1,6 @@
|
||||||
[submodule "dxbc"]
|
[submodule "thirdparty/pe-parse"]
|
||||||
path = dxbc
|
path = thirdparty/pe-parse
|
||||||
url = https://github.com/PancakeTAS/dxbc.git
|
|
||||||
[submodule "pe-parse"]
|
|
||||||
path = pe-parse
|
|
||||||
url = https://github.com/trailofbits/pe-parse
|
url = https://github.com/trailofbits/pe-parse
|
||||||
|
[submodule "thirdparty/dxbc"]
|
||||||
|
path = thirdparty/dxbc
|
||||||
|
url = https://github.com/PancakeTAS/dxbc.git
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@ add_compile_options(-fPIC
|
||||||
-Wno-deprecated-declarations
|
-Wno-deprecated-declarations
|
||||||
-Wno-unused-template)
|
-Wno-unused-template)
|
||||||
|
|
||||||
add_subdirectory(dxbc EXCLUDE_FROM_ALL)
|
add_subdirectory(thirdparty/dxbc EXCLUDE_FROM_ALL)
|
||||||
add_subdirectory(pe-parse/pe-parser-library EXCLUDE_FROM_ALL)
|
add_subdirectory(thirdparty/pe-parse/pe-parser-library EXCLUDE_FROM_ALL)
|
||||||
add_subdirectory(lsfg-vk-common)
|
add_subdirectory(lsfg-vk-common)
|
||||||
add_subdirectory(lsfg-vk-v3.1)
|
add_subdirectory(lsfg-vk-v3.1)
|
||||||
add_subdirectory(lsfg-vk-v3.1p)
|
add_subdirectory(lsfg-vk-v3.1p)
|
||||||
|
|
@ -27,6 +27,7 @@ project(lsfg-vk
|
||||||
LANGUAGES CXX)
|
LANGUAGES CXX)
|
||||||
|
|
||||||
file(GLOB SOURCES
|
file(GLOB SOURCES
|
||||||
|
"src/config/*.cpp"
|
||||||
"src/extract/*.cpp"
|
"src/extract/*.cpp"
|
||||||
"src/mini/*.cpp"
|
"src/mini/*.cpp"
|
||||||
"src/utils/*.cpp"
|
"src/utils/*.cpp"
|
||||||
|
|
|
||||||
51
include/config/config.hpp
Normal file
51
include/config/config.hpp
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
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<std::atomic_bool> 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);
|
||||||
|
|
||||||
|
}
|
||||||
10
include/utils/benchmark.hpp
Normal file
10
include/utils/benchmark.hpp
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Benchmark {
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Run the benchmark.
|
||||||
|
///
|
||||||
|
void run();
|
||||||
|
|
||||||
|
}
|
||||||
1
pe-parse
1
pe-parse
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit 7888f1f8de2f6bc302c291a5b4519fad926c0133
|
|
||||||
15
src/config/config.cpp
Normal file
15
src/config/config.cpp
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
96
src/main.cpp
Normal file
96
src/main.cpp
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
#include "config/config.hpp"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <exception>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <string.h> // NOLINT
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
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<char, 4096> 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<size_t>(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';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include "utils/benchmark.hpp"
|
||||||
#include "extract/extract.hpp"
|
#include "extract/extract.hpp"
|
||||||
#include "extract/trans.hpp"
|
#include "extract/trans.hpp"
|
||||||
#include "utils/log.hpp"
|
#include "utils/log.hpp"
|
||||||
|
|
@ -12,106 +13,91 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace {
|
using namespace Benchmark;
|
||||||
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;
|
|
||||||
|
|
||||||
// fetch benchmark parameters
|
void Benchmark::run() {
|
||||||
const char* lsfgFlowScale = std::getenv("LSFG_FLOW_SCALE");
|
// fetch benchmark parameters
|
||||||
const char* lsfgHdr = std::getenv("LSFG_HDR");
|
const char* lsfgFlowScale = std::getenv("LSFG_FLOW_SCALE");
|
||||||
const char* lsfgMultiplier = std::getenv("LSFG_MULTIPLIER");
|
const char* lsfgHdr = std::getenv("LSFG_HDR");
|
||||||
const char* lsfgExtentWidth = std::getenv("LSFG_EXTENT_WIDTH");
|
const char* lsfgMultiplier = std::getenv("LSFG_MULTIPLIER");
|
||||||
const char* lsfgExtentHeight = std::getenv("LSFG_EXTENT_HEIGHT");
|
const char* lsfgExtentWidth = std::getenv("LSFG_EXTENT_WIDTH");
|
||||||
const char* lsfgPerfMode = std::getenv("LSFG_PERF_MODE");
|
const char* lsfgExtentHeight = std::getenv("LSFG_EXTENT_HEIGHT");
|
||||||
|
const char* lsfgPerfMode = std::getenv("LSFG_PERF_MODE");
|
||||||
|
|
||||||
const float flowScale = lsfgFlowScale
|
const float flowScale = lsfgFlowScale
|
||||||
? std::stof(lsfgFlowScale) : 1.0F;
|
? std::stof(lsfgFlowScale) : 1.0F;
|
||||||
const bool isHdr = lsfgHdr
|
const bool isHdr = lsfgHdr
|
||||||
? *lsfgHdr == '1' : false;
|
? *lsfgHdr == '1' : false;
|
||||||
const uint64_t multiplier = lsfgMultiplier
|
const uint64_t multiplier = lsfgMultiplier
|
||||||
? std::stoull(std::string(lsfgMultiplier)) : 2;
|
? std::stoull(std::string(lsfgMultiplier)) : 2;
|
||||||
const uint32_t width = lsfgExtentWidth
|
const uint32_t width = lsfgExtentWidth
|
||||||
? static_cast<uint32_t>(std::stoul(lsfgExtentWidth)) : 1920;
|
? static_cast<uint32_t>(std::stoul(lsfgExtentWidth)) : 1920;
|
||||||
const uint32_t height = lsfgExtentHeight
|
const uint32_t height = lsfgExtentHeight
|
||||||
? static_cast<uint32_t>(std::stoul(lsfgExtentHeight)) : 1080;
|
? static_cast<uint32_t>(std::stoul(lsfgExtentHeight)) : 1080;
|
||||||
const bool perfMode = lsfgPerfMode
|
const bool perfMode = lsfgPerfMode
|
||||||
? *lsfgPerfMode == '1' : false;
|
? *lsfgPerfMode == '1' : false;
|
||||||
|
|
||||||
auto* lsfgInitialize = LSFG_3_1::initialize;
|
auto* lsfgInitialize = LSFG_3_1::initialize;
|
||||||
auto* lsfgCreateContext = LSFG_3_1::createContext;
|
auto* lsfgCreateContext = LSFG_3_1::createContext;
|
||||||
auto* lsfgPresentContext = LSFG_3_1::presentContext;
|
auto* lsfgPresentContext = LSFG_3_1::presentContext;
|
||||||
if (perfMode) {
|
if (perfMode) {
|
||||||
lsfgInitialize = LSFG_3_1P::initialize;
|
lsfgInitialize = LSFG_3_1P::initialize;
|
||||||
lsfgCreateContext = LSFG_3_1P::createContext;
|
lsfgCreateContext = LSFG_3_1P::createContext;
|
||||||
lsfgPresentContext = LSFG_3_1P::presentContext;
|
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<uint8_t> {
|
|
||||||
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<float>(count) / static_cast<float>(iterations) * 100.0F,
|
|
||||||
count + 1, iterations);
|
|
||||||
}
|
|
||||||
const auto then = std::chrono::high_resolution_clock::now();
|
|
||||||
|
|
||||||
// print results
|
|
||||||
const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(then - now).count();
|
|
||||||
|
|
||||||
const auto perIteration = static_cast<float>(ms) / static_cast<float>(iterations);
|
|
||||||
|
|
||||||
const uint64_t totalGen = (multiplier - 1) * iterations;
|
|
||||||
const auto genFps = static_cast<float>(totalGen) / (static_cast<float>(ms) / 1000.0F);
|
|
||||||
|
|
||||||
const uint64_t totalFrames = iterations * multiplier;
|
|
||||||
const auto totalFps = static_cast<float>(totalFrames) / (static_cast<float>(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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<uint8_t> {
|
||||||
|
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<float>(count) / static_cast<float>(iterations) * 100.0F,
|
||||||
|
count + 1, iterations);
|
||||||
|
}
|
||||||
|
const auto then = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
// print results
|
||||||
|
const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(then - now).count();
|
||||||
|
|
||||||
|
const auto perIteration = static_cast<float>(ms) / static_cast<float>(iterations);
|
||||||
|
|
||||||
|
const uint64_t totalGen = (multiplier - 1) * iterations;
|
||||||
|
const auto genFps = static_cast<float>(totalGen) / (static_cast<float>(ms) / 1000.0F);
|
||||||
|
|
||||||
|
const uint64_t totalFrames = iterations * multiplier;
|
||||||
|
const auto totalFps = static_cast<float>(totalFrames) / (static_cast<float>(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");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
0
dxbc → thirdparty/dxbc
vendored
0
dxbc → thirdparty/dxbc
vendored
1
thirdparty/pe-parse
vendored
Submodule
1
thirdparty/pe-parse
vendored
Submodule
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 31ac5966503689d5693cd9fb520bd525a8710e17
|
||||||
Loading…
Add table
Reference in a new issue