From 28ee6dbce08741bdedc1ff77f05ca6f8a2e38a7e Mon Sep 17 00:00:00 2001 From: PancakeTAS Date: Sun, 30 Nov 2025 13:06:32 +0100 Subject: [PATCH] refactor(cleanup): shader/descriptor set helper --- lsfg-vk-backend/CMakeLists.txt | 3 +- .../src/helpers/managed_shader.cpp | 120 ++++++++++++++++++ .../src/helpers/managed_shader.hpp | 91 +++++++++++++ .../lsfg-vk-common/vulkan/command_buffer.hpp | 19 +++ lsfg-vk-common/src/vulkan/command_buffer.cpp | 30 +++++ 5 files changed, 262 insertions(+), 1 deletion(-) create mode 100644 lsfg-vk-backend/src/helpers/managed_shader.cpp create mode 100644 lsfg-vk-backend/src/helpers/managed_shader.hpp diff --git a/lsfg-vk-backend/CMakeLists.txt b/lsfg-vk-backend/CMakeLists.txt index 60acb69..e84f791 100644 --- a/lsfg-vk-backend/CMakeLists.txt +++ b/lsfg-vk-backend/CMakeLists.txt @@ -1,6 +1,7 @@ set(BACKEND_SOURCES "src/extraction/dll_reader.cpp" - "src/extraction/shader_registry.cpp") + "src/extraction/shader_registry.cpp" + "src/helpers/managed_shader.cpp") add_library(lsfg-vk-backend STATIC ${BACKEND_SOURCES}) diff --git a/lsfg-vk-backend/src/helpers/managed_shader.cpp b/lsfg-vk-backend/src/helpers/managed_shader.cpp new file mode 100644 index 0000000..577511c --- /dev/null +++ b/lsfg-vk-backend/src/helpers/managed_shader.cpp @@ -0,0 +1,120 @@ +#include "managed_shader.hpp" +#include "lsfg-vk-common/vulkan/buffer.hpp" +#include "lsfg-vk-common/vulkan/command_buffer.hpp" +#include "lsfg-vk-common/vulkan/image.hpp" +#include "lsfg-vk-common/vulkan/sampler.hpp" +#include "lsfg-vk-common/vulkan/shader.hpp" +#include "lsfg-vk-common/vulkan/vulkan.hpp" + +#include +#include +#include +#include + +#include + +using namespace ls; + +ManagedShaderBuilder& ManagedShaderBuilder::sampled(const vk::Image& image) { + this->sampledImages.push_back(std::ref(image)); + return *this; +} + +ManagedShaderBuilder& ManagedShaderBuilder::sampleds( + const std::vector& images, + size_t offset, size_t count) { + if (count == 0 || offset + count > images.size()) + count = images.size() - offset; + + for (size_t i = 0; i < count; ++i) + this->sampledImages.push_back(std::ref(images[offset + i])); + return *this; +} + + +ManagedShaderBuilder& ManagedShaderBuilder::storage(const vk::Image& image) { + this->storageImages.push_back(std::ref(image)); + return *this; +} + +ManagedShaderBuilder& ManagedShaderBuilder::storages( + const std::vector& images, + size_t offset, size_t count) { + if (count == 0 || offset + count > images.size()) + count = images.size() - offset; + + for (size_t i = 0; i < count; ++i) + this->storageImages.push_back(std::ref(images[offset + i])); + return *this; +} + +ManagedShaderBuilder& ManagedShaderBuilder::sampler(const vk::Sampler& sampler) { + this->imageSamplers.push_back(std::ref(sampler)); + return *this; +} + +ManagedShaderBuilder& ManagedShaderBuilder::samplers( + const std::vector& samplers) { + for (const auto& sampler : samplers) + this->imageSamplers.push_back(std::ref(sampler)); + return *this; +} + +ManagedShaderBuilder& ManagedShaderBuilder::buffer(const vk::Buffer& buffer) { + this->constantBuffers.push_back(std::ref(buffer)); + return *this; +} + +ManagedShader ManagedShaderBuilder::build(const vk::Vulkan& vk, + const vk::Shader& shader) const { + std::vector barriers; + barriers.reserve(this->storageImages.size() + this->sampledImages.size()); + + for (const auto& img : this->sampledImages) + barriers.push_back({ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .image = img.get().handle(), + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .levelCount = 1, + .layerCount = 1 + } + }); + for (const auto& img : this->storageImages) + barriers.push_back({ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcAccessMask = VK_ACCESS_SHADER_READ_BIT, + .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .image = img.get().handle(), + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .levelCount = 1, + .layerCount = 1 + } + }); + + return { + std::ref(shader), + std::move(barriers), + vk::DescriptorSet(vk, shader, + this->sampledImages, + this->storageImages, + this->imageSamplers, + this->constantBuffers) + }; +} + +void ManagedShader::dispatch(const vk::CommandBuffer& cmd, + VkExtent2D extent) const { + cmd.dispatch(this->shader, + this->descriptorSet, + this->barriers, + extent.width, extent.height, 1 + ); +} diff --git a/lsfg-vk-backend/src/helpers/managed_shader.hpp b/lsfg-vk-backend/src/helpers/managed_shader.hpp new file mode 100644 index 0000000..0e2a2db --- /dev/null +++ b/lsfg-vk-backend/src/helpers/managed_shader.hpp @@ -0,0 +1,91 @@ +#pragma once + +#include "lsfg-vk-common/helpers/pointers.hpp" +#include "lsfg-vk-common/vulkan/command_buffer.hpp" +#include "lsfg-vk-common/vulkan/descriptor_set.hpp" +#include "lsfg-vk-common/vulkan/shader.hpp" + +#include +#include + +#include + +namespace ls { + + /// managed shader handling dispatch and barriers + /// this class is NOT memory-safe + class ManagedShader { + friend class ManagedShaderBuilder; + public: + /// dispatch the managed shader + /// @param cmd command buffer to use + /// @param extent dispatch size + /// @throws ls::vulkan_error on failure + void dispatch(const vk::CommandBuffer& cmd, VkExtent2D extent) const; + private: + ls::R shader; + + std::vector barriers; + vk::DescriptorSet descriptorSet; + + // simple move constructor + ManagedShader(ls::R shader, + std::vector barriers, + vk::DescriptorSet descriptorSet) : + shader(shader), + barriers(std::move(barriers)), + descriptorSet(std::move(descriptorSet)) { + } + }; + + /// class for building managed shaders + /// this class is NOT memory-safe + class ManagedShaderBuilder { + public: + /// default constructor + ManagedShaderBuilder() = default; + + /// add a sampled image + /// @param image image to add + [[nodiscard]] ManagedShaderBuilder& sampled(const vk::Image& image); + /// add multiple sampled images + /// @param images images to add + /// @param offset offset into images + /// @param count number of images to add (0 = all) + [[nodiscard]] ManagedShaderBuilder& sampleds(const std::vector& images, + size_t offset = 0, size_t count = 0); + + /// add a storage image + /// @param image image to add + [[nodiscard]] ManagedShaderBuilder& storage(const vk::Image& image); + /// add multiple storage images + /// @param images images to add + /// @param offset offset into images + /// @param count number of images to add (0 = all) + [[nodiscard]] ManagedShaderBuilder& storages(const std::vector& images, + size_t offset = 0, size_t count = 0); + + /// add a sampler + /// @param sampler sampler to add + [[nodiscard]] ManagedShaderBuilder& sampler(const vk::Sampler& sampler); + /// add multiple samplers + /// @param samplers samplers to add + [[nodiscard]] ManagedShaderBuilder& samplers(const std::vector& samplers); + + /// add a buffer + /// @param buffer buffer to add + [[nodiscard]] ManagedShaderBuilder& buffer(const vk::Buffer& buffer); + + /// build the managed shader + /// @param vk the vulkan instance + /// @param shader the shader to use + /// @returns the built managed shader + [[nodiscard]] ManagedShader build(const vk::Vulkan& vk, const vk::Shader& shader) const; + private: + std::vector> sampledImages; + std::vector> storageImages; + std::vector> imageSamplers; + std::vector> constantBuffers; + }; + +} diff --git a/lsfg-vk-common/include/lsfg-vk-common/vulkan/command_buffer.hpp b/lsfg-vk-common/include/lsfg-vk-common/vulkan/command_buffer.hpp index ca95c5b..2d80873 100644 --- a/lsfg-vk-common/include/lsfg-vk-common/vulkan/command_buffer.hpp +++ b/lsfg-vk-common/include/lsfg-vk-common/vulkan/command_buffer.hpp @@ -1,11 +1,19 @@ #pragma once #include "../helpers/pointers.hpp" +#include "descriptor_set.hpp" +#include "shader.hpp" #include "vulkan.hpp" +#include +#include + #include namespace vk { + + using Barrier = VkImageMemoryBarrier; + /// vulkan command buffer class CommandBuffer { public: @@ -14,6 +22,17 @@ namespace vk { /// @throws ls::vulkan_error on failure CommandBuffer(const vk::Vulkan& vk); + /// dispatch a compute shader + /// @param shader the compute shader + /// @param set the descriptor set + /// @param barriers image memory barriers to apply + /// @param x dispatch size in X + /// @param y dispatch size in Y + /// @param z dispatch size in Z + void dispatch(const vk::Shader& shader, const vk::DescriptorSet& set, + const std::vector& barriers, + uint32_t x, uint32_t y, uint32_t z) const; + /// submit the command buffer /// @throws ls::vulkan_error on failure void submit(); // FIXME: method needs to actually submit, depending on needs diff --git a/lsfg-vk-common/src/vulkan/command_buffer.cpp b/lsfg-vk-common/src/vulkan/command_buffer.cpp index bcc408f..b09c0cb 100644 --- a/lsfg-vk-common/src/vulkan/command_buffer.cpp +++ b/lsfg-vk-common/src/vulkan/command_buffer.cpp @@ -1,8 +1,13 @@ #include "lsfg-vk-common/vulkan/command_buffer.hpp" #include "lsfg-vk-common/helpers/errors.hpp" #include "lsfg-vk-common/helpers/pointers.hpp" +#include "lsfg-vk-common/vulkan/descriptor_set.hpp" +#include "lsfg-vk-common/vulkan/shader.hpp" #include "lsfg-vk-common/vulkan/vulkan.hpp" +#include +#include + #include using namespace vk; @@ -43,6 +48,31 @@ CommandBuffer::CommandBuffer(const vk::Vulkan& vk) throw ls::vulkan_error(res, "vkBeginCommandBuffer() failed"); } +void CommandBuffer::dispatch(const vk::Shader& shader, + const vk::DescriptorSet& set, + const std::vector& barriers, + uint32_t x, uint32_t y, uint32_t z) const { + vkCmdPipelineBarrier(*this->commandBuffer, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + 0, + 0, nullptr, + 0, nullptr, + static_cast(barriers.size()), barriers.data() + ); + vkCmdBindPipeline(*this->commandBuffer, + VK_PIPELINE_BIND_POINT_COMPUTE, + shader.pipeline() + ); + vkCmdBindDescriptorSets(*this->commandBuffer, + VK_PIPELINE_BIND_POINT_COMPUTE, + shader.pipelinelayout(), + 0, 1, &set.handle(), + 0, nullptr + ); + vkCmdDispatch(*this->commandBuffer, x, y, z); +} + void CommandBuffer::submit() { auto res = vkEndCommandBuffer(*this->commandBuffer); if (res != VK_SUCCESS)