diff --git a/engine/src/descriptors/descriptor_pool.cpp b/engine/src/descriptors/descriptor_pool.cpp new file mode 100644 index 0000000..4401fcf --- /dev/null +++ b/engine/src/descriptors/descriptor_pool.cpp @@ -0,0 +1,89 @@ +#include "descriptor_pool.hpp" + +#include + +namespace sophia { + +DescriptorPool::Builder& DescriptorPool::Builder::addPoolSize( + vk::DescriptorType descriptorType, u32 count) { + this->poolSizes.push_back({descriptorType, count}); + return *this; +} + +DescriptorPool::Builder& DescriptorPool::Builder::setPoolFlags( + vk::DescriptorPoolCreateFlags flags) { + this->poolFlags = flags; + return *this; +} + +DescriptorPool::Builder& DescriptorPool::Builder::setMaxSets(u32 count) { + this->maxSets = count; + return *this; +} + +std::unique_ptr DescriptorPool::Builder::build() const { + return std::make_unique(this->device, this->maxSets, + this->poolFlags, this->poolSizes); +} + +DescriptorPool::DescriptorPool( + Device& device, u32 maxSets, vk::DescriptorPoolCreateFlags poolFlags, + const std::vector& poolSizes) + : device{device} { + vk::DescriptorPoolCreateInfo descriptorPoolInfo{}; + descriptorPoolInfo.poolSizeCount = static_cast(poolSizes.size()); + descriptorPoolInfo.pPoolSizes = poolSizes.data(); + descriptorPoolInfo.maxSets = maxSets; + descriptorPoolInfo.flags = poolFlags; + + vk::Result result = this->device.get()->createDescriptorPool( + &descriptorPoolInfo, nullptr, &this->descriptorPool); + + if (result != vk::Result::eSuccess) { + log::fatal("failed to create descriptor pool"); + throw std::runtime_error("failed to create descriptor pool"); + } +} + +DescriptorPool::~DescriptorPool() { + this->device.get()->destroyDescriptorPool(this->descriptorPool); +} + +bool DescriptorPool::allocateDescriptorSet( + const vk::DescriptorSetLayout descriptorSetLayout, + vk::DescriptorSet& descriptor) const { + vk::DescriptorSetAllocateInfo allocInfo{}; + allocInfo.descriptorPool = descriptorPool; + allocInfo.pSetLayouts = &descriptorSetLayout; + allocInfo.descriptorSetCount = 1; + + // Might want to create a "DescriptorPoolManager" class that handles this + // case, and builds a new pool whenever an old pool fills up. But this is + // beyond our current scope + vk::Result result = + this->device.get()->allocateDescriptorSets(&allocInfo, &descriptor); + + if (result == vk::Result::eSuccess) { + return true; + } + + return false; +} + +void DescriptorPool::freeDescriptors( + std::vector& descriptors) const { + vk::Result result = this->device.get()->freeDescriptorSets( + this->descriptorPool, static_cast(descriptors.size()), + descriptors.data()); + + if (result != vk::Result::eSuccess) { + log::fatal("failed to free descriptor sets"); + throw std::runtime_error("failed to free descriptor sets"); + } +} + +void DescriptorPool::resetPool() { + this->device.get()->resetDescriptorPool(this->descriptorPool); +} + +} // namespace sophia diff --git a/engine/src/descriptors/descriptor_pool.hpp b/engine/src/descriptors/descriptor_pool.hpp new file mode 100644 index 0000000..78ee0c7 --- /dev/null +++ b/engine/src/descriptors/descriptor_pool.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include +#include + +#include "device.hpp" + +namespace sophia { + +class DescriptorPool { + public: + DescriptorPool(const DescriptorPool&) = delete; + DescriptorPool& operator=(const DescriptorPool&) = delete; + + class Builder { + public: + Builder(Device& device) : device{device} {} + + Builder& addPoolSize(vk::DescriptorType descriptorType, u32 count); + Builder& setPoolFlags(vk::DescriptorPoolCreateFlags flags); + Builder& setMaxSets(u32 count); + std::unique_ptr build() const; + + private: + Device& device; + std::vector poolSizes{}; + u32 maxSets = 1000; + vk::DescriptorPoolCreateFlags poolFlags{}; + }; + + DescriptorPool(Device& device, u32 maxSets, + vk::DescriptorPoolCreateFlags poolFlags, + const std::vector& poolSizes); + ~DescriptorPool(); + + bool allocateDescriptorSet(const vk::DescriptorSetLayout descriptorSetLayout, + vk::DescriptorSet& descriptor) const; + + void freeDescriptors(std::vector& descriptors) const; + + void resetPool(); + + const vk::DescriptorPool& get() { return this->descriptorPool; } + + private: + Device& device; + vk::DescriptorPool descriptorPool; + + friend class DescriptorWriter; +}; + +} // namespace sophia diff --git a/engine/src/descriptors/descriptor_set_layout.cpp b/engine/src/descriptors/descriptor_set_layout.cpp new file mode 100644 index 0000000..c1e9022 --- /dev/null +++ b/engine/src/descriptors/descriptor_set_layout.cpp @@ -0,0 +1,53 @@ +#include "descriptor_set_layout.hpp" + +#include +#include + +namespace sophia { + +DescriptorSetLayout::Builder& DescriptorSetLayout::Builder::addBinding( + u32 binding, vk::DescriptorType descriptorType, + vk::ShaderStageFlags stageFlags, u32 count) { + assert(this->bindings.count(binding) == 0 && "binding already in use"); + + vk::DescriptorSetLayoutBinding layoutBinding{binding, descriptorType, count, + stageFlags}; + this->bindings[binding] = layoutBinding; + + return *this; +} + +std::unique_ptr DescriptorSetLayout::Builder::build() + const { + return std::make_unique(this->device, this->bindings); +} + +DescriptorSetLayout::DescriptorSetLayout( + Device& device, + std::unordered_map bindings) + : device{device}, bindings{bindings} { + std::vector setLayoutBindings{}; + + for (auto binding : bindings) { + setLayoutBindings.push_back(binding.second); + } + + vk::DescriptorSetLayoutCreateInfo layoutCreateInfo{}; + layoutCreateInfo.bindingCount = + static_cast(setLayoutBindings.size()); + layoutCreateInfo.pBindings = setLayoutBindings.data(); + + vk::Result result = this->device.get()->createDescriptorSetLayout( + &layoutCreateInfo, nullptr, &this->layout); + + if (result != vk::Result::eSuccess) { + log::fatal("failed to create descriptor pool"); + throw std::runtime_error("failed to create descriptor set layout"); + } +} + +DescriptorSetLayout::~DescriptorSetLayout() { + this->device.get()->destroyDescriptorSetLayout(this->layout); +} + +} // namespace sophia diff --git a/engine/src/descriptors/descriptor_set_layout.hpp b/engine/src/descriptors/descriptor_set_layout.hpp new file mode 100644 index 0000000..ff75b55 --- /dev/null +++ b/engine/src/descriptors/descriptor_set_layout.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include + +#include "device.hpp" + +namespace sophia { + +class DescriptorSetLayout { + public: + DescriptorSetLayout(const DescriptorSetLayout&) = delete; + DescriptorSetLayout& operator=(const DescriptorSetLayout&) = delete; + + class Builder { + public: + Builder(Device& device) : device{device} {} + + Builder& addBinding(u32 binding, vk::DescriptorType descriptorType, + vk::ShaderStageFlags stageFlags, u32 count = 1); + + std::unique_ptr build() const; + + private: + Device& device; + std::unordered_map bindings{}; + }; + + DescriptorSetLayout( + Device& device, + std::unordered_map bindings); + ~DescriptorSetLayout(); + + vk::DescriptorSetLayout getDescriptorSetLayout() const { + return this->layout; + } + + private: + Device& device; + vk::DescriptorSetLayout layout; + std::unordered_map bindings; + + friend class DescriptorWriter; +}; + +} // namespace sophia diff --git a/engine/src/descriptors/descriptor_writer.cpp b/engine/src/descriptors/descriptor_writer.cpp new file mode 100644 index 0000000..8b272e5 --- /dev/null +++ b/engine/src/descriptors/descriptor_writer.cpp @@ -0,0 +1,72 @@ +#include "descriptor_writer.hpp" + +#include +#include + +namespace sophia { + +DescriptorWriter::DescriptorWriter(DescriptorSetLayout& setLayout, + DescriptorPool& pool) + : setLayout{setLayout}, pool{pool} {} + +DescriptorWriter& DescriptorWriter::writeBuffer( + u32 binding, vk::DescriptorBufferInfo* bufferInfo) { + assert(this->setLayout.bindings.count(binding) == 1 && + "setLayout does not contain specified binding"); + + auto& bindingDescription = this->setLayout.bindings[binding]; + + assert(bindingDescription.descriptorCount == 1 && + "binding single descriptor info, but binding expects multiple"); + + vk::WriteDescriptorSet write{}; + write.descriptorType = bindingDescription.descriptorType; + write.dstBinding = binding; + write.pBufferInfo = bufferInfo; + write.descriptorCount = 1; + + writes.push_back(write); + return *this; +} + +DescriptorWriter& DescriptorWriter::writeImage( + u32 binding, vk::DescriptorImageInfo* imageInfo) { + assert(this->setLayout.bindings.count(binding) == 1 && + "setLayout does not contain specified binding"); + + auto& bindingDescription = this->setLayout.bindings[binding]; + + assert(bindingDescription.descriptorCount == 1 && + "binding single descriptor info, but binding expects multiple"); + + vk::WriteDescriptorSet write{}; + write.descriptorType = bindingDescription.descriptorType; + write.dstBinding = binding; + write.pImageInfo = imageInfo; + write.descriptorCount = 1; + + writes.push_back(write); + return *this; +} + +bool DescriptorWriter::build(vk::DescriptorSet& set) { + bool success = + this->pool.allocateDescriptorSet(setLayout.getDescriptorSetLayout(), set); + + if (!success) { + return false; + } + overwrite(set); + return true; +} + +void DescriptorWriter::overwrite(vk::DescriptorSet& set) { + for (auto& write : writes) { + write.dstSet = set; + } + + this->pool.device.get()->updateDescriptorSets(writes.size(), writes.data(), 0, + nullptr); +} + +} // namespace sophia diff --git a/engine/src/descriptors/descriptor_writer.hpp b/engine/src/descriptors/descriptor_writer.hpp new file mode 100644 index 0000000..95effbb --- /dev/null +++ b/engine/src/descriptors/descriptor_writer.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include "descriptor_pool.hpp" +#include "descriptor_set_layout.hpp" +#include "device.hpp" + +namespace sophia { + +class DescriptorWriter { + public: + DescriptorWriter(DescriptorSetLayout& setLayout, DescriptorPool& pool); + + DescriptorWriter& writeBuffer(u32 binding, + vk::DescriptorBufferInfo* bufferInfo); + DescriptorWriter& writeImage(u32 binding, vk::DescriptorImageInfo* imageInfo); + + bool build(vk::DescriptorSet& set); + void overwrite(vk::DescriptorSet& set); + + private: + DescriptorSetLayout& setLayout; + DescriptorPool& pool; + std::vector writes; +}; + +} // namespace sophia