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

@ -210,6 +210,8 @@ int main()
Nz::VkRenderWindow& vulkanWindow = *static_cast<Nz::VkRenderWindow*>(window.GetImpl());
Nz::VulkanDevice& vulkanDevice = vulkanWindow.GetDevice();
std::unique_ptr<Nz::CommandPool> commandPool = vulkanWindow.CreateCommandPool(Nz::QueueType::Graphics);
Nz::UInt32 imageCount = vulkanWindow.GetFramebufferCount();
std::vector<std::unique_ptr<Nz::CommandBuffer>> 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<Nz::VulkanCommandBufferBuilder&>(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();

View File

@ -1208,7 +1208,8 @@ namespace Nz
template<typename Block, class Allocator>
Block Bitset<Block, Allocator>::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<typename Block, class Allocator>
void Bitset<Block, Allocator>::ResetExtraBits()
{
Block mask = GetLastBlockMask();
if (mask)
m_blocks.back() &= mask;
m_blocks.back() &= GetLastBlockMask();
}
/*!

View File

@ -31,6 +31,7 @@
#include <Nazara/Renderer/CommandBuffer.hpp>
#include <Nazara/Renderer/CommandBufferBuilder.hpp>
#include <Nazara/Renderer/CommandPool.hpp>
#include <Nazara/Renderer/Config.hpp>
#include <Nazara/Renderer/DebugDrawer.hpp>
#include <Nazara/Renderer/Enums.hpp>

View File

@ -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 <Nazara/Prerequisites.hpp>
#include <Nazara/Renderer/Config.hpp>
#include <functional>
#include <memory> //< 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<CommandBuffer> BuildCommandBuffer(const std::function<void(CommandBufferBuilder& builder)>& callback) = 0;
CommandPool& operator=(const CommandPool&) = delete;
CommandPool& operator=(CommandPool&&) = default;
};
}
#include <Nazara/Renderer/CommandPool.inl>
#endif // NAZARA_COMMANDPOOL_HPP

View File

@ -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 <Nazara/Renderer/CommandPool.hpp>
#include <Nazara/Renderer/Debug.hpp>
namespace Nz
{
}
#include <Nazara/Renderer/DebugOff.hpp>

View File

@ -69,6 +69,23 @@ namespace Nz
using ShaderStageTypeFlags = Flags<ShaderStageType>;
constexpr ShaderStageTypeFlags ShaderStageType_All = ShaderStageType::Fragment | ShaderStageType::Vertex;
enum class QueueType
{
Compute,
Graphics,
Transfer,
Max = Transfer
};
template<>
struct EnumAsFlags<QueueType>
{
static constexpr QueueType max = QueueType::Max;
};
using QueueTypeFlags = Flags<QueueType>;
}
#endif // NAZARA_ENUMS_RENDERER_HPP

View File

@ -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<AbstractBuffer> InstantiateBuffer(BufferType type) = 0;
virtual std::unique_ptr<CommandPool> InstantiateCommandPool(QueueType queueType) = 0;
virtual std::unique_ptr<RenderPipeline> InstantiateRenderPipeline(RenderPipelineInfo pipelineInfo) = 0;
virtual std::shared_ptr<RenderPipelineLayout> InstantiateRenderPipelineLayout(RenderPipelineLayoutInfo pipelineLayoutInfo) = 0;
virtual std::shared_ptr<ShaderStageImpl> InstantiateShaderStage(ShaderStageType type, ShaderLanguage lang, const void* source, std::size_t sourceSize) = 0;

View File

@ -9,6 +9,7 @@
#include <Nazara/Prerequisites.hpp>
#include <Nazara/Renderer/Config.hpp>
#include <Nazara/Renderer/Enums.hpp>
#include <functional>
namespace Nz
@ -23,11 +24,11 @@ namespace Nz
RenderImage() = default;
virtual ~RenderImage();
virtual void Execute(const std::function<void(CommandBufferBuilder& builder)>& callback, bool isGraphical) = 0;
virtual void Execute(const std::function<void(CommandBufferBuilder& builder)>& 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;

View File

@ -13,12 +13,10 @@
#include <Nazara/Renderer/Config.hpp>
#include <Nazara/Renderer/RenderDevice.hpp>
#include <Nazara/Renderer/RenderWindowParameters.hpp>
#include <functional>
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<CommandBuffer> BuildCommandBuffer(const std::function<void(CommandBufferBuilder& builder)>& callback) = 0;
virtual bool Create(RendererImpl* renderer, RenderSurface* surface, const Vector2ui& size, const RenderWindowParameters& parameters) = 0;
virtual std::unique_ptr<CommandPool> CreateCommandPool(QueueType queueType) = 0;
virtual std::shared_ptr<RenderDevice> GetRenderDevice() = 0;
};

View File

@ -37,6 +37,7 @@
#include <Nazara/VulkanRenderer/VulkanBuffer.hpp>
#include <Nazara/VulkanRenderer/VulkanCommandBuffer.hpp>
#include <Nazara/VulkanRenderer/VulkanCommandBufferBuilder.hpp>
#include <Nazara/VulkanRenderer/VulkanCommandPool.hpp>
#include <Nazara/VulkanRenderer/VulkanDevice.hpp>
#include <Nazara/VulkanRenderer/VulkanRenderer.hpp>
#include <Nazara/VulkanRenderer/VulkanRenderImage.hpp>

View File

@ -11,6 +11,7 @@
#include <Nazara/Core/Clock.hpp>
#include <Nazara/Math/Rect.hpp>
#include <Nazara/Math/Vector3.hpp>
#include <Nazara/Renderer/Enums.hpp>
#include <Nazara/Renderer/RendererImpl.hpp>
#include <Nazara/Renderer/RenderWindowImpl.hpp>
#include <Nazara/VulkanRenderer/Config.hpp>
@ -40,9 +41,8 @@ namespace Nz
VulkanRenderImage& Acquire() override;
std::unique_ptr<CommandBuffer> BuildCommandBuffer(const std::function<void(CommandBufferBuilder& builder)>& callback) override;
bool Create(RendererImpl* renderer, RenderSurface* surface, const Vector2ui& size, const RenderWindowParameters& parameters) override;
std::unique_ptr<CommandPool> 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<VulkanDevice> m_device;
std::vector<ImageData> m_imageData;
std::vector<VulkanRenderImage> 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;
};
}

View File

@ -36,7 +36,7 @@ namespace Nz
~Vulkan() = delete;
static std::shared_ptr<VulkanDevice> CreateDevice(const Vk::PhysicalDevice& deviceInfo);
static std::shared_ptr<VulkanDevice> CreateDevice(const Vk::PhysicalDevice& deviceInfo, const Vk::Surface& surface, UInt32* graphicsFamilyIndex, UInt32* presentableFamilyIndex);
static std::shared_ptr<VulkanDevice> CreateDevice(const Vk::PhysicalDevice& deviceInfo, const Vk::Surface& surface, UInt32* graphicsFamilyIndex, UInt32* presentableFamilyIndex, UInt32* transferFamilyIndex);
static std::shared_ptr<VulkanDevice> 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<VulkanDevice> SelectDevice(const Vk::PhysicalDevice& deviceInfo);
static std::shared_ptr<VulkanDevice> SelectDevice(const Vk::PhysicalDevice& deviceInfo, const Vk::Surface& surface, UInt32* graphicsFamilyIndex, UInt32* presentableFamilyIndex);
static std::shared_ptr<VulkanDevice> SelectDevice(const Vk::PhysicalDevice& deviceInfo, const Vk::Surface& surface, UInt32* graphicsFamilyIndex, UInt32* presentableFamilyIndex, UInt32* transferFamilyIndex);
static void Uninitialize();

View File

@ -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 <Nazara/Prerequisites.hpp>
#include <Nazara/Renderer/CommandPool.hpp>
#include <Nazara/VulkanRenderer/Config.hpp>
#include <Nazara/VulkanRenderer/Wrapper/CommandPool.hpp>
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<CommandBuffer> BuildCommandBuffer(const std::function<void(CommandBufferBuilder& builder)>& callback) override;
VulkanCommandPool& operator=(const VulkanCommandPool&) = delete;
VulkanCommandPool& operator=(VulkanCommandPool&&) = delete;
private:
Vk::CommandPool m_commandPool;
};
}
#include <Nazara/VulkanRenderer/VulkanCommandPool.inl>
#endif // NAZARA_VULKANRENDERER_VULKANCOMMANDPOOL_HPP

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 <stdexcept>
#include <Nazara/VulkanRenderer/Debug.hpp>
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 <Nazara/VulkanRenderer/DebugOff.hpp>

View File

@ -24,6 +24,7 @@ namespace Nz
~VulkanDevice();
std::unique_ptr<AbstractBuffer> InstantiateBuffer(BufferType type) override;
std::unique_ptr<CommandPool> InstantiateCommandPool(QueueType queueType) override;
std::unique_ptr<RenderPipeline> InstantiateRenderPipeline(RenderPipelineInfo pipelineInfo) override;
std::shared_ptr<RenderPipelineLayout> InstantiateRenderPipelineLayout(RenderPipelineLayoutInfo pipelineLayoutInfo) override;
std::shared_ptr<ShaderStageImpl> InstantiateShaderStage(ShaderStageType type, ShaderLanguage lang, const void* source, std::size_t sourceSize) override;

View File

@ -28,7 +28,7 @@ namespace Nz
VulkanRenderImage(VulkanRenderImage&&) noexcept = default;
~VulkanRenderImage();
void Execute(const std::function<void(CommandBufferBuilder& builder)>& callback, bool isGraphical) override;
void Execute(const std::function<void(CommandBufferBuilder& builder)>& 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;

View File

@ -8,10 +8,13 @@
#define NAZARA_VULKANRENDERER_VKDEVICE_HPP
#include <Nazara/Prerequisites.hpp>
#include <Nazara/Core/Algorithm.hpp>
#include <Nazara/Renderer/Enums.hpp>
#include <Nazara/VulkanRenderer/Config.hpp>
#include <Nazara/VulkanRenderer/Wrapper/Loader.hpp>
#include <Nazara/VulkanRenderer/Wrapper/PhysicalDevice.hpp>
#include <vulkan/vulkan.h>
#include <array>
#include <memory>
#include <unordered_set>
@ -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<UInt32>::max();
private:
void ResetPointers();
void WaitAndDestroyDevice();
@ -107,6 +112,8 @@ namespace Nz
struct InternalData;
static constexpr std::size_t QueueCount = static_cast<std::size_t>(QueueType::Max) + 1;
std::unique_ptr<InternalData> 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<UInt32, QueueCount> m_defaultQueues;
std::unordered_set<std::string> m_loadedExtensions;
std::unordered_set<std::string> m_loadedLayers;
std::vector<QueueFamilyInfo> m_enabledQueuesInfos;

View File

@ -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)

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,