From 75a186c10e6f96ba77f6b08a9cc1058d5c95044b Mon Sep 17 00:00:00 2001 From: PancakeTAS Date: Sat, 25 Apr 2026 19:55:47 +0200 Subject: [PATCH] feat(bindless): Implement lsfg-vk shader library --- lsfg-vk-backend/src/modules/library.cpp | 113 +++++++++++++++++++++++ lsfg-vk-backend/src/modules/library.hpp | 67 ++++++++++++++ lsfg-vk-backend/src/utility/vkhelper.cpp | 15 +++ lsfg-vk-backend/src/utility/vkhelper.hpp | 18 ++++ 4 files changed, 213 insertions(+) create mode 100644 lsfg-vk-backend/src/modules/library.cpp create mode 100644 lsfg-vk-backend/src/modules/library.hpp diff --git a/lsfg-vk-backend/src/modules/library.cpp b/lsfg-vk-backend/src/modules/library.cpp new file mode 100644 index 0000000..dec2578 --- /dev/null +++ b/lsfg-vk-backend/src/modules/library.cpp @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ + +#include "library.hpp" +#include "library/dll.hpp" +#include "utility/logger.hpp" +#include "utility/vkhelper.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/// All base shaders in the library. +const std::array, 3> BASE_LIBRARY{{ + { "mipmaps", 0 }, + { "generate_8bit", 1 }, + { "generate_16bit", 2 }, +}}; + +/// All non-base shaders in the library. +const std::array, 24> LIBRARY{{ + { "alpha0", 13 }, + { "alpha1", 14 }, + { "alpha2", 15 }, + { "alpha3", 16 }, + { "beta0", 22 }, + { "beta1", 23 }, + { "beta2", 24 }, + { "beta3", 25 }, + { "beta4", 26 }, + { "gamma0", 3 }, + { "gamma1", 4 }, + { "gamma2", 5 }, + { "gamma3", 6 }, + { "gamma4", 7 }, + { "delta0", 8 }, + { "delta1", 9 }, + { "delta2", 10 }, + { "delta3", 11 }, + { "delta4", 12 }, + { "epsilon0", 17 }, + { "epsilon1", 18 }, + { "epsilon2", 19 }, + { "epsilon3", 20 }, + { "epsilon4", 21 } +}}; + +using namespace lsfgvk::library; + +ShaderLibrary::ShaderLibrary( + const vk::detail::DispatchLoaderDynamic& dld, + const vk::Device& device, + bool halfPrecision, + const std::filesystem::path& dll +) { + LOG_DEBUG("Loading shader library from DLL: " << dll) + + if (!std::filesystem::exists(dll)) { + throw std::runtime_error("The specified shader DLL does not exist"); + } + // Create shader modules for each shader in the library + const auto resources = priv::parseDll(dll); + for (const auto& [name, idx] : BASE_LIBRARY) { + const uint32_t rid{idx}; + + const auto& it = resources.find(rid == 0 ? 2147488584U : rid); + if (it == resources.end()) + throw std::runtime_error( + "Unable to find base shader '" + std::string(name) + "' in DLL" + ); + + LOG_DEBUG(" " << std::setw(2) << idx + << ": name=" << name + << ", rid=" << rid + << ", size=" << it->second.size() << " bytes") + + this->m_baseShaders[name] = vkhelper::createShaderModule(dld, device, it->second); + } + + for (const auto& [name, idx] : LIBRARY) { + const std::pair rid{ + idx + (halfPrecision ? 48 : 0), + idx + (halfPrecision ? 48 : 0) + 24 + }; + + const auto& qit{resources.find(rid.first)}; + const auto& pit{resources.find(rid.second)}; + if (qit == resources.end() || pit == resources.end()) + throw std::runtime_error( + "Unable to find shader '" + std::string(name) + "' in DLL" + ); + + LOG_DEBUG(" " << std::setw(2) << idx + << ": name=" << std::setw(8) << name + << ", [Q] " + << "rid="<< std::setw(2) << rid.first + << ", size="<< std::setw(5) << qit->second.size() << " bytes" + << ", [P] " + << "rid="<< std::setw(2) << rid.second + << ", size="<< std::setw(5) << pit->second.size() << " bytes") + + this->m_qualityShaders[name] = vkhelper::createShaderModule(dld, device, qit->second); + this->m_performanceShaders[name] = vkhelper::createShaderModule(dld, device, pit->second); + + } + + LOG_DEBUG("Finished loading shader library") +} diff --git a/lsfg-vk-backend/src/modules/library.hpp b/lsfg-vk-backend/src/modules/library.hpp new file mode 100644 index 0000000..4accd1a --- /dev/null +++ b/lsfg-vk-backend/src/modules/library.hpp @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ + +#pragma once + +#include "utility/vkhelper.hpp" + +#include +#include +#include + +namespace lsfgvk::library { + + /// + /// The lsfg-vk shader library + /// + class ShaderLibrary { + public: + /// + /// Create the shader library + /// + /// @param dld Vulkan dynamic dispatch loader + /// @param device Vulkan device + /// @param halfPrecision Whether to load the half-precision shader variants + /// @param dll Path to the shader DLL file + /// @throws std::runtime_error on failure + /// + explicit ShaderLibrary( + const vk::detail::DispatchLoaderDynamic& dld, + const vk::Device& device, + bool halfPrecision, + const std::filesystem::path& dll + ); + + /// + /// Get a base shader by name + /// + /// @param name Shader name + /// @return A reference to the shader + /// @throws std::out_of_range if the shader is not found + /// + [[nodiscard]] const auto& baseShader(std::string_view name) const { + return this->m_baseShaders.at(name); + } + + /// + /// Get a shader by name + /// + /// @param name Shader name + /// @param perf Whether to get the performance variant of the shader + /// @return A reference to the shader + /// @throws std::out_of_range if the shader is not found + /// + [[nodiscard]] const auto& shader(std::string_view name, bool perf) const { + auto it{this->m_baseShaders.find(name)}; + if (it != this->m_baseShaders.end()) + return it->second; + + return perf ? this->m_performanceShaders.at(name) : this->m_qualityShaders.at(name); + } + + private: + std::unordered_map m_baseShaders; + std::unordered_map m_qualityShaders; + std::unordered_map m_performanceShaders; + }; + +} diff --git a/lsfg-vk-backend/src/utility/vkhelper.cpp b/lsfg-vk-backend/src/utility/vkhelper.cpp index b350ea6..8b42e5c 100644 --- a/lsfg-vk-backend/src/utility/vkhelper.cpp +++ b/lsfg-vk-backend/src/utility/vkhelper.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -155,3 +156,17 @@ std::pair vkhelper::createDevice( device->getQueue(qfi, 0, dld) }; } + +/* Shader modules & pipelines */ + +vk::UniqueShaderModule vkhelper::createShaderModule( + const vk::detail::DispatchLoaderDynamic& dld, + const vk::Device& device, + const std::span& code +) { + const vk::ShaderModuleCreateInfo shaderInfo{ + .codeSize = code.size() * sizeof(uint32_t), + .pCode = code.data() + }; + return device.createShaderModuleUnique(shaderInfo, nullptr, dld); +} diff --git a/lsfg-vk-backend/src/utility/vkhelper.hpp b/lsfg-vk-backend/src/utility/vkhelper.hpp index 5dd9ed5..ab0e9ee 100644 --- a/lsfg-vk-backend/src/utility/vkhelper.hpp +++ b/lsfg-vk-backend/src/utility/vkhelper.hpp @@ -17,6 +17,7 @@ // IWYU pragma: end_exports #include +#include #include #include @@ -98,4 +99,21 @@ namespace vkhelper { bool fp16 ); + /* Shader modules & pipelines */ + + /// + /// Create a Vulkan shader module from SPIR-V bytecode + /// + /// @param dld Dynamic dispatch loader + /// @param device Vulkan device + /// @param code SPIR-V bytecode + /// @return RAII-wrapped Vulkan shader module + /// @throws std::runtime_error on failure + /// + vk::UniqueShaderModule createShaderModule( + const vk::detail::DispatchLoaderDynamic& dld, + const vk::Device& device, + const std::span& code + ); + }