From 7c9dcdfbe490bebc72bb9867f3ea91579edbbd16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Leclercq?= Date: Thu, 27 Aug 2020 18:31:26 +0200 Subject: [PATCH] Allocate command buffers from pools --- .../OpenGLRenderer/OpenGLCommandBuffer.hpp | 12 +++- .../OpenGLRenderer/OpenGLCommandBuffer.inl | 29 ++++++++ .../OpenGLRenderer/OpenGLCommandPool.hpp | 24 ++++++- .../OpenGLRenderer/OpenGLCommandPool.inl | 16 +++++ include/Nazara/OpenGLRenderer/Utils.inl | 1 + include/Nazara/Renderer/CommandBuffer.hpp | 21 +++++- include/Nazara/Renderer/CommandBuffer.inl | 4 ++ include/Nazara/Renderer/CommandPool.hpp | 5 +- include/Nazara/Renderer/ShaderBinding.hpp | 8 ++- .../VulkanRenderer/VulkanCommandBuffer.hpp | 16 ++++- .../VulkanRenderer/VulkanCommandBuffer.inl | 30 +++++++- .../VulkanRenderer/VulkanCommandPool.hpp | 24 ++++++- .../VulkanRenderer/VulkanCommandPool.inl | 32 +++++++++ .../OpenGLRenderer/OpenGLCommandBuffer.cpp | 7 ++ .../OpenGLRenderer/OpenGLCommandPool.cpp | 69 ++++++++++++++++++- .../OpenGLRenderer/OpenGLRenderImage.cpp | 2 +- .../VulkanRenderer/VulkanCommandBuffer.cpp | 5 ++ .../VulkanRenderer/VulkanCommandPool.cpp | 51 +++++++++++++- 18 files changed, 333 insertions(+), 23 deletions(-) diff --git a/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.hpp b/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.hpp index fc80a9ada..32f216de5 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.hpp @@ -22,12 +22,14 @@ namespace Nz { + class OpenGLCommandPool; class OpenGLFramebuffer; class NAZARA_OPENGLRENDERER_API OpenGLCommandBuffer final : public CommandBuffer { public: - OpenGLCommandBuffer() = default; + inline OpenGLCommandBuffer(); + inline OpenGLCommandBuffer(OpenGLCommandPool& owner, std::size_t poolIndex, std::size_t bindingIndex); OpenGLCommandBuffer(const OpenGLCommandBuffer&) = delete; OpenGLCommandBuffer(OpenGLCommandBuffer&&) noexcept = default; ~OpenGLCommandBuffer() = default; @@ -49,6 +51,10 @@ namespace Nz void Execute(); + inline std::size_t GetBindingIndex() const; + inline std::size_t GetPoolIndex() const; + inline const OpenGLCommandPool& GetOwner() const; + inline void SetFramebuffer(const OpenGLFramebuffer& framebuffer, const RenderPass& renderPass, std::initializer_list clearValues); inline void SetScissor(Nz::Recti scissorRegion); inline void SetViewport(Nz::Recti viewportRegion); @@ -60,6 +66,7 @@ namespace Nz struct DrawStates; void ApplyStates(const GL::Context& context, const DrawStates& states); + void Release(); struct BeginDebugRegionData { @@ -139,7 +146,10 @@ namespace Nz >; DrawStates m_currentStates; + std::size_t m_bindingIndex; + std::size_t m_poolIndex; std::vector m_commands; + OpenGLCommandPool* m_owner; }; } diff --git a/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.inl b/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.inl index 892550d8e..bec54f598 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.inl +++ b/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.inl @@ -4,11 +4,24 @@ #include #include +#include #include #include namespace Nz { + inline OpenGLCommandBuffer::OpenGLCommandBuffer() : + m_owner(nullptr) + { + } + + inline OpenGLCommandBuffer::OpenGLCommandBuffer(OpenGLCommandPool& owner, std::size_t poolIndex, std::size_t bindingIndex) : + m_bindingIndex(bindingIndex), + m_poolIndex(poolIndex), + m_owner(&owner) + { + } + inline void OpenGLCommandBuffer::BeginDebugRegion(const std::string_view& regionName, const Nz::Color& color) { BeginDebugRegionData beginDebugRegion; @@ -104,6 +117,22 @@ namespace Nz m_commands.emplace_back(EndDebugRegionData{}); } + inline std::size_t Nz::OpenGLCommandBuffer::GetBindingIndex() const + { + return m_bindingIndex; + } + + inline std::size_t Nz::OpenGLCommandBuffer::GetPoolIndex() const + { + return m_poolIndex; + } + + inline const OpenGLCommandPool& OpenGLCommandBuffer::GetOwner() const + { + assert(m_owner); + return *m_owner; + } + inline void OpenGLCommandBuffer::SetFramebuffer(const OpenGLFramebuffer& framebuffer, const RenderPass& /*renderPass*/, std::initializer_list clearValues) { SetFrameBufferData setFramebuffer; diff --git a/include/Nazara/OpenGLRenderer/OpenGLCommandPool.hpp b/include/Nazara/OpenGLRenderer/OpenGLCommandPool.hpp index bc79593b9..81a3871af 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLCommandPool.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLCommandPool.hpp @@ -8,23 +8,45 @@ #define NAZARA_OPENGLRENDERER_OPENGLCOMMANDPOOL_HPP #include +#include #include #include +#include namespace Nz { class NAZARA_OPENGLRENDERER_API OpenGLCommandPool final : public CommandPool { + friend OpenGLCommandBuffer; + public: OpenGLCommandPool() = default; OpenGLCommandPool(const OpenGLCommandPool&) = delete; OpenGLCommandPool(OpenGLCommandPool&&) noexcept = default; ~OpenGLCommandPool() = default; - std::unique_ptr BuildCommandBuffer(const std::function& callback) override; + CommandBufferPtr BuildCommandBuffer(const std::function& callback) override; OpenGLCommandPool& operator=(const OpenGLCommandPool&) = delete; OpenGLCommandPool& operator=(OpenGLCommandPool&&) = delete; + + private: + struct CommandPool; + + CommandPool& AllocatePool(); + CommandBufferPtr AllocateFromPool(std::size_t poolIndex); + void Release(CommandBuffer& commandBuffer); + inline void TryToShrink(); + + struct CommandPool + { + using BindingStorage = std::aligned_storage_t; + + Bitset freeCommands; + std::unique_ptr storage; + }; + + std::vector m_commandPools; }; } diff --git a/include/Nazara/OpenGLRenderer/OpenGLCommandPool.inl b/include/Nazara/OpenGLRenderer/OpenGLCommandPool.inl index 3da51fb3b..d80cd3a22 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLCommandPool.inl +++ b/include/Nazara/OpenGLRenderer/OpenGLCommandPool.inl @@ -8,6 +8,22 @@ namespace Nz { + inline void OpenGLCommandPool::TryToShrink() + { + std::size_t poolCount = m_commandPools.size(); + if (poolCount >= 2 && m_commandPools.back().freeCommands.TestAll()) + { + for (std::size_t i = poolCount - 1; i > 0; --i) + { + if (!m_commandPools[i].freeCommands.TestAll()) + break; + + poolCount--; + } + + m_commandPools.resize(poolCount); + } + } } #include diff --git a/include/Nazara/OpenGLRenderer/Utils.inl b/include/Nazara/OpenGLRenderer/Utils.inl index 621704a82..61f446590 100644 --- a/include/Nazara/OpenGLRenderer/Utils.inl +++ b/include/Nazara/OpenGLRenderer/Utils.inl @@ -17,6 +17,7 @@ namespace Nz case PixelFormat_A8: return GLTextureFormat { GL_R8, GL_RED, GL_UNSIGNED_BYTE, GL_ZERO, GL_ZERO, GL_ZERO, GL_RED }; case PixelFormat_RGB8: return GLTextureFormat { GL_SRGB8, GL_RGB, GL_UNSIGNED_BYTE, GL_RED, GL_GREEN, GL_BLUE, GL_ZERO }; case PixelFormat_RGBA8: return GLTextureFormat { GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA }; + default: break; } NazaraError("Unhandled PixelFormat 0x" + String::Number(UnderlyingCast(pixelFormat), 16)); diff --git a/include/Nazara/Renderer/CommandBuffer.hpp b/include/Nazara/Renderer/CommandBuffer.hpp index 547d0a3f2..6f28a3352 100644 --- a/include/Nazara/Renderer/CommandBuffer.hpp +++ b/include/Nazara/Renderer/CommandBuffer.hpp @@ -9,19 +9,36 @@ #include #include +#include namespace Nz { + class CommandBuffer; + class CommandBufferDeleter; + + using CommandBufferPtr = std::unique_ptr; + class NAZARA_RENDERER_API CommandBuffer { + friend CommandBufferDeleter; + public: CommandBuffer() = default; CommandBuffer(const CommandBuffer&) = delete; - CommandBuffer(CommandBuffer&&) = default; + CommandBuffer(CommandBuffer&&) = delete; virtual ~CommandBuffer(); CommandBuffer& operator=(const CommandBuffer&) = delete; - CommandBuffer& operator=(CommandBuffer&&) = default; + CommandBuffer& operator=(CommandBuffer&&) = delete; + + protected: + virtual void Release() = 0; + }; + + class CommandBufferDeleter + { + public: + inline void operator()(CommandBuffer* commandBuffer); }; } diff --git a/include/Nazara/Renderer/CommandBuffer.inl b/include/Nazara/Renderer/CommandBuffer.inl index be4849bd3..939582513 100644 --- a/include/Nazara/Renderer/CommandBuffer.inl +++ b/include/Nazara/Renderer/CommandBuffer.inl @@ -7,6 +7,10 @@ namespace Nz { + inline void CommandBufferDeleter::operator()(CommandBuffer* commandBuffer) + { + commandBuffer->Release(); + } } #include diff --git a/include/Nazara/Renderer/CommandPool.hpp b/include/Nazara/Renderer/CommandPool.hpp index ec52d83d2..780f43b1c 100644 --- a/include/Nazara/Renderer/CommandPool.hpp +++ b/include/Nazara/Renderer/CommandPool.hpp @@ -9,12 +9,11 @@ #include #include +#include #include -#include //< temporary namespace Nz { - class CommandBuffer; class CommandBufferBuilder; class NAZARA_RENDERER_API CommandPool @@ -25,7 +24,7 @@ namespace Nz CommandPool(CommandPool&&) = default; virtual ~CommandPool(); - virtual std::unique_ptr BuildCommandBuffer(const std::function& callback) = 0; + virtual CommandBufferPtr BuildCommandBuffer(const std::function& callback) = 0; CommandPool& operator=(const CommandPool&) = delete; CommandPool& operator=(CommandPool&&) = default; diff --git a/include/Nazara/Renderer/ShaderBinding.hpp b/include/Nazara/Renderer/ShaderBinding.hpp index 395afff5a..4105974bc 100644 --- a/include/Nazara/Renderer/ShaderBinding.hpp +++ b/include/Nazara/Renderer/ShaderBinding.hpp @@ -30,10 +30,15 @@ namespace Nz struct Binding; ShaderBinding() = default; + ShaderBinding(const ShaderBinding&) = delete; + ShaderBinding(ShaderBinding&&) = delete; virtual ~ShaderBinding(); virtual void Update(std::initializer_list bindings) = 0; + ShaderBinding& operator=(const ShaderBinding&) = delete; + ShaderBinding& operator=(ShaderBinding&&) = delete; + struct TextureBinding { Texture* texture; @@ -55,9 +60,6 @@ namespace Nz protected: virtual void Release() = 0; - - ShaderBinding(const ShaderBinding&) = delete; - ShaderBinding(ShaderBinding&&) = default; }; class ShaderBindingDeleter diff --git a/include/Nazara/VulkanRenderer/VulkanCommandBuffer.hpp b/include/Nazara/VulkanRenderer/VulkanCommandBuffer.hpp index 61607b865..04f5c560a 100644 --- a/include/Nazara/VulkanRenderer/VulkanCommandBuffer.hpp +++ b/include/Nazara/VulkanRenderer/VulkanCommandBuffer.hpp @@ -15,22 +15,34 @@ namespace Nz { + class VulkanCommandPool; + class NAZARA_VULKANRENDERER_API VulkanCommandBuffer final : public CommandBuffer { public: - inline VulkanCommandBuffer(Vk::AutoCommandBuffer commandBuffer); - inline VulkanCommandBuffer(std::vector commandBuffers); + inline VulkanCommandBuffer(VulkanCommandPool& owner, std::size_t poolIndex, std::size_t bindingIndex, Vk::AutoCommandBuffer commandBuffer); + inline VulkanCommandBuffer(VulkanCommandPool& owner, std::size_t poolIndex, std::size_t bindingIndex, std::vector commandBuffers); VulkanCommandBuffer(const VulkanCommandBuffer&) = delete; VulkanCommandBuffer(VulkanCommandBuffer&&) noexcept = default; ~VulkanCommandBuffer() = default; + inline std::size_t GetBindingIndex() const; inline Vk::CommandBuffer& GetCommandBuffer(std::size_t imageIndex = 0); + inline std::size_t GetPoolIndex() const; + inline const VulkanCommandPool& GetOwner() const; VulkanCommandBuffer& operator=(const VulkanCommandBuffer&) = delete; VulkanCommandBuffer& operator=(VulkanCommandBuffer&&) = delete; private: + inline VulkanCommandBuffer(VulkanCommandPool& owner, std::size_t poolIndex, std::size_t bindingIndex); + + void Release() override; + + std::size_t m_bindingIndex; + std::size_t m_poolIndex; std::vector m_commandBuffers; + VulkanCommandPool& m_owner; }; } diff --git a/include/Nazara/VulkanRenderer/VulkanCommandBuffer.inl b/include/Nazara/VulkanRenderer/VulkanCommandBuffer.inl index 6e0f8fdf9..27e99bce8 100644 --- a/include/Nazara/VulkanRenderer/VulkanCommandBuffer.inl +++ b/include/Nazara/VulkanRenderer/VulkanCommandBuffer.inl @@ -7,20 +7,44 @@ namespace Nz { - inline VulkanCommandBuffer::VulkanCommandBuffer(Vk::AutoCommandBuffer commandBuffer) + inline VulkanCommandBuffer::VulkanCommandBuffer(VulkanCommandPool& owner, std::size_t poolIndex, std::size_t bindingIndex, Vk::AutoCommandBuffer commandBuffer) : + VulkanCommandBuffer(owner, poolIndex, bindingIndex) { m_commandBuffers.push_back(std::move(commandBuffer)); } - inline VulkanCommandBuffer::VulkanCommandBuffer(std::vector commandBuffers) : - m_commandBuffers(std::move(commandBuffers)) + inline VulkanCommandBuffer::VulkanCommandBuffer(VulkanCommandPool& owner, std::size_t poolIndex, std::size_t bindingIndex, std::vector commandBuffers) : + VulkanCommandBuffer(owner, poolIndex, bindingIndex) { + m_commandBuffers = std::move(commandBuffers); + } + + inline VulkanCommandBuffer::VulkanCommandBuffer(VulkanCommandPool& owner, std::size_t poolIndex, std::size_t bindingIndex) : + m_bindingIndex(bindingIndex), + m_poolIndex(poolIndex), + m_owner(owner) + { + } + + inline std::size_t VulkanCommandBuffer::GetBindingIndex() const + { + return m_bindingIndex; } inline Vk::CommandBuffer& VulkanCommandBuffer::GetCommandBuffer(std::size_t imageIndex) { return m_commandBuffers[imageIndex].Get(); } + + inline std::size_t VulkanCommandBuffer::GetPoolIndex() const + { + return m_poolIndex; + } + + inline const VulkanCommandPool& VulkanCommandBuffer::GetOwner() const + { + return m_owner; + } } #include diff --git a/include/Nazara/VulkanRenderer/VulkanCommandPool.hpp b/include/Nazara/VulkanRenderer/VulkanCommandPool.hpp index 5fbdb6ab9..f45c7d1e0 100644 --- a/include/Nazara/VulkanRenderer/VulkanCommandPool.hpp +++ b/include/Nazara/VulkanRenderer/VulkanCommandPool.hpp @@ -8,14 +8,19 @@ #define NAZARA_VULKANRENDERER_VULKANCOMMANDPOOL_HPP #include +#include #include #include +#include #include +#include namespace Nz { class NAZARA_VULKANRENDERER_API VulkanCommandPool final : public CommandPool { + friend VulkanCommandBuffer; + public: inline VulkanCommandPool(Vk::Device& device, QueueType queueType); inline VulkanCommandPool(Vk::Device& device, UInt32 queueFamilyIndex); @@ -23,12 +28,29 @@ namespace Nz VulkanCommandPool(VulkanCommandPool&&) noexcept = default; ~VulkanCommandPool() = default; - std::unique_ptr BuildCommandBuffer(const std::function& callback) override; + CommandBufferPtr BuildCommandBuffer(const std::function& callback) override; VulkanCommandPool& operator=(const VulkanCommandPool&) = delete; VulkanCommandPool& operator=(VulkanCommandPool&&) = delete; private: + struct CommandPool; + + CommandPool& AllocatePool(); + template CommandBufferPtr AllocateFromPool(std::size_t poolIndex, Args&&... args); + void Release(CommandBuffer& commandBuffer); + inline void TryToShrink(); + + struct CommandPool + { + using BindingStorage = std::aligned_storage_t; + + Bitset freeCommands; + std::unique_ptr storage; + }; + + MovablePtr m_device; + std::vector m_commandPools; Vk::CommandPool m_commandPool; }; } diff --git a/include/Nazara/VulkanRenderer/VulkanCommandPool.inl b/include/Nazara/VulkanRenderer/VulkanCommandPool.inl index 77a6066a7..f6689834d 100644 --- a/include/Nazara/VulkanRenderer/VulkanCommandPool.inl +++ b/include/Nazara/VulkanRenderer/VulkanCommandPool.inl @@ -23,6 +23,38 @@ namespace Nz if (!m_commandPool.Create(device, queueFamilyIndex)) throw std::runtime_error("Failed to create command pool: " + TranslateVulkanError(m_commandPool.GetLastErrorCode())); } + + template + CommandBufferPtr VulkanCommandPool::AllocateFromPool(std::size_t poolIndex, Args&&... args) + { + auto& pool = m_commandPools[poolIndex]; + + std::size_t freeBindingId = pool.freeCommands.FindFirst(); + if (freeBindingId == pool.freeCommands.npos) + return {}; //< No free binding in this pool + + pool.freeCommands.Reset(freeBindingId); + + VulkanCommandBuffer* freeBindingMemory = reinterpret_cast(&pool.storage[freeBindingId]); + return CommandBufferPtr(PlacementNew(freeBindingMemory, *this, poolIndex, freeBindingId, std::forward(args)...)); + } + + inline void VulkanCommandPool::TryToShrink() + { + std::size_t poolCount = m_commandPools.size(); + if (poolCount >= 2 && m_commandPools.back().freeCommands.TestAll()) + { + for (std::size_t i = poolCount - 1; i > 0; --i) + { + if (!m_commandPools[i].freeCommands.TestAll()) + break; + + poolCount--; + } + + m_commandPools.resize(poolCount); + } + } } #include diff --git a/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp b/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp index eac73f9ca..1f9f06546 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp @@ -3,6 +3,7 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include #include #include @@ -139,4 +140,10 @@ namespace Nz const GL::VertexArray& vao = context.GetVaoCache().Get(vaoSetup); context.BindVertexArray(vao.GetObjectId(), true); } + + void OpenGLCommandBuffer::Release() + { + assert(m_owner); + m_owner->Release(*this); + } } diff --git a/src/Nazara/OpenGLRenderer/OpenGLCommandPool.cpp b/src/Nazara/OpenGLRenderer/OpenGLCommandPool.cpp index 90bfd3593..488a8e5f9 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLCommandPool.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLCommandPool.cpp @@ -3,19 +3,82 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include #include #include namespace Nz { - std::unique_ptr OpenGLCommandPool::BuildCommandBuffer(const std::function& callback) + CommandBufferPtr OpenGLCommandPool::BuildCommandBuffer(const std::function& callback) { - std::unique_ptr commandBuffer = std::make_unique(); + CommandBufferPtr commandBuffer; + for (std::size_t i = 0; i < m_commandPools.size(); ++i) + { + commandBuffer = AllocateFromPool(i); + if (commandBuffer) + break; + } - OpenGLCommandBufferBuilder builder(*commandBuffer); + if (!commandBuffer) + { + // No allocation could be made, time to allocate a new pool + std::size_t newPoolIndex = m_commandPools.size(); + AllocatePool(); + + commandBuffer = AllocateFromPool(newPoolIndex); + assert(commandBuffer); + } + + OpenGLCommandBufferBuilder builder(static_cast(*commandBuffer.get())); callback(builder); return commandBuffer; } + + auto OpenGLCommandPool::AllocatePool() -> CommandPool& + { + constexpr UInt32 MaxSet = 128; + + CommandPool pool; + pool.freeCommands.Resize(MaxSet, true); + pool.storage = std::make_unique(MaxSet); + + return m_commandPools.emplace_back(std::move(pool)); + } + + CommandBufferPtr OpenGLCommandPool::AllocateFromPool(std::size_t poolIndex) + { + auto& pool = m_commandPools[poolIndex]; + + std::size_t freeBindingId = pool.freeCommands.FindFirst(); + if (freeBindingId == pool.freeCommands.npos) + return {}; //< No free binding in this pool + + pool.freeCommands.Reset(freeBindingId); + + OpenGLCommandBuffer* freeBindingMemory = reinterpret_cast(&pool.storage[freeBindingId]); + return CommandBufferPtr(PlacementNew(freeBindingMemory, *this, poolIndex, freeBindingId)); + } + + void OpenGLCommandPool::Release(CommandBuffer& binding) + { + OpenGLCommandBuffer& openglBinding = static_cast(binding); + + std::size_t poolIndex = openglBinding.GetPoolIndex(); + std::size_t bindingIndex = openglBinding.GetBindingIndex(); + + assert(poolIndex < m_commandPools.size()); + auto& pool = m_commandPools[poolIndex]; + assert(!pool.freeCommands.Test(bindingIndex)); + + OpenGLCommandBuffer* bindingMemory = reinterpret_cast(&pool.storage[bindingIndex]); + PlacementDestroy(bindingMemory); + + pool.freeCommands.Set(bindingIndex); + + // Try to free pool if it's one of the last one + if (poolIndex >= m_commandPools.size() - 1 && poolIndex <= m_commandPools.size()) + TryToShrink(); + } } diff --git a/src/Nazara/OpenGLRenderer/OpenGLRenderImage.cpp b/src/Nazara/OpenGLRenderer/OpenGLRenderImage.cpp index 39c2be6dd..681b4cf46 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLRenderImage.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLRenderImage.cpp @@ -19,7 +19,7 @@ namespace Nz void OpenGLRenderImage::Execute(const std::function& callback, QueueTypeFlags /*queueTypeFlags*/) { - OpenGLCommandBuffer commandBuffer; + OpenGLCommandBuffer commandBuffer; //< TODO: Use a pool and remove default constructor OpenGLCommandBufferBuilder builder(commandBuffer); callback(builder); diff --git a/src/Nazara/VulkanRenderer/VulkanCommandBuffer.cpp b/src/Nazara/VulkanRenderer/VulkanCommandBuffer.cpp index 92f60a953..d0436e030 100644 --- a/src/Nazara/VulkanRenderer/VulkanCommandBuffer.cpp +++ b/src/Nazara/VulkanRenderer/VulkanCommandBuffer.cpp @@ -3,8 +3,13 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include namespace Nz { + void VulkanCommandBuffer::Release() + { + m_owner.Release(*this); + } } diff --git a/src/Nazara/VulkanRenderer/VulkanCommandPool.cpp b/src/Nazara/VulkanRenderer/VulkanCommandPool.cpp index fd514c83e..a654c45b6 100644 --- a/src/Nazara/VulkanRenderer/VulkanCommandPool.cpp +++ b/src/Nazara/VulkanRenderer/VulkanCommandPool.cpp @@ -3,6 +3,7 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include #include #include @@ -10,14 +11,14 @@ namespace Nz { - std::unique_ptr VulkanCommandPool::BuildCommandBuffer(const std::function& callback) + CommandBufferPtr VulkanCommandPool::BuildCommandBuffer(const std::function& callback) { std::vector commandBuffers; auto BuildCommandBuffer = [&](std::size_t imageIndex) { Vk::AutoCommandBuffer& commandBuffer = commandBuffers.emplace_back(m_commandPool.AllocateCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY)); - if (!commandBuffer->Begin(VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT)) + if (!commandBuffer->Begin()) throw std::runtime_error("failed to begin command buffer: " + TranslateVulkanError(commandBuffer->GetLastErrorCode())); VulkanCommandBufferBuilder builder(commandBuffer.Get(), imageIndex); @@ -33,6 +34,50 @@ namespace Nz for (std::size_t i = 1; i < maxFramebufferCount; ++i) BuildCommandBuffer(i); - return std::make_unique(std::move(commandBuffers)); + for (std::size_t i = 0; i < m_commandPools.size(); ++i) + { + if (m_commandPools[i].freeCommands.TestNone()) + continue; + + return AllocateFromPool(i, std::move(commandBuffers)); + } + + // No allocation could be made, time to allocate a new pool + std::size_t newPoolIndex = m_commandPools.size(); + AllocatePool(); + + return AllocateFromPool(newPoolIndex, std::move(commandBuffers)); + } + + auto VulkanCommandPool::AllocatePool() -> CommandPool& + { + constexpr UInt32 MaxSet = 128; + + CommandPool pool; + pool.freeCommands.Resize(MaxSet, true); + pool.storage = std::make_unique(MaxSet); + + return m_commandPools.emplace_back(std::move(pool)); + } + + void VulkanCommandPool::Release(CommandBuffer& binding) + { + VulkanCommandBuffer& vulkanBinding = static_cast(binding); + + std::size_t poolIndex = vulkanBinding.GetPoolIndex(); + std::size_t bindingIndex = vulkanBinding.GetBindingIndex(); + + assert(poolIndex < m_commandPools.size()); + auto& pool = m_commandPools[poolIndex]; + assert(!pool.freeCommands.Test(bindingIndex)); + + VulkanCommandBuffer* bindingMemory = reinterpret_cast(&pool.storage[bindingIndex]); + PlacementDestroy(bindingMemory); + + pool.freeCommands.Set(bindingIndex); + + // Try to free pool if it's one of the last one + if (poolIndex >= m_commandPools.size() - 1 && poolIndex <= m_commandPools.size()) + TryToShrink(); } }