mirror of
				https://github.com/PancakeTAS/lsfg-vk.git
				synced 2025-10-30 07:01:10 +00:00 
			
		
		
		
	Compare commits
	
		
			6 commits
		
	
	
		
			0a6455a381
			...
			5e04b4f5f6
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
							 | 
						5e04b4f5f6 | ||
| 
							 | 
						82fb53f089 | ||
| 
							 | 
						77c9cc632d | ||
| 
							 | 
						daa1e410eb | ||
| 
							 | 
						f8526a1fe3 | ||
| 
							 | 
						bfebf9b27e | 
					 21 changed files with 24439 additions and 298 deletions
				
			
		
							
								
								
									
										9
									
								
								.gitmodules
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								.gitmodules
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,9 +0,0 @@
 | 
				
			||||||
[submodule "thirdparty/pe-parse"]
 | 
					 | 
				
			||||||
	path = thirdparty/pe-parse
 | 
					 | 
				
			||||||
	url = https://github.com/trailofbits/pe-parse
 | 
					 | 
				
			||||||
[submodule "thirdparty/toml11"]
 | 
					 | 
				
			||||||
	path = thirdparty/toml11
 | 
					 | 
				
			||||||
	url = https://github.com/ToruNiina/toml11
 | 
					 | 
				
			||||||
[submodule "thirdparty/volk"]
 | 
					 | 
				
			||||||
	path = thirdparty/volk
 | 
					 | 
				
			||||||
	url = https://github.com/zeux/volk
 | 
					 | 
				
			||||||
| 
						 | 
					@ -12,9 +12,6 @@ add_compile_options(-fPIC
 | 
				
			||||||
    -Wno-deprecated-declarations
 | 
					    -Wno-deprecated-declarations
 | 
				
			||||||
    -Wno-unused-template)
 | 
					    -Wno-unused-template)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
add_subdirectory(thirdparty/pe-parse/pe-parser-library EXCLUDE_FROM_ALL)
 | 
					 | 
				
			||||||
add_subdirectory(thirdparty/toml11 EXCLUDE_FROM_ALL)
 | 
					 | 
				
			||||||
add_subdirectory(thirdparty/volk EXCLUDE_FROM_ALL)
 | 
					 | 
				
			||||||
add_subdirectory(framegen)
 | 
					add_subdirectory(framegen)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if(LSFGVK_EXCESS_DEBUG)
 | 
					if(LSFGVK_EXCESS_DEBUG)
 | 
				
			||||||
| 
						 | 
					@ -41,15 +38,13 @@ add_library(lsfg-vk SHARED ${SOURCES})
 | 
				
			||||||
set_target_properties(lsfg-vk PROPERTIES
 | 
					set_target_properties(lsfg-vk PROPERTIES
 | 
				
			||||||
    CXX_STANDARD 20
 | 
					    CXX_STANDARD 20
 | 
				
			||||||
    CXX_STANDARD_REQUIRED ON)
 | 
					    CXX_STANDARD_REQUIRED ON)
 | 
				
			||||||
 | 
					target_include_directories(lsfg-vk SYSTEM
 | 
				
			||||||
 | 
					    PUBLIC include/thirdparty)
 | 
				
			||||||
target_include_directories(lsfg-vk
 | 
					target_include_directories(lsfg-vk
 | 
				
			||||||
    PUBLIC include)
 | 
					    PUBLIC include)
 | 
				
			||||||
target_link_libraries(lsfg-vk PUBLIC
 | 
					target_link_libraries(lsfg-vk PUBLIC
 | 
				
			||||||
    pe-parse toml11
 | 
					 | 
				
			||||||
    lsfg-vk-framegen)
 | 
					    lsfg-vk-framegen)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
get_target_property(TOML11_INCLUDE_DIRS toml11 INTERFACE_INCLUDE_DIRECTORIES)
 | 
					 | 
				
			||||||
target_include_directories(lsfg-vk SYSTEM PRIVATE ${TOML11_INCLUDE_DIRS})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# diagnostics
 | 
					# diagnostics
 | 
				
			||||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
 | 
					if(CMAKE_BUILD_TYPE STREQUAL "Debug")
 | 
				
			||||||
    set_target_properties(lsfg-vk PROPERTIES
 | 
					    set_target_properties(lsfg-vk PROPERTIES
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,7 +11,7 @@ endif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
project(lsfg-vk-framegen
 | 
					project(lsfg-vk-framegen
 | 
				
			||||||
    DESCRIPTION "Lossless Scaling Frame Generation Backend"
 | 
					    DESCRIPTION "Lossless Scaling Frame Generation Backend"
 | 
				
			||||||
    LANGUAGES CXX)
 | 
					    LANGUAGES C CXX)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
file(GLOB SOURCES
 | 
					file(GLOB SOURCES
 | 
				
			||||||
    "src/common/*.cpp"
 | 
					    "src/common/*.cpp"
 | 
				
			||||||
| 
						 | 
					@ -29,6 +29,7 @@ file(GLOB SOURCES
 | 
				
			||||||
    "v3.1p_src/shaders/*.cpp"
 | 
					    "v3.1p_src/shaders/*.cpp"
 | 
				
			||||||
    "v3.1p_src/utils/*.cpp"
 | 
					    "v3.1p_src/utils/*.cpp"
 | 
				
			||||||
    "v3.1p_src/*.cpp"
 | 
					    "v3.1p_src/*.cpp"
 | 
				
			||||||
 | 
					    "src/thirdparty/*.c"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
add_library(lsfg-vk-framegen STATIC ${SOURCES})
 | 
					add_library(lsfg-vk-framegen STATIC ${SOURCES})
 | 
				
			||||||
| 
						 | 
					@ -37,13 +38,13 @@ add_library(lsfg-vk-framegen STATIC ${SOURCES})
 | 
				
			||||||
set_target_properties(lsfg-vk-framegen PROPERTIES
 | 
					set_target_properties(lsfg-vk-framegen PROPERTIES
 | 
				
			||||||
    CXX_STANDARD 20
 | 
					    CXX_STANDARD 20
 | 
				
			||||||
    CXX_STANDARD_REQUIRED ON)
 | 
					    CXX_STANDARD_REQUIRED ON)
 | 
				
			||||||
 | 
					target_include_directories(lsfg-vk-framegen SYSTEM
 | 
				
			||||||
 | 
					    PUBLIC include/thirdparty)
 | 
				
			||||||
target_include_directories(lsfg-vk-framegen
 | 
					target_include_directories(lsfg-vk-framegen
 | 
				
			||||||
    PUBLIC include
 | 
					    PUBLIC include
 | 
				
			||||||
    PUBLIC public
 | 
					    PUBLIC public
 | 
				
			||||||
    PRIVATE v3.1_include
 | 
					    PRIVATE v3.1_include
 | 
				
			||||||
    PRIVATE v3.1p_include)
 | 
					    PRIVATE v3.1p_include)
 | 
				
			||||||
target_link_libraries(lsfg-vk-framegen
 | 
					 | 
				
			||||||
    PUBLIC volk)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# diagnostics
 | 
					# diagnostics
 | 
				
			||||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
 | 
					if(CMAKE_BUILD_TYPE STREQUAL "Debug")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										2647
									
								
								framegen/include/thirdparty/volk.h
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2647
									
								
								framegen/include/thirdparty/volk.h
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										3468
									
								
								framegen/src/thirdparty/volk.c
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3468
									
								
								framegen/src/thirdparty/volk.c
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
					@ -9,15 +9,21 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Config {
 | 
					namespace Config {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// lsfg-vk configuration
 | 
					    /// Global lsfg-vk configuration.
 | 
				
			||||||
    struct Configuration {
 | 
					    struct GlobalConfiguration {
 | 
				
			||||||
        /// Whether lsfg-vk should be loaded in the first place.
 | 
					 | 
				
			||||||
        bool enable{false};
 | 
					 | 
				
			||||||
        /// Path to Lossless.dll.
 | 
					        /// Path to Lossless.dll.
 | 
				
			||||||
        std::string dll;
 | 
					        std::string dll;
 | 
				
			||||||
        /// Whether FP16 is force-disabled
 | 
					        /// Whether FP16 is force-disabled
 | 
				
			||||||
        bool no_fp16{false};
 | 
					        bool no_fp16{false};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// Path to the configuration file.
 | 
				
			||||||
 | 
					        std::filesystem::path config_file;
 | 
				
			||||||
 | 
					        /// File timestamp of the configuration file
 | 
				
			||||||
 | 
					        std::chrono::time_point<std::chrono::file_clock> timestamp;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Per-application lsfg-vk configuration.
 | 
				
			||||||
 | 
					    struct GameConfiguration {
 | 
				
			||||||
        /// The frame generation muliplier
 | 
					        /// The frame generation muliplier
 | 
				
			||||||
        size_t multiplier{2};
 | 
					        size_t multiplier{2};
 | 
				
			||||||
        /// The internal flow scale factor
 | 
					        /// The internal flow scale factor
 | 
				
			||||||
| 
						 | 
					@ -28,35 +34,34 @@ namespace Config {
 | 
				
			||||||
        bool hdr{false};
 | 
					        bool hdr{false};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// Experimental flag for overriding the synchronization method.
 | 
					        /// Experimental flag for overriding the synchronization method.
 | 
				
			||||||
        VkPresentModeKHR e_present;
 | 
					        VkPresentModeKHR e_present{ VK_PRESENT_MODE_FIFO_KHR };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// Path to the configuration file.
 | 
					 | 
				
			||||||
        std::filesystem::path config_file;
 | 
					 | 
				
			||||||
        /// File timestamp of the configuration file
 | 
					 | 
				
			||||||
        std::chrono::time_point<std::chrono::file_clock> timestamp;
 | 
					 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Active configuration. Must be set in main.cpp.
 | 
					    /// Global configuration.
 | 
				
			||||||
    extern Configuration activeConf;
 | 
					    extern GlobalConfiguration globalConf;
 | 
				
			||||||
 | 
					    /// Currently active configuration.
 | 
				
			||||||
 | 
					    extern std::optional<GameConfiguration> currentConf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// Read the configuration file while preserving the previous configuration
 | 
					    /// Read the configuration file while preserving the previous configuration
 | 
				
			||||||
    /// in case of an error.
 | 
					    /// in case of an error.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// @param file The path to the configuration file.
 | 
					    /// @param file The path to the configuration file.
 | 
				
			||||||
 | 
					    /// @param name The preset to activate
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// @throws std::runtime_error if an error occurs while loading the configuration file.
 | 
					    /// @throws std::runtime_error if an error occurs while loading the configuration file.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    void updateConfig(const std::string& file);
 | 
					    void updateConfig(
 | 
				
			||||||
 | 
					        const std::string& file,
 | 
				
			||||||
 | 
					        const std::pair<std::string, std::string>& name
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// Get the configuration for a game.
 | 
					    /// Check if the configuration file is still up-to-date
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// @param name The name of the executable to fetch.
 | 
					    /// @return Whether the configuration is up-to-date or not.
 | 
				
			||||||
    /// @return The configuration for the game or global configuration.
 | 
					 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// @throws std::runtime_error if the configuration is invalid.
 | 
					    bool checkStatus();
 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    Configuration getConfig(const std::pair<std::string, std::string>& name);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										22
									
								
								include/extract/dll.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								include/extract/dll.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,22 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <unordered_map>
 | 
				
			||||||
 | 
					#include <cstdint>
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace DLL {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Parse all resources from a DLL file.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// *Shouldn't* cause any segmentation faults.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// @param filename Path to the DLL file.
 | 
				
			||||||
 | 
					    /// @return A map of resource IDs to their binary data.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// @throws std::runtime_error on various failure points.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    std::unordered_map<uint32_t, std::vector<uint8_t>> parse_dll(const std::string& filename);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										17880
									
								
								include/thirdparty/toml.hpp
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17880
									
								
								include/thirdparty/toml.hpp
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
					@ -1,33 +1,30 @@
 | 
				
			||||||
#include "config/config.hpp"
 | 
					#include "config/config.hpp"
 | 
				
			||||||
#include "common/exception.hpp"
 | 
					#include "common/exception.hpp"
 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "config/default_conf.hpp"
 | 
					#include "config/default_conf.hpp"
 | 
				
			||||||
 | 
					#include "utils/utils.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <vulkan/vulkan_core.h>
 | 
					#define TOML_ENABLE_FORMATTERS 0 // NOLINT
 | 
				
			||||||
#include <toml11/find.hpp>
 | 
					 | 
				
			||||||
#include <toml11/parser.hpp>
 | 
					 | 
				
			||||||
#include <toml.hpp>
 | 
					#include <toml.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <unordered_map>
 | 
					#include <vulkan/vulkan_core.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <filesystem>
 | 
					#include <filesystem>
 | 
				
			||||||
#include <algorithm>
 | 
					 | 
				
			||||||
#include <exception>
 | 
					#include <exception>
 | 
				
			||||||
#include <stdexcept>
 | 
					#include <stdexcept>
 | 
				
			||||||
#include <iostream>
 | 
					#include <iostream>
 | 
				
			||||||
#include <optional>
 | 
					#include <optional>
 | 
				
			||||||
#include <fstream>
 | 
					#include <fstream>
 | 
				
			||||||
 | 
					#include <cstdint>
 | 
				
			||||||
#include <cstdlib>
 | 
					#include <cstdlib>
 | 
				
			||||||
#include <utility>
 | 
					#include <utility>
 | 
				
			||||||
 | 
					#include <chrono>
 | 
				
			||||||
 | 
					#include <thread>
 | 
				
			||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using namespace Config;
 | 
					using namespace Config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace {
 | 
					GlobalConfiguration Config::globalConf{};
 | 
				
			||||||
    Configuration globalConf{};
 | 
					std::optional<GameConfiguration> Config::currentConf{};
 | 
				
			||||||
    std::optional<std::unordered_map<std::string, Configuration>> gameConfs;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Configuration Config::activeConf{};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace {
 | 
					namespace {
 | 
				
			||||||
    /// Turn a string into a VkPresentModeKHR enum value.
 | 
					    /// Turn a string into a VkPresentModeKHR enum value.
 | 
				
			||||||
| 
						 | 
					@ -42,7 +39,30 @@ namespace {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Config::updateConfig(const std::string& file) {
 | 
					void Config::updateConfig(
 | 
				
			||||||
 | 
					        const std::string& file,
 | 
				
			||||||
 | 
					        const std::pair<std::string, std::string>& name) {
 | 
				
			||||||
 | 
					    // process unchecked legacy environment variables
 | 
				
			||||||
 | 
					    if (std::getenv("LSFG_LEGACY")) {
 | 
				
			||||||
 | 
					        const char* dll = std::getenv("LSFG_DLL_PATH");
 | 
				
			||||||
 | 
					        if (dll) globalConf.dll = std::string(dll);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        currentConf.emplace();
 | 
				
			||||||
 | 
					        const char* multiplier = std::getenv("LSFG_MULTIPLIER");
 | 
				
			||||||
 | 
					        if (multiplier) currentConf->multiplier = std::stoul(multiplier);
 | 
				
			||||||
 | 
					        const char* flow_scale = std::getenv("LSFG_FLOW_SCALE");
 | 
				
			||||||
 | 
					        if (flow_scale) currentConf->flowScale = std::stof(flow_scale);
 | 
				
			||||||
 | 
					        const char* performance = std::getenv("LSFG_PERFORMANCE_MODE");
 | 
				
			||||||
 | 
					        if (performance) currentConf->performance = std::string(performance) == "1";
 | 
				
			||||||
 | 
					        const char* hdr = std::getenv("LSFG_HDR_MODE");
 | 
				
			||||||
 | 
					        if (hdr) currentConf->hdr = std::string(hdr) == "1";
 | 
				
			||||||
 | 
					        const char* e_present = std::getenv("LSFG_EXPERIMENTAL_PRESENT_MODE");
 | 
				
			||||||
 | 
					        if (e_present) currentConf->e_present = into_present(std::string(e_present));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ensure configuration file exists
 | 
				
			||||||
    if (!std::filesystem::exists(file)) {
 | 
					    if (!std::filesystem::exists(file)) {
 | 
				
			||||||
        std::cerr << "lsfg-vk: Placing default configuration file at " << file << '\n';
 | 
					        std::cerr << "lsfg-vk: Placing default configuration file at " << file << '\n';
 | 
				
			||||||
        const auto parent = std::filesystem::path(file).parent_path();
 | 
					        const auto parent = std::filesystem::path(file).parent_path();
 | 
				
			||||||
| 
						 | 
					@ -58,105 +78,97 @@ void Config::updateConfig(const std::string& file) {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // parse config file
 | 
					    // parse config file
 | 
				
			||||||
    std::optional<toml::value> parsed;
 | 
					    toml::table config{};
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
        parsed.emplace(toml::parse(file));
 | 
					        config = toml::parse_file(file);
 | 
				
			||||||
        if (!parsed->contains("version"))
 | 
					
 | 
				
			||||||
            throw std::runtime_error("Configuration file is missing 'version' field");
 | 
					        const auto* version = config.get_as<toml::value<int64_t>>("version");
 | 
				
			||||||
        if (parsed->at("version").as_integer() != 1)
 | 
					        if (!version || *version != 1)
 | 
				
			||||||
            throw std::runtime_error("Configuration file version is not supported, expected 1");
 | 
					            throw std::runtime_error("Configuration file version is not supported, expected 1");
 | 
				
			||||||
    } catch (const std::exception& e) {
 | 
					    } catch (const std::exception& e) {
 | 
				
			||||||
        throw LSFG::rethrowable_error("Unable to parse configuration file", e);
 | 
					        throw LSFG::rethrowable_error("Unable to parse configuration file", e);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    auto& toml = *parsed;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // parse global configuration
 | 
					    // parse global configuration
 | 
				
			||||||
    const toml::value globalTable = toml::find_or_default<toml::table>(toml, "global");
 | 
					    Config::globalConf = {
 | 
				
			||||||
    const Configuration global{
 | 
					 | 
				
			||||||
        .dll =     toml::find_or(globalTable, "dll", std::string()),
 | 
					 | 
				
			||||||
        .no_fp16 = toml::find_or(globalTable, "no_fp16", false),
 | 
					 | 
				
			||||||
        .config_file = file,
 | 
					        .config_file = file,
 | 
				
			||||||
        .timestamp = std::filesystem::last_write_time(file)
 | 
					        .timestamp = std::filesystem::last_write_time(file)
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					    if (const auto* global = config.get_as<toml::table>("global")) {
 | 
				
			||||||
    // validate global configuration
 | 
					        if (const auto* val = global->get_as<toml::value<std::string>>("dll"))
 | 
				
			||||||
    if (global.multiplier < 2)
 | 
					            globalConf.dll = val->get();
 | 
				
			||||||
        throw std::runtime_error("Global Multiplier cannot be less than 2");
 | 
					        if (const auto* val = global->get_as<toml::value<bool>>("no_fp16"))
 | 
				
			||||||
    if (global.flowScale < 0.25F || global.flowScale > 1.0F)
 | 
					            globalConf.no_fp16 = val->get();
 | 
				
			||||||
        throw std::runtime_error("Flow scale must be between 0.25 and 1.0");
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // parse game-specific configuration
 | 
					    // parse game-specific configuration
 | 
				
			||||||
    std::unordered_map<std::string, Configuration> games;
 | 
					    std::optional<GameConfiguration> gameConf;
 | 
				
			||||||
    const toml::value gamesList = toml::find_or_default<toml::array>(toml, "game");
 | 
					    if (const auto* games = config["game"].as_array()) {
 | 
				
			||||||
    for (const auto& gameTable : gamesList.as_array()) {
 | 
					        for (auto&& elem : *games) {
 | 
				
			||||||
        if (!gameTable.is_table())
 | 
					            if (!elem.is_table())
 | 
				
			||||||
            throw std::runtime_error("Invalid game configuration entry");
 | 
					                throw std::runtime_error("Invalid game configuration entry");
 | 
				
			||||||
        if (!gameTable.contains("exe"))
 | 
					            const auto* game = elem.as_table();
 | 
				
			||||||
            throw std::runtime_error("Game override missing 'exe' field");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const std::string exe = toml::find<std::string>(gameTable, "exe");
 | 
					            const auto* exe = game->at("exe").value_or("?");
 | 
				
			||||||
        Configuration game{
 | 
					            if (!name.first.ends_with(exe) && name.second != exe)
 | 
				
			||||||
            .enable = true,
 | 
					                continue;
 | 
				
			||||||
            .dll = global.dll,
 | 
					 | 
				
			||||||
            .no_fp16 = global.no_fp16,
 | 
					 | 
				
			||||||
            .multiplier = toml::find_or(gameTable, "multiplier", 2U),
 | 
					 | 
				
			||||||
            .flowScale = toml::find_or(gameTable, "flow_scale", 1.0F),
 | 
					 | 
				
			||||||
            .performance = toml::find_or(gameTable, "performance_mode", false),
 | 
					 | 
				
			||||||
            .hdr = toml::find_or(gameTable, "hdr_mode", false),
 | 
					 | 
				
			||||||
            .e_present =   into_present(toml::find_or(gameTable, "experimental_present_mode", "")),
 | 
					 | 
				
			||||||
            .config_file = file,
 | 
					 | 
				
			||||||
            .timestamp = global.timestamp
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // validate the configuration
 | 
					            gameConf = Config::currentConf;
 | 
				
			||||||
        if (game.multiplier < 1)
 | 
					            if (!gameConf.has_value()) gameConf.emplace();
 | 
				
			||||||
            throw std::runtime_error("Multiplier cannot be less than 1");
 | 
					
 | 
				
			||||||
        if (game.flowScale < 0.25F || game.flowScale > 1.0F)
 | 
					            if (const auto* val = game->get_as<toml::value<int64_t>>("multiplier"))
 | 
				
			||||||
            throw std::runtime_error("Flow scale must be between 0.25 and 1.0");
 | 
					                gameConf->multiplier = static_cast<size_t>(val->get());
 | 
				
			||||||
        games[exe] = std::move(game);
 | 
					            if (const auto* val = game->get_as<toml::value<double>>("flow_scale"))
 | 
				
			||||||
 | 
					                gameConf->flowScale = static_cast<float>(val->get());
 | 
				
			||||||
 | 
					            if (const auto* val = game->get_as<toml::value<bool>>("performance_mode"))
 | 
				
			||||||
 | 
					                gameConf->performance = val->get();
 | 
				
			||||||
 | 
					            if (const auto* val = game->get_as<toml::value<bool>>("hdr_mode"))
 | 
				
			||||||
 | 
					                gameConf->hdr = val->get();
 | 
				
			||||||
 | 
					            if (const auto* val = game->get_as<toml::value<std::string>>("experimental_present_mode"))
 | 
				
			||||||
 | 
					                gameConf->e_present = into_present(val->get());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // store configurations
 | 
					    if (!gameConf.has_value()) {
 | 
				
			||||||
    globalConf = global;
 | 
					        Config::currentConf.reset();
 | 
				
			||||||
    gameConfs = std::move(games);
 | 
					        std::cerr << "lsfg-vk: Configuration entry disappeared, disabling.\n";
 | 
				
			||||||
}
 | 
					        return;
 | 
				
			||||||
 | 
					 | 
				
			||||||
Configuration Config::getConfig(const std::pair<std::string, std::string>& name) {
 | 
					 | 
				
			||||||
    // process legacy environment variables
 | 
					 | 
				
			||||||
    if (std::getenv("LSFG_LEGACY")) {
 | 
					 | 
				
			||||||
        Configuration conf{
 | 
					 | 
				
			||||||
            .enable = true,
 | 
					 | 
				
			||||||
            .multiplier = 2,
 | 
					 | 
				
			||||||
            .flowScale = 1.0F,
 | 
					 | 
				
			||||||
            .e_present = VkPresentModeKHR::VK_PRESENT_MODE_FIFO_KHR
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const char* dll = std::getenv("LSFG_DLL_PATH");
 | 
					 | 
				
			||||||
        if (dll) conf.dll = std::string(dll);
 | 
					 | 
				
			||||||
        const char* multiplier = std::getenv("LSFG_MULTIPLIER");
 | 
					 | 
				
			||||||
        if (multiplier) conf.multiplier = std::stoul(multiplier);
 | 
					 | 
				
			||||||
        const char* flow_scale = std::getenv("LSFG_FLOW_SCALE");
 | 
					 | 
				
			||||||
        if (flow_scale) conf.flowScale = std::stof(flow_scale);
 | 
					 | 
				
			||||||
        const char* performance = std::getenv("LSFG_PERFORMANCE_MODE");
 | 
					 | 
				
			||||||
        if (performance) conf.performance = std::string(performance) == "1";
 | 
					 | 
				
			||||||
        const char* hdr = std::getenv("LSFG_HDR_MODE");
 | 
					 | 
				
			||||||
        if (hdr) conf.hdr = std::string(hdr) == "1";
 | 
					 | 
				
			||||||
        const char* e_present = std::getenv("LSFG_EXPERIMENTAL_PRESENT_MODE");
 | 
					 | 
				
			||||||
        if (e_present) conf.e_present = into_present(std::string(e_present));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return conf;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // process new configuration system
 | 
					    Config::currentConf = *gameConf;
 | 
				
			||||||
    if (!gameConfs.has_value())
 | 
					 | 
				
			||||||
        return globalConf;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const auto& games = *gameConfs;
 | 
					    // print updated config info
 | 
				
			||||||
    auto it = std::ranges::find_if(games, [&name](const auto& pair) {
 | 
					    std::cerr << "lsfg-vk: Loaded configuration for " << name.first << ":\n";
 | 
				
			||||||
        return name.first.ends_with(pair.first) || (name.second == pair.first);
 | 
					    if (!globalConf.dll.empty()) std::cerr << "  Using DLL from: " << globalConf.dll << '\n';
 | 
				
			||||||
    });
 | 
					    if (globalConf.no_fp16) std::cerr << "  FP16 Acceleration: Force-disabled\n";
 | 
				
			||||||
    if (it != games.end())
 | 
					    std::cerr << "  Multiplier: " << gameConf->multiplier << '\n';
 | 
				
			||||||
        return it->second;
 | 
					    std::cerr << "  Flow Scale: " << gameConf->flowScale << '\n';
 | 
				
			||||||
 | 
					    std::cerr << "  Performance Mode: " << (gameConf->performance ? "Enabled" : "Disabled") << '\n';
 | 
				
			||||||
    return globalConf;
 | 
					    std::cerr << "  HDR Mode: " << (gameConf->hdr ? "Enabled" : "Disabled") << '\n';
 | 
				
			||||||
 | 
					    if (gameConf->e_present != 2) std::cerr << "  ! Present Mode: " << gameConf->e_present << '\n';
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool Config::checkStatus() {
 | 
				
			||||||
 | 
					    // check if config is up-to-date
 | 
				
			||||||
 | 
					    auto& globalConf = Config::globalConf;
 | 
				
			||||||
 | 
					    if (globalConf.config_file.empty())
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    if (!std::filesystem::exists(globalConf.config_file))
 | 
				
			||||||
 | 
					        return true; // ignore deletion
 | 
				
			||||||
 | 
					    if (std::filesystem::last_write_time(globalConf.config_file) == globalConf.timestamp)
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // reload config
 | 
				
			||||||
 | 
					    std::cerr << "lsfg-vk: Rereading configuration, as it is no longer valid.\n";
 | 
				
			||||||
 | 
					    std::this_thread::sleep_for(std::chrono::milliseconds(73));
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					        Config::updateConfig(Utils::getConfigFile(), Utils::getProcessName());
 | 
				
			||||||
 | 
					    } catch (const std::exception& e) {
 | 
				
			||||||
 | 
					        std::cerr << "lsfg-vk: Failed to update configuration, continuing using old:\n";
 | 
				
			||||||
 | 
					        std::cerr << "- " << e.what() << '\n';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,58 +10,23 @@
 | 
				
			||||||
#include <lsfg_3_1.hpp>
 | 
					#include <lsfg_3_1.hpp>
 | 
				
			||||||
#include <lsfg_3_1p.hpp>
 | 
					#include <lsfg_3_1p.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <filesystem>
 | 
					#include <stdexcept>
 | 
				
			||||||
#include <exception>
 | 
					 | 
				
			||||||
#include <iostream>
 | 
					 | 
				
			||||||
#include <cstdint>
 | 
					#include <cstdint>
 | 
				
			||||||
#include <cstdlib>
 | 
					#include <cstdlib>
 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
#include <chrono>
 | 
					 | 
				
			||||||
#include <memory>
 | 
					#include <memory>
 | 
				
			||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
#include <thread>
 | 
					 | 
				
			||||||
#include <array>
 | 
					#include <array>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LsContext::LsContext(const Hooks::DeviceInfo& info, VkSwapchainKHR swapchain,
 | 
					LsContext::LsContext(const Hooks::DeviceInfo& info, VkSwapchainKHR swapchain,
 | 
				
			||||||
        VkExtent2D extent, const std::vector<VkImage>& swapchainImages)
 | 
					        VkExtent2D extent, const std::vector<VkImage>& swapchainImages)
 | 
				
			||||||
        : swapchain(swapchain), swapchainImages(swapchainImages),
 | 
					        : swapchain(swapchain), swapchainImages(swapchainImages),
 | 
				
			||||||
          extent(extent) {
 | 
					          extent(extent) {
 | 
				
			||||||
    // get updated configuration
 | 
					    if (!Config::currentConf.has_value())
 | 
				
			||||||
    auto& conf = Config::activeConf;
 | 
					        throw std::runtime_error("No configuration set");
 | 
				
			||||||
    if (!conf.config_file.empty()
 | 
					    auto& globalConf = Config::globalConf;
 | 
				
			||||||
            && (
 | 
					    auto& conf = *Config::currentConf;
 | 
				
			||||||
                    !std::filesystem::exists(conf.config_file)
 | 
					 | 
				
			||||||
                  || conf.timestamp != std::filesystem::last_write_time(conf.config_file)
 | 
					 | 
				
			||||||
            )) {
 | 
					 | 
				
			||||||
        std::cerr << "lsfg-vk: Rereading configuration, as it is no longer valid.\n";
 | 
					 | 
				
			||||||
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // reread configuration
 | 
					 | 
				
			||||||
        const std::string file = Utils::getConfigFile();
 | 
					 | 
				
			||||||
        const auto name = Utils::getProcessName();
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            Config::updateConfig(file);
 | 
					 | 
				
			||||||
            conf = Config::getConfig(name);
 | 
					 | 
				
			||||||
        } catch (const std::exception& e) {
 | 
					 | 
				
			||||||
            std::cerr << "lsfg-vk: Failed to update configuration, continuing using old:\n";
 | 
					 | 
				
			||||||
            std::cerr << "- " << e.what() << '\n';
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        LSFG_3_1P::finalize();
 | 
					 | 
				
			||||||
        LSFG_3_1::finalize();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // print config
 | 
					 | 
				
			||||||
        std::cerr << "lsfg-vk: Reloaded configuration for " << name.first << ":\n";
 | 
					 | 
				
			||||||
        if (!conf.dll.empty()) std::cerr << "  Using DLL from: " << conf.dll << '\n';
 | 
					 | 
				
			||||||
        if (conf.no_fp16) std::cerr << "  FP16 Acceleration: Force-disabled\n";
 | 
					 | 
				
			||||||
        std::cerr << "  Multiplier: " << conf.multiplier << '\n';
 | 
					 | 
				
			||||||
        std::cerr << "  Flow Scale: " << conf.flowScale << '\n';
 | 
					 | 
				
			||||||
        std::cerr << "  Performance Mode: " << (conf.performance ? "Enabled" : "Disabled") << '\n';
 | 
					 | 
				
			||||||
        std::cerr << "  HDR Mode: " << (conf.hdr ? "Enabled" : "Disabled") << '\n';
 | 
					 | 
				
			||||||
        if (conf.e_present != 2) std::cerr << "  ! Present Mode: " << conf.e_present << '\n';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (conf.multiplier <= 1) return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    // we could take the format from the swapchain,
 | 
					    // we could take the format from the swapchain,
 | 
				
			||||||
    // but honestly this is safer.
 | 
					    // but honestly this is safer.
 | 
				
			||||||
    const VkFormat format = conf.hdr
 | 
					    const VkFormat format = conf.hdr
 | 
				
			||||||
| 
						 | 
					@ -99,7 +64,7 @@ LsContext::LsContext(const Hooks::DeviceInfo& info, VkSwapchainKHR swapchain,
 | 
				
			||||||
    lsfgInitialize(
 | 
					    lsfgInitialize(
 | 
				
			||||||
        Utils::getDeviceUUID(info.physicalDevice),
 | 
					        Utils::getDeviceUUID(info.physicalDevice),
 | 
				
			||||||
        conf.hdr, 1.0F / conf.flowScale, conf.multiplier - 1,
 | 
					        conf.hdr, 1.0F / conf.flowScale, conf.multiplier - 1,
 | 
				
			||||||
        conf.no_fp16,
 | 
					        globalConf.no_fp16,
 | 
				
			||||||
        Extract::getShader
 | 
					        Extract::getShader
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -126,7 +91,10 @@ LsContext::LsContext(const Hooks::DeviceInfo& info, VkSwapchainKHR swapchain,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
VkResult LsContext::present(const Hooks::DeviceInfo& info, const void* pNext, VkQueue queue,
 | 
					VkResult LsContext::present(const Hooks::DeviceInfo& info, const void* pNext, VkQueue queue,
 | 
				
			||||||
        const std::vector<VkSemaphore>& gameRenderSemaphores, uint32_t presentIdx) {
 | 
					        const std::vector<VkSemaphore>& gameRenderSemaphores, uint32_t presentIdx) {
 | 
				
			||||||
    const auto& conf = Config::activeConf;
 | 
					    if (!Config::currentConf.has_value())
 | 
				
			||||||
 | 
					        throw std::runtime_error("No configuration set");
 | 
				
			||||||
 | 
					    auto& conf = *Config::currentConf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto& pass = this->passInfos.at(this->frameIdx % 8);
 | 
					    auto& pass = this->passInfos.at(this->frameIdx % 8);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 1. copy swapchain image to frame_0/frame_1
 | 
					    // 1. copy swapchain image to frame_0/frame_1
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										203
									
								
								src/extract/dll.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								src/extract/dll.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,203 @@
 | 
				
			||||||
 | 
					#include "extract/dll.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <unordered_map>
 | 
				
			||||||
 | 
					#include <stdexcept>
 | 
				
			||||||
 | 
					#include <algorithm>
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <optional>
 | 
				
			||||||
 | 
					#include <cstddef>
 | 
				
			||||||
 | 
					#include <cstdint>
 | 
				
			||||||
 | 
					#include <fstream>
 | 
				
			||||||
 | 
					#include <utility>
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					#include <array>
 | 
				
			||||||
 | 
					#include <span>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// DOS file header
 | 
				
			||||||
 | 
					struct DOSHeader {
 | 
				
			||||||
 | 
					    uint16_t magic; // 0x5A4D
 | 
				
			||||||
 | 
					    std::array<uint16_t, 29> pad;
 | 
				
			||||||
 | 
					    int32_t pe_offset; // file offset
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// PE header
 | 
				
			||||||
 | 
					struct PEHeader {
 | 
				
			||||||
 | 
					    uint32_t signature; // "PE\0\0"
 | 
				
			||||||
 | 
					    std::array<uint16_t, 1> pad1;
 | 
				
			||||||
 | 
					    uint16_t sect_count;
 | 
				
			||||||
 | 
					    std::array<uint16_t, 6> pad2;
 | 
				
			||||||
 | 
					    uint16_t opt_hdr_size;
 | 
				
			||||||
 | 
					    std::array<uint16_t, 1> pad3;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// (partial!) PE optional header
 | 
				
			||||||
 | 
					struct PEOptionalHeader {
 | 
				
			||||||
 | 
					    uint16_t magic; // 0x20B
 | 
				
			||||||
 | 
					    std::array<uint16_t, 63> pad4;
 | 
				
			||||||
 | 
					    std::pair<uint32_t, uint32_t> resource_table; // file offset/size
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Section header
 | 
				
			||||||
 | 
					struct SectionHeader {
 | 
				
			||||||
 | 
					    std::array<uint16_t, 4> pad1;
 | 
				
			||||||
 | 
					    uint32_t vsize; // virtual
 | 
				
			||||||
 | 
					    uint32_t vaddress;
 | 
				
			||||||
 | 
					    uint32_t fsize; // raw
 | 
				
			||||||
 | 
					    uint32_t foffset;
 | 
				
			||||||
 | 
					    std::array<uint16_t, 8> pad2;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Resource directory
 | 
				
			||||||
 | 
					struct ResourceDirectory {
 | 
				
			||||||
 | 
					    std::array<uint16_t, 6> pad;
 | 
				
			||||||
 | 
					    uint16_t name_count;
 | 
				
			||||||
 | 
					    uint16_t id_count;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Resource directory entry
 | 
				
			||||||
 | 
					struct ResourceDirectoryEntry {
 | 
				
			||||||
 | 
					    uint32_t id;
 | 
				
			||||||
 | 
					    uint32_t offset; // high bit = directory
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Resource data entry
 | 
				
			||||||
 | 
					struct ResourceDataEntry {
 | 
				
			||||||
 | 
					    uint32_t offset;
 | 
				
			||||||
 | 
					    uint32_t size;
 | 
				
			||||||
 | 
					    std::array<uint32_t, 2> pad;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma clang diagnostic push
 | 
				
			||||||
 | 
					#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-container"
 | 
				
			||||||
 | 
					namespace {
 | 
				
			||||||
 | 
					    /// Safely cast a vector to a pointer of type T
 | 
				
			||||||
 | 
					    template<typename T>
 | 
				
			||||||
 | 
					    const T* safe_cast(const std::vector<uint8_t>& data, size_t offset) {
 | 
				
			||||||
 | 
					        const size_t end = offset + sizeof(T);
 | 
				
			||||||
 | 
					        if (end > data.size() || end < offset)
 | 
				
			||||||
 | 
					            throw std::runtime_error("Buffer overflow during safe cast");
 | 
				
			||||||
 | 
					        return reinterpret_cast<const T*>(&data.at(offset));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Safely cast a vector to a span of T
 | 
				
			||||||
 | 
					    template<typename T>
 | 
				
			||||||
 | 
					    std::span<const T> span_cast(const std::vector<uint8_t>& data, size_t offset, size_t count) {
 | 
				
			||||||
 | 
					        const size_t end = offset + (count * sizeof(T));
 | 
				
			||||||
 | 
					        if (end > data.size() || end < offset)
 | 
				
			||||||
 | 
					            throw std::runtime_error("Buffer overflow during safe cast");
 | 
				
			||||||
 | 
					        return std::span<const T>(reinterpret_cast<const T*>(&data.at(offset)), count);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#pragma clang diagnostic pop
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::unordered_map<uint32_t, std::vector<uint8_t>> DLL::parse_dll(const std::string& filename) {
 | 
				
			||||||
 | 
					    std::ifstream file(filename, std::ios::binary | std::ios::ate);
 | 
				
			||||||
 | 
					    if (!file.is_open())
 | 
				
			||||||
 | 
					        throw std::runtime_error("Failed to open Lossless.dll");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const auto size = file.tellg();
 | 
				
			||||||
 | 
					    file.seekg(0, std::ios::beg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::vector<uint8_t> data(static_cast<size_t>(size));
 | 
				
			||||||
 | 
					    if (!file.read(reinterpret_cast<char*>(data.data()), size))
 | 
				
			||||||
 | 
					        throw std::runtime_error("Failed to read Lossless.dll");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // parse dos header
 | 
				
			||||||
 | 
					    size_t fileOffset = 0;
 | 
				
			||||||
 | 
					    const auto* dosHdr = safe_cast<const DOSHeader>(data, 0);
 | 
				
			||||||
 | 
					    if (dosHdr->magic != 0x5A4D)
 | 
				
			||||||
 | 
					        throw std::runtime_error("Invalid DOS header magic number");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // parse pe header
 | 
				
			||||||
 | 
					    fileOffset += static_cast<size_t>(dosHdr->pe_offset);
 | 
				
			||||||
 | 
					    const auto* peHdr = safe_cast<const PEHeader>(data, fileOffset);
 | 
				
			||||||
 | 
					    if (peHdr->signature != 0x00004550)
 | 
				
			||||||
 | 
					        throw std::runtime_error("Invalid PE header signature");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // parse optional pe header
 | 
				
			||||||
 | 
					    fileOffset += sizeof(PEHeader);
 | 
				
			||||||
 | 
					    const auto* peOptHdr = safe_cast<const PEOptionalHeader>(data, fileOffset);
 | 
				
			||||||
 | 
					    if (peOptHdr->magic != 0x20B)
 | 
				
			||||||
 | 
					        throw std::runtime_error("Unsupported PE format (not PE32+)");
 | 
				
			||||||
 | 
					    const auto& [rsrc_rva, rsrc_size] = peOptHdr->resource_table;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // locate section containing resources
 | 
				
			||||||
 | 
					    std::optional<size_t> rsrc_offset;
 | 
				
			||||||
 | 
					    fileOffset += peHdr->opt_hdr_size;
 | 
				
			||||||
 | 
					    const auto sectHdrs = span_cast<const SectionHeader>(data, fileOffset, peHdr->sect_count);
 | 
				
			||||||
 | 
					    for (const auto& sectHdr : sectHdrs) {
 | 
				
			||||||
 | 
					        if (rsrc_rva < sectHdr.vaddress || rsrc_rva > (sectHdr.vaddress + sectHdr.vsize))
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        rsrc_offset.emplace((rsrc_rva - sectHdr.vaddress) + sectHdr.foffset);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (!rsrc_offset)
 | 
				
			||||||
 | 
					        throw std::runtime_error("Failed to locate resource section");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // parse resource directory
 | 
				
			||||||
 | 
					    fileOffset = rsrc_offset.value();
 | 
				
			||||||
 | 
					    const auto* rsrcDir = safe_cast<const ResourceDirectory>(data, fileOffset);
 | 
				
			||||||
 | 
					    if (rsrcDir->id_count < 3)
 | 
				
			||||||
 | 
					        throw std::runtime_error("Incorrect resource directory");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // find resource table with data type
 | 
				
			||||||
 | 
					    std::optional<size_t> rsrc_tbl_offset;
 | 
				
			||||||
 | 
					    fileOffset = rsrc_offset.value() + sizeof(ResourceDirectory);
 | 
				
			||||||
 | 
					    const auto rsrcDirEntries = span_cast<const ResourceDirectoryEntry>(
 | 
				
			||||||
 | 
					        data, fileOffset, rsrcDir->name_count + rsrcDir->id_count);
 | 
				
			||||||
 | 
					    for (const auto& rsrcDirEntry : rsrcDirEntries) {
 | 
				
			||||||
 | 
					        if (rsrcDirEntry.id != 10) // RT_RCDATA
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        if ((rsrcDirEntry.offset & 0x80000000) == 0)
 | 
				
			||||||
 | 
					            throw std::runtime_error("Expected resource directory, but found data entry");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        rsrc_tbl_offset.emplace(rsrcDirEntry.offset & 0x7FFFFFFF);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (!rsrc_tbl_offset)
 | 
				
			||||||
 | 
					        throw std::runtime_error("Failed to locate RT_RCDATA directory");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // parse data type resource directory
 | 
				
			||||||
 | 
					    fileOffset = rsrc_offset.value() + rsrc_tbl_offset.value();
 | 
				
			||||||
 | 
					    const auto* rsrcTbl = safe_cast<const ResourceDirectory>(data, fileOffset);
 | 
				
			||||||
 | 
					    if (rsrcTbl->id_count < 1)
 | 
				
			||||||
 | 
					        throw std::runtime_error("Incorrect RT_RCDATA directory");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // collect all resources
 | 
				
			||||||
 | 
					    fileOffset += sizeof(ResourceDirectory);
 | 
				
			||||||
 | 
					    const auto rsrcTblEntries = span_cast<const ResourceDirectoryEntry>(
 | 
				
			||||||
 | 
					        data, fileOffset, rsrcTbl->name_count + rsrcTbl->id_count);
 | 
				
			||||||
 | 
					    std::unordered_map<uint32_t, std::vector<uint8_t>> resources;
 | 
				
			||||||
 | 
					    for (const auto& rsrcTblEntry : rsrcTblEntries) {
 | 
				
			||||||
 | 
					        if ((rsrcTblEntry.offset & 0x80000000) == 0)
 | 
				
			||||||
 | 
					            throw std::runtime_error("Expected resource directory, but found data entry");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // skip over language directory
 | 
				
			||||||
 | 
					        fileOffset = rsrc_offset.value() + (rsrcTblEntry.offset & 0x7FFFFFFF);
 | 
				
			||||||
 | 
					        const auto* langDir = safe_cast<const ResourceDirectory>(data, fileOffset);
 | 
				
			||||||
 | 
					        if (langDir->id_count < 1)
 | 
				
			||||||
 | 
					            throw std::runtime_error("Incorrect language directory");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fileOffset += sizeof(ResourceDirectory);
 | 
				
			||||||
 | 
					        const auto* langDirEntry = safe_cast<const ResourceDirectoryEntry>(data, fileOffset);
 | 
				
			||||||
 | 
					        if ((langDirEntry->offset & 0x80000000) != 0)
 | 
				
			||||||
 | 
					            throw std::runtime_error("Expected resource data entry, but found directory");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // parse resource data entry
 | 
				
			||||||
 | 
					        fileOffset = rsrc_offset.value() + (langDirEntry->offset & 0x7FFFFFFF);
 | 
				
			||||||
 | 
					        const auto* entry = safe_cast<const ResourceDataEntry>(data, fileOffset);
 | 
				
			||||||
 | 
					        if (entry->offset < rsrc_rva || entry->offset > (rsrc_rva + rsrc_size))
 | 
				
			||||||
 | 
					            throw std::runtime_error("Resource data entry points outside resource section");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // extract resource
 | 
				
			||||||
 | 
					        std::vector<uint8_t> resource(entry->size);
 | 
				
			||||||
 | 
					        fileOffset = (entry->offset - rsrc_rva) + rsrc_offset.value();
 | 
				
			||||||
 | 
					        if (fileOffset + entry->size > data.size())
 | 
				
			||||||
 | 
					            throw std::runtime_error("Resource data entry points outside file");
 | 
				
			||||||
 | 
					        std::copy_n(&data.at(fileOffset), entry->size, resource.data());
 | 
				
			||||||
 | 
					        resources.emplace(rsrcTblEntry.id, std::move(resource));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return resources;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,16 +1,14 @@
 | 
				
			||||||
#include "extract/extract.hpp"
 | 
					#include "extract/extract.hpp"
 | 
				
			||||||
#include "config/config.hpp"
 | 
					#include "config/config.hpp"
 | 
				
			||||||
 | 
					#include "extract/dll.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <pe-parse/parse.h>
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <unordered_map>
 | 
					#include <unordered_map>
 | 
				
			||||||
#include <filesystem>
 | 
					#include <filesystem>
 | 
				
			||||||
#include <algorithm>
 | 
					 | 
				
			||||||
#include <stdexcept>
 | 
					#include <stdexcept>
 | 
				
			||||||
#include <cstdint>
 | 
					#include <cstdint>
 | 
				
			||||||
#include <cstdlib>
 | 
					#include <cstdlib>
 | 
				
			||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
#include <utility>
 | 
					 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
#include <array>
 | 
					#include <array>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -73,22 +71,11 @@ const std::unordered_map<std::string, uint32_t> nameIdxTable = {{
 | 
				
			||||||
}};
 | 
					}};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace {
 | 
					namespace {
 | 
				
			||||||
    auto& pshaders() {
 | 
					    auto& shaders() {
 | 
				
			||||||
        static std::unordered_map<uint32_t, std::array<std::vector<uint8_t>, 2>> shaderData;
 | 
					        static std::unordered_map<uint32_t, std::array<std::vector<uint8_t>, 2>> shaderData;
 | 
				
			||||||
        return shaderData;
 | 
					        return shaderData;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    int on_resource(void* ptr, const peparse::resource& res) {
 | 
					 | 
				
			||||||
        if (res.type != peparse::RT_RCDATA || res.buf == nullptr || res.buf->bufLen <= 0)
 | 
					 | 
				
			||||||
            return 0;
 | 
					 | 
				
			||||||
        std::vector<uint8_t> resource_data(res.buf->bufLen);
 | 
					 | 
				
			||||||
        std::copy_n(res.buf->buf, res.buf->bufLen, resource_data.data());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        auto* shaders = reinterpret_cast<std::unordered_map<uint32_t, std::vector<uint8_t>>*>(ptr);
 | 
					 | 
				
			||||||
        shaders->emplace(res.name, std::move(resource_data));
 | 
					 | 
				
			||||||
        return 0;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const std::vector<std::filesystem::path> PATHS{{
 | 
					    const std::vector<std::filesystem::path> PATHS{{
 | 
				
			||||||
        ".local/share/Steam/steamapps/common",
 | 
					        ".local/share/Steam/steamapps/common",
 | 
				
			||||||
        ".steam/steam/steamapps/common",
 | 
					        ".steam/steam/steamapps/common",
 | 
				
			||||||
| 
						 | 
					@ -99,7 +86,7 @@ namespace {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::string getDllPath() {
 | 
					    std::string getDllPath() {
 | 
				
			||||||
        // overriden path
 | 
					        // overriden path
 | 
				
			||||||
        std::string dllPath = Config::activeConf.dll;
 | 
					        std::string dllPath = Config::globalConf.dll;
 | 
				
			||||||
        if (!dllPath.empty())
 | 
					        if (!dllPath.empty())
 | 
				
			||||||
            return dllPath;
 | 
					            return dllPath;
 | 
				
			||||||
        // home based paths
 | 
					        // home based paths
 | 
				
			||||||
| 
						 | 
					@ -121,44 +108,39 @@ namespace {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Extract::extractShaders() {
 | 
					void Extract::extractShaders() {
 | 
				
			||||||
    if (!pshaders().empty())
 | 
					    if (!shaders().empty())
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::unordered_map<uint32_t, std::vector<uint8_t>> shaders{};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // parse the dll
 | 
					    // parse the dll
 | 
				
			||||||
    peparse::parsed_pe* dll = peparse::ParsePEFromFile(getDllPath().c_str());
 | 
					    const auto resources = DLL::parse_dll(getDllPath());
 | 
				
			||||||
    if (!dll)
 | 
					    std::cerr << "lsfg-vk: Extracted " << resources.size() << " resources from dll.\n";
 | 
				
			||||||
        throw std::runtime_error("Unable to read Lossless.dll, is it installed?");
 | 
					 | 
				
			||||||
    peparse::IterRsrc(dll, on_resource, reinterpret_cast<void*>(&shaders));
 | 
					 | 
				
			||||||
    peparse::DestructParsedPE(dll);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // ensure all shaders are present
 | 
					    // ensure all shaders are present
 | 
				
			||||||
    for (const auto& [name, idx] : nameIdxTable) {
 | 
					    for (const auto& [name, idx] : nameIdxTable) {
 | 
				
			||||||
        auto fp16 = shaders.find(idx);
 | 
					        auto fp16 = resources.find(idx);
 | 
				
			||||||
        if (fp16 == shaders.end())
 | 
					        if (fp16 == resources.end())
 | 
				
			||||||
            throw std::runtime_error("Shader not found: " + name + " (FP16).\n- Is Lossless Scaling up to date?");
 | 
					            throw std::runtime_error("Shader not found: " + name + " (FP16).\n- Is Lossless Scaling up to date?");
 | 
				
			||||||
        auto fp32 = shaders.find(idx + FP);
 | 
					        auto fp32 = resources.find(idx + FP);
 | 
				
			||||||
        if (fp32 == shaders.end())
 | 
					        if (fp32 == resources.end())
 | 
				
			||||||
            throw std::runtime_error("Shader not found: " + name + " (FP32).\n- Is Lossless Scaling up to date?");
 | 
					            throw std::runtime_error("Shader not found: " + name + " (FP32).\n- Is Lossless Scaling up to date?");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        pshaders().emplace(idx, std::array<std::vector<uint8_t>, 2>{
 | 
					        shaders().emplace(idx, std::array<std::vector<uint8_t>, 2>{
 | 
				
			||||||
            std::move(fp32->second),
 | 
					            fp32->second,
 | 
				
			||||||
            std::move(fp16->second)
 | 
					            fp16->second
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::vector<uint8_t> Extract::getShader(const std::string& name, bool fp16) {
 | 
					std::vector<uint8_t> Extract::getShader(const std::string& name, bool fp16) {
 | 
				
			||||||
    if (pshaders().empty())
 | 
					    if (shaders().empty())
 | 
				
			||||||
        throw std::runtime_error("Shaders are not loaded.");
 | 
					        throw std::runtime_error("Shaders are not loaded.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto hit = nameIdxTable.find(name);
 | 
					    auto hit = nameIdxTable.find(name);
 | 
				
			||||||
    if (hit == nameIdxTable.end())
 | 
					    if (hit == nameIdxTable.end())
 | 
				
			||||||
        throw std::runtime_error("Shader hash not found: " + name);
 | 
					        throw std::runtime_error("Shader hash not found: " + name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto sit = pshaders().find(hit->second);
 | 
					    auto sit = shaders().find(hit->second);
 | 
				
			||||||
    if (sit == pshaders().end())
 | 
					    if (sit == shaders().end())
 | 
				
			||||||
        throw std::runtime_error("Shader not found: " + name);
 | 
					        throw std::runtime_error("Shader not found: " + name);
 | 
				
			||||||
    return fp16 ? sit->second.at(1) : sit->second.at(0);
 | 
					    return fp16 ? sit->second.at(1) : sit->second.at(0);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,6 @@
 | 
				
			||||||
#include <vulkan/vulkan_core.h>
 | 
					#include <vulkan/vulkan_core.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <unordered_map>
 | 
					#include <unordered_map>
 | 
				
			||||||
#include <filesystem>
 | 
					 | 
				
			||||||
#include <stdexcept>
 | 
					#include <stdexcept>
 | 
				
			||||||
#include <algorithm>
 | 
					#include <algorithm>
 | 
				
			||||||
#include <exception>
 | 
					#include <exception>
 | 
				
			||||||
| 
						 | 
					@ -107,7 +106,6 @@ namespace {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::unordered_map<VkSwapchainKHR, LsContext> swapchains;
 | 
					    std::unordered_map<VkSwapchainKHR, LsContext> swapchains;
 | 
				
			||||||
    std::unordered_map<VkSwapchainKHR, VkDevice> swapchainToDeviceTable;
 | 
					    std::unordered_map<VkSwapchainKHR, VkDevice> swapchainToDeviceTable;
 | 
				
			||||||
    std::unordered_map<VkSwapchainKHR, VkPresentModeKHR> swapchainToPresent;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// Adjust swapchain creation parameters and create a swapchain context.
 | 
					    /// Adjust swapchain creation parameters and create a swapchain context.
 | 
				
			||||||
| 
						 | 
					@ -117,6 +115,21 @@ namespace {
 | 
				
			||||||
            const VkSwapchainCreateInfoKHR* pCreateInfo,
 | 
					            const VkSwapchainCreateInfoKHR* pCreateInfo,
 | 
				
			||||||
            const VkAllocationCallbacks* pAllocator,
 | 
					            const VkAllocationCallbacks* pAllocator,
 | 
				
			||||||
            VkSwapchainKHR* pSwapchain) noexcept {
 | 
					            VkSwapchainKHR* pSwapchain) noexcept {
 | 
				
			||||||
 | 
					        // retire potential old swapchain
 | 
				
			||||||
 | 
					        if (pCreateInfo->oldSwapchain) {
 | 
				
			||||||
 | 
					            swapchains.erase(pCreateInfo->oldSwapchain);
 | 
				
			||||||
 | 
					            swapchainToDeviceTable.erase(pCreateInfo->oldSwapchain);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // ensure configuration is up to date
 | 
				
			||||||
 | 
					        Config::checkStatus();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // return early if disabled
 | 
				
			||||||
 | 
					        if (!Config::currentConf.has_value() || Config::currentConf->multiplier <= 1)
 | 
				
			||||||
 | 
					            return Layer::ovkCreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain);
 | 
				
			||||||
 | 
					        auto& conf = Config::currentConf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // find device
 | 
					        // find device
 | 
				
			||||||
        auto it = deviceToInfo.find(device);
 | 
					        auto it = deviceToInfo.find(device);
 | 
				
			||||||
        if (it == deviceToInfo.end()) {
 | 
					        if (it == deviceToInfo.end()) {
 | 
				
			||||||
| 
						 | 
					@ -150,13 +163,7 @@ namespace {
 | 
				
			||||||
        createInfo.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
 | 
					        createInfo.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // enforce present mode
 | 
					        // enforce present mode
 | 
				
			||||||
        createInfo.presentMode = Config::activeConf.e_present;
 | 
					        createInfo.presentMode = conf->e_present;
 | 
				
			||||||
 | 
					 | 
				
			||||||
        // retire potential old swapchain
 | 
					 | 
				
			||||||
        if (pCreateInfo->oldSwapchain) {
 | 
					 | 
				
			||||||
            swapchains.erase(pCreateInfo->oldSwapchain);
 | 
					 | 
				
			||||||
            swapchainToDeviceTable.erase(pCreateInfo->oldSwapchain);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // create swapchain
 | 
					        // create swapchain
 | 
				
			||||||
        auto res = Layer::ovkCreateSwapchainKHR(device, &createInfo, pAllocator, pSwapchain);
 | 
					        auto res = Layer::ovkCreateSwapchainKHR(device, &createInfo, pAllocator, pSwapchain);
 | 
				
			||||||
| 
						 | 
					@ -164,8 +171,6 @@ namespace {
 | 
				
			||||||
            return res; // can't be caused by lsfg-vk (yet)
 | 
					            return res; // can't be caused by lsfg-vk (yet)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            swapchainToPresent.emplace(*pSwapchain, createInfo.presentMode);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // get all swapchain images
 | 
					            // get all swapchain images
 | 
				
			||||||
            uint32_t imageCount{};
 | 
					            uint32_t imageCount{};
 | 
				
			||||||
            res = Layer::ovkGetSwapchainImagesKHR(device, *pSwapchain, &imageCount, nullptr);
 | 
					            res = Layer::ovkGetSwapchainImagesKHR(device, *pSwapchain, &imageCount, nullptr);
 | 
				
			||||||
| 
						 | 
					@ -205,6 +210,16 @@ namespace {
 | 
				
			||||||
    VkResult myvkQueuePresentKHR(
 | 
					    VkResult myvkQueuePresentKHR(
 | 
				
			||||||
            VkQueue queue,
 | 
					            VkQueue queue,
 | 
				
			||||||
            const VkPresentInfoKHR* pPresentInfo) noexcept {
 | 
					            const VkPresentInfoKHR* pPresentInfo) noexcept {
 | 
				
			||||||
 | 
					        // ensure configuration is up to date
 | 
				
			||||||
 | 
					        if (!Config::checkStatus())
 | 
				
			||||||
 | 
					            return VK_ERROR_OUT_OF_DATE_KHR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // return early if disabled
 | 
				
			||||||
 | 
					        if (!Config::currentConf.has_value() || Config::currentConf->multiplier <= 1)
 | 
				
			||||||
 | 
					            return Layer::ovkQueuePresentKHR(queue, pPresentInfo);
 | 
				
			||||||
 | 
					        auto& conf = Config::currentConf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // find swapchain device
 | 
					        // find swapchain device
 | 
				
			||||||
        auto it = swapchainToDeviceTable.find(*pPresentInfo->pSwapchains);
 | 
					        auto it = swapchainToDeviceTable.find(*pPresentInfo->pSwapchains);
 | 
				
			||||||
        if (it == swapchainToDeviceTable.end()) {
 | 
					        if (it == swapchainToDeviceTable.end()) {
 | 
				
			||||||
| 
						 | 
					@ -212,34 +227,28 @@ namespace {
 | 
				
			||||||
                "Swapchain not found in map");
 | 
					                "Swapchain not found in map");
 | 
				
			||||||
            return Layer::ovkQueuePresentKHR(queue, pPresentInfo);
 | 
					            return Layer::ovkQueuePresentKHR(queue, pPresentInfo);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        Utils::resetLimitN("swapMap");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // find device info
 | 
					        // find device info
 | 
				
			||||||
        auto it2 = deviceToInfo.find(it->second);
 | 
					        auto it2 = deviceToInfo.find(it->second);
 | 
				
			||||||
        if (it2 == deviceToInfo.end()) {
 | 
					        if (it2 == deviceToInfo.end()) {
 | 
				
			||||||
            Utils::logLimitN("swapMap", 5,
 | 
					            Utils::logLimitN("deviceMap", 5,
 | 
				
			||||||
                "Device not found in map");
 | 
					                "Device not found in map");
 | 
				
			||||||
            return Layer::ovkQueuePresentKHR(queue, pPresentInfo);
 | 
					            return Layer::ovkQueuePresentKHR(queue, pPresentInfo);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        Utils::resetLimitN("deviceMap");
 | 
				
			||||||
        auto& deviceInfo = it2->second;
 | 
					        auto& deviceInfo = it2->second;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // find swapchain context
 | 
					        // find swapchain context
 | 
				
			||||||
        auto it3 = swapchains.find(*pPresentInfo->pSwapchains);
 | 
					        auto it3 = swapchains.find(*pPresentInfo->pSwapchains);
 | 
				
			||||||
        if (it3 == swapchains.end()) {
 | 
					        if (it3 == swapchains.end()) {
 | 
				
			||||||
            Utils::logLimitN("swapMap", 5,
 | 
					            Utils::logLimitN("swapCtxMap", 5,
 | 
				
			||||||
                "Swapchain context not found in map");
 | 
					                "Swapchain context not found in map");
 | 
				
			||||||
            return Layer::ovkQueuePresentKHR(queue, pPresentInfo);
 | 
					            return Layer::ovkQueuePresentKHR(queue, pPresentInfo);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        Utils::resetLimitN("swapCtxMap");
 | 
				
			||||||
        auto& swapchain = it3->second;
 | 
					        auto& swapchain = it3->second;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // find present mode
 | 
					 | 
				
			||||||
        auto it4 = swapchainToPresent.find(*pPresentInfo->pSwapchains);
 | 
					 | 
				
			||||||
        if (it4 == swapchainToPresent.end()) {
 | 
					 | 
				
			||||||
            Utils::logLimitN("swapMap", 5,
 | 
					 | 
				
			||||||
                "Swapchain present mode not found in map");
 | 
					 | 
				
			||||||
            return Layer::ovkQueuePresentKHR(queue, pPresentInfo);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        auto& present = it4->second;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // enforce present mode | NOLINTBEGIN
 | 
					        // enforce present mode | NOLINTBEGIN
 | 
				
			||||||
        #pragma clang diagnostic push
 | 
					        #pragma clang diagnostic push
 | 
				
			||||||
        #pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
 | 
					        #pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
 | 
				
			||||||
| 
						 | 
					@ -249,7 +258,7 @@ namespace {
 | 
				
			||||||
            if (presentModeInfo->sType == VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODE_INFO_EXT) {
 | 
					            if (presentModeInfo->sType == VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODE_INFO_EXT) {
 | 
				
			||||||
                for (size_t i = 0; i < presentModeInfo->swapchainCount; i++)
 | 
					                for (size_t i = 0; i < presentModeInfo->swapchainCount; i++)
 | 
				
			||||||
                    const_cast<VkPresentModeKHR*>(presentModeInfo->pPresentModes)[i] =
 | 
					                    const_cast<VkPresentModeKHR*>(presentModeInfo->pPresentModes)[i] =
 | 
				
			||||||
                        present;
 | 
					                        conf->e_present;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            presentModeInfo =
 | 
					            presentModeInfo =
 | 
				
			||||||
                reinterpret_cast<const VkSwapchainPresentModeInfoEXT*>(presentModeInfo->pNext);
 | 
					                reinterpret_cast<const VkSwapchainPresentModeInfoEXT*>(presentModeInfo->pNext);
 | 
				
			||||||
| 
						 | 
					@ -259,27 +268,6 @@ namespace {
 | 
				
			||||||
        // NOLINTEND | present the next frame
 | 
					        // NOLINTEND | present the next frame
 | 
				
			||||||
        VkResult res{}; // might return VK_SUBOPTIMAL_KHR
 | 
					        VkResult res{}; // might return VK_SUBOPTIMAL_KHR
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            // ensure config is valid
 | 
					 | 
				
			||||||
            auto& conf = Config::activeConf;
 | 
					 | 
				
			||||||
            if (!conf.config_file.empty()
 | 
					 | 
				
			||||||
                    && (
 | 
					 | 
				
			||||||
                            !std::filesystem::exists(conf.config_file)
 | 
					 | 
				
			||||||
                          || conf.timestamp != std::filesystem::last_write_time(conf.config_file)
 | 
					 | 
				
			||||||
                    )) {
 | 
					 | 
				
			||||||
                Layer::ovkQueuePresentKHR(queue, pPresentInfo);
 | 
					 | 
				
			||||||
                return VK_ERROR_OUT_OF_DATE_KHR;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // ensure present mode is still valid
 | 
					 | 
				
			||||||
            if (present != conf.e_present) {
 | 
					 | 
				
			||||||
                Layer::ovkQueuePresentKHR(queue, pPresentInfo);
 | 
					 | 
				
			||||||
                return VK_ERROR_OUT_OF_DATE_KHR;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // skip if disabled
 | 
					 | 
				
			||||||
            if (conf.multiplier <= 1)
 | 
					 | 
				
			||||||
                return Layer::ovkQueuePresentKHR(queue, pPresentInfo);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // present the swapchain
 | 
					            // present the swapchain
 | 
				
			||||||
            std::vector<VkSemaphore> semaphores(pPresentInfo->waitSemaphoreCount);
 | 
					            std::vector<VkSemaphore> semaphores(pPresentInfo->waitSemaphoreCount);
 | 
				
			||||||
            std::copy_n(pPresentInfo->pWaitSemaphores, semaphores.size(), semaphores.data());
 | 
					            std::copy_n(pPresentInfo->pWaitSemaphores, semaphores.size(), semaphores.data());
 | 
				
			||||||
| 
						 | 
					@ -304,7 +292,6 @@ namespace {
 | 
				
			||||||
            const VkAllocationCallbacks* pAllocator) noexcept {
 | 
					            const VkAllocationCallbacks* pAllocator) noexcept {
 | 
				
			||||||
        swapchains.erase(swapchain);
 | 
					        swapchains.erase(swapchain);
 | 
				
			||||||
        swapchainToDeviceTable.erase(swapchain);
 | 
					        swapchainToDeviceTable.erase(swapchain);
 | 
				
			||||||
        swapchainToPresent.erase(swapchain);
 | 
					 | 
				
			||||||
        Layer::ovkDestroySwapchainKHR(device, swapchain, pAllocator);
 | 
					        Layer::ovkDestroySwapchainKHR(device, swapchain, pAllocator);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,6 +9,7 @@
 | 
				
			||||||
#include <unordered_map>
 | 
					#include <unordered_map>
 | 
				
			||||||
#include <exception>
 | 
					#include <exception>
 | 
				
			||||||
#include <iostream>
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <cstdlib>
 | 
				
			||||||
#include <cstdint>
 | 
					#include <cstdint>
 | 
				
			||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -103,7 +104,7 @@ namespace {
 | 
				
			||||||
                    "Failed to get instance function pointer for vkCreateInstance");
 | 
					                    "Failed to get instance function pointer for vkCreateInstance");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // NOLINTEND | skip initialization if the layer is disabled
 | 
					            // NOLINTEND | skip initialization if the layer is disabled
 | 
				
			||||||
            if (!Config::activeConf.enable) {
 | 
					            if (!Config::currentConf.has_value() || std::getenv("LSFG_BENCHMARK")) {
 | 
				
			||||||
                auto res = next_vkCreateInstance(pCreateInfo, pAllocator, pInstance);
 | 
					                auto res = next_vkCreateInstance(pCreateInfo, pAllocator, pInstance);
 | 
				
			||||||
                initInstanceFunc(*pInstance, "vkCreateDevice", &next_vkCreateDevice);
 | 
					                initInstanceFunc(*pInstance, "vkCreateDevice", &next_vkCreateDevice);
 | 
				
			||||||
                return res;
 | 
					                return res;
 | 
				
			||||||
| 
						 | 
					@ -182,7 +183,7 @@ namespace {
 | 
				
			||||||
            next_vSetDeviceLoaderData = layerDesc2->u.pfnSetDeviceLoaderData;
 | 
					            next_vSetDeviceLoaderData = layerDesc2->u.pfnSetDeviceLoaderData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // NOLINTEND | skip initialization if the layer is disabled
 | 
					            // NOLINTEND | skip initialization if the layer is disabled
 | 
				
			||||||
            if (!Config::activeConf.enable)
 | 
					            if (!Config::currentConf.has_value() || std::getenv("LSFG_BENCHMARK"))
 | 
				
			||||||
                return next_vkCreateDevice(physicalDevice, pCreateInfo, pAllocator, pDevice);
 | 
					                return next_vkCreateDevice(physicalDevice, pCreateInfo, pAllocator, pDevice);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // create device
 | 
					            // create device
 | 
				
			||||||
| 
						 | 
					@ -262,7 +263,7 @@ PFN_vkVoidFunction layer_vkGetInstanceProcAddr(VkInstance instance, const char*
 | 
				
			||||||
        return it->second;
 | 
					        return it->second;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it = Hooks::hooks.find(name);
 | 
					    it = Hooks::hooks.find(name);
 | 
				
			||||||
    if (it != Hooks::hooks.end() && Config::activeConf.enable)
 | 
					    if (it != Hooks::hooks.end() && (Config::currentConf.has_value() && !std::getenv("LSFG_BENCHMARK")))
 | 
				
			||||||
        return it->second;
 | 
					        return it->second;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return next_vkGetInstanceProcAddr(instance, pName);
 | 
					    return next_vkGetInstanceProcAddr(instance, pName);
 | 
				
			||||||
| 
						 | 
					@ -275,7 +276,7 @@ PFN_vkVoidFunction layer_vkGetDeviceProcAddr(VkDevice device, const char* pName)
 | 
				
			||||||
        return it->second;
 | 
					        return it->second;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it = Hooks::hooks.find(name);
 | 
					    it = Hooks::hooks.find(name);
 | 
				
			||||||
    if (it != Hooks::hooks.end() && Config::activeConf.enable)
 | 
					    if (it != Hooks::hooks.end() && (Config::currentConf.has_value() && !std::getenv("LSFG_BENCHMARK")))
 | 
				
			||||||
        return it->second;
 | 
					        return it->second;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return next_vkGetDeviceProcAddr(device, pName);
 | 
					    return next_vkGetDeviceProcAddr(device, pName);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										33
									
								
								src/main.cpp
									
										
									
									
									
								
							
							
						
						
									
										33
									
								
								src/main.cpp
									
										
									
									
									
								
							| 
						 | 
					@ -18,41 +18,17 @@ namespace {
 | 
				
			||||||
        std::cerr << std::unitbuf;
 | 
					        std::cerr << std::unitbuf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // read configuration
 | 
					        // read configuration
 | 
				
			||||||
        const std::string file = Utils::getConfigFile();
 | 
					 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            Config::updateConfig(file);
 | 
					            Config::updateConfig(Utils::getConfigFile(), Utils::getProcessName());
 | 
				
			||||||
        } catch (const std::exception& e) {
 | 
					        } catch (const std::exception& e) {
 | 
				
			||||||
            std::cerr << "lsfg-vk: An error occured while trying to parse the configuration, IGNORING:\n";
 | 
					            std::cerr << "lsfg-vk: An error occured while trying to parse the configuration, IGNORING:\n";
 | 
				
			||||||
            std::cerr << "- " << e.what() << '\n';
 | 
					            std::cerr << "- " << e.what() << '\n';
 | 
				
			||||||
            return; // default configuration will unload
 | 
					            return;
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const auto name = Utils::getProcessName();
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            Config::activeConf = Config::getConfig(name);
 | 
					 | 
				
			||||||
        } catch (const std::exception& e) {
 | 
					 | 
				
			||||||
            std::cerr << "lsfg-vk: The configuration for " << name.first << " is invalid, IGNORING:\n";
 | 
					 | 
				
			||||||
            std::cerr << e.what() << '\n';
 | 
					 | 
				
			||||||
            return; // default configuration will unload
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // exit silently if not enabled
 | 
					        // exit silently if not enabled
 | 
				
			||||||
        auto& conf = Config::activeConf;
 | 
					        if (!Config::currentConf.has_value())
 | 
				
			||||||
        if (!conf.enable && name.second != "benchmark")
 | 
					            return;
 | 
				
			||||||
            return; // default configuration will unload
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // print config
 | 
					 | 
				
			||||||
        std::cerr << "lsfg-vk: Loaded configuration for " << name.first << ":\n";
 | 
					 | 
				
			||||||
        if (!conf.dll.empty()) std::cerr << "  Using DLL from: " << conf.dll << '\n';
 | 
					 | 
				
			||||||
        if (conf.no_fp16) std::cerr << "  FP16 Acceleration: Force-disabled\n";
 | 
					 | 
				
			||||||
        std::cerr << "  Multiplier: " << conf.multiplier << '\n';
 | 
					 | 
				
			||||||
        std::cerr << "  Flow Scale: " << conf.flowScale << '\n';
 | 
					 | 
				
			||||||
        std::cerr << "  Performance Mode: " << (conf.performance ? "Enabled" : "Disabled") << '\n';
 | 
					 | 
				
			||||||
        std::cerr << "  HDR Mode: " << (conf.hdr ? "Enabled" : "Disabled") << '\n';
 | 
					 | 
				
			||||||
        if (conf.e_present != 2) std::cerr << "  ! Present Mode: " << conf.e_present << '\n';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // remove mesa var in favor of config
 | 
					 | 
				
			||||||
        unsetenv("MESA_VK_WSI_PRESENT_MODE"); // NOLINT
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // load shaders
 | 
					        // load shaders
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
| 
						 | 
					@ -105,6 +81,5 @@ namespace {
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        benchmark.detach();
 | 
					        benchmark.detach();
 | 
				
			||||||
        conf.enable = false;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,7 +19,10 @@
 | 
				
			||||||
using namespace Benchmark;
 | 
					using namespace Benchmark;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Benchmark::run(uint32_t width, uint32_t height) {
 | 
					void Benchmark::run(uint32_t width, uint32_t height) {
 | 
				
			||||||
    const auto& conf = Config::activeConf;
 | 
					    if (!Config::currentConf.has_value())
 | 
				
			||||||
 | 
					        _exit(1); // not possible
 | 
				
			||||||
 | 
					    const auto& globalConf = Config::globalConf;
 | 
				
			||||||
 | 
					    const auto& conf = *Config::currentConf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto* lsfgInitialize = LSFG_3_1::initialize;
 | 
					    auto* lsfgInitialize = LSFG_3_1::initialize;
 | 
				
			||||||
    auto* lsfgCreateContext = LSFG_3_1::createContext;
 | 
					    auto* lsfgCreateContext = LSFG_3_1::createContext;
 | 
				
			||||||
| 
						 | 
					@ -41,7 +44,7 @@ void Benchmark::run(uint32_t width, uint32_t height) {
 | 
				
			||||||
    lsfgInitialize(
 | 
					    lsfgInitialize(
 | 
				
			||||||
        deviceUUID, // some magic number if not given
 | 
					        deviceUUID, // some magic number if not given
 | 
				
			||||||
        conf.hdr, 1.0F / conf.flowScale, conf.multiplier - 1,
 | 
					        conf.hdr, 1.0F / conf.flowScale, conf.multiplier - 1,
 | 
				
			||||||
        conf.no_fp16,
 | 
					        globalConf.no_fp16,
 | 
				
			||||||
        Extract::getShader
 | 
					        Extract::getShader
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    const int32_t ctx = lsfgCreateContext(-1, -1, {},
 | 
					    const int32_t ctx = lsfgCreateContext(-1, -1, {},
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -209,17 +209,17 @@ void Utils::resetLimitN(const std::string& id) noexcept {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::pair<std::string, std::string> Utils::getProcessName() {
 | 
					std::pair<std::string, std::string> Utils::getProcessName() {
 | 
				
			||||||
    // check override first
 | 
					    // check benchmark flag
 | 
				
			||||||
    const char* process_name = std::getenv("LSFG_PROCESS");
 | 
					 | 
				
			||||||
    if (process_name && *process_name != '\0')
 | 
					 | 
				
			||||||
        return { process_name, process_name };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // then check benchmark flag
 | 
					 | 
				
			||||||
    const char* benchmark_flag = std::getenv("LSFG_BENCHMARK");
 | 
					    const char* benchmark_flag = std::getenv("LSFG_BENCHMARK");
 | 
				
			||||||
    if (benchmark_flag)
 | 
					    if (benchmark_flag)
 | 
				
			||||||
        return { "benchmark", "benchmark" };
 | 
					        return { "benchmark", "benchmark" };
 | 
				
			||||||
    std::array<char, 4096> exe{};
 | 
					    std::array<char, 4096> exe{};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // then check override
 | 
				
			||||||
 | 
					    const char* process_name = std::getenv("LSFG_PROCESS");
 | 
				
			||||||
 | 
					    if (process_name && *process_name != '\0')
 | 
				
			||||||
 | 
					        return { process_name, process_name };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // find executed binary
 | 
					    // find executed binary
 | 
				
			||||||
    const ssize_t exe_len = readlink("/proc/self/exe", exe.data(), exe.size() - 1);
 | 
					    const ssize_t exe_len = readlink("/proc/self/exe", exe.data(), exe.size() - 1);
 | 
				
			||||||
    if (exe_len <= 0)
 | 
					    if (exe_len <= 0)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,6 +5,10 @@ if(NOT LSFGVK_EXCESS_DEBUG)
 | 
				
			||||||
    set(CMAKE_CXX_VISIBILITY_PRESET "hidden")
 | 
					    set(CMAKE_CXX_VISIBILITY_PRESET "hidden")
 | 
				
			||||||
endif()
 | 
					endif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if(LSFGVK_EXCESS_DEBUG)
 | 
				
			||||||
 | 
					    add_compile_definitions(LSFGVK_EXCESS_DEBUG)
 | 
				
			||||||
 | 
					endif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
project(lsfg-vk-test
 | 
					project(lsfg-vk-test
 | 
				
			||||||
    DESCRIPTION "Test: lsfg-vk"
 | 
					    DESCRIPTION "Test: lsfg-vk"
 | 
				
			||||||
    LANGUAGES CXX)
 | 
					    LANGUAGES CXX)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										1
									
								
								thirdparty/pe-parse
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								thirdparty/pe-parse
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1 +0,0 @@
 | 
				
			||||||
Subproject commit 31ac5966503689d5693cd9fb520bd525a8710e17
 | 
					 | 
				
			||||||
							
								
								
									
										1
									
								
								thirdparty/toml11
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								thirdparty/toml11
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1 +0,0 @@
 | 
				
			||||||
Subproject commit be08ba2be2a964edcdb3d3e3ea8d100abc26f286
 | 
					 | 
				
			||||||
							
								
								
									
										1
									
								
								thirdparty/volk
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								thirdparty/volk
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1 +0,0 @@
 | 
				
			||||||
Subproject commit be3dbd49bf77052665e96b6c7484af855e7e5f67
 | 
					 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue