diff --git a/include/Nazara/Vulkan/Vulkan.hpp b/include/Nazara/Vulkan/Vulkan.hpp index 59c1bb4f3..97a58d7ee 100644 --- a/include/Nazara/Vulkan/Vulkan.hpp +++ b/include/Nazara/Vulkan/Vulkan.hpp @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include namespace Nz { @@ -21,17 +23,22 @@ namespace Nz Vulkan() = delete; ~Vulkan() = delete; + static Vk::Device& CreateDevice(VkPhysicalDevice gpu, const Vk::Surface& surface, UInt32* presentableFamilyQueue); + static Vk::Instance& GetInstance(); static bool Initialize(); static bool IsInitialized(); + static Vk::Device& SelectDevice(VkPhysicalDevice gpu, const Vk::Surface& surface, UInt32* presentableFamilyQueue); + static void SetParameters(const ParameterList& parameters); static void Uninitialize(); private: + static std::list s_devices; static Vk::Instance s_instance; static ParameterList s_initializationParameters; static unsigned int s_moduleReferenceCounter; diff --git a/src/Nazara/Vulkan/Vulkan.cpp b/src/Nazara/Vulkan/Vulkan.cpp index dc7a201ff..4f8fd7c51 100644 --- a/src/Nazara/Vulkan/Vulkan.cpp +++ b/src/Nazara/Vulkan/Vulkan.cpp @@ -5,9 +5,11 @@ #include #include #include +#include #include #include #include +#include #include namespace Nz @@ -156,6 +158,127 @@ namespace Nz return s_moduleReferenceCounter != 0; } + Vk::Device& Vulkan::CreateDevice(VkPhysicalDevice gpu, const Vk::Surface& surface, UInt32* presentableFamilyQueue) + { + Nz::ErrorFlags errFlags(ErrorFlag_ThrowException, true); + + std::vector queueFamilies; + s_instance.GetPhysicalDeviceQueueFamilyProperties(gpu, &queueFamilies); + + // Find a queue that supports graphics operations + UInt32 graphicsQueueNodeIndex = UINT32_MAX; + UInt32 presentQueueNodeIndex = UINT32_MAX; + UInt32 transfertQueueNodeFamily = UINT32_MAX; + for (UInt32 i = 0; i < queueFamilies.size(); i++) + { + bool supportPresentation = false; + if (!surface.GetSupportPresentation(gpu, i, &supportPresentation)) + NazaraWarning("Failed to get presentation support of queue family #" + String::Number(i)); + + if (queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) + { + if (graphicsQueueNodeIndex == UINT32_MAX) + graphicsQueueNodeIndex = i; + + if (supportPresentation) + { + graphicsQueueNodeIndex = i; + presentQueueNodeIndex = i; + break; + } + } + else if (supportPresentation) + presentQueueNodeIndex = i; + } + + for (UInt32 i = 0; i < queueFamilies.size(); i++) + { + if (queueFamilies[i].queueFlags & VK_QUEUE_TRANSFER_BIT) + { + transfertQueueNodeFamily = i; + if (transfertQueueNodeFamily != graphicsQueueNodeIndex) + break; + } + } + + std::array usedQueueFamilies = {graphicsQueueNodeIndex, presentQueueNodeIndex, transfertQueueNodeFamily}; + float priority = 1.f; + + std::vector queueCreateInfos; + for (UInt32 queueFamily : usedQueueFamilies) + { + VkDeviceQueueCreateInfo createInfo = { + VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + nullptr, + 0, + queueFamily, + 1, + &priority + }; + + queueCreateInfos.emplace_back(createInfo); + } + + std::array enabledExtensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; + std::array enabledLayers; + + VkDeviceCreateInfo createInfo = { + VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + nullptr, + 0, + UInt32(queueCreateInfos.size()), + queueCreateInfos.data(), + UInt32(enabledLayers.size()), + enabledLayers.data(), + UInt32(enabledLayers.size()), + enabledLayers.data(), + nullptr + }; + + ///TODO: First create then move + s_devices.emplace_back(s_instance); + + Vk::Device& device = s_devices.back(); + device.Create(gpu, createInfo); + + *presentableFamilyQueue = presentQueueNodeIndex; + + return device; + } + + Vk::Device& Vulkan::SelectDevice(VkPhysicalDevice gpu, const Vk::Surface& surface, UInt32* presentableFamilyQueue) + { + // First, try to find a device compatible with that surface + for (Vk::Device& device : s_devices) + { + if (device.GetPhysicalDevice() == gpu) + { + const std::vector& queueFamilyInfo = device.GetEnabledQueues(); + UInt32 presentableQueueFamilyIndex = UINT32_MAX; + for (Vk::Device::QueueFamilyInfo queueInfo : queueFamilyInfo) + { + bool supported = false; + if (surface.GetSupportPresentation(gpu, queueInfo.familyIndex, &supported) && supported) + { + if (presentableQueueFamilyIndex == UINT32_MAX || queueInfo.flags & VK_QUEUE_GRAPHICS_BIT) + { + presentableQueueFamilyIndex = queueInfo.familyIndex; + if (queueInfo.flags & VK_QUEUE_GRAPHICS_BIT) + break; + } + } + } + + if (presentableQueueFamilyIndex != UINT32_MAX) + *presentableFamilyQueue = presentableQueueFamilyIndex; + } + } + + // No device had support for that surface, create one + return CreateDevice(gpu, surface, presentableFamilyQueue); + + } + void Vulkan::SetParameters(const ParameterList& parameters) { s_initializationParameters = parameters; @@ -185,6 +308,7 @@ namespace Nz Utility::Uninitialize(); } + std::list Vulkan::s_devices; Vk::Instance Vulkan::s_instance; ParameterList Vulkan::s_initializationParameters; unsigned int Vulkan::s_moduleReferenceCounter = 0;