From 87f120932714687c3cce3f2b9ede290beb5ba117 Mon Sep 17 00:00:00 2001 From: Lynix Date: Tue, 7 Apr 2020 21:10:16 +0200 Subject: [PATCH] Vulkan: Refactor command buffer and introduce command pool --- examples/VulkanTest/main.cpp | 8 ++- include/Nazara/Core/Bitset.inl | 7 +-- include/Nazara/Renderer.hpp | 1 + include/Nazara/Renderer/CommandPool.hpp | 37 +++++++++++ include/Nazara/Renderer/CommandPool.inl | 12 ++++ include/Nazara/Renderer/Enums.hpp | 17 ++++++ include/Nazara/Renderer/RenderDevice.hpp | 2 + include/Nazara/Renderer/RenderImage.hpp | 5 +- include/Nazara/Renderer/RenderWindowImpl.hpp | 7 +-- include/Nazara/VulkanRenderer.hpp | 1 + .../Nazara/VulkanRenderer/VkRenderWindow.hpp | 6 +- include/Nazara/VulkanRenderer/Vulkan.hpp | 4 +- .../VulkanRenderer/VulkanCommandPool.hpp | 38 ++++++++++++ .../VulkanRenderer/VulkanCommandPool.inl | 28 +++++++++ .../Nazara/VulkanRenderer/VulkanDevice.hpp | 1 + .../VulkanRenderer/VulkanRenderImage.hpp | 6 +- .../Nazara/VulkanRenderer/Wrapper/Device.hpp | 13 +++- .../Nazara/VulkanRenderer/Wrapper/Device.inl | 4 +- src/Nazara/Renderer/CommandPool.cpp | 11 ++++ src/Nazara/VulkanRenderer/VkRenderWindow.cpp | 51 ++++++++-------- src/Nazara/VulkanRenderer/Vulkan.cpp | 24 +++++++- src/Nazara/VulkanRenderer/VulkanBuffer.cpp | 4 +- .../VulkanRenderer/VulkanCommandPool.cpp | 28 +++++++++ src/Nazara/VulkanRenderer/VulkanDevice.cpp | 6 ++ .../VulkanRenderer/VulkanRenderImage.cpp | 12 ++-- src/Nazara/VulkanRenderer/VulkanTexture.cpp | 4 +- src/Nazara/VulkanRenderer/Wrapper/Device.cpp | 61 +++++++++++++------ 27 files changed, 313 insertions(+), 85 deletions(-) create mode 100644 include/Nazara/Renderer/CommandPool.hpp create mode 100644 include/Nazara/Renderer/CommandPool.inl create mode 100644 include/Nazara/VulkanRenderer/VulkanCommandPool.hpp create mode 100644 include/Nazara/VulkanRenderer/VulkanCommandPool.inl create mode 100644 src/Nazara/Renderer/CommandPool.cpp create mode 100644 src/Nazara/VulkanRenderer/VulkanCommandPool.cpp diff --git a/examples/VulkanTest/main.cpp b/examples/VulkanTest/main.cpp index 2d6febd0e..14d124da4 100644 --- a/examples/VulkanTest/main.cpp +++ b/examples/VulkanTest/main.cpp @@ -210,6 +210,8 @@ int main() Nz::VkRenderWindow& vulkanWindow = *static_cast(window.GetImpl()); Nz::VulkanDevice& vulkanDevice = vulkanWindow.GetDevice(); + std::unique_ptr commandPool = vulkanWindow.CreateCommandPool(Nz::QueueType::Graphics); + Nz::UInt32 imageCount = vulkanWindow.GetFramebufferCount(); std::vector> renderCmds(imageCount); @@ -258,7 +260,7 @@ int main() clearValues.data() // const VkClearValue *pClearValues }; - commandBufferPtr = vulkanWindow.BuildCommandBuffer([&](Nz::CommandBufferBuilder& builder) + commandBufferPtr = commandPool->BuildCommandBuffer([&](Nz::CommandBufferBuilder& builder) { Nz::Vk::CommandBuffer& vkCommandBuffer = static_cast(builder).GetCommandBuffer(); @@ -372,11 +374,11 @@ int main() builder.PostTransferBarrier(); } builder.EndDebugRegion(); - }, false); + }, Nz::QueueType::Transfer); Nz::UInt32 imageIndex = renderImage.GetImageIndex(); - renderImage.SubmitCommandBuffer(renderCmds[imageIndex].get(), true); + renderImage.SubmitCommandBuffer(renderCmds[imageIndex].get(), Nz::QueueType::Graphics); renderImage.Present(); diff --git a/include/Nazara/Core/Bitset.inl b/include/Nazara/Core/Bitset.inl index 6100e5171..cc6d97250 100644 --- a/include/Nazara/Core/Bitset.inl +++ b/include/Nazara/Core/Bitset.inl @@ -1208,7 +1208,8 @@ namespace Nz template Block Bitset::GetLastBlockMask() const { - return (Block(1U) << GetBitIndex(m_bitCount)) - 1U; + std::size_t bitIndex = GetBitIndex(m_bitCount); + return (bitIndex) ? (Block(1U) << bitIndex) - 1U : fullBitMask; } /*! @@ -1218,9 +1219,7 @@ namespace Nz template void Bitset::ResetExtraBits() { - Block mask = GetLastBlockMask(); - if (mask) - m_blocks.back() &= mask; + m_blocks.back() &= GetLastBlockMask(); } /*! diff --git a/include/Nazara/Renderer.hpp b/include/Nazara/Renderer.hpp index df330e594..9ab23b09c 100644 --- a/include/Nazara/Renderer.hpp +++ b/include/Nazara/Renderer.hpp @@ -31,6 +31,7 @@ #include #include +#include #include #include #include diff --git a/include/Nazara/Renderer/CommandPool.hpp b/include/Nazara/Renderer/CommandPool.hpp new file mode 100644 index 000000000..ec52d83d2 --- /dev/null +++ b/include/Nazara/Renderer/CommandPool.hpp @@ -0,0 +1,37 @@ +// Copyright (C) 2020 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_COMMANDPOOL_HPP +#define NAZARA_COMMANDPOOL_HPP + +#include +#include +#include +#include //< temporary + +namespace Nz +{ + class CommandBuffer; + class CommandBufferBuilder; + + class NAZARA_RENDERER_API CommandPool + { + public: + CommandPool() = default; + CommandPool(const CommandPool&) = delete; + CommandPool(CommandPool&&) = default; + virtual ~CommandPool(); + + virtual std::unique_ptr BuildCommandBuffer(const std::function& callback) = 0; + + CommandPool& operator=(const CommandPool&) = delete; + CommandPool& operator=(CommandPool&&) = default; + }; +} + +#include + +#endif // NAZARA_COMMANDPOOL_HPP diff --git a/include/Nazara/Renderer/CommandPool.inl b/include/Nazara/Renderer/CommandPool.inl new file mode 100644 index 000000000..874c9c648 --- /dev/null +++ b/include/Nazara/Renderer/CommandPool.inl @@ -0,0 +1,12 @@ +// Copyright (C) 2020 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ +} + +#include diff --git a/include/Nazara/Renderer/Enums.hpp b/include/Nazara/Renderer/Enums.hpp index 4721662ff..a9b33ba5f 100644 --- a/include/Nazara/Renderer/Enums.hpp +++ b/include/Nazara/Renderer/Enums.hpp @@ -69,6 +69,23 @@ namespace Nz using ShaderStageTypeFlags = Flags; constexpr ShaderStageTypeFlags ShaderStageType_All = ShaderStageType::Fragment | ShaderStageType::Vertex; + + enum class QueueType + { + Compute, + Graphics, + Transfer, + + Max = Transfer + }; + + template<> + struct EnumAsFlags + { + static constexpr QueueType max = QueueType::Max; + }; + + using QueueTypeFlags = Flags; } #endif // NAZARA_ENUMS_RENDERER_HPP diff --git a/include/Nazara/Renderer/RenderDevice.hpp b/include/Nazara/Renderer/RenderDevice.hpp index ad48155a4..df8029bf2 100644 --- a/include/Nazara/Renderer/RenderDevice.hpp +++ b/include/Nazara/Renderer/RenderDevice.hpp @@ -20,6 +20,7 @@ namespace Nz { + class CommandPool; class ShaderStageImpl; class NAZARA_RENDERER_API RenderDevice @@ -29,6 +30,7 @@ namespace Nz virtual ~RenderDevice(); virtual std::unique_ptr InstantiateBuffer(BufferType type) = 0; + virtual std::unique_ptr InstantiateCommandPool(QueueType queueType) = 0; virtual std::unique_ptr InstantiateRenderPipeline(RenderPipelineInfo pipelineInfo) = 0; virtual std::shared_ptr InstantiateRenderPipelineLayout(RenderPipelineLayoutInfo pipelineLayoutInfo) = 0; virtual std::shared_ptr InstantiateShaderStage(ShaderStageType type, ShaderLanguage lang, const void* source, std::size_t sourceSize) = 0; diff --git a/include/Nazara/Renderer/RenderImage.hpp b/include/Nazara/Renderer/RenderImage.hpp index 2661f45c7..1ab5310ff 100644 --- a/include/Nazara/Renderer/RenderImage.hpp +++ b/include/Nazara/Renderer/RenderImage.hpp @@ -9,6 +9,7 @@ #include #include +#include #include namespace Nz @@ -23,11 +24,11 @@ namespace Nz RenderImage() = default; virtual ~RenderImage(); - virtual void Execute(const std::function& callback, bool isGraphical) = 0; + virtual void Execute(const std::function& callback, QueueTypeFlags queueTypeFlags) = 0; virtual UploadPool& GetUploadPool() = 0; - virtual void SubmitCommandBuffer(CommandBuffer* commandBuffer, bool isGraphical) = 0; + virtual void SubmitCommandBuffer(CommandBuffer* commandBuffer, QueueTypeFlags queueTypeFlags) = 0; virtual void Present() = 0; diff --git a/include/Nazara/Renderer/RenderWindowImpl.hpp b/include/Nazara/Renderer/RenderWindowImpl.hpp index f0636e77a..3e0e0bce1 100644 --- a/include/Nazara/Renderer/RenderWindowImpl.hpp +++ b/include/Nazara/Renderer/RenderWindowImpl.hpp @@ -13,12 +13,10 @@ #include #include #include -#include namespace Nz { - class CommandBuffer; - class CommandBufferBuilder; + class CommandPool; class RendererImpl; class RenderImage; class RenderSurface; @@ -31,9 +29,8 @@ namespace Nz virtual RenderImage& Acquire() = 0; - virtual std::unique_ptr BuildCommandBuffer(const std::function& callback) = 0; - virtual bool Create(RendererImpl* renderer, RenderSurface* surface, const Vector2ui& size, const RenderWindowParameters& parameters) = 0; + virtual std::unique_ptr CreateCommandPool(QueueType queueType) = 0; virtual std::shared_ptr GetRenderDevice() = 0; }; diff --git a/include/Nazara/VulkanRenderer.hpp b/include/Nazara/VulkanRenderer.hpp index b3a003a2a..ea1ffc4b9 100644 --- a/include/Nazara/VulkanRenderer.hpp +++ b/include/Nazara/VulkanRenderer.hpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include diff --git a/include/Nazara/VulkanRenderer/VkRenderWindow.hpp b/include/Nazara/VulkanRenderer/VkRenderWindow.hpp index 719e9bd8e..ae1771e16 100644 --- a/include/Nazara/VulkanRenderer/VkRenderWindow.hpp +++ b/include/Nazara/VulkanRenderer/VkRenderWindow.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -40,9 +41,8 @@ namespace Nz VulkanRenderImage& Acquire() override; - std::unique_ptr BuildCommandBuffer(const std::function& callback) override; - bool Create(RendererImpl* renderer, RenderSurface* surface, const Vector2ui& size, const RenderWindowParameters& parameters) override; + std::unique_ptr CreateCommandPool(QueueType queueType) override; inline const Vk::Framebuffer& GetFrameBuffer(UInt32 imageIndex) const override; inline UInt32 GetFramebufferCount() const override; @@ -77,12 +77,12 @@ namespace Nz std::shared_ptr m_device; std::vector m_imageData; std::vector m_concurrentImageData; - Vk::CommandPool m_graphicsCommandPool; Vk::DeviceMemory m_depthBufferMemory; Vk::Image m_depthBuffer; Vk::ImageView m_depthBufferView; Vk::QueueHandle m_graphicsQueue; Vk::QueueHandle m_presentQueue; + Vk::QueueHandle m_transferQueue; Vk::Swapchain m_swapchain; }; } diff --git a/include/Nazara/VulkanRenderer/Vulkan.hpp b/include/Nazara/VulkanRenderer/Vulkan.hpp index d87afbec1..12db5feb9 100644 --- a/include/Nazara/VulkanRenderer/Vulkan.hpp +++ b/include/Nazara/VulkanRenderer/Vulkan.hpp @@ -36,7 +36,7 @@ namespace Nz ~Vulkan() = delete; static std::shared_ptr CreateDevice(const Vk::PhysicalDevice& deviceInfo); - static std::shared_ptr CreateDevice(const Vk::PhysicalDevice& deviceInfo, const Vk::Surface& surface, UInt32* graphicsFamilyIndex, UInt32* presentableFamilyIndex); + static std::shared_ptr CreateDevice(const Vk::PhysicalDevice& deviceInfo, const Vk::Surface& surface, UInt32* graphicsFamilyIndex, UInt32* presentableFamilyIndex, UInt32* transferFamilyIndex); static std::shared_ptr CreateDevice(const Vk::PhysicalDevice& deviceInfo, const QueueFamily* queueFamilies, std::size_t queueFamilyCount); static Vk::Instance& GetInstance(); @@ -49,7 +49,7 @@ namespace Nz static bool IsInitialized(); static std::shared_ptr SelectDevice(const Vk::PhysicalDevice& deviceInfo); - static std::shared_ptr SelectDevice(const Vk::PhysicalDevice& deviceInfo, const Vk::Surface& surface, UInt32* graphicsFamilyIndex, UInt32* presentableFamilyIndex); + static std::shared_ptr SelectDevice(const Vk::PhysicalDevice& deviceInfo, const Vk::Surface& surface, UInt32* graphicsFamilyIndex, UInt32* presentableFamilyIndex, UInt32* transferFamilyIndex); static void Uninitialize(); diff --git a/include/Nazara/VulkanRenderer/VulkanCommandPool.hpp b/include/Nazara/VulkanRenderer/VulkanCommandPool.hpp new file mode 100644 index 000000000..5fbdb6ab9 --- /dev/null +++ b/include/Nazara/VulkanRenderer/VulkanCommandPool.hpp @@ -0,0 +1,38 @@ +// Copyright (C) 2020 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 + +#pragma once + +#ifndef NAZARA_VULKANRENDERER_VULKANCOMMANDPOOL_HPP +#define NAZARA_VULKANRENDERER_VULKANCOMMANDPOOL_HPP + +#include +#include +#include +#include + +namespace Nz +{ + class NAZARA_VULKANRENDERER_API VulkanCommandPool final : public CommandPool + { + public: + inline VulkanCommandPool(Vk::Device& device, QueueType queueType); + inline VulkanCommandPool(Vk::Device& device, UInt32 queueFamilyIndex); + VulkanCommandPool(const VulkanCommandPool&) = delete; + VulkanCommandPool(VulkanCommandPool&&) noexcept = default; + ~VulkanCommandPool() = default; + + std::unique_ptr BuildCommandBuffer(const std::function& callback) override; + + VulkanCommandPool& operator=(const VulkanCommandPool&) = delete; + VulkanCommandPool& operator=(VulkanCommandPool&&) = delete; + + private: + Vk::CommandPool m_commandPool; + }; +} + +#include + +#endif // NAZARA_VULKANRENDERER_VULKANCOMMANDPOOL_HPP diff --git a/include/Nazara/VulkanRenderer/VulkanCommandPool.inl b/include/Nazara/VulkanRenderer/VulkanCommandPool.inl new file mode 100644 index 000000000..77a6066a7 --- /dev/null +++ b/include/Nazara/VulkanRenderer/VulkanCommandPool.inl @@ -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 +#include +#include + +namespace Nz +{ + inline VulkanCommandPool::VulkanCommandPool(Vk::Device& device, QueueType queueType) + { + UInt32 queueFamilyIndex = device.GetDefaultFamilyIndex(queueType); + if (queueFamilyIndex == Vk::Device::InvalidQueue) + throw std::runtime_error("QueueType " + std::to_string(UnderlyingCast(queueType)) + " is not supported"); + + if (!m_commandPool.Create(device, queueFamilyIndex)) + throw std::runtime_error("Failed to create command pool: " + TranslateVulkanError(m_commandPool.GetLastErrorCode())); + } + + inline VulkanCommandPool::VulkanCommandPool(Vk::Device& device, UInt32 queueFamilyIndex) + { + if (!m_commandPool.Create(device, queueFamilyIndex)) + throw std::runtime_error("Failed to create command pool: " + TranslateVulkanError(m_commandPool.GetLastErrorCode())); + } +} + +#include diff --git a/include/Nazara/VulkanRenderer/VulkanDevice.hpp b/include/Nazara/VulkanRenderer/VulkanDevice.hpp index ce717c414..658a81401 100644 --- a/include/Nazara/VulkanRenderer/VulkanDevice.hpp +++ b/include/Nazara/VulkanRenderer/VulkanDevice.hpp @@ -24,6 +24,7 @@ namespace Nz ~VulkanDevice(); std::unique_ptr InstantiateBuffer(BufferType type) override; + std::unique_ptr InstantiateCommandPool(QueueType queueType) override; std::unique_ptr InstantiateRenderPipeline(RenderPipelineInfo pipelineInfo) override; std::shared_ptr InstantiateRenderPipelineLayout(RenderPipelineLayoutInfo pipelineLayoutInfo) override; std::shared_ptr InstantiateShaderStage(ShaderStageType type, ShaderLanguage lang, const void* source, std::size_t sourceSize) override; diff --git a/include/Nazara/VulkanRenderer/VulkanRenderImage.hpp b/include/Nazara/VulkanRenderer/VulkanRenderImage.hpp index 576109245..f7033b799 100644 --- a/include/Nazara/VulkanRenderer/VulkanRenderImage.hpp +++ b/include/Nazara/VulkanRenderer/VulkanRenderImage.hpp @@ -28,7 +28,7 @@ namespace Nz VulkanRenderImage(VulkanRenderImage&&) noexcept = default; ~VulkanRenderImage(); - void Execute(const std::function& callback, bool isGraphical) override; + void Execute(const std::function& callback, QueueTypeFlags queueTypeFlags) override; inline Vk::Fence& GetInFlightFence(); inline Vk::Semaphore& GetImageAvailableSemaphore(); @@ -36,8 +36,8 @@ namespace Nz inline Vk::Semaphore& GetRenderFinishedSemaphore(); VulkanUploadPool& GetUploadPool() override; - void SubmitCommandBuffer(CommandBuffer* commandBuffer, bool isGraphical) override; - void SubmitCommandBuffer(VkCommandBuffer commandBuffer, bool isGraphical); + void SubmitCommandBuffer(CommandBuffer* commandBuffer, QueueTypeFlags queueTypeFlags) override; + void SubmitCommandBuffer(VkCommandBuffer commandBuffer, QueueTypeFlags queueTypeFlags); void Present() override; diff --git a/include/Nazara/VulkanRenderer/Wrapper/Device.hpp b/include/Nazara/VulkanRenderer/Wrapper/Device.hpp index 86c42ad5f..2f3ded74f 100644 --- a/include/Nazara/VulkanRenderer/Wrapper/Device.hpp +++ b/include/Nazara/VulkanRenderer/Wrapper/Device.hpp @@ -8,10 +8,13 @@ #define NAZARA_VULKANRENDERER_VKDEVICE_HPP #include +#include +#include #include #include #include #include +#include #include #include @@ -38,7 +41,7 @@ namespace Nz Device(Device&&) = delete; ~Device(); - AutoCommandBuffer AllocateTransferCommandBuffer(); + AutoCommandBuffer AllocateCommandBuffer(QueueType queueType); bool Create(const Vk::PhysicalDevice& deviceInfo, const VkDeviceCreateInfo& createInfo, const VkAllocationCallbacks* allocator = nullptr); inline void Destroy(); @@ -54,7 +57,7 @@ namespace Nz inline VkPhysicalDevice GetPhysicalDevice() const; inline const Vk::PhysicalDevice& GetPhysicalDeviceInfo() const; - inline UInt32 GetTransferQueueFamilyIndex() const; + inline UInt32 GetDefaultFamilyIndex(QueueType queueType) const; inline bool IsExtensionLoaded(const std::string& extensionName); inline bool IsLayerLoaded(const std::string& layerName); @@ -99,6 +102,8 @@ namespace Nz UInt32 timestampValidBits; }; + static constexpr UInt32 InvalidQueue = std::numeric_limits::max(); + private: void ResetPointers(); void WaitAndDestroyDevice(); @@ -107,6 +112,8 @@ namespace Nz struct InternalData; + static constexpr std::size_t QueueCount = static_cast(QueueType::Max) + 1; + std::unique_ptr m_internalData; Instance& m_instance; const Vk::PhysicalDevice* m_physicalDevice; @@ -114,7 +121,7 @@ namespace Nz VkDevice m_device; VkResult m_lastErrorCode; VmaAllocator m_memAllocator; - UInt32 m_transferQueueFamilyIndex; + std::array m_defaultQueues; std::unordered_set m_loadedExtensions; std::unordered_set m_loadedLayers; std::vector m_enabledQueuesInfos; diff --git a/include/Nazara/VulkanRenderer/Wrapper/Device.inl b/include/Nazara/VulkanRenderer/Wrapper/Device.inl index 1e8617731..e3552e199 100644 --- a/include/Nazara/VulkanRenderer/Wrapper/Device.inl +++ b/include/Nazara/VulkanRenderer/Wrapper/Device.inl @@ -54,9 +54,9 @@ namespace Nz return *m_physicalDevice; } - inline UInt32 Device::GetTransferQueueFamilyIndex() const + inline UInt32 Device::GetDefaultFamilyIndex(QueueType queueType) const { - return m_transferQueueFamilyIndex; + return m_defaultQueues[UnderlyingCast(queueType)]; } inline bool Device::IsExtensionLoaded(const std::string& extensionName) diff --git a/src/Nazara/Renderer/CommandPool.cpp b/src/Nazara/Renderer/CommandPool.cpp new file mode 100644 index 000000000..6d0b412e4 --- /dev/null +++ b/src/Nazara/Renderer/CommandPool.cpp @@ -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 +#include + +namespace Nz +{ + CommandPool::~CommandPool() = default; +} diff --git a/src/Nazara/VulkanRenderer/VkRenderWindow.cpp b/src/Nazara/VulkanRenderer/VkRenderWindow.cpp index d9719dca5..7e5ce6fff 100644 --- a/src/Nazara/VulkanRenderer/VkRenderWindow.cpp +++ b/src/Nazara/VulkanRenderer/VkRenderWindow.cpp @@ -7,8 +7,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -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 VkRenderWindow::BuildCommandBuffer(const std::function& 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(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 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 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(*m_device, queueFamilyIndex); + } + bool VkRenderWindow::SetupDepthBuffer(const Vector2ui& size) { VkImageCreateInfo imageCreateInfo = { diff --git a/src/Nazara/VulkanRenderer/Vulkan.cpp b/src/Nazara/VulkanRenderer/Vulkan.cpp index a71043605..fd1e8ca9e 100644 --- a/src/Nazara/VulkanRenderer/Vulkan.cpp +++ b/src/Nazara/VulkanRenderer/Vulkan.cpp @@ -297,7 +297,7 @@ namespace Nz return CreateDevice(deviceInfo, queuesFamilies.data(), queuesFamilies.size()); } - std::shared_ptr Vulkan::CreateDevice(const Vk::PhysicalDevice& deviceInfo, const Vk::Surface& surface, UInt32* graphicsFamilyIndex, UInt32* presentableFamilyIndex) + std::shared_ptr 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 Vulkan::SelectDevice(const Vk::PhysicalDevice& deviceInfo, const Vk::Surface& surface, UInt32* graphicsFamilyIndex, UInt32* presentableFamilyIndex) + std::shared_ptr 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() diff --git a/src/Nazara/VulkanRenderer/VulkanBuffer.cpp b/src/Nazara/VulkanRenderer/VulkanBuffer.cpp index e338ac9e7..90ffe3615 100644 --- a/src/Nazara/VulkanRenderer/VulkanBuffer.cpp +++ b/src/Nazara/VulkanRenderer/VulkanBuffer.cpp @@ -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; diff --git a/src/Nazara/VulkanRenderer/VulkanCommandPool.cpp b/src/Nazara/VulkanRenderer/VulkanCommandPool.cpp new file mode 100644 index 000000000..771acded3 --- /dev/null +++ b/src/Nazara/VulkanRenderer/VulkanCommandPool.cpp @@ -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 +#include +#include +#include +#include + +namespace Nz +{ + std::unique_ptr VulkanCommandPool::BuildCommandBuffer(const std::function& 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(std::move(commandBuffer)); + } +} diff --git a/src/Nazara/VulkanRenderer/VulkanDevice.cpp b/src/Nazara/VulkanRenderer/VulkanDevice.cpp index 0f8ce9a9f..b20932dda 100644 --- a/src/Nazara/VulkanRenderer/VulkanDevice.cpp +++ b/src/Nazara/VulkanRenderer/VulkanDevice.cpp @@ -3,6 +3,7 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include #include #include @@ -19,6 +20,11 @@ namespace Nz return std::make_unique(*this, type); } + std::unique_ptr VulkanDevice::InstantiateCommandPool(QueueType queueType) + { + return std::make_unique(*this, queueType); + } + std::unique_ptr VulkanDevice::InstantiateRenderPipeline(RenderPipelineInfo pipelineInfo) { return std::make_unique(*this, std::move(pipelineInfo)); diff --git a/src/Nazara/VulkanRenderer/VulkanRenderImage.cpp b/src/Nazara/VulkanRenderer/VulkanRenderImage.cpp index 9cade7921..67ed19601 100644 --- a/src/Nazara/VulkanRenderer/VulkanRenderImage.cpp +++ b/src/Nazara/VulkanRenderer/VulkanRenderImage.cpp @@ -34,7 +34,7 @@ namespace Nz m_inFlightCommandBuffers.clear(); } - void VulkanRenderImage::Execute(const std::function& callback, bool isGraphical) + void VulkanRenderImage::Execute(const std::function& 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(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 { diff --git a/src/Nazara/VulkanRenderer/VulkanTexture.cpp b/src/Nazara/VulkanRenderer/VulkanTexture.cpp index f991051d3..186bbebbb 100644 --- a/src/Nazara/VulkanRenderer/VulkanTexture.cpp +++ b/src/Nazara/VulkanRenderer/VulkanTexture.cpp @@ -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; diff --git a/src/Nazara/VulkanRenderer/Wrapper/Device.cpp b/src/Nazara/VulkanRenderer/Wrapper/Device.cpp index 6c02c7381..7d9d5f874 100644 --- a/src/Nazara/VulkanRenderer/Wrapper/Device.cpp +++ b/src/Nazara/VulkanRenderer/Wrapper/Device.cpp @@ -22,7 +22,7 @@ namespace Nz { struct Device::InternalData { - Vk::CommandPool transferCommandPool; + std::array 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(); - 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(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,