From 7e1d46189ec60a415e4f06e375dd26503b93d0f2 Mon Sep 17 00:00:00 2001 From: PancakeTAS Date: Thu, 10 Jul 2025 16:15:07 +0200 Subject: [PATCH] lsfg-vk-v3.1: move out shader extraction to surrounding project --- include/extract/extract.hpp | 26 ++++++ include/extract/trans.hpp | 16 ++++ lsfg-vk-v3.1/include/utils/trans.hpp | 16 ---- lsfg-vk-v3.1/src/pool/shaderpool.cpp | 9 +-- lsfg-vk-v3.1/src/utils/trans.cpp | 31 -------- src/extract/extract.cpp | 114 +++++++++++++++++++++++++++ src/extract/trans.cpp | 35 ++++++++ 7 files changed, 193 insertions(+), 54 deletions(-) create mode 100644 include/extract/extract.hpp create mode 100644 include/extract/trans.hpp delete mode 100644 lsfg-vk-v3.1/include/utils/trans.hpp delete mode 100644 lsfg-vk-v3.1/src/utils/trans.cpp create mode 100644 src/extract/extract.cpp create mode 100644 src/extract/trans.cpp diff --git a/include/extract/extract.hpp b/include/extract/extract.hpp new file mode 100644 index 0000000..812a2bd --- /dev/null +++ b/include/extract/extract.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +namespace Extract { + + /// + /// Extract all known shaders. + /// + /// @throws std::runtime_error if shader extraction fails. + /// + void extractShaders(); + + /// + /// Get a shader by name. + /// + /// @param name The name of the shader to get. + /// @return The shader bytecode. + /// + /// @throws std::runtime_error if the shader is not found. + /// + std::vector getShader(const std::string& name); + +} diff --git a/include/extract/trans.hpp b/include/extract/trans.hpp new file mode 100644 index 0000000..9da1df9 --- /dev/null +++ b/include/extract/trans.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +namespace Extract { + + /// + /// Translate DXBC bytecode to SPIR-V bytecode. + /// + /// @param bytecode The DXBC bytecode to translate. + /// @return The translated SPIR-V bytecode. + /// + std::vector translateShader(std::vector bytecode); + +} diff --git a/lsfg-vk-v3.1/include/utils/trans.hpp b/lsfg-vk-v3.1/include/utils/trans.hpp deleted file mode 100644 index 71e28be..0000000 --- a/lsfg-vk-v3.1/include/utils/trans.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include -#include - -namespace LSFG::Utils::Trans { - - /// - /// Translate shader bytecode to SPIR-V. - /// - /// @param bytecode The shader bytecode to translate. - /// @return A vector containing the translated SPIR-V bytecode. - /// - [[nodiscard]] std::vector translateShader(std::vector bytecode); - -} diff --git a/lsfg-vk-v3.1/src/pool/shaderpool.cpp b/lsfg-vk-v3.1/src/pool/shaderpool.cpp index 22b4246..7aaf00b 100644 --- a/lsfg-vk-v3.1/src/pool/shaderpool.cpp +++ b/lsfg-vk-v3.1/src/pool/shaderpool.cpp @@ -2,7 +2,6 @@ #include "core/shadermodule.hpp" #include "core/device.hpp" #include "core/pipeline.hpp" -#include "utils/trans.hpp" #include @@ -27,12 +26,8 @@ Core::ShaderModule ShaderPool::getShader( if (bytecode.empty()) throw std::runtime_error("Shader code is empty: " + name); - // create the translated shader module - auto spirvBytecode = Utils::Trans::translateShader(bytecode); - if (spirvBytecode.empty()) - throw std::runtime_error("Shader code translation failed: " + name); - - Core::ShaderModule shader(device, spirvBytecode, types); + // create the shader module + Core::ShaderModule shader(device, bytecode, types); shaders[name] = shader; return shader; } diff --git a/lsfg-vk-v3.1/src/utils/trans.cpp b/lsfg-vk-v3.1/src/utils/trans.cpp deleted file mode 100644 index 25984e3..0000000 --- a/lsfg-vk-v3.1/src/utils/trans.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "utils/trans.hpp" - -#include -#include - -using namespace LSFG::Utils; - -std::vector Trans::translateShader(std::vector bytecode) { - return bytecode; // on windows we expect the bytecode to be spir-v -// // compile the shader -// dxvk::DxbcReader reader(reinterpret_cast(bytecode.data()), bytecode.size()); -// dxvk::DxbcModule module(reader); -// const dxvk::DxbcModuleInfo info{}; -// auto shader = module.compile(info, "CS"); - -// // extract spir-v from d3d11 shader -// auto code = shader->getRawCode(); - -// // patch binding offsets -// #pragma clang diagnostic push -// #pragma clang diagnostic ignored "-Wunsafe-buffer-usage" -// for (size_t i = 0; i < shader->m_bindingOffsets.size(); i++) -// code.data()[shader->m_bindingOffsets.at(i).bindingOffset] = static_cast(i); // NOLINT -// #pragma clang diagnostic pop - -// std::vector spirvBytecode(code.size()); -// std::copy_n(reinterpret_cast(code.data()), -// code.size(), spirvBytecode.data()); -// return spirvBytecode; -// #endif -} diff --git a/src/extract/extract.cpp b/src/extract/extract.cpp new file mode 100644 index 0000000..2ba0c67 --- /dev/null +++ b/src/extract/extract.cpp @@ -0,0 +1,114 @@ +#include "extract/extract.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace Extract; + +const std::unordered_map nameHashTable = {{ + { "mipmaps", 0xe365474d }, + { "alpha[0]", 0xf37c8aa8 }, + { "alpha[1]", 0xeb7a52d4 }, + { "alpha[2]", 0x8901788e }, + { "alpha[3]", 0xa06a5e36 }, + { "beta[0]", 0x63c16b89 }, + { "beta[1]", 0xe3967ed5 }, + { "beta[2]", 0x570085ee }, + { "beta[3]", 0x4f4530db }, + { "beta[4]", 0x39727389 }, + { "gamma[0]", 0x94c4edf6 }, + { "gamma[1]", 0xf4e32702 }, + { "gamma[2]", 0xa3dc56fc }, + { "gamma[3]", 0x8b5ed8f6 }, + { "gamma[4]", 0x1cbf3c4d }, + { "delta[0]", 0x94c4edf6 }, + { "delta[1]", 0xacfd805b }, + { "delta[2]", 0x891dc48b }, + { "delta[3]", 0x98536d9d }, + { "delta[4]", 0x8e3f2155 }, + { "delta[5]", 0x8f0e70a1 }, + { "delta[6]", 0xd5eca8f1 }, + { "delta[7]", 0xa9e54e37 }, + { "delta[8]", 0x1dee8b84 }, + { "delta[9]", 0x1576c3f5 }, + { "generate", 0x5c040bd5 } +}}; + +namespace { + std::unordered_map> shaderData; + + uint32_t fnv1a_hash(const std::vector& data) { + uint32_t hash = 0x811C9DC5; + for (auto byte : data) { + hash ^= byte; + hash *= 0x01000193; + } + return hash; + } + + int on_resource(void* data, const peparse::resource& res) { // NOLINT + if (res.type != peparse::RT_RCDATA || res.buf == nullptr || res.buf->bufLen <= 0) + return 0; + std::vector resource_data(res.buf->bufLen); + std::copy_n(res.buf->buf, res.buf->bufLen, resource_data.data()); + + const uint32_t hash = fnv1a_hash(resource_data); + shaderData[hash] = resource_data; + return 0; + } +} + +void Extract::extractShaders() { + if (shaderData.size() > 0) + return; + + // find path to dll (absolutely beautiful code) + char* dllPath = getenv("LSFG_DLL_PATH"); + std::string dllPathStr; + if (dllPath && *dllPath != '\0') { + dllPathStr = std::string(dllPath); + } else { + const char* dataDir = getenv("XDG_DATA_HOME"); + if (dataDir && *dataDir != '\0') { + dllPathStr = std::string(dataDir) + + "/Steam/steamapps/common/Lossless Scaling/Lossless.dll"; + } else { + const char* homeDir = getenv("HOME"); + if (homeDir && *homeDir != '\0') { + dllPathStr = std::string(homeDir) + + "/.local/share/Steam/steamapps/common/Lossless Scaling/Lossless.dll"; + } else { + dllPathStr = "Lossless.dll"; + } + } + } + + // parse the dll + peparse::parsed_pe* dll = peparse::ParsePEFromFile(dllPathStr.c_str()); + if (!dll) + throw std::runtime_error("Unable to read Lossless.dll, is it installed?"); + peparse::IterRsrc(dll, on_resource, nullptr); + peparse::DestructParsedPE(dll); +} + +std::vector Extract::getShader(const std::string& name) { + if (shaderData.empty()) + throw std::runtime_error("Shaders are not loaded."); + + auto hit = nameHashTable.find(name); + if (hit == nameHashTable.end()) + throw std::runtime_error("Shader not found: " + name); + + auto sit = shaderData.find(hit->second); + if (sit == shaderData.end()) + throw std::runtime_error("Shader not found: " + name); + + return sit->second; +} diff --git a/src/extract/trans.cpp b/src/extract/trans.cpp new file mode 100644 index 0000000..badb34c --- /dev/null +++ b/src/extract/trans.cpp @@ -0,0 +1,35 @@ +#include "extract/trans.hpp" + +#include +#include +#include + +#include +#include + +using namespace Extract; + +std::vector Extract::translateShader(std::vector bytecode) { + // compile the shader + dxvk::DxbcReader reader(reinterpret_cast(bytecode.data()), bytecode.size()); + dxvk::DxbcModule module(reader); + const dxvk::DxbcModuleInfo info{}; + auto shader = module.compile(info, "CS"); + + // extract spir-v from d3d11 shader + auto code = shader->getRawCode(); + + // patch binding offsets +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" + for (size_t i = 0; i < shader->m_bindingOffsets.size(); i++) + code.data()[shader->m_bindingOffsets.at(i).bindingOffset] // NOLINT + = static_cast(i); +#pragma clang diagnostic pop + + // return the new bytecode + std::vector spirvBytecode(code.size()); + std::copy_n(reinterpret_cast(code.data()), + code.size(), spirvBytecode.data()); + return spirvBytecode; +}