From 8e7dd4e4aac4392fe5aab244206fa6bdf3020012 Mon Sep 17 00:00:00 2001 From: PancakeTAS Date: Sat, 25 Apr 2026 20:03:59 +0200 Subject: [PATCH] feat(bindless): Build descriptor layout for pipelines --- lsfg-vk-backend/src/modules/pipeline.cpp | 112 +++++++++++++++++++++++ lsfg-vk-backend/src/modules/pipeline.hpp | 75 +++++++++++++++ lsfg-vk-backend/src/utility/vkhelper.cpp | 31 +++++++ lsfg-vk-backend/src/utility/vkhelper.hpp | 19 ++++ 4 files changed, 237 insertions(+) create mode 100644 lsfg-vk-backend/src/modules/pipeline.cpp create mode 100644 lsfg-vk-backend/src/modules/pipeline.hpp diff --git a/lsfg-vk-backend/src/modules/pipeline.cpp b/lsfg-vk-backend/src/modules/pipeline.cpp new file mode 100644 index 0000000..ed3955b --- /dev/null +++ b/lsfg-vk-backend/src/modules/pipeline.cpp @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ + +#include "pipeline.hpp" +#include "library.hpp" +#include "modules/pipeline/signature.hpp" +#include "modules/pipeline/signature/helpers.hpp" +#include "modules/pipeline/signature/image.hpp" +#include "utility/logger.hpp" +#include "utility/vkhelper.hpp" + +#include +#include +#include + +using namespace lsfgvk::pipeline; + +namespace { + /// Helper method to apply extent operations + vk::Extent2D apply( + const vk::Extent2D& base, + const vk::Extent2D& flow, + const ExtentOp& op + ) { + vk::Extent2D result{op.flow() ? flow : base}; + for (const auto& [add, shift] : op.operations()) { + result.width = (result.width + add) >> shift; + result.height = (result.height + add) >> shift; + } + return { result.width, result.height }; + } +} + +Pipeline::Pipeline( + const vk::detail::DispatchLoaderDynamic& dld, + const vk::Device& device, + const vk::PhysicalDevice& physdev, + const vk::Queue& queue, + uint32_t queueFamilyIndex, + const library::ShaderLibrary& library, + const PipelineSignature& signature, + vk::Extent2D extent, + float flow, + bool perf, + bool hdr +) { + LOG_DEBUG("Building pipeline for " + << extent.width << "x" << extent.height + << " at " << flow << " flow") + + // Build the Vulkan descriptor set layout + uint32_t sampledImageCount{}; + uint32_t storageImageCount{}; + + std::vector bindings; + bindings.reserve(4 + signature.descriptors.size()); + + bindings.push_back({ + .binding = 0, + .descriptorType = vk::DescriptorType::eUniformBuffer, + .descriptorCount = 1, + .stageFlags = vk::ShaderStageFlagBits::eCompute + }); + + for (uint32_t i = 1; i <= 3; i++) { + bindings.push_back({ + .binding = i, + .descriptorType = vk::DescriptorType::eSampler, + .descriptorCount = 1, + .stageFlags = vk::ShaderStageFlagBits::eCompute + }); + } + + uint32_t bindingIdx{4}; + for (const auto& binding : signature.descriptors) { + uint32_t descriptorCount{static_cast(binding.resources.size())}; + if (descriptorCount == 1) { + const auto& image{signature.images.at(binding.resources.front())}; + if (image.flags & ImageFlag::Mipmaps) + descriptorCount = image.count; + } + + bindings.push_back({ + .binding = bindingIdx++, + .descriptorType = binding.type == BindingType::StorageImage ? + vk::DescriptorType::eStorageImage : vk::DescriptorType::eSampledImage, + .descriptorCount = descriptorCount, + .stageFlags = vk::ShaderStageFlagBits::eCompute + }); + + if (binding.type == BindingType::StorageImage) + storageImageCount += descriptorCount; + else + sampledImageCount += descriptorCount; + } + + auto [layout, pipelineLayout] = vkhelper::createLayout( + dld, + device, + bindings, + sizeof(PushConstants) + ); + this->m_layout = { + .layout = std::move(layout), + .pipelineLayout = std::move(pipelineLayout) + }; + + LOG_DEBUG(" Built descriptor set layout with " << bindings.size() << " bindings (" + << sampledImageCount << " sampled images, " + << storageImageCount << " storage images)") + + LOG_DEBUG("Finished building pipeline") +} diff --git a/lsfg-vk-backend/src/modules/pipeline.hpp b/lsfg-vk-backend/src/modules/pipeline.hpp new file mode 100644 index 0000000..0894e8b --- /dev/null +++ b/lsfg-vk-backend/src/modules/pipeline.hpp @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ + +#pragma once + +#include "library.hpp" +#include "pipeline/signature.hpp" +#include "utility/vkhelper.hpp" + +#include + +namespace lsfgvk::pipeline { + + // TODO: Improve API design + + /// Struct for the uniform buffer + struct UniformBuffer { + float timestamp; + uint32_t iteration; + uint32_t advancedColorKind; + uint32_t hdrSupport; + float resolutionInvScale; + float uiThreshold; + }; + + /// Struct for push constants + struct PushConstants { + uint32_t specialFlag; + uint32_t subiteration; + }; + + /// + /// Vulkan pipeline created from a signature + /// + class Pipeline { + public: + /// + /// Create a new pipeline + /// + /// @param dld Vulkan dispatch loader + /// @param dev Vulkan device + /// @param physdev Vulkan physical device + /// @param queue Vulkan compute queue + /// @param queueFamilyIndex Compute queue family index + /// @param library Shader library + /// @param signature Pipeline signature + /// @param extent Base extent + /// @param flow Flow scale + /// @param perf Performance mode + /// @param hdr HDR variant + /// @throws std::runtime_error on failure + /// + explicit Pipeline( + const vk::detail::DispatchLoaderDynamic& dld, + const vk::Device& dev, + const vk::PhysicalDevice& physdev, + const vk::Queue& queue, + uint32_t queueFamilyIndex, + const library::ShaderLibrary& library, + const PipelineSignature& signature, + vk::Extent2D extent, + float flow, + bool perf, + bool hdr + ); + + private: + /// Vulkan descriptor set & pipeline layout + struct Layout { + vk::UniqueDescriptorSetLayout layout; + vk::UniquePipelineLayout pipelineLayout; + }; + Layout m_layout; + }; + +} diff --git a/lsfg-vk-backend/src/utility/vkhelper.cpp b/lsfg-vk-backend/src/utility/vkhelper.cpp index 8b42e5c..19ba529 100644 --- a/lsfg-vk-backend/src/utility/vkhelper.cpp +++ b/lsfg-vk-backend/src/utility/vkhelper.cpp @@ -3,7 +3,9 @@ #include "vkhelper.hpp" #include +#include #include +#include #include #include #include @@ -12,6 +14,7 @@ #include #include #include +#include /* Device initialization */ @@ -170,3 +173,31 @@ vk::UniqueShaderModule vkhelper::createShaderModule( }; return device.createShaderModuleUnique(shaderInfo, nullptr, dld); } + +std::pair vkhelper::createLayout( + const vk::detail::DispatchLoaderDynamic& dld, + const vk::Device& device, + const std::vector& bindings, + size_t pushConstantSize +) { + const vk::DescriptorSetLayoutCreateInfo layoutInfo{ + .flags = vk::DescriptorSetLayoutCreateFlagBits::eUpdateAfterBindPool, + .bindingCount = static_cast(bindings.size()), + .pBindings = bindings.data() + }; + auto descriptorSetLayout{device.createDescriptorSetLayoutUnique(layoutInfo, nullptr, dld)}; + + const vk::PushConstantRange pushConstantRange{ + .stageFlags = vk::ShaderStageFlagBits::eCompute, + .size = static_cast(pushConstantSize) + }; + const vk::PipelineLayoutCreateInfo pipelineLayoutInfo{ + .setLayoutCount = 1, + .pSetLayouts = &*descriptorSetLayout, + .pushConstantRangeCount = 1, + .pPushConstantRanges = &pushConstantRange + }; + auto pipelineLayout{device.createPipelineLayoutUnique(pipelineLayoutInfo, nullptr, dld)}; + + return { std::move(descriptorSetLayout), std::move(pipelineLayout) }; +} diff --git a/lsfg-vk-backend/src/utility/vkhelper.hpp b/lsfg-vk-backend/src/utility/vkhelper.hpp index ab0e9ee..0748560 100644 --- a/lsfg-vk-backend/src/utility/vkhelper.hpp +++ b/lsfg-vk-backend/src/utility/vkhelper.hpp @@ -16,10 +16,12 @@ #include // IWYU pragma: end_exports +#include #include #include #include #include +#include namespace vkhelper { @@ -116,4 +118,21 @@ namespace vkhelper { const std::span& code ); + /// + /// Create a Vulkan descriptor set layout + /// + /// @param dld Dynamic dispatch loader + /// @param device Vulkan device + /// @param bindings List of descriptor set layout bindings + /// @param pushConstantSize Size of push constant range + /// @return RAII-wrapped Vulkan descriptor set & pipeline layout + /// @throws std::runtime_error on failure + /// + std::pair createLayout( + const vk::detail::DispatchLoaderDynamic& dld, + const vk::Device& device, + const std::vector& bindings, + size_t pushConstantSize + ); + }