mirror of
				https://github.com/PancakeTAS/lsfg-vk.git
				synced 2025-10-30 07:01:10 +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"] | ||||
| 	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 | ||||
|  |  | |||
|  | @ -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" | ||||
|  |  | |||
							
								
								
									
										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/trans.hpp" | ||||
| #include "utils/log.hpp" | ||||
|  | @ -12,106 +13,91 @@ | |||
| #include <string> | ||||
| #include <vector> | ||||
| 
 | ||||
| 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<uint32_t>(std::stoul(lsfgExtentWidth)) : 1920; | ||||
|         const uint32_t height = lsfgExtentHeight | ||||
|             ? static_cast<uint32_t>(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<uint32_t>(std::stoul(lsfgExtentWidth)) : 1920; | ||||
|     const uint32_t height = lsfgExtentHeight | ||||
|         ? static_cast<uint32_t>(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<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"); | ||||
|     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<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
	
	 PancakeTAS
						PancakeTAS