mirror of
https://github.com/PancakeTAS/lsfg-vk.git
synced 2026-04-25 20:02:13 +00:00
refactor(cleanup): backend initialization logic
This commit is contained in:
parent
6bd907516a
commit
883f3d2556
8 changed files with 699 additions and 14 deletions
|
|
@ -1,8 +1,8 @@
|
|||
set(LAYER_SOURCES
|
||||
"src/config.cpp"
|
||||
"src/detection.cpp"
|
||||
"src/entrypoint.cpp"
|
||||
"src/layer.cpp")
|
||||
"src/configuration/config.cpp"
|
||||
"src/configuration/detection.cpp"
|
||||
"src/context/instance.cpp"
|
||||
"src/entrypoint.cpp")
|
||||
|
||||
add_library(lsfg-vk-layer SHARED ${LAYER_SOURCES})
|
||||
|
||||
|
|
|
|||
244
lsfg-vk-layer/src/configuration/config.cpp
Normal file
244
lsfg-vk-layer/src/configuration/config.cpp
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
#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'
|
||||
]
|
||||
gpu = 'NVIDIA GeForce RTX 5080' # see the wiki for more info
|
||||
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'
|
||||
gpu = 'NVIDIA GeForce RTX 5080'
|
||||
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"]),
|
||||
.gpu = tbl["gpu"].value<std::string>(),
|
||||
.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 = {},
|
||||
.gpu = std::nullopt,
|
||||
|
||||
.multiplier = 2,
|
||||
.flow_scale = 1.0F,
|
||||
.performance_mode = false,
|
||||
.pacing = Pacing::None
|
||||
};
|
||||
|
||||
const char* gpu = std::getenv("LSFGVK_GPU");
|
||||
if (gpu) conf.gpu = std::string(gpu);
|
||||
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;
|
||||
}
|
||||
71
lsfg-vk-layer/src/configuration/config.hpp
Normal file
71
lsfg-vk-layer/src/configuration/config.hpp
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
#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;
|
||||
/// gpu to use (in case of multiple)
|
||||
std::optional<std::string> gpu;
|
||||
/// 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/configuration/detection.cpp
Normal file
121
lsfg-vk-layer/src/configuration/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/configuration/detection.hpp
Normal file
40
lsfg-vk-layer/src/configuration/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);
|
||||
}
|
||||
149
lsfg-vk-layer/src/context/instance.cpp
Normal file
149
lsfg-vk-layer/src/context/instance.cpp
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
#include "instance.hpp"
|
||||
#include "../configuration/config.hpp"
|
||||
#include "../configuration/detection.hpp"
|
||||
#include "lsfg-vk-backend/lsfgvk.hpp"
|
||||
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
using namespace lsfgvk;
|
||||
using namespace lsfgvk::layer;
|
||||
|
||||
Root::Root() : 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;
|
||||
}
|
||||
}
|
||||
|
||||
bool Root::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;
|
||||
}
|
||||
|
||||
std::vector<const char*> Root::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<const char*> Root::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",
|
||||
"VK_KHR_timeline_semaphore"
|
||||
};
|
||||
}
|
||||
|
||||
namespace {
|
||||
std::filesystem::path findShaderDll() {
|
||||
const std::vector<std::filesystem::path> FRAGMENTS{{
|
||||
".local/share/Steam/steamapps/common",
|
||||
".steam/steam/steamapps/common",
|
||||
".steam/debian-installation/steamapps/common",
|
||||
".var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/common",
|
||||
"snap/steam/common/.local/share/Steam/steamapps/common"
|
||||
}};
|
||||
|
||||
// check XDG overridden location
|
||||
const char* xdgPath = std::getenv("XDG_DATA_HOME");
|
||||
if (xdgPath && *xdgPath != '\0') {
|
||||
auto base = std::filesystem::path(xdgPath);
|
||||
|
||||
for (const auto& frag : FRAGMENTS) {
|
||||
auto full = base / frag / "Lossless Scaling" / "Lossless.dll";
|
||||
if (std::filesystem::exists(full))
|
||||
return full;
|
||||
}
|
||||
}
|
||||
|
||||
// check home directory
|
||||
const char* homePath = std::getenv("HOME");
|
||||
if (homePath && *homePath != '\0') {
|
||||
auto base = std::filesystem::path(homePath);
|
||||
|
||||
for (const auto& frag : FRAGMENTS) {
|
||||
auto full = base / frag / "Lossless Scaling" / "Lossless.dll";
|
||||
if (std::filesystem::exists(full))
|
||||
return full;
|
||||
}
|
||||
}
|
||||
|
||||
// fallback to same directory
|
||||
auto local = std::filesystem::current_path() / "Lossless.dll";
|
||||
if (std::filesystem::exists(local))
|
||||
return local;
|
||||
|
||||
throw lsfgvk::error("unable to locate Lossless.dll, please set the path in the configuration");
|
||||
}
|
||||
|
||||
lsfgvk::Instance createBackend(const Root& root) {
|
||||
const auto& conf = root.conf();
|
||||
const auto& profile = root.active();
|
||||
if (!profile.has_value()) // should not happen
|
||||
throw lsfgvk::error("no profile active");
|
||||
|
||||
return {
|
||||
[profile = *profile](
|
||||
const std::string& deviceName,
|
||||
std::pair<const std::string&, const std::string&> ids,
|
||||
const std::optional<std::string>& pci
|
||||
) {
|
||||
if (!profile.gpu)
|
||||
return true;
|
||||
|
||||
return (deviceName == *profile.gpu)
|
||||
|| (ids.first + ":" + ids.second == *profile.gpu)
|
||||
|| (pci && *pci == *profile.gpu);
|
||||
},
|
||||
conf.dll.value_or(findShaderDll()),
|
||||
conf.allow_fp16
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
layer::Instance::Instance(const Root& root, vk::Vulkan vk)
|
||||
: vk(std::move(vk))/*, backend(createBackend(layer))*/ {
|
||||
}
|
||||
60
lsfg-vk-layer/src/context/instance.hpp
Normal file
60
lsfg-vk-layer/src/context/instance.hpp
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
#pragma once
|
||||
|
||||
#include "../configuration/config.hpp"
|
||||
#include "../configuration/detection.hpp"
|
||||
#include "lsfg-vk-backend/lsfgvk.hpp"
|
||||
#include "lsfg-vk-common/helpers/pointers.hpp"
|
||||
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace lsfgvk::layer {
|
||||
|
||||
/// root of the lsfg-vk layer
|
||||
class Root {
|
||||
public:
|
||||
/// create a new root
|
||||
/// @throws lsfgvk::error on failure
|
||||
Root();
|
||||
|
||||
/// get the global configuration
|
||||
/// @return configuration
|
||||
[[nodiscard]] const auto& conf() const { return config.getGlobalConf(); }
|
||||
/// 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<const char*> instanceExtensions() const;
|
||||
/// required device extensions
|
||||
/// @return list of extension names
|
||||
[[nodiscard]] std::vector<const char*> deviceExtensions() const;
|
||||
|
||||
/// tick the root
|
||||
/// @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;
|
||||
};
|
||||
|
||||
|
||||
/// instance of the lsfg-vk layer on a VkInstance/VkDevice pair.
|
||||
class Instance {
|
||||
public:
|
||||
/// create a new layer instance
|
||||
/// @param root root of the layer
|
||||
/// @param vk vulkan instance
|
||||
Instance(const Root& root, vk::Vulkan vk);
|
||||
private:
|
||||
vk::Vulkan vk;
|
||||
ls::lazy<lsfgvk::Instance> backend; // lazy due to KhronosGroup/Vulkan-Loader#1739
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
#include "layer.hpp"
|
||||
#include "context/instance.hpp"
|
||||
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
|
@ -55,10 +55,10 @@ namespace {
|
|||
|
||||
// global layer info initialized at layer negotiation
|
||||
struct LayerInfo {
|
||||
layer::Layer layer; //!< basic layer info
|
||||
std::unordered_map<std::string, PFN_vkVoidFunction> map; //!< function pointer override map
|
||||
|
||||
PFN_vkGetInstanceProcAddr GetInstanceProcAddr;
|
||||
|
||||
layer::Root root;
|
||||
}* layer_info;
|
||||
|
||||
// create instance
|
||||
|
|
@ -106,7 +106,7 @@ namespace {
|
|||
auto extensions = add_extensions(
|
||||
info->ppEnabledExtensionNames,
|
||||
info->enabledExtensionCount,
|
||||
layer_info->layer.instanceExtensions());
|
||||
layer_info->root.instanceExtensions());
|
||||
|
||||
VkInstanceCreateInfo newInfo = *info;
|
||||
newInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
|
||||
|
|
@ -183,7 +183,7 @@ namespace {
|
|||
auto extensions = add_extensions(
|
||||
info->ppEnabledExtensionNames,
|
||||
info->enabledExtensionCount,
|
||||
layer_info->layer.deviceExtensions());
|
||||
layer_info->root.deviceExtensions());
|
||||
|
||||
VkDeviceCreateInfo newInfo = *info;
|
||||
newInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
|
||||
|
|
@ -205,7 +205,7 @@ namespace {
|
|||
.handle = *device,
|
||||
.funcs = vk::initVulkanDeviceFuncs(instance_info->funcs, *device),
|
||||
.layer = layer::Instance(
|
||||
layer_info->layer,
|
||||
layer_info->root,
|
||||
vk::Vulkan(
|
||||
instance_info->handle, *device, physdev,
|
||||
instance_info->funcs,
|
||||
|
|
@ -316,12 +316,11 @@ VkResult vkNegotiateLoaderLayerInterfaceVersion(VkNegotiateLayerInterface* pVers
|
|||
|
||||
// load the layer configuration
|
||||
try {
|
||||
layer::Layer layer{};
|
||||
if (!layer.active()) // skip inactive
|
||||
layer::Root root{};
|
||||
if (!root.active()) // skip inactive
|
||||
return VK_ERROR_INITIALIZATION_FAILED;
|
||||
|
||||
layer_info = new LayerInfo{
|
||||
.layer = std::move(layer),
|
||||
.map = {
|
||||
#define VKPTR(name) reinterpret_cast<PFN_vkVoidFunction>(name)
|
||||
{ "vkCreateInstance", VKPTR(myvkCreateInstance) },
|
||||
|
|
@ -329,7 +328,8 @@ VkResult vkNegotiateLoaderLayerInterfaceVersion(VkNegotiateLayerInterface* pVers
|
|||
{ "vkDestroyDevice", VKPTR(myvkDestroyDevice) },
|
||||
{ "vkDestroyInstance", VKPTR(myvkDestroyInstance) }
|
||||
#undef VKPTR
|
||||
}
|
||||
},
|
||||
.root = std::move(root)
|
||||
};
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "lsfg-vk: something went wrong during lsfg-vk layer initialization:\n";
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue