VulkanRenderer: Improve transient command buffer usage

Allocate and reuse them between frames, by resetting the command pool
This commit is contained in:
SirLynix 2023-07-21 09:00:34 +02:00
parent ce2693114c
commit fbf4da3c4f
5 changed files with 25 additions and 32 deletions

View File

@ -14,7 +14,6 @@
#include <Nazara/VulkanRenderer/Wrapper/CommandPool.hpp> #include <Nazara/VulkanRenderer/Wrapper/CommandPool.hpp>
#include <Nazara/VulkanRenderer/Wrapper/Fence.hpp> #include <Nazara/VulkanRenderer/Wrapper/Fence.hpp>
#include <Nazara/VulkanRenderer/Wrapper/Semaphore.hpp> #include <Nazara/VulkanRenderer/Wrapper/Semaphore.hpp>
#include <vector>
namespace Nz namespace Nz
{ {
@ -26,7 +25,7 @@ namespace Nz
VulkanRenderImage(VulkanSwapchain& owner); VulkanRenderImage(VulkanSwapchain& owner);
VulkanRenderImage(const VulkanRenderImage&) = delete; VulkanRenderImage(const VulkanRenderImage&) = delete;
VulkanRenderImage(VulkanRenderImage&&) = delete; VulkanRenderImage(VulkanRenderImage&&) = delete;
~VulkanRenderImage(); ~VulkanRenderImage() = default;
void Execute(const FunctionRef<void(CommandBufferBuilder& builder)>& callback, QueueTypeFlags queueTypeFlags) override; void Execute(const FunctionRef<void(CommandBufferBuilder& builder)>& callback, QueueTypeFlags queueTypeFlags) override;
@ -47,9 +46,9 @@ namespace Nz
VulkanRenderImage& operator=(VulkanRenderImage&&) = delete; VulkanRenderImage& operator=(VulkanRenderImage&&) = delete;
private: private:
std::size_t m_currentCommandBuffer; std::size_t m_freeCommandBufferIndex;
std::vector<Vk::AutoCommandBuffer> m_inFlightCommandBuffers; std::vector<VkCommandBuffer> m_allocatedCommandBuffers;;
std::vector<VkCommandBuffer> m_graphicalCommandsBuffers; std::vector<VkCommandBuffer> m_graphicalCommandBuffers;
VulkanSwapchain& m_owner; VulkanSwapchain& m_owner;
Vk::CommandPool m_commandPool; Vk::CommandPool m_commandPool;
Vk::Fence m_inFlightFence; Vk::Fence m_inFlightFence;

View File

@ -30,9 +30,10 @@ namespace Nz
{ {
FlushReleaseQueue(); FlushReleaseQueue();
m_graphicalCommandsBuffers.clear(); m_graphicalCommandBuffers.clear();
m_currentCommandBuffer = 0; m_freeCommandBufferIndex = 0;
m_imageIndex = imageIndex; m_imageIndex = imageIndex;
m_commandPool.Reset();
m_uploadPool.Reset(); m_uploadPool.Reset();
} }
} }

View File

@ -22,6 +22,7 @@ namespace Nz::Vk
public: public:
inline CommandBuffer(); inline CommandBuffer();
inline CommandBuffer(CommandPool& pool, VkCommandBuffer handle);
CommandBuffer(const CommandBuffer&) = delete; CommandBuffer(const CommandBuffer&) = delete;
inline CommandBuffer(CommandBuffer&& commandBuffer) noexcept; inline CommandBuffer(CommandBuffer&& commandBuffer) noexcept;
~CommandBuffer() = default; ~CommandBuffer() = default;
@ -111,8 +112,6 @@ namespace Nz::Vk
inline operator VkCommandBuffer() const; inline operator VkCommandBuffer() const;
private: private:
inline CommandBuffer(CommandPool& pool, VkCommandBuffer handle);
CommandPool* m_pool; CommandPool* m_pool;
VkCommandBuffer m_handle; VkCommandBuffer m_handle;
VkResult m_lastErrorCode; VkResult m_lastErrorCode;

View File

@ -32,7 +32,7 @@ namespace Nz
using DeviceObject::Create; using DeviceObject::Create;
inline bool Create(Device& device, UInt32 queueFamilyIndex, VkCommandPoolCreateFlags flags = 0, const VkAllocationCallbacks* allocator = nullptr); inline bool Create(Device& device, UInt32 queueFamilyIndex, VkCommandPoolCreateFlags flags = 0, const VkAllocationCallbacks* allocator = nullptr);
inline bool Reset(VkCommandPoolResetFlags flags); inline bool Reset(VkCommandPoolResetFlags flags = 0);
CommandPool& operator=(const CommandPool&) = delete; CommandPool& operator=(const CommandPool&) = delete;
CommandPool& operator=(CommandPool&&) = delete; CommandPool& operator=(CommandPool&&) = delete;

View File

@ -6,56 +6,50 @@
#include <Nazara/VulkanRenderer/VulkanCommandBuffer.hpp> #include <Nazara/VulkanRenderer/VulkanCommandBuffer.hpp>
#include <Nazara/VulkanRenderer/VulkanCommandBufferBuilder.hpp> #include <Nazara/VulkanRenderer/VulkanCommandBufferBuilder.hpp>
#include <Nazara/VulkanRenderer/VulkanSwapchain.hpp> #include <Nazara/VulkanRenderer/VulkanSwapchain.hpp>
#include <cassert>
#include <stdexcept> #include <stdexcept>
#include <Nazara/VulkanRenderer/Debug.hpp> #include <Nazara/VulkanRenderer/Debug.hpp>
namespace Nz namespace Nz
{ {
VulkanRenderImage::VulkanRenderImage(VulkanSwapchain& owner) : VulkanRenderImage::VulkanRenderImage(VulkanSwapchain& owner) :
m_freeCommandBufferIndex(0),
m_owner(owner), m_owner(owner),
m_uploadPool(m_owner.GetDevice(), 2 * 1024 * 1024) m_uploadPool(m_owner.GetDevice(), 2 * 1024 * 1024)
{ {
Vk::QueueHandle& graphicsQueue = m_owner.GetGraphicsQueue(); Vk::QueueHandle& graphicsQueue = m_owner.GetGraphicsQueue();
if (!m_commandPool.Create(m_owner.GetDevice(), graphicsQueue.GetQueueFamilyIndex(), VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT)) if (!m_commandPool.Create(m_owner.GetDevice(), graphicsQueue.GetQueueFamilyIndex(), VK_COMMAND_POOL_CREATE_TRANSIENT_BIT))
throw std::runtime_error("failed to create command pool: " + TranslateVulkanError(m_commandPool.GetLastErrorCode())); throw std::runtime_error("failed to create command pool: " + TranslateVulkanError(m_commandPool.GetLastErrorCode()));
if (!m_imageAvailableSemaphore.Create(m_owner.GetDevice())) if (!m_imageAvailableSemaphore.Create(m_owner.GetDevice()))
throw std::runtime_error("failed to create image available semaphore: " + TranslateVulkanError(m_imageAvailableSemaphore.GetLastErrorCode())); throw std::runtime_error("failed to create image available semaphore: " + TranslateVulkanError(m_imageAvailableSemaphore.GetLastErrorCode()));
if (!m_renderFinishedSemaphore.Create(m_owner.GetDevice())) if (!m_renderFinishedSemaphore.Create(m_owner.GetDevice()))
throw std::runtime_error("failed to create image finished semaphore: " + TranslateVulkanError(m_imageAvailableSemaphore.GetLastErrorCode())); throw std::runtime_error("failed to create image finished semaphore: " + TranslateVulkanError(m_renderFinishedSemaphore.GetLastErrorCode()));
if (!m_inFlightFence.Create(m_owner.GetDevice(), VK_FENCE_CREATE_SIGNALED_BIT)) if (!m_inFlightFence.Create(m_owner.GetDevice(), VK_FENCE_CREATE_SIGNALED_BIT))
throw std::runtime_error("failed to create in-flight fence: " + TranslateVulkanError(m_inFlightFence.GetLastErrorCode())); throw std::runtime_error("failed to create in-flight fence: " + TranslateVulkanError(m_inFlightFence.GetLastErrorCode()));
} }
VulkanRenderImage::~VulkanRenderImage()
{
m_inFlightCommandBuffers.clear();
}
void VulkanRenderImage::Execute(const FunctionRef<void(CommandBufferBuilder& builder)>& callback, QueueTypeFlags queueTypeFlags) void VulkanRenderImage::Execute(const FunctionRef<void(CommandBufferBuilder& builder)>& callback, QueueTypeFlags queueTypeFlags)
{ {
Vk::CommandBuffer* commandBuffer; if (m_freeCommandBufferIndex >= m_allocatedCommandBuffers.size())
if (m_currentCommandBuffer >= m_inFlightCommandBuffers.size())
{ {
Vk::AutoCommandBuffer& newlyAllocatedBuffer = m_inFlightCommandBuffers.emplace_back(m_commandPool.AllocateCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY)); assert(m_freeCommandBufferIndex == m_allocatedCommandBuffers.size());
commandBuffer = &newlyAllocatedBuffer.Get(); m_allocatedCommandBuffers.push_back(m_commandPool.AllocateCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY));
m_currentCommandBuffer++;
} }
else
commandBuffer = &m_inFlightCommandBuffers[m_currentCommandBuffer++].Get();
if (!commandBuffer->Begin(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT)) Vk::CommandBuffer commandBuffer(m_commandPool, m_allocatedCommandBuffers[m_freeCommandBufferIndex++]);
throw std::runtime_error("failed to begin command buffer: " + TranslateVulkanError(commandBuffer->GetLastErrorCode())); if (!commandBuffer.Begin(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT))
throw std::runtime_error("failed to begin command buffer: " + TranslateVulkanError(commandBuffer.GetLastErrorCode()));
VulkanCommandBufferBuilder builder(*commandBuffer); VulkanCommandBufferBuilder builder(commandBuffer);
callback(builder); callback(builder);
if (!commandBuffer->End()) if (!commandBuffer.End())
throw std::runtime_error("failed to build command buffer: " + TranslateVulkanError(commandBuffer->GetLastErrorCode())); throw std::runtime_error("failed to build command buffer: " + TranslateVulkanError(commandBuffer.GetLastErrorCode()));
SubmitCommandBuffer(*commandBuffer, queueTypeFlags); SubmitCommandBuffer(commandBuffer, queueTypeFlags);
} }
VulkanUploadPool& VulkanRenderImage::GetUploadPool() VulkanUploadPool& VulkanRenderImage::GetUploadPool()
@ -66,7 +60,7 @@ namespace Nz
void VulkanRenderImage::Present() void VulkanRenderImage::Present()
{ {
Vk::QueueHandle& graphicsQueue = m_owner.GetGraphicsQueue(); Vk::QueueHandle& graphicsQueue = m_owner.GetGraphicsQueue();
if (!graphicsQueue.Submit(UInt32(m_graphicalCommandsBuffers.size()), m_graphicalCommandsBuffers.data(), m_imageAvailableSemaphore, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, m_renderFinishedSemaphore, m_inFlightFence)) if (!graphicsQueue.Submit(UInt32(m_graphicalCommandBuffers.size()), m_graphicalCommandBuffers.data(), m_imageAvailableSemaphore, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, m_renderFinishedSemaphore, m_inFlightFence))
throw std::runtime_error("Failed to submit command buffers: " + TranslateVulkanError(graphicsQueue.GetLastErrorCode())); throw std::runtime_error("Failed to submit command buffers: " + TranslateVulkanError(graphicsQueue.GetLastErrorCode()));
m_owner.Present(m_imageIndex, m_renderFinishedSemaphore); m_owner.Present(m_imageIndex, m_renderFinishedSemaphore);
@ -82,7 +76,7 @@ namespace Nz
void VulkanRenderImage::SubmitCommandBuffer(VkCommandBuffer commandBuffer, QueueTypeFlags queueTypeFlags) void VulkanRenderImage::SubmitCommandBuffer(VkCommandBuffer commandBuffer, QueueTypeFlags queueTypeFlags)
{ {
if (queueTypeFlags & QueueType::Graphics) if (queueTypeFlags & QueueType::Graphics)
m_graphicalCommandsBuffers.push_back(commandBuffer); m_graphicalCommandBuffers.push_back(commandBuffer);
else else
{ {
Vk::QueueHandle& graphicsQueue = m_owner.GetGraphicsQueue(); Vk::QueueHandle& graphicsQueue = m_owner.GetGraphicsQueue();