mirror of
https://github.com/PancakeTAS/lsfg-vk.git
synced 2026-04-22 02:11:43 +00:00
refactor(cleanup): new config system
This commit is contained in:
parent
69a9767551
commit
5fa3ddc8e3
13 changed files with 646 additions and 559 deletions
|
|
@ -1,67 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
namespace Config {
|
||||
|
||||
/// 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
|
||||
float flowScale{1.0F};
|
||||
/// Whether performance mode is enabled
|
||||
bool performance{false};
|
||||
/// Whether HDR is enabled
|
||||
bool hdr{false};
|
||||
|
||||
/// Experimental flag for overriding the synchronization method.
|
||||
VkPresentModeKHR e_present{ VK_PRESENT_MODE_FIFO_KHR };
|
||||
|
||||
};
|
||||
|
||||
/// 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,
|
||||
const std::pair<std::string, std::string>& name
|
||||
);
|
||||
|
||||
///
|
||||
/// Check if the configuration file is still up-to-date
|
||||
///
|
||||
/// @return Whether the configuration is up-to-date or not.
|
||||
///
|
||||
bool checkStatus();
|
||||
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
const std::string DEFAULT_CONFIG = R"(version = 1
|
||||
[global]
|
||||
# override the location of Lossless Scaling
|
||||
# dll = "/games/Lossless Scaling/Lossless.dll"
|
||||
# force-disable fp16 (use on older nvidia cards)
|
||||
# no_fp16 = true
|
||||
|
||||
# [[game]] # example entry
|
||||
# exe = "Game.exe"
|
||||
#
|
||||
# multiplier = 3
|
||||
# flow_scale = 0.7
|
||||
# performance_mode = true
|
||||
# hdr_mode = false
|
||||
#
|
||||
# experimental_present_mode = "fifo"
|
||||
|
||||
[[game]] # default vkcube entry
|
||||
exe = "vkcube"
|
||||
|
||||
multiplier = 4
|
||||
performance_mode = true
|
||||
|
||||
[[game]] # default benchmark entry
|
||||
exe = "benchmark"
|
||||
|
||||
multiplier = 4
|
||||
performance_mode = false
|
||||
|
||||
[[game]] # override Genshin Impact
|
||||
exe = "GenshinImpact.exe"
|
||||
|
||||
multiplier = 3
|
||||
)";
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
set(LAYER_SOURCES
|
||||
"src/config.cpp"
|
||||
"src/detection.cpp"
|
||||
"src/entrypoint.cpp"
|
||||
"src/layer.cpp")
|
||||
|
||||
|
|
@ -8,5 +10,8 @@ target_link_libraries(lsfg-vk-layer
|
|||
PUBLIC lsfg-vk-common
|
||||
PUBLIC lsfg-vk-backend)
|
||||
|
||||
target_include_directories(lsfg-vk-layer SYSTEM
|
||||
PRIVATE thirdparty/include)
|
||||
|
||||
set_target_properties(lsfg-vk-layer PROPERTIES
|
||||
CXX_VISIBILITY_PRESET hidden)
|
||||
|
|
|
|||
238
lsfg-vk-layer/src/config.cpp
Normal file
238
lsfg-vk-layer/src/config.cpp
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
#include "config.hpp"
|
||||
#include "lsfg-vk-backend/lsfgvk.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#define TOML_ENABLE_FORMATTERS 0
|
||||
#include <toml.hpp>
|
||||
|
||||
using namespace lsfgvk::layer;
|
||||
|
||||
namespace {
|
||||
const char* DEFAULT_CONFIG = R"(version = 2
|
||||
|
||||
[global]
|
||||
# dll = '/media/games/Lossless Scaling/Lossless.dll' # if you don't have LS in the default location
|
||||
allow_fp16 = true # this will improve give a MASSIVE performance boost on AMD, but be super slow on older (!) NVIDIA GPUs
|
||||
|
||||
[[profile]]
|
||||
name = "4x FG / 85% [Performance]"
|
||||
active_in = [ # see the wiki for more info
|
||||
'vkcube',
|
||||
'vkcubepp'
|
||||
]
|
||||
multiplier = 4
|
||||
flow_scale = 0.85
|
||||
performance_mode = true
|
||||
pacing = 'none' # see the wiki for more info
|
||||
|
||||
[[profile]]
|
||||
name = "2x FG / 100%"
|
||||
active_in = 'GenshinImpact.exe'
|
||||
multiplier = 2
|
||||
)";
|
||||
|
||||
/// parse an activity array from toml value
|
||||
std::vector<std::string> activityFromString(const toml::node_view<const toml::node>& val) {
|
||||
std::vector<std::string> active_in{};
|
||||
|
||||
if (const auto& as_str = val.value<std::string>()) {
|
||||
active_in.push_back(*as_str);
|
||||
}
|
||||
|
||||
if (const auto& as_arr = val.as_array()) {
|
||||
for (const auto& item : *as_arr) {
|
||||
if (const auto& item_str = item.value<std::string>())
|
||||
active_in.push_back(*item_str);
|
||||
}
|
||||
}
|
||||
|
||||
return active_in;
|
||||
}
|
||||
/// parse a pacing method from string
|
||||
Pacing parcingFromString(const std::string& str) {
|
||||
if (str == "none")
|
||||
return Pacing::None;
|
||||
throw lsfgvk::error("unknown pacing method: " + str);
|
||||
}
|
||||
/// try to find the config
|
||||
std::filesystem::path findPath() {
|
||||
// always honor LSFGVK_CONFIG if set
|
||||
const char* envPath = std::getenv("LSFGVK_CONFIG");
|
||||
if (envPath && *envPath != '\0')
|
||||
return{envPath};
|
||||
|
||||
// then check the XDG overriden location
|
||||
const char* xdgPath = std::getenv("XDG_CONFIG_HOME");
|
||||
if (xdgPath && *xdgPath != '\0')
|
||||
return std::filesystem::path(xdgPath)
|
||||
/ "lsfg-vk" / "conf.toml";
|
||||
|
||||
// fallback to typical user home
|
||||
const char* homePath = std::getenv("HOME");
|
||||
if (homePath && *homePath != '\0')
|
||||
return std::filesystem::path(homePath)
|
||||
/ ".config" / "lsfg-vk" / "conf.toml";
|
||||
|
||||
// finally, use system-wide config
|
||||
return "/etc/lsfg-vk/conf.toml";
|
||||
}
|
||||
/// parse the global configuration
|
||||
GlobalConf parseGlobalConf(const toml::table& tbl) {
|
||||
const GlobalConf conf{
|
||||
.dll = tbl["dll"].value<std::string>(),
|
||||
.allow_fp16 = tbl["allow_fp16"].value_or(true)
|
||||
};
|
||||
|
||||
if (conf.dll && !std::filesystem::exists(*conf.dll))
|
||||
throw lsfgvk::error("path to dll is invalid");
|
||||
|
||||
return conf;
|
||||
}
|
||||
/// parse a game profile configuration
|
||||
GameConf parseGameConf(const toml::table& tbl) {
|
||||
const GameConf conf{
|
||||
.name = tbl["name"].value_or<std::string>("unnamed"),
|
||||
.active_in = activityFromString(tbl["active_in"]),
|
||||
.multiplier = tbl["multiplier"].value_or(2U),
|
||||
.flow_scale = tbl["flow_scale"].value_or(1.0F),
|
||||
.performance_mode = tbl["performance_mode"].value_or(false),
|
||||
.pacing = parcingFromString(tbl["pacing"].value_or<std::string>("none"))
|
||||
};
|
||||
|
||||
if (conf.multiplier <= 1)
|
||||
throw lsfgvk::error("multiplier must be greater than 1");
|
||||
if (conf.flow_scale < 0.25F || conf.flow_scale > 1.0F)
|
||||
throw lsfgvk::error("flow_scale must be between 0.25 and 1.0");
|
||||
|
||||
return conf;
|
||||
}
|
||||
/// parse the global configuration from the environment
|
||||
GlobalConf parseGlobalConfFromEnv() {
|
||||
GlobalConf conf{
|
||||
.dll = std::nullopt,
|
||||
.allow_fp16 = true
|
||||
};
|
||||
|
||||
const char* dll = std::getenv("LSFGVK_DLL_PATH");
|
||||
if (dll && *dll != '\0')
|
||||
conf.dll = std::string(dll);
|
||||
const char* no_fp16 = std::getenv("LSFGVK_NO_FP16");
|
||||
if (no_fp16 && *no_fp16 != '\0')
|
||||
conf.allow_fp16 = std::string(no_fp16) != "1";
|
||||
|
||||
if (conf.dll && !std::filesystem::exists(*conf.dll))
|
||||
throw lsfgvk::error("path to dll is invalid");
|
||||
|
||||
return conf;
|
||||
}
|
||||
/// parse a game profile configuration from the environment
|
||||
GameConf parseGameConfFromEnv() {
|
||||
GameConf conf{
|
||||
.name = "(environment)",
|
||||
.active_in = {},
|
||||
|
||||
.multiplier = 2,
|
||||
.flow_scale = 1.0F,
|
||||
.performance_mode = false,
|
||||
.pacing = Pacing::None
|
||||
};
|
||||
|
||||
const char* multiplier = std::getenv("LSFGVK_MULTIPLIER");
|
||||
if (multiplier) conf.multiplier = static_cast<size_t>(std::stoul(multiplier));
|
||||
const char* flow_scale = std::getenv("LSFGVK_FLOW_SCALE");
|
||||
if (flow_scale) conf.flow_scale = std::stof(flow_scale);
|
||||
const char* performance = std::getenv("LSFGVK_PERFORMANCE_MODE");
|
||||
if (performance) conf.performance_mode = std::string(performance) == "1";
|
||||
const char* pacing = std::getenv("LSFGVK_PACING");
|
||||
if (pacing) conf.pacing = parcingFromString(std::string(pacing));
|
||||
|
||||
if (conf.multiplier <= 1)
|
||||
throw lsfgvk::error("multiplier must be greater than 1");
|
||||
if (conf.flow_scale < 0.25F || conf.flow_scale > 1.0F)
|
||||
throw lsfgvk::error("flow_scale must be between 0.25 and 1.0");
|
||||
|
||||
return conf;
|
||||
}
|
||||
}
|
||||
|
||||
Configuration::Configuration() :
|
||||
path(findPath()),
|
||||
from_env(std::getenv("LSFGVK_ENV") != nullptr) {
|
||||
if (std::filesystem::exists(this->path) || this->from_env)
|
||||
return;
|
||||
|
||||
try {
|
||||
std::filesystem::create_directories(this->path.parent_path());
|
||||
if (!std::filesystem::exists(this->path.parent_path()))
|
||||
throw lsfgvk::error("unable to create configuration directory");
|
||||
|
||||
std::ofstream ofs(this->path);
|
||||
if (!ofs.is_open())
|
||||
throw lsfgvk::error("unable to create default configuration file");
|
||||
|
||||
ofs << DEFAULT_CONFIG;
|
||||
ofs.close();
|
||||
} catch (const std::filesystem::filesystem_error& e) {
|
||||
throw lsfgvk::error("unable to create default configuration file", e);
|
||||
}
|
||||
}
|
||||
|
||||
bool Configuration::tick() {
|
||||
if (this->from_env) {
|
||||
if (this->profiles.empty()) {
|
||||
this->global = parseGlobalConfFromEnv();
|
||||
this->profiles.push_back(parseGameConfFromEnv());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false; // no need to tick if from env
|
||||
}
|
||||
|
||||
// check for updates
|
||||
try {
|
||||
auto time = std::filesystem::last_write_time(this->path);
|
||||
if (time == this->timestamp)
|
||||
return false;
|
||||
|
||||
this->timestamp = time;
|
||||
} catch (const std::filesystem::filesystem_error& e) {
|
||||
throw lsfgvk::error("unable to access configuration file", e);
|
||||
}
|
||||
|
||||
// parse configuration
|
||||
GlobalConf global{};
|
||||
std::vector<GameConf> profiles{};
|
||||
|
||||
toml::table tbl;
|
||||
try {
|
||||
tbl = toml::parse_file(this->path.string());
|
||||
} catch (const toml::parse_error& e) {
|
||||
throw lsfgvk::error("unable to parse configuration", e);
|
||||
}
|
||||
|
||||
auto vrs = tbl["version"];
|
||||
if (!vrs || !vrs.is_integer() || *vrs.as_integer() != 2)
|
||||
throw lsfgvk::error("unsupported configuration version");
|
||||
|
||||
auto gbl = tbl["global"];
|
||||
if (gbl && gbl.is_table()) {
|
||||
global = parseGlobalConf(*gbl.as_table());
|
||||
}
|
||||
|
||||
auto pfls = tbl["profile"];
|
||||
if (pfls && pfls.is_array_of_tables()) {
|
||||
for (const auto& pfl : *pfls.as_array())
|
||||
profiles.push_back(parseGameConf(*pfl.as_table()));
|
||||
}
|
||||
|
||||
this->global = std::move(global);
|
||||
this->profiles = std::move(profiles);
|
||||
return true;
|
||||
}
|
||||
69
lsfg-vk-layer/src/config.hpp
Normal file
69
lsfg-vk-layer/src/config.hpp
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace lsfgvk::layer {
|
||||
|
||||
/// global configuration
|
||||
struct GlobalConf {
|
||||
/// optional dll override
|
||||
std::optional<std::string> dll;
|
||||
/// should fp16 be allowed
|
||||
bool allow_fp16;
|
||||
};
|
||||
|
||||
/// pacing methods
|
||||
enum class Pacing {
|
||||
/// do not perform any pacing (vsync+novrr)
|
||||
None
|
||||
};
|
||||
|
||||
/// game profile configuration
|
||||
struct GameConf {
|
||||
/// name of the profile
|
||||
std::string name;
|
||||
/// optional activation string/array
|
||||
std::vector<std::string> active_in;
|
||||
/// multiplier for frame generation
|
||||
size_t multiplier;
|
||||
/// non-inverted flow scale
|
||||
float flow_scale;
|
||||
/// use performance mode
|
||||
bool performance_mode;
|
||||
/// pacing method
|
||||
Pacing pacing;
|
||||
};
|
||||
|
||||
/// automatically updating configuration
|
||||
class Configuration {
|
||||
public:
|
||||
/// create a new configuration
|
||||
/// @throws lsfgvk::error on failure
|
||||
Configuration();
|
||||
|
||||
/// reload the configuration from disk if the file has changed
|
||||
/// @throws lsfgvk::error on failure
|
||||
/// @return true if the configuration was reloaded
|
||||
bool tick();
|
||||
|
||||
/// get the global configuration
|
||||
/// @return global configuration
|
||||
[[nodiscard]] const GlobalConf& getGlobalConf() const { return global; }
|
||||
|
||||
/// get the game profiles
|
||||
/// @return list of game profiles
|
||||
[[nodiscard]] const std::vector<GameConf>& getProfiles() const { return profiles; }
|
||||
private:
|
||||
std::filesystem::path path;
|
||||
std::chrono::time_point<std::chrono::file_clock> timestamp;
|
||||
bool from_env{};
|
||||
|
||||
GlobalConf global;
|
||||
std::vector<GameConf> profiles;
|
||||
};
|
||||
|
||||
}
|
||||
121
lsfg-vk-layer/src/detection.cpp
Normal file
121
lsfg-vk-layer/src/detection.cpp
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
#include "detection.hpp"
|
||||
#include "config.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
using namespace lsfgvk;
|
||||
using namespace lsfgvk::layer;
|
||||
|
||||
namespace {
|
||||
// try to match a profile by id
|
||||
std::optional<GameConf> match(const std::vector<GameConf>& profiles, const std::string& id) {
|
||||
for (const auto& profile : profiles)
|
||||
for (const auto& activation : profile.active_in)
|
||||
if (activation == id)
|
||||
return profile;
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
Identification layer::identify() {
|
||||
Identification id{};
|
||||
|
||||
// fetch LSFGVK_PROFILE
|
||||
const char* override = std::getenv("LSFGVK_PROFILE");
|
||||
if (override && *override != '\0')
|
||||
id.override = std::string(override);
|
||||
|
||||
// fetch process exe path
|
||||
std::array<char, 4096> buf{};
|
||||
const ssize_t len = readlink("/proc/self/exe", buf.data(), buf.size() - 1);
|
||||
if (len > 0) {
|
||||
buf.at(static_cast<size_t>(len)) = '\0';
|
||||
id.executable = std::string(buf.data());
|
||||
}
|
||||
|
||||
// if running under wine, fetch the actual exe path
|
||||
if (id.executable.find("wine") != std::string::npos
|
||||
|| id.executable.find("proton") != std::string::npos) {
|
||||
|
||||
std::ifstream maps("/proc/self/maps");
|
||||
std::string line;
|
||||
while (maps.is_open() && std::getline(maps, line)) {
|
||||
if (!line.ends_with(".exe"))
|
||||
continue;
|
||||
|
||||
size_t pos = line.find_first_of('/');
|
||||
if (pos == std::string::npos) {
|
||||
pos = line.find_last_of(' ');
|
||||
if (pos == std::string::npos)
|
||||
continue;
|
||||
pos += 1; // skip space
|
||||
}
|
||||
|
||||
const std::string wine_executable = line.substr(pos);
|
||||
if (wine_executable.empty())
|
||||
continue;
|
||||
|
||||
id.wine_executable = wine_executable;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// fetch process name
|
||||
std::ifstream comm("/proc/self/comm");
|
||||
if (comm.is_open()) {
|
||||
comm.read(buf.data(), buf.size() - 1);
|
||||
buf.at(static_cast<size_t>(comm.gcount())) = '\0';
|
||||
|
||||
id.process_name = std::string(buf.data());
|
||||
if (id.process_name.back() == '\n')
|
||||
id.process_name.pop_back();
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
std::optional<std::pair<IdentType, GameConf>> layer::findProfile(
|
||||
const Configuration& config, const Identification& id) {
|
||||
const auto& profiles = config.getProfiles();
|
||||
|
||||
// check for the environment option first
|
||||
if (std::getenv("LSFGVK_ENV") != nullptr)
|
||||
return std::make_pair(IdentType::OVERRIDE, config.getProfiles().front());
|
||||
|
||||
// then override first
|
||||
if (id.override.has_value()) {
|
||||
const auto profile = match(profiles, id.override.value());
|
||||
if (profile.has_value())
|
||||
return std::make_pair(IdentType::OVERRIDE, profile.value());
|
||||
}
|
||||
|
||||
// then check executable
|
||||
const auto exe_profile = match(profiles, id.executable);
|
||||
if (exe_profile.has_value())
|
||||
return std::make_pair(IdentType::EXECUTABLE, exe_profile.value());
|
||||
|
||||
// if present, check wine executable next
|
||||
if (id.wine_executable.has_value()) {
|
||||
const auto wine_profile = match(profiles, id.wine_executable.value());
|
||||
if (wine_profile.has_value())
|
||||
return std::make_pair(IdentType::WINE_EXECUTABLE, wine_profile.value());
|
||||
}
|
||||
|
||||
// finally, fallback to process name
|
||||
if (!id.process_name.empty()) {
|
||||
const auto proc_profile = match(profiles, id.process_name);
|
||||
if (proc_profile.has_value())
|
||||
return std::make_pair(IdentType::PROCESS_NAME, proc_profile.value());
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
40
lsfg-vk-layer/src/detection.hpp
Normal file
40
lsfg-vk-layer/src/detection.hpp
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
|
||||
#include "config.hpp"
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace lsfgvk::layer {
|
||||
|
||||
/// identification data for a process
|
||||
struct Identification {
|
||||
/// optional override name
|
||||
std::optional<std::string> override;
|
||||
/// path to exe file
|
||||
std::string executable;
|
||||
/// path to exe file when running under wine
|
||||
std::optional<std::string> wine_executable;
|
||||
/// traditional process name (e.g. GameThread)
|
||||
std::string process_name;
|
||||
};
|
||||
|
||||
/// enum describing which identification method was used
|
||||
enum class IdentType {
|
||||
OVERRIDE, // identified by override
|
||||
EXECUTABLE, // identified by executable path
|
||||
WINE_EXECUTABLE, // identified by wine executable path
|
||||
PROCESS_NAME // identified by process name
|
||||
};
|
||||
|
||||
/// identify the current process
|
||||
Identification identify();
|
||||
|
||||
/// find a profile for the current process
|
||||
/// @param config configuration to search in
|
||||
/// @param id identification data
|
||||
/// @return ident pair if found
|
||||
std::optional<std::pair<IdentType, GameConf>> findProfile(
|
||||
const Configuration& config, const Identification& id);
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
|
@ -12,6 +13,8 @@
|
|||
#include <vulkan/vk_layer.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
using namespace lsfgvk;
|
||||
|
||||
namespace {
|
||||
/// reinterpret cast helper with const_cast
|
||||
template<typename T, typename U>
|
||||
|
|
@ -42,6 +45,11 @@ namespace {
|
|||
PFN_vkGetInstanceProcAddr nxvkGetInstanceProcAddr{nullptr};
|
||||
PFN_vkGetDeviceProcAddr nxvkGetDeviceProcAddr = nullptr;
|
||||
|
||||
auto& layer() {
|
||||
static std::optional<layer::Layer> instance; // NOLINT
|
||||
return instance;
|
||||
}
|
||||
|
||||
VkInstance gInstance{VK_NULL_HANDLE}; // if there are multiple instances, we scream out loud, oke?
|
||||
|
||||
// create instance
|
||||
|
|
@ -49,6 +57,15 @@ namespace {
|
|||
const VkInstanceCreateInfo* info,
|
||||
const VkAllocationCallbacks* alloc,
|
||||
VkInstance* instance) {
|
||||
// try to load lsfg-vk layer
|
||||
try {
|
||||
if (!layer().has_value())
|
||||
layer().emplace();
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "lsfg-vk: something went wrong during lsfg-vk layer initialization:\n";
|
||||
std::cerr << "- " << e.what() << '\n';
|
||||
}
|
||||
|
||||
// apply layer chaining
|
||||
auto* layerInfo = vcast<VkLayerInstanceCreateInfo*>(info->pNext);
|
||||
while (layerInfo && (layerInfo->sType != VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO
|
||||
|
|
@ -86,11 +103,14 @@ namespace {
|
|||
return VK_ERROR_INITIALIZATION_FAILED;
|
||||
}
|
||||
|
||||
auto requiredExtensions = lsfgvk::requiredInstanceExtensions();
|
||||
const auto& l = layer();
|
||||
if (!l.has_value() || !l->active()) // skip inactive
|
||||
return vkCreateInstance(info, alloc, instance);
|
||||
|
||||
auto extensions = add_extensions(
|
||||
info->ppEnabledExtensionNames,
|
||||
info->enabledExtensionCount,
|
||||
requiredExtensions);
|
||||
l->instanceExtensions());
|
||||
|
||||
VkInstanceCreateInfo newInfo = *info;
|
||||
newInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
|
||||
|
|
@ -109,8 +129,8 @@ namespace {
|
|||
}
|
||||
|
||||
// map of devices to layer instances
|
||||
std::unordered_map<VkDevice, lsfgvk::LayerInstance>& device2InstanceMap() {
|
||||
static std::unordered_map<VkDevice, lsfgvk::LayerInstance> map; // NOLINT
|
||||
std::unordered_map<VkDevice, layer::LayerInstance>& device2InstanceMap() {
|
||||
static std::unordered_map<VkDevice, layer::LayerInstance> map; // NOLINT
|
||||
return map;
|
||||
}
|
||||
|
||||
|
|
@ -174,11 +194,14 @@ namespace {
|
|||
return VK_ERROR_INITIALIZATION_FAILED;
|
||||
}
|
||||
|
||||
auto requiredExtensions = lsfgvk::requiredDeviceExtensions();
|
||||
const auto& l = layer();
|
||||
if (!l.has_value() || !l->active()) // skip inactive
|
||||
return vkCreateDevice(physicalDevice, info, alloc, device);
|
||||
|
||||
auto extensions = add_extensions(
|
||||
info->ppEnabledExtensionNames,
|
||||
info->enabledExtensionCount,
|
||||
requiredExtensions);
|
||||
l->deviceExtensions());
|
||||
|
||||
VkDeviceCreateInfo newInfo = *info;
|
||||
newInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
|
||||
|
|
@ -195,7 +218,7 @@ namespace {
|
|||
// create layer instance
|
||||
try {
|
||||
device2InstanceMap().emplace(*device,
|
||||
lsfgvk::LayerInstance(gInstance, *device, setLoaderData));
|
||||
layer::LayerInstance(*l, gInstance, *device, setLoaderData));
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "lsfg-vk: something went wrong during lsfg-vk initialization:\n";
|
||||
std::cerr << "- " << e.what() << '\n';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "layer.hpp"
|
||||
#include "detection.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
|
@ -8,16 +9,71 @@
|
|||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
using namespace lsfgvk;
|
||||
using namespace lsfgvk::layer;
|
||||
|
||||
std::vector<std::string> lsfgvk::requiredInstanceExtensions() noexcept {
|
||||
return {};
|
||||
Layer::Layer() : identification(identify()) {
|
||||
this->tick();
|
||||
|
||||
if (!this->profile.has_value())
|
||||
return;
|
||||
|
||||
std::cerr << "lsfg-vk: using profile with name '" << this->profile->name << "' ";
|
||||
switch (this->identType) {
|
||||
case IdentType::OVERRIDE:
|
||||
std::cerr << "(identified via override)\n";
|
||||
break;
|
||||
case IdentType::EXECUTABLE:
|
||||
std::cerr << "(identified via executable)\n";
|
||||
break;
|
||||
case IdentType::WINE_EXECUTABLE:
|
||||
std::cerr << "(identified via wine executable)\n";
|
||||
break;
|
||||
case IdentType::PROCESS_NAME:
|
||||
std::cerr << "(identified via process name)\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> lsfgvk::requiredDeviceExtensions() noexcept {
|
||||
return {};
|
||||
bool Layer::tick() {
|
||||
auto res = this->config.tick();
|
||||
if (!res)
|
||||
return false;
|
||||
|
||||
// try to find a profile
|
||||
const auto& detec = findProfile(this->config, identification);
|
||||
if (!detec.has_value())
|
||||
return this->profile.has_value();
|
||||
|
||||
this->identType = detec->first;
|
||||
this->profile = detec->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
lsfgvk::LayerInstance::LayerInstance(VkInstance instance, VkDevice device,
|
||||
std::vector<std::string> Layer::instanceExtensions() const {
|
||||
if (!this->profile.has_value())
|
||||
return {};
|
||||
|
||||
return {
|
||||
"VK_KHR_get_physical_device_properties2",
|
||||
"VK_KHR_external_memory_capabilities",
|
||||
"VK_KHR_external_semaphore_capabilities"
|
||||
};
|
||||
}
|
||||
|
||||
std::vector<std::string> Layer::deviceExtensions() const {
|
||||
if (!this->profile.has_value())
|
||||
return {};
|
||||
|
||||
return {
|
||||
"VK_KHR_external_memory",
|
||||
"VK_KHR_external_memory_fd",
|
||||
"VK_KHR_external_semaphore",
|
||||
"VK_KHR_external_semaphore_fd"
|
||||
};
|
||||
}
|
||||
|
||||
layer::LayerInstance::LayerInstance(const Layer& layer,
|
||||
VkInstance instance, VkDevice device,
|
||||
PFN_vkSetDeviceLoaderData setLoaderData) {
|
||||
std::cerr << "lsfg-vk: Hello, world!\n";
|
||||
std::cerr << "lsfg-vk: instance=" << instance << ", device=" << device << '\n';
|
||||
|
|
|
|||
|
|
@ -1,30 +1,61 @@
|
|||
#pragma once
|
||||
|
||||
#include "config.hpp"
|
||||
#include "detection.hpp"
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <vulkan/vk_layer.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
namespace lsfgvk {
|
||||
namespace lsfgvk::layer {
|
||||
|
||||
/// required instance extensions
|
||||
/// @return list of extension names
|
||||
std::vector<std::string> requiredInstanceExtensions() noexcept;
|
||||
/// lsfg-vk layer
|
||||
class Layer {
|
||||
public:
|
||||
/// create a new layer
|
||||
/// @throws lsfgvk::error on failure
|
||||
Layer();
|
||||
|
||||
/// get the active profile
|
||||
/// @return game configuration
|
||||
[[nodiscard]] const auto& active() const { return profile; }
|
||||
|
||||
/// required instance extensions
|
||||
/// @return list of extension names
|
||||
[[nodiscard]] std::vector<std::string> instanceExtensions() const;
|
||||
/// required device extensions
|
||||
/// @return list of extension names
|
||||
[[nodiscard]] std::vector<std::string> deviceExtensions() const;
|
||||
|
||||
/// tick the layer
|
||||
/// @throws lsfgvk::error on failure
|
||||
/// @return true if profile changed
|
||||
bool tick();
|
||||
private:
|
||||
Configuration config;
|
||||
Identification identification;
|
||||
|
||||
IdentType identType{}; // type used to deduce the profile
|
||||
std::optional<GameConf> profile;
|
||||
};
|
||||
|
||||
/// required device extensions
|
||||
/// @return list of extension names
|
||||
std::vector<std::string> requiredDeviceExtensions() noexcept;
|
||||
|
||||
/// instance of the lsfg-vk layer on a VkInstance/VkDevice pair.
|
||||
class LayerInstance {
|
||||
public:
|
||||
/// create a new layer instance
|
||||
/// @param layer parent layer
|
||||
/// @param instance Vulkan instance
|
||||
/// @param device Vulkan device
|
||||
/// @param setLoaderData function to set device loader data
|
||||
LayerInstance(VkInstance instance, VkDevice device,
|
||||
LayerInstance(const Layer& layer,
|
||||
VkInstance instance, VkDevice device,
|
||||
PFN_vkSetDeviceLoaderData setLoaderData);
|
||||
private:
|
||||
Configuration config;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -212,14 +212,6 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef TOML_NVCC
|
||||
#ifdef __NVCOMPILER_MAJOR__
|
||||
#define TOML_NVCC __NVCOMPILER_MAJOR__
|
||||
#else
|
||||
#define TOML_NVCC 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef TOML_ARCH_ITANIUM
|
||||
#if defined(__ia64__) || defined(__ia64) || defined(_IA64) || defined(__IA64__) || defined(_M_IA64)
|
||||
#define TOML_ARCH_ITANIUM 1
|
||||
|
|
@ -428,7 +420,6 @@
|
|||
#endif
|
||||
|
||||
// TOML_ALWAYS_INLINE
|
||||
#ifndef TOML_ALWAYS_INLINE
|
||||
#ifdef _MSC_VER
|
||||
#define TOML_ALWAYS_INLINE __forceinline
|
||||
#elif TOML_GCC || TOML_CLANG || TOML_HAS_ATTR(__always_inline__)
|
||||
|
|
@ -438,10 +429,8 @@
|
|||
#else
|
||||
#define TOML_ALWAYS_INLINE inline
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// TOML_NEVER_INLINE
|
||||
#ifndef TOML_NEVER_INLINE
|
||||
#ifdef _MSC_VER
|
||||
#define TOML_NEVER_INLINE TOML_DECLSPEC(noinline)
|
||||
#elif TOML_CUDA // https://gitlab.gnome.org/GNOME/glib/-/issues/2555
|
||||
|
|
@ -454,27 +443,19 @@
|
|||
#ifndef TOML_NEVER_INLINE
|
||||
#define TOML_NEVER_INLINE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// MSVC attributes
|
||||
#ifndef TOML_ABSTRACT_INTERFACE
|
||||
#define TOML_ABSTRACT_INTERFACE TOML_DECLSPEC(novtable)
|
||||
#endif
|
||||
#ifndef TOML_EMPTY_BASES
|
||||
#define TOML_EMPTY_BASES TOML_DECLSPEC(empty_bases)
|
||||
#endif
|
||||
|
||||
// TOML_TRIVIAL_ABI
|
||||
#ifndef TOML_TRIVIAL_ABI
|
||||
#if TOML_CLANG || TOML_HAS_ATTR(__trivial_abi__)
|
||||
#define TOML_TRIVIAL_ABI TOML_ATTR(__trivial_abi__)
|
||||
#else
|
||||
#define TOML_TRIVIAL_ABI
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// TOML_NODISCARD
|
||||
#ifndef TOML_NODISCARD
|
||||
#if TOML_CPP >= 17 && TOML_HAS_CPP_ATTR(nodiscard) >= 201603
|
||||
#define TOML_NODISCARD [[nodiscard]]
|
||||
#elif TOML_CLANG || TOML_GCC || TOML_HAS_ATTR(__warn_unused_result__)
|
||||
|
|
@ -482,16 +463,13 @@
|
|||
#else
|
||||
#define TOML_NODISCARD
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// TOML_NODISCARD_CTOR
|
||||
#ifndef TOML_NODISCARD_CTOR
|
||||
#if TOML_CPP >= 17 && TOML_HAS_CPP_ATTR(nodiscard) >= 201907
|
||||
#define TOML_NODISCARD_CTOR [[nodiscard]]
|
||||
#else
|
||||
#define TOML_NODISCARD_CTOR
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// pure + const
|
||||
#ifndef TOML_PURE
|
||||
|
|
@ -541,7 +519,6 @@
|
|||
#endif
|
||||
|
||||
// TOML_ASSUME
|
||||
#ifndef TOML_ASSUME
|
||||
#ifdef _MSC_VER
|
||||
#define TOML_ASSUME(expr) __assume(expr)
|
||||
#elif TOML_ICC || TOML_CLANG || TOML_HAS_BUILTIN(__builtin_assume)
|
||||
|
|
@ -553,10 +530,8 @@
|
|||
#else
|
||||
#define TOML_ASSUME(expr) static_cast<void>(0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// TOML_UNREACHABLE
|
||||
#ifndef TOML_UNREACHABLE
|
||||
#ifdef _MSC_VER
|
||||
#define TOML_UNREACHABLE __assume(0)
|
||||
#elif TOML_ICC || TOML_CLANG || TOML_GCC || TOML_HAS_BUILTIN(__builtin_unreachable)
|
||||
|
|
@ -564,7 +539,6 @@
|
|||
#else
|
||||
#define TOML_UNREACHABLE static_cast<void>(0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// TOML_LIKELY
|
||||
#if TOML_CPP >= 20 && TOML_HAS_CPP_ATTR(likely) >= 201803
|
||||
|
|
@ -1091,10 +1065,6 @@
|
|||
// 256 is crazy high! if you're hitting this limit with real input, TOML is probably the wrong tool for the job...
|
||||
#endif
|
||||
|
||||
#ifndef TOML_MAX_DOTTED_KEYS_DEPTH
|
||||
#define TOML_MAX_DOTTED_KEYS_DEPTH 1024
|
||||
#endif
|
||||
|
||||
#ifdef TOML_CHAR_8_STRINGS
|
||||
#if TOML_CHAR_8_STRINGS
|
||||
#error TOML_CHAR_8_STRINGS was removed in toml++ 2.0.0; all value setters and getters now work with char8_t strings implicitly.
|
||||
|
|
@ -1133,20 +1103,6 @@ TOML_ENABLE_WARNINGS;
|
|||
#define TOML_ENABLE_FLOAT16 0
|
||||
#endif
|
||||
|
||||
#ifndef TOML_DISABLE_CONDITIONAL_NOEXCEPT_LAMBDA
|
||||
#define TOML_DISABLE_CONDITIONAL_NOEXCEPT_LAMBDA 0
|
||||
#endif
|
||||
|
||||
#ifndef TOML_DISABLE_NOEXCEPT_NOEXCEPT
|
||||
#define TOML_DISABLE_NOEXCEPT_NOEXCEPT 0
|
||||
#ifdef _MSC_VER
|
||||
#if _MSC_VER <= 1943 // Up to Visual Studio 2022 Version 17.13.6
|
||||
#undef TOML_DISABLE_NOEXCEPT_NOEXCEPT
|
||||
#define TOML_DISABLE_NOEXCEPT_NOEXCEPT 1
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(TOML_FLOAT_CHARCONV) && (TOML_GCC || TOML_CLANG || (TOML_ICC && !TOML_ICC_CL))
|
||||
// not supported by any version of GCC or Clang as of 26/11/2020
|
||||
// not supported by any version of ICC on Linux as of 11/01/2021
|
||||
|
|
@ -2644,60 +2600,6 @@ TOML_NAMESPACE_START
|
|||
return lhs;
|
||||
}
|
||||
};
|
||||
|
||||
TOML_NODISCARD
|
||||
constexpr optional<std::string_view> get_line(std::string_view doc, source_index line_num) noexcept
|
||||
{
|
||||
if (line_num == 0)
|
||||
{
|
||||
// Invalid line number. Should be greater than zero.
|
||||
return {};
|
||||
}
|
||||
|
||||
// The position of the first character of the specified line.
|
||||
const auto begin_of_line = [doc, line_num]() -> std::size_t
|
||||
{
|
||||
if (line_num == 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto num_chars_of_doc = doc.size();
|
||||
std::size_t current_line_num{ 1 };
|
||||
|
||||
for (std::size_t i{}; i < num_chars_of_doc; ++i)
|
||||
{
|
||||
if (doc[i] == '\n')
|
||||
{
|
||||
++current_line_num;
|
||||
|
||||
if (current_line_num == line_num)
|
||||
{
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::string_view::npos;
|
||||
}();
|
||||
|
||||
if (begin_of_line >= doc.size())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
if (const auto end_of_line = doc.find('\n', begin_of_line); end_of_line != std::string_view::npos)
|
||||
{
|
||||
const auto num_chars_of_line = end_of_line - begin_of_line;
|
||||
|
||||
// Trim an optional trailing carriage return.
|
||||
return doc.substr(begin_of_line,
|
||||
((num_chars_of_line > 0) && (doc[end_of_line - 1] == '\r')) ? num_chars_of_line - 1
|
||||
: num_chars_of_line);
|
||||
}
|
||||
|
||||
// Return the last line. Apparently this doc has no trailing line break character at the end.
|
||||
return doc.substr(begin_of_line);
|
||||
}
|
||||
}
|
||||
TOML_NAMESPACE_END;
|
||||
|
||||
|
|
@ -3668,7 +3570,7 @@ TOML_NAMESPACE_START
|
|||
{
|
||||
TOML_NODISCARD
|
||||
TOML_ALWAYS_INLINE
|
||||
path operator""_tpath(const char* str, size_t len)
|
||||
path operator"" _tpath(const char* str, size_t len)
|
||||
{
|
||||
return path(std::string_view{ str, len });
|
||||
}
|
||||
|
|
@ -3712,16 +3614,6 @@ TOML_PUSH_WARNINGS;
|
|||
#undef max
|
||||
#endif
|
||||
|
||||
// workaround for this: https://github.com/marzer/tomlplusplus/issues/220
|
||||
#if TOML_NVCC
|
||||
#define TOML_NVCC_WORKAROUND \
|
||||
{ \
|
||||
return {}; \
|
||||
}
|
||||
#else
|
||||
#define TOML_NVCC_WORKAROUND = 0
|
||||
#endif
|
||||
|
||||
TOML_NAMESPACE_START
|
||||
{
|
||||
class TOML_ABSTRACT_INTERFACE TOML_EXPORTED_CLASS node
|
||||
|
|
@ -3842,10 +3734,10 @@ TOML_NAMESPACE_START
|
|||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
virtual ~node() noexcept;
|
||||
|
||||
TOML_NODISCARD
|
||||
TOML_PURE_GETTER
|
||||
virtual bool is_homogeneous(node_type ntype, node*& first_nonmatch) noexcept = 0;
|
||||
|
||||
TOML_NODISCARD
|
||||
TOML_PURE_GETTER
|
||||
virtual bool is_homogeneous(node_type ntype, const node*& first_nonmatch) const noexcept = 0;
|
||||
|
||||
TOML_PURE_GETTER
|
||||
|
|
@ -3864,16 +3756,16 @@ TOML_NAMESPACE_START
|
|||
}
|
||||
|
||||
TOML_PURE_GETTER
|
||||
virtual node_type type() const noexcept TOML_NVCC_WORKAROUND;
|
||||
virtual node_type type() const noexcept = 0;
|
||||
|
||||
TOML_PURE_GETTER
|
||||
virtual bool is_table() const noexcept TOML_NVCC_WORKAROUND;
|
||||
virtual bool is_table() const noexcept = 0;
|
||||
|
||||
TOML_PURE_GETTER
|
||||
virtual bool is_array() const noexcept = 0;
|
||||
|
||||
TOML_PURE_GETTER
|
||||
virtual bool is_array_of_tables() const noexcept TOML_NVCC_WORKAROUND;
|
||||
virtual bool is_array_of_tables() const noexcept = 0;
|
||||
|
||||
TOML_PURE_GETTER
|
||||
virtual bool is_value() const noexcept = 0;
|
||||
|
|
@ -3928,8 +3820,6 @@ TOML_NAMESPACE_START
|
|||
return is_time();
|
||||
else if constexpr (std::is_same_v<type, date_time>)
|
||||
return is_date_time();
|
||||
|
||||
TOML_UNREACHABLE;
|
||||
}
|
||||
|
||||
TOML_PURE_GETTER
|
||||
|
|
@ -4012,8 +3902,6 @@ TOML_NAMESPACE_START
|
|||
return as_time();
|
||||
else if constexpr (std::is_same_v<unwrapped_type, date_time>)
|
||||
return as_date_time();
|
||||
|
||||
TOML_UNREACHABLE;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
|
@ -4042,8 +3930,6 @@ TOML_NAMESPACE_START
|
|||
return as_time();
|
||||
else if constexpr (std::is_same_v<unwrapped_type, date_time>)
|
||||
return as_date_time();
|
||||
|
||||
TOML_UNREACHABLE;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
|
@ -4321,8 +4207,6 @@ TOML_IMPL_NAMESPACE_START
|
|||
}
|
||||
TOML_IMPL_NAMESPACE_END;
|
||||
|
||||
#undef TOML_NVCC_WORKAROUND
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma pop_macro("min")
|
||||
#pragma pop_macro("max")
|
||||
|
|
@ -4504,7 +4388,7 @@ TOML_NAMESPACE_START
|
|||
return node_->is_homogeneous(ntype, first_nonmatch);
|
||||
}
|
||||
|
||||
TOML_PURE_GETTER
|
||||
TOML_NODISCARD
|
||||
bool is_homogeneous(node_type ntype) const noexcept
|
||||
{
|
||||
return node_ ? node_->is_homogeneous(ntype) : false;
|
||||
|
|
@ -5098,11 +4982,8 @@ TOML_NAMESPACE_START
|
|||
(impl::value_variadic_ctor_allowed<value<ValueType>, impl::remove_cvref<Args>...>::value),
|
||||
typename... Args)
|
||||
TOML_NODISCARD_CTOR
|
||||
explicit value(Args&&... args)
|
||||
#if !TOML_DISABLE_NOEXCEPT_NOEXCEPT
|
||||
noexcept(noexcept(value_type(
|
||||
impl::native_value_maker<value_type, std::decay_t<Args>...>::make(static_cast<Args&&>(args)...))))
|
||||
#endif
|
||||
explicit value(Args&&... args) noexcept(noexcept(value_type(
|
||||
impl::native_value_maker<value_type, std::decay_t<Args>...>::make(static_cast<Args&&>(args)...))))
|
||||
: val_(impl::native_value_maker<value_type, std::decay_t<Args>...>::make(static_cast<Args&&>(args)...))
|
||||
{
|
||||
#if TOML_LIFETIME_HOOKS
|
||||
|
|
@ -5193,7 +5074,7 @@ TOML_NAMESPACE_START
|
|||
return ntype == node_type::none || ntype == impl::node_type_of<value_type>;
|
||||
}
|
||||
|
||||
TOML_NODISCARD
|
||||
TOML_PURE_GETTER
|
||||
bool is_homogeneous(node_type ntype, node*& first_nonmatch) noexcept final
|
||||
{
|
||||
if (ntype != node_type::none && ntype != impl::node_type_of<value_type>)
|
||||
|
|
@ -5204,7 +5085,7 @@ TOML_NAMESPACE_START
|
|||
return true;
|
||||
}
|
||||
|
||||
TOML_NODISCARD
|
||||
TOML_PURE_GETTER
|
||||
bool is_homogeneous(node_type ntype, const node*& first_nonmatch) const noexcept final
|
||||
{
|
||||
if (ntype != node_type::none && ntype != impl::node_type_of<value_type>)
|
||||
|
|
@ -5718,11 +5599,11 @@ TOML_NAMESPACE_START
|
|||
"Retrieving values as wide-character strings with node::value_exact() is only "
|
||||
"supported on Windows with TOML_ENABLE_WINDOWS_COMPAT enabled.");
|
||||
|
||||
static_assert((is_native<T> || can_represent_native<T>) && !is_cvref<T>,
|
||||
static_assert((is_native<T> || can_represent_native<T>)&&!is_cvref<T>,
|
||||
TOML_SA_VALUE_EXACT_FUNC_MESSAGE("return type of node::value_exact()"));
|
||||
|
||||
// prevent additional compiler error spam when the static_assert fails by gating behind if constexpr
|
||||
if constexpr ((is_native<T> || can_represent_native<T>) && !is_cvref<T>)
|
||||
if constexpr ((is_native<T> || can_represent_native<T>)&&!is_cvref<T>)
|
||||
{
|
||||
if (type() == node_type_of<T>)
|
||||
return { this->get_value_exact<T>() };
|
||||
|
|
@ -5740,7 +5621,7 @@ TOML_NAMESPACE_START
|
|||
static_assert(!is_wide_string<T> || TOML_ENABLE_WINDOWS_COMPAT,
|
||||
"Retrieving values as wide-character strings with node::value() is only "
|
||||
"supported on Windows with TOML_ENABLE_WINDOWS_COMPAT enabled.");
|
||||
static_assert((is_native<T> || can_represent_native<T> || can_partially_represent_native<T>) && !is_cvref<T>,
|
||||
static_assert((is_native<T> || can_represent_native<T> || can_partially_represent_native<T>)&&!is_cvref<T>,
|
||||
TOML_SA_VALUE_FUNC_MESSAGE("return type of node::value()"));
|
||||
|
||||
// when asking for strings, dates, times and date_times there's no 'fuzzy' conversion
|
||||
|
|
@ -6453,11 +6334,11 @@ TOML_NAMESPACE_START
|
|||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
bool is_homogeneous(node_type ntype) const noexcept final;
|
||||
|
||||
TOML_NODISCARD
|
||||
TOML_PURE_GETTER
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
bool is_homogeneous(node_type ntype, node*& first_nonmatch) noexcept final;
|
||||
|
||||
TOML_NODISCARD
|
||||
TOML_PURE_GETTER
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
bool is_homogeneous(node_type ntype, const node*& first_nonmatch) const noexcept final;
|
||||
|
||||
|
|
@ -6870,10 +6751,7 @@ TOML_NAMESPACE_START
|
|||
static_cast<node_ref>(static_cast<Array&&>(arr)[i])
|
||||
.visit(
|
||||
[&]([[maybe_unused]] auto&& elem) //
|
||||
// Define this macro as a workaround to compile errors caused by a bug in MSVC's "legacy lambda processor".
|
||||
#if !TOML_DISABLE_CONDITIONAL_NOEXCEPT_LAMBDA
|
||||
noexcept(for_each_is_nothrow_one<Func&&, Array&&, decltype(elem)>::value)
|
||||
#endif
|
||||
{
|
||||
using elem_ref = for_each_elem_ref<decltype(elem), Array&&>;
|
||||
static_assert(std::is_reference_v<elem_ref>);
|
||||
|
|
@ -7145,8 +7023,8 @@ TOML_NAMESPACE_START
|
|||
{
|
||||
using raw_elem_type = impl::remove_cvref<ElemType>;
|
||||
using elem_type = std::conditional_t<std::is_void_v<raw_elem_type>, //
|
||||
impl::emplaced_type_of<Args&&...>,
|
||||
raw_elem_type>;
|
||||
impl::emplaced_type_of<Args&&...>,
|
||||
raw_elem_type>;
|
||||
|
||||
using type = impl::remove_cvref<impl::unwrap_node<elem_type>>;
|
||||
static_assert(impl::is_native<type> || impl::is_one_of<type, table, array>,
|
||||
|
|
@ -7183,8 +7061,8 @@ TOML_NAMESPACE_START
|
|||
{
|
||||
using raw_elem_type = impl::remove_cvref<ElemType>;
|
||||
using elem_type = std::conditional_t<std::is_void_v<raw_elem_type>, //
|
||||
impl::emplaced_type_of<Args&&...>,
|
||||
raw_elem_type>;
|
||||
impl::emplaced_type_of<Args&&...>,
|
||||
raw_elem_type>;
|
||||
|
||||
static constexpr auto moving_node_ptr = std::is_same_v<elem_type, impl::node_ptr> //
|
||||
&& sizeof...(Args) == 1u //
|
||||
|
|
@ -7799,11 +7677,11 @@ TOML_NAMESPACE_START
|
|||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
bool is_homogeneous(node_type ntype) const noexcept final;
|
||||
|
||||
TOML_NODISCARD
|
||||
TOML_PURE_GETTER
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
bool is_homogeneous(node_type ntype, node*& first_nonmatch) noexcept final;
|
||||
|
||||
TOML_NODISCARD
|
||||
TOML_PURE_GETTER
|
||||
TOML_EXPORTED_MEMBER_FUNCTION
|
||||
bool is_homogeneous(node_type ntype, const node*& first_nonmatch) const noexcept final;
|
||||
|
||||
|
|
@ -8235,10 +8113,7 @@ TOML_NAMESPACE_START
|
|||
static_cast<node_ref>(*kvp.second)
|
||||
.visit(
|
||||
[&]([[maybe_unused]] auto&& v) //
|
||||
// Define this macro as a workaround to compile errors caused by a bug in MSVC's "legacy lambda processor".
|
||||
#if !TOML_DISABLE_CONDITIONAL_NOEXCEPT_LAMBDA
|
||||
noexcept(for_each_is_nothrow_one<Func&&, Table&&, decltype(v)>::value)
|
||||
#endif
|
||||
{
|
||||
using value_ref = for_each_value_ref<decltype(v), Table&&>;
|
||||
static_assert(std::is_reference_v<value_ref>);
|
||||
|
|
@ -8541,8 +8416,6 @@ TOML_NAMESPACE_START
|
|||
}
|
||||
return iterator{ ipos };
|
||||
}
|
||||
|
||||
TOML_UNREACHABLE;
|
||||
}
|
||||
|
||||
TOML_CONSTRAINED_TEMPLATE((is_key_or_convertible<KeyType&&> || impl::is_wide_string<KeyType>),
|
||||
|
|
@ -9725,7 +9598,7 @@ TOML_NAMESPACE_START
|
|||
|
||||
TOML_NODISCARD
|
||||
TOML_ALWAYS_INLINE
|
||||
parse_result operator""_toml(const char* str, size_t len)
|
||||
parse_result operator"" _toml(const char* str, size_t len)
|
||||
{
|
||||
return parse(std::string_view{ str, len });
|
||||
}
|
||||
|
|
@ -9734,7 +9607,7 @@ TOML_NAMESPACE_START
|
|||
|
||||
TOML_NODISCARD
|
||||
TOML_ALWAYS_INLINE
|
||||
parse_result operator""_toml(const char8_t* str, size_t len)
|
||||
parse_result operator"" _toml(const char8_t* str, size_t len)
|
||||
{
|
||||
return parse(std::u8string_view{ str, len });
|
||||
}
|
||||
|
|
@ -10771,11 +10644,6 @@ TOML_IMPL_NAMESPACE_START
|
|||
void TOML_CALLCONV print_to_stream(std::ostream & stream, const source_region& val)
|
||||
{
|
||||
print_to_stream(stream, val.begin);
|
||||
if (val.begin != val.end)
|
||||
{
|
||||
print_to_stream(stream, " to "sv);
|
||||
print_to_stream(stream, val.end);
|
||||
}
|
||||
if (val.path)
|
||||
{
|
||||
print_to_stream(stream, " of '"sv);
|
||||
|
|
@ -11945,7 +11813,7 @@ TOML_NAMESPACE_START
|
|||
return true;
|
||||
}
|
||||
|
||||
TOML_NODISCARD
|
||||
TOML_PURE_GETTER
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
bool array::is_homogeneous(node_type ntype, node * &first_nonmatch) noexcept
|
||||
{
|
||||
|
|
@ -11967,7 +11835,7 @@ TOML_NAMESPACE_START
|
|||
return true;
|
||||
}
|
||||
|
||||
TOML_NODISCARD
|
||||
TOML_PURE_GETTER
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
bool array::is_homogeneous(node_type ntype, const node*& first_nonmatch) const noexcept
|
||||
{
|
||||
|
|
@ -12300,7 +12168,7 @@ TOML_NAMESPACE_START
|
|||
return true;
|
||||
}
|
||||
|
||||
TOML_NODISCARD
|
||||
TOML_PURE_GETTER
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
bool table::is_homogeneous(node_type ntype, node * &first_nonmatch) noexcept
|
||||
{
|
||||
|
|
@ -12323,7 +12191,7 @@ TOML_NAMESPACE_START
|
|||
return true;
|
||||
}
|
||||
|
||||
TOML_NODISCARD
|
||||
TOML_PURE_GETTER
|
||||
TOML_EXTERNAL_LINKAGE
|
||||
bool table::is_homogeneous(node_type ntype, const node*& first_nonmatch) const noexcept
|
||||
{
|
||||
|
|
@ -12763,7 +12631,7 @@ TOML_ANON_NAMESPACE_START
|
|||
return value;
|
||||
}
|
||||
};
|
||||
static_assert(std::is_trivially_default_constructible_v<utf8_codepoint> && std::is_trivially_copyable_v<utf8_codepoint>);
|
||||
static_assert(std::is_trivial_v<utf8_codepoint>);
|
||||
static_assert(std::is_standard_layout_v<utf8_codepoint>);
|
||||
|
||||
struct TOML_ABSTRACT_INTERFACE utf8_reader_interface
|
||||
|
|
@ -13640,8 +13508,7 @@ TOML_IMPL_NAMESPACE_START
|
|||
class parser
|
||||
{
|
||||
private:
|
||||
static constexpr size_t max_nested_values = TOML_MAX_NESTED_VALUES;
|
||||
static constexpr size_t max_dotted_keys_depth = TOML_MAX_DOTTED_KEYS_DEPTH;
|
||||
static constexpr size_t max_nested_values = TOML_MAX_NESTED_VALUES;
|
||||
|
||||
utf8_buffered_reader reader;
|
||||
table root;
|
||||
|
|
@ -14821,7 +14688,7 @@ TOML_IMPL_NAMESPACE_START
|
|||
set_error_and_return_default("'"sv,
|
||||
traits::full_prefix,
|
||||
std::string_view{ digits, length },
|
||||
"' is not representable as a signed 64-bit integer"sv);
|
||||
"' is not representable in 64 bits"sv);
|
||||
|
||||
// do the thing
|
||||
{
|
||||
|
|
@ -14845,7 +14712,7 @@ TOML_IMPL_NAMESPACE_START
|
|||
set_error_and_return_default("'"sv,
|
||||
traits::full_prefix,
|
||||
std::string_view{ digits, length },
|
||||
"' is not representable as a signed 64-bit integer"sv);
|
||||
"' is not representable in 64 bits"sv);
|
||||
|
||||
if constexpr (traits::is_signed)
|
||||
{
|
||||
|
|
@ -15662,11 +15529,6 @@ TOML_IMPL_NAMESPACE_START
|
|||
// store segment
|
||||
key_buffer.push_back(key_segment, key_begin, key_end);
|
||||
|
||||
if TOML_UNLIKELY(key_buffer.size() > max_dotted_keys_depth)
|
||||
set_error_and_return_default("exceeded maximum dotted keys depth of "sv,
|
||||
max_dotted_keys_depth,
|
||||
" (TOML_MAX_DOTTED_KEYS_DEPTH)"sv);
|
||||
|
||||
// eof or no more key to come
|
||||
if (is_eof() || *cp != U'.')
|
||||
break;
|
||||
|
|
@ -16343,10 +16205,16 @@ TOML_ANON_NAMESPACE_START
|
|||
{
|
||||
#if TOML_EXCEPTIONS
|
||||
#define TOML_PARSE_FILE_ERROR(msg, path) \
|
||||
throw parse_error(msg, source_position{}, std::make_shared<const std::string>(std::move(path)))
|
||||
throw parse_error{ msg, source_position{}, std::make_shared<const std::string>(std::move(path)) }
|
||||
#else
|
||||
#define TOML_PARSE_FILE_ERROR(msg, path) \
|
||||
return parse_result(parse_error(msg, source_position{}, std::make_shared<const std::string>(std::move(path))))
|
||||
return parse_result \
|
||||
{ \
|
||||
parse_error \
|
||||
{ \
|
||||
msg, source_position{}, std::make_shared<const std::string>(std::move(path)) \
|
||||
} \
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string file_path_str(file_path);
|
||||
|
|
@ -16355,7 +16223,7 @@ TOML_ANON_NAMESPACE_START
|
|||
std::ifstream file;
|
||||
TOML_OVERALIGNED char file_buffer[sizeof(void*) * 1024u];
|
||||
file.rdbuf()->pubsetbuf(file_buffer, sizeof(file_buffer));
|
||||
#if TOML_WINDOWS && !(defined(__MINGW32__) || defined(__MINGW64__))
|
||||
#if TOML_WINDOWS
|
||||
file.open(impl::widen(file_path_str).c_str(), std::ifstream::in | std::ifstream::binary | std::ifstream::ate);
|
||||
#else
|
||||
file.open(file_path_str, std::ifstream::in | std::ifstream::binary | std::ifstream::ate);
|
||||
|
|
@ -17107,7 +16975,8 @@ TOML_ANON_NAMESPACE_START
|
|||
weight += 1u;
|
||||
val *= -1.0;
|
||||
}
|
||||
return weight + static_cast<size_t>(abs(log10(val))) + 1u;
|
||||
return weight + static_cast<size_t>(log10(val)) + 1u;
|
||||
break;
|
||||
}
|
||||
|
||||
case node_type::boolean: return 5u;
|
||||
|
|
@ -17832,7 +17701,6 @@ TOML_POP_WARNINGS;
|
|||
#undef TOML_NEVER_INLINE
|
||||
#undef TOML_NODISCARD
|
||||
#undef TOML_NODISCARD_CTOR
|
||||
#undef TOML_NVCC
|
||||
#undef TOML_OPEN_ENUM
|
||||
#undef TOML_OPEN_FLAGS_ENUM
|
||||
#undef TOML_PARSER_TYPENAME
|
||||
|
|
@ -1,174 +0,0 @@
|
|||
#include "config/config.hpp"
|
||||
#include "common/exception.hpp"
|
||||
#include "config/default_conf.hpp"
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
#define TOML_ENABLE_FORMATTERS 0 // NOLINT
|
||||
#include <toml.hpp>
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <filesystem>
|
||||
#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;
|
||||
|
||||
GlobalConfiguration Config::globalConf{};
|
||||
std::optional<GameConfiguration> Config::currentConf{};
|
||||
|
||||
namespace {
|
||||
/// Turn a string into a VkPresentModeKHR enum value.
|
||||
VkPresentModeKHR into_present(const std::string& mode) {
|
||||
if (mode == "fifo" || mode == "vsync")
|
||||
return VkPresentModeKHR::VK_PRESENT_MODE_FIFO_KHR;
|
||||
if (mode == "mailbox")
|
||||
return VkPresentModeKHR::VK_PRESENT_MODE_MAILBOX_KHR;
|
||||
if (mode == "immediate")
|
||||
return VkPresentModeKHR::VK_PRESENT_MODE_IMMEDIATE_KHR;
|
||||
return VkPresentModeKHR::VK_PRESENT_MODE_FIFO_KHR;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
if (!std::filesystem::exists(parent))
|
||||
if (!std::filesystem::create_directories(parent))
|
||||
throw std::runtime_error("Unable to create configuration directory at " + parent.string());
|
||||
|
||||
std::ofstream out(file);
|
||||
if (!out.is_open())
|
||||
throw std::runtime_error("Unable to create configuration file at " + file);
|
||||
out << DEFAULT_CONFIG;
|
||||
out.close();
|
||||
}
|
||||
|
||||
// parse config file
|
||||
toml::table config{};
|
||||
try {
|
||||
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);
|
||||
}
|
||||
|
||||
// parse global configuration
|
||||
Config::globalConf = {
|
||||
.config_file = file,
|
||||
.timestamp = std::filesystem::last_write_time(file)
|
||||
};
|
||||
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::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 auto* exe = game->at("exe").value_or("?");
|
||||
if (!name.first.ends_with(exe) && name.second != exe)
|
||||
continue;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (!gameConf.has_value()) {
|
||||
Config::currentConf.reset();
|
||||
std::cerr << "lsfg-vk: Configuration entry disappeared, disabling.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
Config::currentConf = *gameConf;
|
||||
|
||||
// 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;
|
||||
}
|
||||
85
src/main.cpp
85
src/main.cpp
|
|
@ -1,85 +0,0 @@
|
|||
#include "config/config.hpp"
|
||||
#include "extract/extract.hpp"
|
||||
#include "utils/benchmark.hpp"
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
namespace {
|
||||
[[gnu::constructor]]
|
||||
[[gnu::visibility("default")]]
|
||||
void lsfgvk_init() {
|
||||
std::cerr << std::unitbuf;
|
||||
|
||||
// read configuration
|
||||
try {
|
||||
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;
|
||||
}
|
||||
|
||||
// exit silently if not enabled
|
||||
if (!Config::currentConf.has_value())
|
||||
return;
|
||||
|
||||
// load shaders
|
||||
try {
|
||||
Extract::extractShaders();
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "lsfg-vk: An error occurred while trying to extract the shaders, exiting:\n";
|
||||
std::cerr << "- " << e.what() << '\n';
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
std::cerr << "lsfg-vk: Shaders extracted successfully.\n";
|
||||
|
||||
// run benchmark if requested
|
||||
const char* benchmark_flag = std::getenv("LSFG_BENCHMARK");
|
||||
if (!benchmark_flag)
|
||||
return;
|
||||
|
||||
const std::string resolution(benchmark_flag);
|
||||
uint32_t width{};
|
||||
uint32_t height{};
|
||||
try {
|
||||
const size_t x = resolution.find('x');
|
||||
if (x == std::string::npos)
|
||||
throw std::runtime_error("Unable to find 'x' in benchmark string");
|
||||
|
||||
const std::string width_str = resolution.substr(0, x);
|
||||
const std::string height_str = resolution.substr(x + 1);
|
||||
if (width_str.empty() || height_str.empty())
|
||||
throw std::runtime_error("Invalid resolution");
|
||||
|
||||
const int32_t w = std::stoi(width_str);
|
||||
const int32_t h = std::stoi(height_str);
|
||||
if (w < 0 || h < 0)
|
||||
throw std::runtime_error("Resolution cannot be negative");
|
||||
|
||||
width = static_cast<uint32_t>(w);
|
||||
height = static_cast<uint32_t>(h);
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "lsfg-vk: An error occurred while trying to parse the resolution, exiting:\n";
|
||||
std::cerr << "- " << e.what() << '\n';
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
std::thread benchmark([width, height]() {
|
||||
try {
|
||||
Benchmark::run(width, height);
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "lsfg-vk: An error occurred during the benchmark:\n";
|
||||
std::cerr << "- " << e.what() << '\n';
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
});
|
||||
benchmark.detach();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue