Vulkan: Refactor command buffer and introduce command pool
This commit is contained in:
11
src/Nazara/Renderer/CommandPool.cpp
Normal file
11
src/Nazara/Renderer/CommandPool.cpp
Normal 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;
|
||||
}
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
28
src/Nazara/VulkanRenderer/VulkanCommandPool.cpp
Normal file
28
src/Nazara/VulkanRenderer/VulkanCommandPool.cpp
Normal 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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user