Vulkan: Refactor command buffer and introduce command pool

This commit is contained in:
Lynix
2020-04-07 21:10:16 +02:00
parent f6d21d066e
commit 87f1209327
27 changed files with 313 additions and 85 deletions

View File

@@ -0,0 +1,11 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Renderer module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Renderer/CommandPool.hpp>
#include <Nazara/Renderer/Debug.hpp>
namespace Nz
{
CommandPool::~CommandPool() = default;
}

View File

@@ -7,8 +7,7 @@
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/Utility/PixelFormat.hpp>
#include <Nazara/VulkanRenderer/Vulkan.hpp>
#include <Nazara/VulkanRenderer/VulkanCommandBuffer.hpp>
#include <Nazara/VulkanRenderer/VulkanCommandBufferBuilder.hpp>
#include <Nazara/VulkanRenderer/VulkanCommandPool.hpp>
#include <Nazara/VulkanRenderer/VulkanDevice.hpp>
#include <Nazara/VulkanRenderer/VulkanSurface.hpp>
#include <array>
@@ -29,7 +28,6 @@ namespace Nz
m_device->WaitForIdle();
m_concurrentImageData.clear();
m_graphicsCommandPool.Destroy();
m_imageData.clear();
m_renderPass.Destroy();
m_swapchain.Destroy();
@@ -60,22 +58,6 @@ namespace Nz
return currentFrame;
}
std::unique_ptr<CommandBuffer> VkRenderWindow::BuildCommandBuffer(const std::function<void(CommandBufferBuilder& builder)>& callback)
{
Vk::AutoCommandBuffer commandBuffer = m_graphicsCommandPool.AllocateCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY);
if (!commandBuffer->Begin())
throw std::runtime_error("failed to begin command buffer: " + TranslateVulkanError(commandBuffer->GetLastErrorCode()));
VulkanCommandBufferBuilder builder(commandBuffer.Get());
callback(builder);
if (!commandBuffer->End())
throw std::runtime_error("failed to build command buffer: " + TranslateVulkanError(commandBuffer->GetLastErrorCode()));
return std::make_unique<VulkanCommandBuffer>(std::move(commandBuffer));
}
bool VkRenderWindow::Create(RendererImpl* /*renderer*/, RenderSurface* surface, const Vector2ui& size, const RenderWindowParameters& parameters)
{
const auto& deviceInfo = Vulkan::GetPhysicalDevices()[0];
@@ -84,7 +66,8 @@ namespace Nz
UInt32 graphicsFamilyQueueIndex;
UInt32 presentableFamilyQueueIndex;
m_device = Vulkan::SelectDevice(deviceInfo, vulkanSurface, &graphicsFamilyQueueIndex, &presentableFamilyQueueIndex);
UInt32 transferFamilyQueueIndex;
m_device = Vulkan::SelectDevice(deviceInfo, vulkanSurface, &graphicsFamilyQueueIndex, &presentableFamilyQueueIndex, &transferFamilyQueueIndex);
if (!m_device)
{
NazaraError("Failed to get compatible Vulkan device");
@@ -93,6 +76,7 @@ namespace Nz
m_graphicsQueue = m_device->GetQueue(graphicsFamilyQueueIndex, 0);
m_presentQueue = m_device->GetQueue(presentableFamilyQueueIndex, 0);
m_transferQueue = m_device->GetQueue(transferFamilyQueueIndex, 0);
std::vector<VkSurfaceFormatKHR> surfaceFormats;
if (!vulkanSurface.GetFormats(deviceInfo.physDevice, &surfaceFormats))
@@ -204,12 +188,6 @@ namespace Nz
}
}
if (!m_graphicsCommandPool.Create(*m_device, m_graphicsQueue.GetQueueFamilyIndex()))
{
NazaraError("Failed to create graphics command pool: " + TranslateVulkanError(m_graphicsCommandPool.GetLastErrorCode()));
return false;
}
const std::size_t MaxConcurrentImage = imageCount;
m_concurrentImageData.reserve(MaxConcurrentImage);
@@ -221,6 +199,27 @@ namespace Nz
return true;
}
std::unique_ptr<CommandPool> VkRenderWindow::CreateCommandPool(QueueType queueType)
{
UInt32 queueFamilyIndex;
switch (queueType)
{
case QueueType::Compute:
queueFamilyIndex = m_device->GetDefaultFamilyIndex(QueueType::Compute);
break;
case QueueType::Graphics:
queueFamilyIndex = m_graphicsQueue.GetQueueFamilyIndex();
break;
case QueueType::Transfer:
queueFamilyIndex = m_transferQueue.GetQueueFamilyIndex();
break;
}
return std::make_unique<VulkanCommandPool>(*m_device, queueFamilyIndex);
}
bool VkRenderWindow::SetupDepthBuffer(const Vector2ui& size)
{
VkImageCreateInfo imageCreateInfo = {

View File

@@ -297,7 +297,7 @@ namespace Nz
return CreateDevice(deviceInfo, queuesFamilies.data(), queuesFamilies.size());
}
std::shared_ptr<VulkanDevice> Vulkan::CreateDevice(const Vk::PhysicalDevice& deviceInfo, const Vk::Surface& surface, UInt32* graphicsFamilyIndex, UInt32* presentableFamilyIndex)
std::shared_ptr<VulkanDevice> Vulkan::CreateDevice(const Vk::PhysicalDevice& deviceInfo, const Vk::Surface& surface, UInt32* graphicsFamilyIndex, UInt32* presentableFamilyIndex, UInt32* transferFamilyIndex)
{
Nz::ErrorFlags errFlags(ErrorFlag_ThrowException, true);
@@ -364,6 +364,7 @@ namespace Nz
*graphicsFamilyIndex = graphicsQueueNodeIndex;
*presentableFamilyIndex = presentQueueNodeIndex;
*transferFamilyIndex = transferQueueNodeFamily;
return CreateDevice(deviceInfo, queuesFamilies.data(), queuesFamilies.size());
}
@@ -497,7 +498,7 @@ namespace Nz
return CreateDevice(deviceInfo);
}
std::shared_ptr<VulkanDevice> Vulkan::SelectDevice(const Vk::PhysicalDevice& deviceInfo, const Vk::Surface& surface, UInt32* graphicsFamilyIndex, UInt32* presentableFamilyIndex)
std::shared_ptr<VulkanDevice> Vulkan::SelectDevice(const Vk::PhysicalDevice& deviceInfo, const Vk::Surface& surface, UInt32* graphicsFamilyIndex, UInt32* presentableFamilyIndex, UInt32* transferFamilyIndex)
{
// First, try to find a device compatible with that surface
for (auto it = s_devices.begin(); it != s_devices.end();)
@@ -540,6 +541,23 @@ namespace Nz
if (presentableQueueFamilyIndex != UINT32_MAX)
{
*presentableFamilyIndex = presentableQueueFamilyIndex;
UInt32 transferQueueNodeFamily = UINT32_MAX;
// Search for a transfer queue (first one being different to the graphics one)
for (const Vk::Device::QueueFamilyInfo& queueInfo : queueFamilyInfo)
{
// Transfer bit is not mandatory if compute and graphics bits are set (as they implicitly support transfer)
if (queueInfo.flags & (VK_QUEUE_COMPUTE_BIT | VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_TRANSFER_BIT))
{
transferQueueNodeFamily = queueInfo.familyIndex;
if (transferQueueNodeFamily != *graphicsFamilyIndex)
break;
}
}
assert(transferQueueNodeFamily != UINT32_MAX);
*transferFamilyIndex = transferQueueNodeFamily;
return devicePtr;
}
}
@@ -548,7 +566,7 @@ namespace Nz
}
// No device had support for that surface, create one
return CreateDevice(deviceInfo, surface, graphicsFamilyIndex, presentableFamilyIndex);
return CreateDevice(deviceInfo, surface, graphicsFamilyIndex, presentableFamilyIndex, transferFamilyIndex);
}
void Vulkan::Uninitialize()

View File

@@ -126,7 +126,7 @@ namespace Nz
}
else
{
Vk::AutoCommandBuffer copyCommandBuffer = m_device.AllocateTransferCommandBuffer();
Vk::AutoCommandBuffer copyCommandBuffer = m_device.AllocateCommandBuffer(QueueType::Transfer);
if (!copyCommandBuffer->Begin(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT))
return false;
@@ -134,7 +134,7 @@ namespace Nz
if (!copyCommandBuffer->End())
return false;
Vk::QueueHandle transferQueue = m_device.GetQueue(m_device.GetTransferQueueFamilyIndex(), 0);
Vk::QueueHandle transferQueue = m_device.GetQueue(m_device.GetDefaultFamilyIndex(QueueType::Transfer), 0);
if (!transferQueue.Submit(copyCommandBuffer))
return false;

View File

@@ -0,0 +1,28 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - Vulkan Renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/VulkanRenderer/VulkanCommandPool.hpp>
#include <Nazara/VulkanRenderer/VulkanCommandBuffer.hpp>
#include <Nazara/VulkanRenderer/VulkanCommandBufferBuilder.hpp>
#include <Nazara/VulkanRenderer/Wrapper/CommandBuffer.hpp>
#include <Nazara/VulkanRenderer/Debug.hpp>
namespace Nz
{
std::unique_ptr<CommandBuffer> VulkanCommandPool::BuildCommandBuffer(const std::function<void(CommandBufferBuilder& builder)>& callback)
{
Vk::AutoCommandBuffer commandBuffer = m_commandPool.AllocateCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY);
if (!commandBuffer->Begin())
throw std::runtime_error("failed to begin command buffer: " + TranslateVulkanError(commandBuffer->GetLastErrorCode()));
VulkanCommandBufferBuilder builder(commandBuffer.Get());
callback(builder);
if (!commandBuffer->End())
throw std::runtime_error("failed to build command buffer: " + TranslateVulkanError(commandBuffer->GetLastErrorCode()));
return std::make_unique<VulkanCommandBuffer>(std::move(commandBuffer));
}
}

View File

@@ -3,6 +3,7 @@
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/VulkanRenderer/VulkanDevice.hpp>
#include <Nazara/VulkanRenderer/VulkanCommandPool.hpp>
#include <Nazara/VulkanRenderer/VulkanRenderPipeline.hpp>
#include <Nazara/VulkanRenderer/VulkanRenderPipelineLayout.hpp>
#include <Nazara/VulkanRenderer/VulkanShaderStage.hpp>
@@ -19,6 +20,11 @@ namespace Nz
return std::make_unique<VulkanBuffer>(*this, type);
}
std::unique_ptr<CommandPool> VulkanDevice::InstantiateCommandPool(QueueType queueType)
{
return std::make_unique<VulkanCommandPool>(*this, queueType);
}
std::unique_ptr<RenderPipeline> VulkanDevice::InstantiateRenderPipeline(RenderPipelineInfo pipelineInfo)
{
return std::make_unique<VulkanRenderPipeline>(*this, std::move(pipelineInfo));

View File

@@ -34,7 +34,7 @@ namespace Nz
m_inFlightCommandBuffers.clear();
}
void VulkanRenderImage::Execute(const std::function<void(CommandBufferBuilder& builder)>& callback, bool isGraphical)
void VulkanRenderImage::Execute(const std::function<void(CommandBufferBuilder& builder)>& callback, QueueTypeFlags queueTypeFlags)
{
Vk::CommandBuffer* commandBuffer;
if (m_currentCommandBuffer >= m_inFlightCommandBuffers.size())
@@ -55,7 +55,7 @@ namespace Nz
if (!commandBuffer->End())
throw std::runtime_error("failed to build command buffer: " + TranslateVulkanError(commandBuffer->GetLastErrorCode()));
SubmitCommandBuffer(*commandBuffer, isGraphical);
SubmitCommandBuffer(*commandBuffer, queueTypeFlags);
}
VulkanUploadPool& VulkanRenderImage::GetUploadPool()
@@ -63,16 +63,16 @@ namespace Nz
return m_uploadPool;
}
void VulkanRenderImage::SubmitCommandBuffer(CommandBuffer* commandBuffer, bool isGraphical)
void VulkanRenderImage::SubmitCommandBuffer(CommandBuffer* commandBuffer, QueueTypeFlags queueTypeFlags)
{
VulkanCommandBuffer& vkCommandBuffer = *static_cast<VulkanCommandBuffer*>(commandBuffer);
return SubmitCommandBuffer(vkCommandBuffer.GetCommandBuffer(), isGraphical);
return SubmitCommandBuffer(vkCommandBuffer.GetCommandBuffer(), queueTypeFlags);
}
void VulkanRenderImage::SubmitCommandBuffer(VkCommandBuffer commandBuffer, bool isGraphical)
void VulkanRenderImage::SubmitCommandBuffer(VkCommandBuffer commandBuffer, QueueTypeFlags queueTypeFlags)
{
if (isGraphical)
if (queueTypeFlags & QueueType::Graphics)
m_graphicalCommandsBuffers.push_back(commandBuffer);
else
{

View File

@@ -207,7 +207,7 @@ namespace Nz
std::memcpy(allocationInfo.pMappedData, ptr, textureSize);
Vk::AutoCommandBuffer copyCommandBuffer = m_device.AllocateTransferCommandBuffer();
Vk::AutoCommandBuffer copyCommandBuffer = m_device.AllocateCommandBuffer(QueueType::Graphics);
if (!copyCommandBuffer->Begin(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT))
return false;
@@ -220,7 +220,7 @@ namespace Nz
if (!copyCommandBuffer->End())
return false;
Vk::QueueHandle transferQueue = m_device.GetQueue(m_device.GetTransferQueueFamilyIndex(), 0);
Vk::QueueHandle transferQueue = m_device.GetQueue(m_device.GetDefaultFamilyIndex(QueueType::Graphics), 0);
if (!transferQueue.Submit(copyCommandBuffer))
return false;

View File

@@ -22,7 +22,7 @@ namespace Nz
{
struct Device::InternalData
{
Vk::CommandPool transferCommandPool;
std::array<Vk::CommandPool, QueueCount> commandPools;
};
Device::Device(Instance& instance) :
@@ -40,9 +40,9 @@ namespace Nz
WaitAndDestroyDevice();
}
AutoCommandBuffer Device::AllocateTransferCommandBuffer()
AutoCommandBuffer Device::AllocateCommandBuffer(QueueType queueType)
{
return m_internalData->transferCommandPool.AllocateCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY);
return m_internalData->commandPools[UnderlyingCast(queueType)].AllocateCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY);
}
bool Device::Create(const Vk::PhysicalDevice& deviceInfo, const VkDeviceCreateInfo& createInfo, const VkAllocationCallbacks* allocator)
@@ -107,8 +107,6 @@ namespace Nz
}
// And retains informations about queues
m_transferQueueFamilyIndex = UINT32_MAX;
UInt32 maxFamilyIndex = 0;
m_enabledQueuesInfos.resize(createInfo.queueCreateInfoCount);
for (UInt32 i = 0; i < createInfo.queueCreateInfoCount; ++i)
@@ -133,17 +131,6 @@ namespace Nz
queueInfo.priority = queueCreateInfo.pQueuePriorities[queueIndex];
vkGetDeviceQueue(m_device, info.familyIndex, queueIndex, &queueInfo.queue);
}
if (info.flags & (VK_QUEUE_TRANSFER_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_GRAPHICS_BIT))
{
if (m_transferQueueFamilyIndex == UINT32_MAX)
m_transferQueueFamilyIndex = info.familyIndex;
else if ((info.flags & (VK_QUEUE_COMPUTE_BIT | VK_QUEUE_GRAPHICS_BIT)) == 0)
{
m_transferQueueFamilyIndex = info.familyIndex;
break;
}
}
}
m_queuesByFamily.resize(maxFamilyIndex + 1);
@@ -151,12 +138,48 @@ namespace Nz
m_queuesByFamily[familyInfo.familyIndex] = &familyInfo.queues;
m_internalData = std::make_unique<InternalData>();
if (!m_internalData->transferCommandPool.Create(*this, m_transferQueueFamilyIndex, VK_COMMAND_POOL_CREATE_TRANSIENT_BIT))
m_defaultQueues.fill(InvalidQueue);
for (QueueType queueType : { QueueType::Graphics, QueueType::Compute, QueueType::Transfer })
{
NazaraError("Failed to create transfer command pool: " + TranslateVulkanError(m_internalData->transferCommandPool.GetLastErrorCode()));
return false;
auto QueueTypeToFlags = [](QueueType type) -> VkQueueFlags
{
switch (type)
{
case QueueType::Compute: return VK_QUEUE_COMPUTE_BIT;
case QueueType::Graphics: return VK_QUEUE_GRAPHICS_BIT;
case QueueType::Transfer: return VK_QUEUE_COMPUTE_BIT | VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_TRANSFER_BIT; //< Compute/graphics imply transfer
}
return 0U;
};
std::size_t queueIndex = static_cast<std::size_t>(queueType);
for (const QueueFamilyInfo& familyInfo : m_enabledQueuesInfos)
{
if (familyInfo.flags & QueueTypeToFlags(queueType) == 0)
continue;
m_defaultQueues[queueIndex] = familyInfo.familyIndex;
// Break only if queue has not been selected before
auto queueBegin = m_defaultQueues.begin();
auto queueEnd = queueBegin + queueIndex;
if (std::find(queueBegin, queueEnd, familyInfo.familyIndex) == queueEnd)
break;
}
Vk::CommandPool& commandPool = m_internalData->commandPools[queueIndex];
if (!commandPool.Create(*this, m_defaultQueues[queueIndex], VK_COMMAND_POOL_CREATE_TRANSIENT_BIT))
{
NazaraError("Failed to create command pool: " + TranslateVulkanError(commandPool.GetLastErrorCode()));
return false;
}
}
assert(GetDefaultFamilyIndex(QueueType::Transfer) != InvalidQueue);
// Initialize VMA
VmaVulkanFunctions vulkanFunctions = {
m_instance.vkGetPhysicalDeviceProperties,