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-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)
|
||||
|
||||
if(LSFGVK_EXCESS_DEBUG)
|
||||
|
|
@ -41,15 +38,13 @@ add_library(lsfg-vk SHARED ${SOURCES})
|
|||
set_target_properties(lsfg-vk PROPERTIES
|
||||
CXX_STANDARD 20
|
||||
CXX_STANDARD_REQUIRED ON)
|
||||
target_include_directories(lsfg-vk SYSTEM
|
||||
PUBLIC include/thirdparty)
|
||||
target_include_directories(lsfg-vk
|
||||
PUBLIC include)
|
||||
target_link_libraries(lsfg-vk PUBLIC
|
||||
pe-parse toml11
|
||||
lsfg-vk-framegen)
|
||||
|
||||
get_target_property(TOML11_INCLUDE_DIRS toml11 INTERFACE_INCLUDE_DIRECTORIES)
|
||||
target_include_directories(lsfg-vk SYSTEM PRIVATE ${TOML11_INCLUDE_DIRS})
|
||||
|
||||
# diagnostics
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set_target_properties(lsfg-vk PROPERTIES
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ endif()
|
|||
|
||||
project(lsfg-vk-framegen
|
||||
DESCRIPTION "Lossless Scaling Frame Generation Backend"
|
||||
LANGUAGES CXX)
|
||||
LANGUAGES C CXX)
|
||||
|
||||
file(GLOB SOURCES
|
||||
"src/common/*.cpp"
|
||||
|
|
@ -29,6 +29,7 @@ file(GLOB SOURCES
|
|||
"v3.1p_src/shaders/*.cpp"
|
||||
"v3.1p_src/utils/*.cpp"
|
||||
"v3.1p_src/*.cpp"
|
||||
"src/thirdparty/*.c"
|
||||
)
|
||||
|
||||
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
|
||||
CXX_STANDARD 20
|
||||
CXX_STANDARD_REQUIRED ON)
|
||||
target_include_directories(lsfg-vk-framegen SYSTEM
|
||||
PUBLIC include/thirdparty)
|
||||
target_include_directories(lsfg-vk-framegen
|
||||
PUBLIC include
|
||||
PUBLIC public
|
||||
PRIVATE v3.1_include
|
||||
PRIVATE v3.1p_include)
|
||||
target_link_libraries(lsfg-vk-framegen
|
||||
PUBLIC volk)
|
||||
|
||||
# diagnostics
|
||||
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 {
|
||||
|
||||
/// lsfg-vk configuration
|
||||
struct Configuration {
|
||||
/// Whether lsfg-vk should be loaded in the first place.
|
||||
bool enable{false};
|
||||
/// Global lsfg-vk configuration.
|
||||
struct GlobalConfiguration {
|
||||
/// Path to Lossless.dll.
|
||||
std::string dll;
|
||||
/// Whether FP16 is force-disabled
|
||||
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
|
||||
size_t multiplier{2};
|
||||
/// The internal flow scale factor
|
||||
|
|
@ -28,35 +34,34 @@ namespace Config {
|
|||
bool hdr{false};
|
||||
|
||||
/// 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.
|
||||
extern Configuration activeConf;
|
||||
/// Global configuration.
|
||||
extern GlobalConfiguration globalConf;
|
||||
/// Currently active configuration.
|
||||
extern std::optional<GameConfiguration> currentConf;
|
||||
|
||||
///
|
||||
/// Read the configuration file while preserving the previous configuration
|
||||
/// in case of an error.
|
||||
///
|
||||
/// @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.
|
||||
///
|
||||
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 The configuration for the game or global configuration.
|
||||
/// @return Whether the configuration is up-to-date or not.
|
||||
///
|
||||
/// @throws std::runtime_error if the configuration is invalid.
|
||||
///
|
||||
Configuration getConfig(const std::pair<std::string, std::string>& name);
|
||||
bool checkStatus();
|
||||
|
||||
}
|
||||
|
|
|
|||
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 "common/exception.hpp"
|
||||
|
||||
#include "config/default_conf.hpp"
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
#include <toml11/find.hpp>
|
||||
#include <toml11/parser.hpp>
|
||||
#define TOML_ENABLE_FORMATTERS 0 // NOLINT
|
||||
#include <toml.hpp>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <algorithm>
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <fstream>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <utility>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <string>
|
||||
|
||||
using namespace Config;
|
||||
|
||||
namespace {
|
||||
Configuration globalConf{};
|
||||
std::optional<std::unordered_map<std::string, Configuration>> gameConfs;
|
||||
}
|
||||
|
||||
Configuration Config::activeConf{};
|
||||
GlobalConfiguration Config::globalConf{};
|
||||
std::optional<GameConfiguration> Config::currentConf{};
|
||||
|
||||
namespace {
|
||||
/// 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)) {
|
||||
std::cerr << "lsfg-vk: Placing default configuration file at " << file << '\n';
|
||||
const auto parent = std::filesystem::path(file).parent_path();
|
||||
|
|
@ -58,105 +78,97 @@ void Config::updateConfig(const std::string& file) {
|
|||
}
|
||||
|
||||
// parse config file
|
||||
std::optional<toml::value> parsed;
|
||||
toml::table config{};
|
||||
try {
|
||||
parsed.emplace(toml::parse(file));
|
||||
if (!parsed->contains("version"))
|
||||
throw std::runtime_error("Configuration file is missing 'version' field");
|
||||
if (parsed->at("version").as_integer() != 1)
|
||||
config = toml::parse_file(file);
|
||||
|
||||
const auto* version = config.get_as<toml::value<int64_t>>("version");
|
||||
if (!version || *version != 1)
|
||||
throw std::runtime_error("Configuration file version is not supported, expected 1");
|
||||
} catch (const std::exception& e) {
|
||||
throw LSFG::rethrowable_error("Unable to parse configuration file", e);
|
||||
}
|
||||
auto& toml = *parsed;
|
||||
|
||||
// parse global configuration
|
||||
const toml::value globalTable = toml::find_or_default<toml::table>(toml, "global");
|
||||
const Configuration global{
|
||||
.dll = toml::find_or(globalTable, "dll", std::string()),
|
||||
.no_fp16 = toml::find_or(globalTable, "no_fp16", false),
|
||||
Config::globalConf = {
|
||||
.config_file = file,
|
||||
.timestamp = std::filesystem::last_write_time(file)
|
||||
};
|
||||
|
||||
// validate global configuration
|
||||
if (global.multiplier < 2)
|
||||
throw std::runtime_error("Global Multiplier cannot be less than 2");
|
||||
if (global.flowScale < 0.25F || global.flowScale > 1.0F)
|
||||
throw std::runtime_error("Flow scale must be between 0.25 and 1.0");
|
||||
if (const auto* global = config.get_as<toml::table>("global")) {
|
||||
if (const auto* val = global->get_as<toml::value<std::string>>("dll"))
|
||||
globalConf.dll = val->get();
|
||||
if (const auto* val = global->get_as<toml::value<bool>>("no_fp16"))
|
||||
globalConf.no_fp16 = val->get();
|
||||
}
|
||||
|
||||
// parse game-specific configuration
|
||||
std::unordered_map<std::string, Configuration> games;
|
||||
const toml::value gamesList = toml::find_or_default<toml::array>(toml, "game");
|
||||
for (const auto& gameTable : gamesList.as_array()) {
|
||||
if (!gameTable.is_table())
|
||||
throw std::runtime_error("Invalid game configuration entry");
|
||||
if (!gameTable.contains("exe"))
|
||||
throw std::runtime_error("Game override missing 'exe' field");
|
||||
std::optional<GameConfiguration> gameConf;
|
||||
if (const auto* games = config["game"].as_array()) {
|
||||
for (auto&& elem : *games) {
|
||||
if (!elem.is_table())
|
||||
throw std::runtime_error("Invalid game configuration entry");
|
||||
const auto* game = elem.as_table();
|
||||
|
||||
const std::string exe = toml::find<std::string>(gameTable, "exe");
|
||||
Configuration game{
|
||||
.enable = true,
|
||||
.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
|
||||
};
|
||||
const auto* exe = game->at("exe").value_or("?");
|
||||
if (!name.first.ends_with(exe) && name.second != exe)
|
||||
continue;
|
||||
|
||||
// validate the configuration
|
||||
if (game.multiplier < 1)
|
||||
throw std::runtime_error("Multiplier cannot be less than 1");
|
||||
if (game.flowScale < 0.25F || game.flowScale > 1.0F)
|
||||
throw std::runtime_error("Flow scale must be between 0.25 and 1.0");
|
||||
games[exe] = std::move(game);
|
||||
gameConf = Config::currentConf;
|
||||
if (!gameConf.has_value()) gameConf.emplace();
|
||||
|
||||
if (const auto* val = game->get_as<toml::value<int64_t>>("multiplier"))
|
||||
gameConf->multiplier = static_cast<size_t>(val->get());
|
||||
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
|
||||
globalConf = global;
|
||||
gameConfs = std::move(games);
|
||||
}
|
||||
|
||||
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;
|
||||
if (!gameConf.has_value()) {
|
||||
Config::currentConf.reset();
|
||||
std::cerr << "lsfg-vk: Configuration entry disappeared, disabling.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
// process new configuration system
|
||||
if (!gameConfs.has_value())
|
||||
return globalConf;
|
||||
Config::currentConf = *gameConf;
|
||||
|
||||
const auto& games = *gameConfs;
|
||||
auto it = std::ranges::find_if(games, [&name](const auto& pair) {
|
||||
return name.first.ends_with(pair.first) || (name.second == pair.first);
|
||||
});
|
||||
if (it != games.end())
|
||||
return it->second;
|
||||
|
||||
return globalConf;
|
||||
// print updated config info
|
||||
std::cerr << "lsfg-vk: Loaded configuration for " << name.first << ":\n";
|
||||
if (!globalConf.dll.empty()) std::cerr << " Using DLL from: " << globalConf.dll << '\n';
|
||||
if (globalConf.no_fp16) std::cerr << " FP16 Acceleration: Force-disabled\n";
|
||||
std::cerr << " Multiplier: " << gameConf->multiplier << '\n';
|
||||
std::cerr << " Flow Scale: " << gameConf->flowScale << '\n';
|
||||
std::cerr << " Performance Mode: " << (gameConf->performance ? "Enabled" : "Disabled") << '\n';
|
||||
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_1p.hpp>
|
||||
|
||||
#include <filesystem>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <array>
|
||||
|
||||
LsContext::LsContext(const Hooks::DeviceInfo& info, VkSwapchainKHR swapchain,
|
||||
VkExtent2D extent, const std::vector<VkImage>& swapchainImages)
|
||||
: swapchain(swapchain), swapchainImages(swapchainImages),
|
||||
extent(extent) {
|
||||
// get updated configuration
|
||||
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)
|
||||
)) {
|
||||
std::cerr << "lsfg-vk: Rereading configuration, as it is no longer valid.\n";
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
if (!Config::currentConf.has_value())
|
||||
throw std::runtime_error("No configuration set");
|
||||
auto& globalConf = Config::globalConf;
|
||||
auto& conf = *Config::currentConf;
|
||||
|
||||
// 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,
|
||||
// but honestly this is safer.
|
||||
const VkFormat format = conf.hdr
|
||||
|
|
@ -99,7 +64,7 @@ LsContext::LsContext(const Hooks::DeviceInfo& info, VkSwapchainKHR swapchain,
|
|||
lsfgInitialize(
|
||||
Utils::getDeviceUUID(info.physicalDevice),
|
||||
conf.hdr, 1.0F / conf.flowScale, conf.multiplier - 1,
|
||||
conf.no_fp16,
|
||||
globalConf.no_fp16,
|
||||
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,
|
||||
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);
|
||||
|
||||
// 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 "config/config.hpp"
|
||||
#include "extract/dll.hpp"
|
||||
|
||||
#include <pe-parse/parse.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
#include <filesystem>
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
|
|
@ -73,22 +71,11 @@ const std::unordered_map<std::string, uint32_t> nameIdxTable = {{
|
|||
}};
|
||||
|
||||
namespace {
|
||||
auto& pshaders() {
|
||||
auto& shaders() {
|
||||
static std::unordered_map<uint32_t, std::array<std::vector<uint8_t>, 2>> 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{{
|
||||
".local/share/Steam/steamapps/common",
|
||||
".steam/steam/steamapps/common",
|
||||
|
|
@ -99,7 +86,7 @@ namespace {
|
|||
|
||||
std::string getDllPath() {
|
||||
// overriden path
|
||||
std::string dllPath = Config::activeConf.dll;
|
||||
std::string dllPath = Config::globalConf.dll;
|
||||
if (!dllPath.empty())
|
||||
return dllPath;
|
||||
// home based paths
|
||||
|
|
@ -121,44 +108,39 @@ namespace {
|
|||
}
|
||||
|
||||
void Extract::extractShaders() {
|
||||
if (!pshaders().empty())
|
||||
if (!shaders().empty())
|
||||
return;
|
||||
|
||||
std::unordered_map<uint32_t, std::vector<uint8_t>> shaders{};
|
||||
|
||||
// parse the dll
|
||||
peparse::parsed_pe* dll = peparse::ParsePEFromFile(getDllPath().c_str());
|
||||
if (!dll)
|
||||
throw std::runtime_error("Unable to read Lossless.dll, is it installed?");
|
||||
peparse::IterRsrc(dll, on_resource, reinterpret_cast<void*>(&shaders));
|
||||
peparse::DestructParsedPE(dll);
|
||||
const auto resources = DLL::parse_dll(getDllPath());
|
||||
std::cerr << "lsfg-vk: Extracted " << resources.size() << " resources from dll.\n";
|
||||
|
||||
// ensure all shaders are present
|
||||
for (const auto& [name, idx] : nameIdxTable) {
|
||||
auto fp16 = shaders.find(idx);
|
||||
if (fp16 == shaders.end())
|
||||
auto fp16 = resources.find(idx);
|
||||
if (fp16 == resources.end())
|
||||
throw std::runtime_error("Shader not found: " + name + " (FP16).\n- Is Lossless Scaling up to date?");
|
||||
auto fp32 = shaders.find(idx + FP);
|
||||
if (fp32 == shaders.end())
|
||||
auto fp32 = resources.find(idx + FP);
|
||||
if (fp32 == resources.end())
|
||||
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>{
|
||||
std::move(fp32->second),
|
||||
std::move(fp16->second)
|
||||
shaders().emplace(idx, std::array<std::vector<uint8_t>, 2>{
|
||||
fp32->second,
|
||||
fp16->second
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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.");
|
||||
|
||||
auto hit = nameIdxTable.find(name);
|
||||
if (hit == nameIdxTable.end())
|
||||
throw std::runtime_error("Shader hash not found: " + name);
|
||||
|
||||
auto sit = pshaders().find(hit->second);
|
||||
if (sit == pshaders().end())
|
||||
auto sit = shaders().find(hit->second);
|
||||
if (sit == shaders().end())
|
||||
throw std::runtime_error("Shader not found: " + name);
|
||||
return fp16 ? sit->second.at(1) : sit->second.at(0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <filesystem>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
#include <exception>
|
||||
|
|
@ -107,7 +106,6 @@ namespace {
|
|||
|
||||
std::unordered_map<VkSwapchainKHR, LsContext> swapchains;
|
||||
std::unordered_map<VkSwapchainKHR, VkDevice> swapchainToDeviceTable;
|
||||
std::unordered_map<VkSwapchainKHR, VkPresentModeKHR> swapchainToPresent;
|
||||
|
||||
///
|
||||
/// Adjust swapchain creation parameters and create a swapchain context.
|
||||
|
|
@ -117,6 +115,21 @@ namespace {
|
|||
const VkSwapchainCreateInfoKHR* pCreateInfo,
|
||||
const VkAllocationCallbacks* pAllocator,
|
||||
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
|
||||
auto it = deviceToInfo.find(device);
|
||||
if (it == deviceToInfo.end()) {
|
||||
|
|
@ -150,13 +163,7 @@ namespace {
|
|||
createInfo.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
|
||||
// enforce present mode
|
||||
createInfo.presentMode = Config::activeConf.e_present;
|
||||
|
||||
// retire potential old swapchain
|
||||
if (pCreateInfo->oldSwapchain) {
|
||||
swapchains.erase(pCreateInfo->oldSwapchain);
|
||||
swapchainToDeviceTable.erase(pCreateInfo->oldSwapchain);
|
||||
}
|
||||
createInfo.presentMode = conf->e_present;
|
||||
|
||||
// create swapchain
|
||||
auto res = Layer::ovkCreateSwapchainKHR(device, &createInfo, pAllocator, pSwapchain);
|
||||
|
|
@ -164,8 +171,6 @@ namespace {
|
|||
return res; // can't be caused by lsfg-vk (yet)
|
||||
|
||||
try {
|
||||
swapchainToPresent.emplace(*pSwapchain, createInfo.presentMode);
|
||||
|
||||
// get all swapchain images
|
||||
uint32_t imageCount{};
|
||||
res = Layer::ovkGetSwapchainImagesKHR(device, *pSwapchain, &imageCount, nullptr);
|
||||
|
|
@ -205,6 +210,16 @@ namespace {
|
|||
VkResult myvkQueuePresentKHR(
|
||||
VkQueue queue,
|
||||
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
|
||||
auto it = swapchainToDeviceTable.find(*pPresentInfo->pSwapchains);
|
||||
if (it == swapchainToDeviceTable.end()) {
|
||||
|
|
@ -212,34 +227,28 @@ namespace {
|
|||
"Swapchain not found in map");
|
||||
return Layer::ovkQueuePresentKHR(queue, pPresentInfo);
|
||||
}
|
||||
Utils::resetLimitN("swapMap");
|
||||
|
||||
// find device info
|
||||
auto it2 = deviceToInfo.find(it->second);
|
||||
if (it2 == deviceToInfo.end()) {
|
||||
Utils::logLimitN("swapMap", 5,
|
||||
Utils::logLimitN("deviceMap", 5,
|
||||
"Device not found in map");
|
||||
return Layer::ovkQueuePresentKHR(queue, pPresentInfo);
|
||||
}
|
||||
Utils::resetLimitN("deviceMap");
|
||||
auto& deviceInfo = it2->second;
|
||||
|
||||
// find swapchain context
|
||||
auto it3 = swapchains.find(*pPresentInfo->pSwapchains);
|
||||
if (it3 == swapchains.end()) {
|
||||
Utils::logLimitN("swapMap", 5,
|
||||
Utils::logLimitN("swapCtxMap", 5,
|
||||
"Swapchain context not found in map");
|
||||
return Layer::ovkQueuePresentKHR(queue, pPresentInfo);
|
||||
}
|
||||
Utils::resetLimitN("swapCtxMap");
|
||||
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
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
|
||||
|
|
@ -249,7 +258,7 @@ namespace {
|
|||
if (presentModeInfo->sType == VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODE_INFO_EXT) {
|
||||
for (size_t i = 0; i < presentModeInfo->swapchainCount; i++)
|
||||
const_cast<VkPresentModeKHR*>(presentModeInfo->pPresentModes)[i] =
|
||||
present;
|
||||
conf->e_present;
|
||||
}
|
||||
presentModeInfo =
|
||||
reinterpret_cast<const VkSwapchainPresentModeInfoEXT*>(presentModeInfo->pNext);
|
||||
|
|
@ -259,27 +268,6 @@ namespace {
|
|||
// NOLINTEND | present the next frame
|
||||
VkResult res{}; // might return VK_SUBOPTIMAL_KHR
|
||||
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
|
||||
std::vector<VkSemaphore> semaphores(pPresentInfo->waitSemaphoreCount);
|
||||
std::copy_n(pPresentInfo->pWaitSemaphores, semaphores.size(), semaphores.data());
|
||||
|
|
@ -304,7 +292,6 @@ namespace {
|
|||
const VkAllocationCallbacks* pAllocator) noexcept {
|
||||
swapchains.erase(swapchain);
|
||||
swapchainToDeviceTable.erase(swapchain);
|
||||
swapchainToPresent.erase(swapchain);
|
||||
Layer::ovkDestroySwapchainKHR(device, swapchain, pAllocator);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <unordered_map>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
|
|
@ -103,7 +104,7 @@ namespace {
|
|||
"Failed to get instance function pointer for vkCreateInstance");
|
||||
|
||||
// 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);
|
||||
initInstanceFunc(*pInstance, "vkCreateDevice", &next_vkCreateDevice);
|
||||
return res;
|
||||
|
|
@ -182,7 +183,7 @@ namespace {
|
|||
next_vSetDeviceLoaderData = layerDesc2->u.pfnSetDeviceLoaderData;
|
||||
|
||||
// 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);
|
||||
|
||||
// create device
|
||||
|
|
@ -262,7 +263,7 @@ PFN_vkVoidFunction layer_vkGetInstanceProcAddr(VkInstance instance, const char*
|
|||
return it->second;
|
||||
|
||||
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 next_vkGetInstanceProcAddr(instance, pName);
|
||||
|
|
@ -275,7 +276,7 @@ PFN_vkVoidFunction layer_vkGetDeviceProcAddr(VkDevice device, const char* pName)
|
|||
return it->second;
|
||||
|
||||
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 next_vkGetDeviceProcAddr(device, pName);
|
||||
|
|
|
|||
33
src/main.cpp
33
src/main.cpp
|
|
@ -18,41 +18,17 @@ namespace {
|
|||
std::cerr << std::unitbuf;
|
||||
|
||||
// read configuration
|
||||
const std::string file = Utils::getConfigFile();
|
||||
try {
|
||||
Config::updateConfig(file);
|
||||
Config::updateConfig(Utils::getConfigFile(), Utils::getProcessName());
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "lsfg-vk: An error occured while trying to parse the configuration, IGNORING:\n";
|
||||
std::cerr << "- " << e.what() << '\n';
|
||||
return; // default configuration will unload
|
||||
}
|
||||
|
||||
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
|
||||
return;
|
||||
}
|
||||
|
||||
// exit silently if not enabled
|
||||
auto& conf = Config::activeConf;
|
||||
if (!conf.enable && name.second != "benchmark")
|
||||
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
|
||||
if (!Config::currentConf.has_value())
|
||||
return;
|
||||
|
||||
// load shaders
|
||||
try {
|
||||
|
|
@ -105,6 +81,5 @@ namespace {
|
|||
}
|
||||
});
|
||||
benchmark.detach();
|
||||
conf.enable = false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,10 @@
|
|||
using namespace Benchmark;
|
||||
|
||||
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* lsfgCreateContext = LSFG_3_1::createContext;
|
||||
|
|
@ -41,7 +44,7 @@ void Benchmark::run(uint32_t width, uint32_t height) {
|
|||
lsfgInitialize(
|
||||
deviceUUID, // some magic number if not given
|
||||
conf.hdr, 1.0F / conf.flowScale, conf.multiplier - 1,
|
||||
conf.no_fp16,
|
||||
globalConf.no_fp16,
|
||||
Extract::getShader
|
||||
);
|
||||
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() {
|
||||
// check override first
|
||||
const char* process_name = std::getenv("LSFG_PROCESS");
|
||||
if (process_name && *process_name != '\0')
|
||||
return { process_name, process_name };
|
||||
|
||||
// then check benchmark flag
|
||||
// check benchmark flag
|
||||
const char* benchmark_flag = std::getenv("LSFG_BENCHMARK");
|
||||
if (benchmark_flag)
|
||||
return { "benchmark", "benchmark" };
|
||||
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
|
||||
const ssize_t exe_len = readlink("/proc/self/exe", exe.data(), exe.size() - 1);
|
||||
if (exe_len <= 0)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ if(NOT LSFGVK_EXCESS_DEBUG)
|
|||
set(CMAKE_CXX_VISIBILITY_PRESET "hidden")
|
||||
endif()
|
||||
|
||||
if(LSFGVK_EXCESS_DEBUG)
|
||||
add_compile_definitions(LSFGVK_EXCESS_DEBUG)
|
||||
endif()
|
||||
|
||||
project(lsfg-vk-test
|
||||
DESCRIPTION "Test: lsfg-vk"
|
||||
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