feat(bindless): Build descriptor layout for pipelines

This commit is contained in:
PancakeTAS 2026-04-25 20:03:59 +02:00
parent 75a186c10e
commit 8e7dd4e4aa
No known key found for this signature in database
4 changed files with 237 additions and 0 deletions

View file

@ -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 <cstdint>
#include <utility>
#include <vector>
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<vk::DescriptorSetLayoutBinding> 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<uint32_t>(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")
}

View file

@ -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 <cstdint>
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;
};
}

View file

@ -3,7 +3,9 @@
#include "vkhelper.hpp"
#include <array>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <iomanip>
#include <ios>
#include <iostream>
@ -12,6 +14,7 @@
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
/* Device initialization */
@ -170,3 +173,31 @@ vk::UniqueShaderModule vkhelper::createShaderModule(
};
return device.createShaderModuleUnique(shaderInfo, nullptr, dld);
}
std::pair<vk::UniqueDescriptorSetLayout, vk::UniquePipelineLayout> vkhelper::createLayout(
const vk::detail::DispatchLoaderDynamic& dld,
const vk::Device& device,
const std::vector<vk::DescriptorSetLayoutBinding>& bindings,
size_t pushConstantSize
) {
const vk::DescriptorSetLayoutCreateInfo layoutInfo{
.flags = vk::DescriptorSetLayoutCreateFlagBits::eUpdateAfterBindPool,
.bindingCount = static_cast<uint32_t>(bindings.size()),
.pBindings = bindings.data()
};
auto descriptorSetLayout{device.createDescriptorSetLayoutUnique(layoutInfo, nullptr, dld)};
const vk::PushConstantRange pushConstantRange{
.stageFlags = vk::ShaderStageFlagBits::eCompute,
.size = static_cast<uint32_t>(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) };
}

View file

@ -16,10 +16,12 @@
#include <vulkan/vulkan_structs.hpp>
// IWYU pragma: end_exports
#include <cstddef>
#include <cstdint>
#include <span>
#include <string>
#include <utility>
#include <vector>
namespace vkhelper {
@ -116,4 +118,21 @@ namespace vkhelper {
const std::span<const uint32_t>& 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<vk::UniqueDescriptorSetLayout, vk::UniquePipelineLayout> createLayout(
const vk::detail::DispatchLoaderDynamic& dld,
const vk::Device& device,
const std::vector<vk::DescriptorSetLayoutBinding>& bindings,
size_t pushConstantSize
);
}