From d3a903524a8c760b2dd3c1d8ec30ab356f2e09e1 Mon Sep 17 00:00:00 2001 From: PancakeTAS Date: Sun, 29 Jun 2025 18:08:17 +0200 Subject: [PATCH] core descriptor pool/set objects --- include/core/descriptorpool.hpp | 49 ++++++++++++++++++++++++ include/core/descriptorset.hpp | 66 +++++++++++++++++++++++++++++++++ src/core/descriptorpool.cpp | 37 ++++++++++++++++++ src/core/descriptorset.cpp | 43 +++++++++++++++++++++ 4 files changed, 195 insertions(+) create mode 100644 include/core/descriptorpool.hpp create mode 100644 include/core/descriptorset.hpp create mode 100644 src/core/descriptorpool.cpp create mode 100644 src/core/descriptorset.cpp diff --git a/include/core/descriptorpool.hpp b/include/core/descriptorpool.hpp new file mode 100644 index 0000000..9b4d470 --- /dev/null +++ b/include/core/descriptorpool.hpp @@ -0,0 +1,49 @@ +#ifndef DESCRIPTORPOOL_HPP +#define DESCRIPTORPOOL_HPP + +#include "device.hpp" + +#include + +#include + +namespace Vulkan::Core { + + /// + /// C++ wrapper class for a Vulkan descriptor pool. + /// + /// This class manages the lifetime of a Vulkan descriptor pool. + /// + class DescriptorPool { + public: + /// + /// Create the descriptor pool. + /// + /// @param device Vulkan device + /// + /// @throws std::invalid_argument if the device is invalid. + /// @throws ls::vulkan_error if object creation fails. + /// + DescriptorPool(const Device& device); + + /// Get the Vulkan handle. + [[nodiscard]] auto handle() const { return *this->descriptorPool; } + + /// Check whether the object is valid. + [[nodiscard]] bool isValid() const { return static_cast(this->descriptorPool); } + /// if (obj) operator. Checks if the object is valid. + explicit operator bool() const { return this->isValid(); } + + /// Trivially copyable, moveable and destructible + DescriptorPool(const DescriptorPool&) noexcept = default; + DescriptorPool& operator=(const DescriptorPool&) noexcept = default; + DescriptorPool(DescriptorPool&&) noexcept = default; + DescriptorPool& operator=(DescriptorPool&&) noexcept = default; + ~DescriptorPool() = default; + private: + std::shared_ptr descriptorPool; + }; + +} + +#endif // DESCRIPTORPOOL_HPP diff --git a/include/core/descriptorset.hpp b/include/core/descriptorset.hpp new file mode 100644 index 0000000..bfd110f --- /dev/null +++ b/include/core/descriptorset.hpp @@ -0,0 +1,66 @@ +#ifndef DESCRIPTORSET_HPP +#define DESCRIPTORSET_HPP + +#include "core/commandbuffer.hpp" +#include "core/descriptorpool.hpp" +#include "core/pipeline.hpp" +#include "core/shadermodule.hpp" +#include "device.hpp" + +#include + +#include + +namespace Vulkan::Core { + + /// + /// C++ wrapper class for a Vulkan descriptor set. + /// + /// This class manages the lifetime of a Vulkan descriptor set. + /// + class DescriptorSet { + public: + /// + /// Create the descriptor set. + /// + /// @param device Vulkan device + /// @param pool Descriptor pool to allocate from + /// @param shaderModule Shader module to use for the descriptor set + /// + /// @throws std::invalid_argument if the device or pool is invalid. + /// @throws ls::vulkan_error if object creation fails. + /// + DescriptorSet(const Device& device, + DescriptorPool pool, const ShaderModule& shaderModule); + + /// + /// Bind a descriptor set to a command buffer. + /// + /// @param commandBuffer Command buffer to bind the descriptor set to. + /// @param pipeline Pipeline to bind the descriptor set to. + /// + /// @throws std::invalid_argument if the command buffer is invalid. + /// + void bind(const CommandBuffer& commandBuffer, const Pipeline& pipeline) const; + + /// Get the Vulkan handle. + [[nodiscard]] auto handle() const { return *this->descriptorSet; } + + /// Check whether the object is valid. + [[nodiscard]] bool isValid() const { return static_cast(this->descriptorSet); } + /// if (obj) operator. Checks if the object is valid. + explicit operator bool() const { return this->isValid(); } + + /// Trivially copyable, moveable and destructible + DescriptorSet(const DescriptorSet&) noexcept = default; + DescriptorSet& operator=(const DescriptorSet&) noexcept = default; + DescriptorSet(DescriptorSet&&) noexcept = default; + DescriptorSet& operator=(DescriptorSet&&) noexcept = default; + ~DescriptorSet() = default; + private: + std::shared_ptr descriptorSet; + }; + +} + +#endif // DESCRIPTORSET_HPP diff --git a/src/core/descriptorpool.cpp b/src/core/descriptorpool.cpp new file mode 100644 index 0000000..340092e --- /dev/null +++ b/src/core/descriptorpool.cpp @@ -0,0 +1,37 @@ +#include "core/descriptorpool.hpp" +#include "utils/exceptions.hpp" + +#include + +using namespace Vulkan::Core; + +DescriptorPool::DescriptorPool(const Device& device) { + if (!device) + throw std::invalid_argument("Invalid Vulkan device"); + + // create descriptor pool + const std::array pools{{ // arbitrary limits + { .type = VK_DESCRIPTOR_TYPE_SAMPLER, .descriptorCount = 4096 }, + { .type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, .descriptorCount = 4096 }, + { .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, .descriptorCount = 4096 }, + { .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .descriptorCount = 4096 } + }}; + const VkDescriptorPoolCreateInfo desc{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .maxSets = 16384, // arbitrary limit + .poolSizeCount = static_cast(pools.size()), + .pPoolSizes = pools.data() + }; + VkDescriptorPool poolHandle{}; + auto res = vkCreateDescriptorPool(device.handle(), &desc, nullptr, &poolHandle); + if (res != VK_SUCCESS || poolHandle == VK_NULL_HANDLE) + throw ls::vulkan_error(res, "Unable to create descriptor pool"); + + /// store pool in shared ptr + this->descriptorPool = std::shared_ptr( + new VkDescriptorPool(poolHandle), + [dev = device.handle()](VkDescriptorPool* poolHandle) { + vkDestroyDescriptorPool(dev, *poolHandle, nullptr); + } + ); +} diff --git a/src/core/descriptorset.cpp b/src/core/descriptorset.cpp new file mode 100644 index 0000000..73a0a60 --- /dev/null +++ b/src/core/descriptorset.cpp @@ -0,0 +1,43 @@ +#include "core/descriptorset.hpp" +#include "utils/exceptions.hpp" + +using namespace Vulkan::Core; + +DescriptorSet::DescriptorSet(const Device& device, + DescriptorPool pool, const ShaderModule& shaderModule) { + if (!device || !pool) + throw std::invalid_argument("Invalid Vulkan device"); + + // create descriptor set + VkDescriptorSetLayout layout = shaderModule.getDescriptorSetLayout(); + const VkDescriptorSetAllocateInfo desc{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .descriptorPool = pool.handle(), + .descriptorSetCount = 1, + .pSetLayouts = &layout + }; + VkDescriptorSet descriptorSetHandle{}; + auto res = vkAllocateDescriptorSets(device.handle(), &desc, &descriptorSetHandle); + if (res != VK_SUCCESS || descriptorSetHandle == VK_NULL_HANDLE) + throw ls::vulkan_error(res, "Unable to allocate descriptor set"); + + /// store descriptor set in shared ptr + this->descriptorSet = std::shared_ptr( + new VkDescriptorSet(descriptorSetHandle), + [dev = device.handle(), pool = std::move(pool)](VkDescriptorSet* setHandle) { + vkFreeDescriptorSets(dev, pool.handle(), 1, setHandle); + } + ); +} + +void DescriptorSet::bind(const CommandBuffer& commandBuffer, const Pipeline& pipeline) const { + if (!commandBuffer) + throw std::invalid_argument("Invalid command buffer"); + + // bind descriptor set + VkDescriptorSet descriptorSetHandle = this->handle(); + vkCmdBindDescriptorSets(commandBuffer.handle(), + VK_PIPELINE_BIND_POINT_COMPUTE, pipeline.getLayout(), + 0, 1, &descriptorSetHandle, + 0, nullptr); +}