From ac8b908079a33a932732ea5d00a210b634ef1cec Mon Sep 17 00:00:00 2001 From: Lynix Date: Mon, 6 Apr 2020 21:13:59 +0200 Subject: [PATCH] Vulkan: Replace ShaderBinding& by ShaderBindingPtr --- examples/VulkanTest/main.cpp | 6 +- .../Nazara/Renderer/RenderPipelineLayout.hpp | 8 +- include/Nazara/Renderer/ShaderBinding.hpp | 15 +++ include/Nazara/Renderer/ShaderBinding.inl | 4 + .../VulkanRenderPipelineLayout.hpp | 21 +++- .../VulkanRenderPipelineLayout.inl | 15 +++ .../VulkanRenderer/VulkanShaderBinding.hpp | 8 +- .../VulkanRenderer/VulkanShaderBinding.inl | 16 ++- .../VulkanRenderPipelineLayout.cpp | 114 +++++++++++++----- .../VulkanRenderer/VulkanShaderBinding.cpp | 5 + 10 files changed, 171 insertions(+), 41 deletions(-) diff --git a/examples/VulkanTest/main.cpp b/examples/VulkanTest/main.cpp index 9612cacd9..2d6febd0e 100644 --- a/examples/VulkanTest/main.cpp +++ b/examples/VulkanTest/main.cpp @@ -166,7 +166,7 @@ int main() std::shared_ptr renderPipelineLayout = device->InstantiateRenderPipelineLayout(pipelineLayoutInfo); - Nz::ShaderBinding& shaderBinding = renderPipelineLayout->AllocateShaderBinding(); + Nz::ShaderBindingPtr shaderBinding = renderPipelineLayout->AllocateShaderBinding(); std::unique_ptr uniformBuffer = device->InstantiateBuffer(Nz::BufferType_Uniform); if (!uniformBuffer->Initialize(uniformSize, Nz::BufferUsage_DeviceLocal)) @@ -175,7 +175,7 @@ int main() return __LINE__; } - shaderBinding.Update({ + shaderBinding->Update({ { 0, Nz::ShaderBinding::UniformBufferBinding { @@ -268,7 +268,7 @@ int main() { builder.BindIndexBuffer(indexBufferImpl); builder.BindVertexBuffer(0, vertexBufferImpl); - builder.BindShaderBinding(shaderBinding); + builder.BindShaderBinding(*shaderBinding); builder.SetScissor(Nz::Recti{ 0, 0, int(windowSize.x), int(windowSize.y) }); builder.SetViewport(Nz::Recti{ 0, 0, int(windowSize.x), int(windowSize.y) }); diff --git a/include/Nazara/Renderer/RenderPipelineLayout.hpp b/include/Nazara/Renderer/RenderPipelineLayout.hpp index 7cbaa54de..c15787c35 100644 --- a/include/Nazara/Renderer/RenderPipelineLayout.hpp +++ b/include/Nazara/Renderer/RenderPipelineLayout.hpp @@ -7,8 +7,10 @@ #ifndef NAZARA_RENDERPIPELINELAYOUT_HPP #define NAZARA_RENDERPIPELINELAYOUT_HPP -#include +#include #include +#include +#include #include #include @@ -27,15 +29,13 @@ namespace Nz std::vector bindings; }; - class ShaderBinding; - class NAZARA_RENDERER_API RenderPipelineLayout { public: RenderPipelineLayout() = default; virtual ~RenderPipelineLayout(); - virtual ShaderBinding& AllocateShaderBinding() = 0; + virtual ShaderBindingPtr AllocateShaderBinding() = 0; }; } diff --git a/include/Nazara/Renderer/ShaderBinding.hpp b/include/Nazara/Renderer/ShaderBinding.hpp index 1a2daba5e..395afff5a 100644 --- a/include/Nazara/Renderer/ShaderBinding.hpp +++ b/include/Nazara/Renderer/ShaderBinding.hpp @@ -9,16 +9,23 @@ #include #include +#include #include namespace Nz { class AbstractBuffer; + class ShaderBinding; + class ShaderBindingDeleter; class Texture; class TextureSampler; + using ShaderBindingPtr = std::unique_ptr; + class NAZARA_RENDERER_API ShaderBinding { + friend ShaderBindingDeleter; + public: struct Binding; @@ -47,9 +54,17 @@ namespace Nz }; protected: + virtual void Release() = 0; + ShaderBinding(const ShaderBinding&) = delete; ShaderBinding(ShaderBinding&&) = default; }; + + class ShaderBindingDeleter + { + public: + inline void operator()(ShaderBinding* binding); + }; } #include diff --git a/include/Nazara/Renderer/ShaderBinding.inl b/include/Nazara/Renderer/ShaderBinding.inl index dccd89939..6d1f7f3d0 100644 --- a/include/Nazara/Renderer/ShaderBinding.inl +++ b/include/Nazara/Renderer/ShaderBinding.inl @@ -7,6 +7,10 @@ namespace Nz { + inline void ShaderBindingDeleter::operator()(ShaderBinding* binding) + { + binding->Release(); + } } #include diff --git a/include/Nazara/VulkanRenderer/VulkanRenderPipelineLayout.hpp b/include/Nazara/VulkanRenderer/VulkanRenderPipelineLayout.hpp index 1dc17f883..17e1a8800 100644 --- a/include/Nazara/VulkanRenderer/VulkanRenderPipelineLayout.hpp +++ b/include/Nazara/VulkanRenderer/VulkanRenderPipelineLayout.hpp @@ -8,6 +8,7 @@ #define NAZARA_VULKANRENDERER_VULKANRENDERPIPELINELAYOUT_HPP #include +#include #include #include #include @@ -16,17 +17,21 @@ #include #include #include +#include +#include #include namespace Nz { class NAZARA_VULKANRENDERER_API VulkanRenderPipelineLayout : public RenderPipelineLayout { + friend VulkanShaderBinding; + public: VulkanRenderPipelineLayout() = default; - ~VulkanRenderPipelineLayout() = default; + ~VulkanRenderPipelineLayout(); - VulkanShaderBinding& AllocateShaderBinding() override; + ShaderBindingPtr AllocateShaderBinding() override; bool Create(Vk::Device& device, RenderPipelineLayoutInfo layoutInfo); @@ -36,10 +41,20 @@ namespace Nz inline const Vk::PipelineLayout& GetPipelineLayout() const; private: + struct DescriptorPool; + + DescriptorPool& AllocatePool(); + ShaderBindingPtr AllocateFromPool(std::size_t poolIndex); + void Release(ShaderBinding& binding); + inline void TryToShrink(); + struct DescriptorPool { + using BindingStorage = std::aligned_storage_t; + + Bitset freeBindings; Vk::DescriptorPool descriptorPool; - std::vector allocatedSets; + std::unique_ptr storage; }; MovablePtr m_device; diff --git a/include/Nazara/VulkanRenderer/VulkanRenderPipelineLayout.inl b/include/Nazara/VulkanRenderer/VulkanRenderPipelineLayout.inl index e4a9f73c4..5dd664b5c 100644 --- a/include/Nazara/VulkanRenderer/VulkanRenderPipelineLayout.inl +++ b/include/Nazara/VulkanRenderer/VulkanRenderPipelineLayout.inl @@ -21,6 +21,21 @@ namespace Nz { return m_pipelineLayout; } + + inline void VulkanRenderPipelineLayout::TryToShrink() + { + std::size_t poolCount = m_descriptorPools.size(); + if (poolCount >= 2 && m_descriptorPools.back().freeBindings.TestAll()) + { + for (std::size_t i = poolCount - 1; i > 0; ++i) + { + if (m_descriptorPools[i].freeBindings.TestAll()) + poolCount--; + } + + m_descriptorPools.resize(poolCount); + } + } } #include diff --git a/include/Nazara/VulkanRenderer/VulkanShaderBinding.hpp b/include/Nazara/VulkanRenderer/VulkanShaderBinding.hpp index 8fa00eccf..78051b624 100644 --- a/include/Nazara/VulkanRenderer/VulkanShaderBinding.hpp +++ b/include/Nazara/VulkanRenderer/VulkanShaderBinding.hpp @@ -18,12 +18,14 @@ namespace Nz class NAZARA_VULKANRENDERER_API VulkanShaderBinding : public ShaderBinding { public: - inline VulkanShaderBinding(VulkanRenderPipelineLayout& owner, Vk::DescriptorSet descriptorSet); + inline VulkanShaderBinding(VulkanRenderPipelineLayout& owner, std::size_t poolIndex, std::size_t bindingIndex, Vk::DescriptorSet descriptorSet); VulkanShaderBinding(const VulkanShaderBinding&) = default; VulkanShaderBinding(VulkanShaderBinding&&) noexcept = default; ~VulkanShaderBinding() = default; + inline std::size_t GetBindingIndex() const; inline Vk::DescriptorSet& GetDescriptorSet(); + inline std::size_t GetPoolIndex() const; inline VulkanRenderPipelineLayout& GetOwner(); void Update(std::initializer_list bindings) override; @@ -32,8 +34,12 @@ namespace Nz VulkanShaderBinding& operator=(VulkanShaderBinding&&) = delete; private: + void Release() override; + Vk::AutoDescriptorSet m_descriptorSet; VulkanRenderPipelineLayout& m_owner; + std::size_t m_bindingIndex; + std::size_t m_poolIndex; }; } diff --git a/include/Nazara/VulkanRenderer/VulkanShaderBinding.inl b/include/Nazara/VulkanRenderer/VulkanShaderBinding.inl index f82cd6303..c3b447fcf 100644 --- a/include/Nazara/VulkanRenderer/VulkanShaderBinding.inl +++ b/include/Nazara/VulkanRenderer/VulkanShaderBinding.inl @@ -7,12 +7,24 @@ namespace Nz { - inline VulkanShaderBinding::VulkanShaderBinding(VulkanRenderPipelineLayout& owner, Vk::DescriptorSet descriptorSet) : + inline VulkanShaderBinding::VulkanShaderBinding(VulkanRenderPipelineLayout& owner, std::size_t poolIndex, std::size_t bindingIndex, Vk::DescriptorSet descriptorSet) : m_descriptorSet(std::move(descriptorSet)), - m_owner(owner) + m_owner(owner), + m_bindingIndex(bindingIndex), + m_poolIndex(poolIndex) { } + inline std::size_t VulkanShaderBinding::GetBindingIndex() const + { + return m_bindingIndex; + } + + inline std::size_t VulkanShaderBinding::GetPoolIndex() const + { + return m_poolIndex; + } + inline Vk::DescriptorSet& VulkanShaderBinding::GetDescriptorSet() { return m_descriptorSet; diff --git a/src/Nazara/VulkanRenderer/VulkanRenderPipelineLayout.cpp b/src/Nazara/VulkanRenderer/VulkanRenderPipelineLayout.cpp index 9257f6d72..ad76aa5b9 100644 --- a/src/Nazara/VulkanRenderer/VulkanRenderPipelineLayout.cpp +++ b/src/Nazara/VulkanRenderer/VulkanRenderPipelineLayout.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -12,43 +13,35 @@ namespace Nz { - VulkanShaderBinding& VulkanRenderPipelineLayout::AllocateShaderBinding() + VulkanRenderPipelineLayout::~VulkanRenderPipelineLayout() { - // TODO: Watch descriptor set count for each pool for (auto& pool : m_descriptorPools) { - if (pool.allocatedSets.capacity() <= pool.allocatedSets.size()) + if (!pool.freeBindings.TestAll()) + NazaraWarning("Not all ShaderBinding have been released!"); + } + } + + ShaderBindingPtr VulkanRenderPipelineLayout::AllocateShaderBinding() + { + for (std::size_t i = 0; i < m_descriptorPools.size(); ++i) + { + ShaderBindingPtr bindingPtr = AllocateFromPool(i); + if (!bindingPtr) continue; - Vk::DescriptorSet descriptorSet = pool.descriptorPool.AllocateDescriptorSet(m_descriptorSetLayout); - if (descriptorSet) - return pool.allocatedSets.emplace_back(*this, std::move(descriptorSet)); + return bindingPtr; } - // Allocate a new descriptor pool - StackVector poolSizes = NazaraStackVector(VkDescriptorPoolSize, m_layoutInfo.bindings.size()); + // No allocation could be made, time to allocate a new pool + std::size_t newPoolIndex = m_descriptorPools.size(); + AllocatePool(); - constexpr UInt32 MaxSet = 100; + ShaderBindingPtr bindingPtr = AllocateFromPool(newPoolIndex); + if (!bindingPtr) + throw std::runtime_error("Failed to allocate shader binding"); - for (const auto& bindingInfo : m_layoutInfo.bindings) - { - VkDescriptorPoolSize& poolSize = poolSizes.emplace_back(); - poolSize.descriptorCount = MaxSet; - poolSize.type = ToVulkan(bindingInfo.type); - } - - DescriptorPool pool; - if (!pool.descriptorPool.Create(*m_device, MaxSet, UInt32(poolSizes.size()), poolSizes.data(), VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT)) - throw std::runtime_error("Failed to allocate new descriptor pool: " + TranslateVulkanError(pool.descriptorPool.GetLastErrorCode())); - - pool.allocatedSets.reserve(MaxSet); - - auto& poolData = m_descriptorPools.emplace_back(std::move(pool)); - Vk::DescriptorSet descriptorSet = poolData.descriptorPool.AllocateDescriptorSet(m_descriptorSetLayout); - if (!descriptorSet) - throw std::runtime_error("Failed to allocate descriptor set: " + TranslateVulkanError(pool.descriptorPool.GetLastErrorCode())); - - return poolData.allocatedSets.emplace_back(*this, std::move(descriptorSet)); + return bindingPtr; } bool VulkanRenderPipelineLayout::Create(Vk::Device& device, RenderPipelineLayoutInfo layoutInfo) @@ -75,4 +68,69 @@ namespace Nz return true; } + + auto VulkanRenderPipelineLayout::AllocatePool() -> DescriptorPool& + { + StackVector poolSizes = NazaraStackVector(VkDescriptorPoolSize, m_layoutInfo.bindings.size()); + + constexpr UInt32 MaxSet = 128; + + for (const auto& bindingInfo : m_layoutInfo.bindings) + { + VkDescriptorPoolSize& poolSize = poolSizes.emplace_back(); + poolSize.descriptorCount = MaxSet; + poolSize.type = ToVulkan(bindingInfo.type); + } + + DescriptorPool pool; + if (!pool.descriptorPool.Create(*m_device, MaxSet, UInt32(poolSizes.size()), poolSizes.data(), VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT)) + throw std::runtime_error("Failed to allocate new descriptor pool: " + TranslateVulkanError(pool.descriptorPool.GetLastErrorCode())); + + pool.freeBindings.Resize(MaxSet, true); + pool.storage = std::make_unique(MaxSet); + + return m_descriptorPools.emplace_back(std::move(pool)); + } + + ShaderBindingPtr VulkanRenderPipelineLayout::AllocateFromPool(std::size_t poolIndex) + { + auto& pool = m_descriptorPools[poolIndex]; + + std::size_t freeBindingId = pool.freeBindings.FindFirst(); + if (freeBindingId == pool.freeBindings.npos) + return {}; //< No free binding in this pool + + Vk::DescriptorSet descriptorSet = pool.descriptorPool.AllocateDescriptorSet(m_descriptorSetLayout); + if (!descriptorSet) + { + NazaraWarning("Failed to allocate descriptor set: " + TranslateVulkanError(pool.descriptorPool.GetLastErrorCode())); + return {}; + } + + pool.freeBindings.Reset(freeBindingId); + + VulkanShaderBinding* freeBindingMemory = reinterpret_cast(&pool.storage[freeBindingId]); + return ShaderBindingPtr(PlacementNew(freeBindingMemory, *this, poolIndex, freeBindingId, std::move(descriptorSet))); + } + + void VulkanRenderPipelineLayout::Release(ShaderBinding& binding) + { + VulkanShaderBinding& vulkanBinding = static_cast(binding); + + std::size_t poolIndex = vulkanBinding.GetPoolIndex(); + std::size_t bindingIndex = vulkanBinding.GetBindingIndex(); + + assert(poolIndex < m_descriptorPools.size()); + auto& pool = m_descriptorPools[poolIndex]; + assert(!pool.freeBindings.Test(bindingIndex)); + + VulkanShaderBinding* bindingMemory = reinterpret_cast(&pool.storage[bindingIndex]); + PlacementDestroy(bindingMemory); + + pool.freeBindings.Set(bindingIndex); + + // Try to free pool if it's one of the last one + if (poolIndex >= m_descriptorPools.size() - 1 && poolIndex <= m_descriptorPools.size()) + TryToShrink(); + } } diff --git a/src/Nazara/VulkanRenderer/VulkanShaderBinding.cpp b/src/Nazara/VulkanRenderer/VulkanShaderBinding.cpp index 4f1d4754b..58f9731ac 100644 --- a/src/Nazara/VulkanRenderer/VulkanShaderBinding.cpp +++ b/src/Nazara/VulkanRenderer/VulkanShaderBinding.cpp @@ -67,4 +67,9 @@ namespace Nz m_owner.GetDevice()->vkUpdateDescriptorSets(*m_owner.GetDevice(), UInt32(writeOps.size()), writeOps.data(), 0U, nullptr); } + + void VulkanShaderBinding::Release() + { + m_owner.Release(*this); + } }