From a5be5a3a765207b55b16661cc182e5c5bbbf55de Mon Sep 17 00:00:00 2001 From: spxtr Date: Mon, 4 Sep 2017 14:35:53 -0700 Subject: [PATCH] Vulkan: Use a separate queue for presenting. Before this change, we simply fail if the device does not expose one queue family that supports both graphics and present. Currently this is fine, since devices tend to lay out their queues in this way. NV, for instance, tends to have one queue family for all graphics operations and one more for transfer only. However, it's not a hard requirement, and it is cheap to use a separate queue, so we might as well. --- .../Vulkan/CommandBufferManager.cpp | 2 +- .../Core/VideoBackends/Vulkan/SwapChain.cpp | 12 ++- .../VideoBackends/Vulkan/VulkanContext.cpp | 95 ++++++++++++------- .../Core/VideoBackends/Vulkan/VulkanContext.h | 4 + 4 files changed, 79 insertions(+), 34 deletions(-) diff --git a/Source/Core/VideoBackends/Vulkan/CommandBufferManager.cpp b/Source/Core/VideoBackends/Vulkan/CommandBufferManager.cpp index 19ebbf3cb7..350757194e 100644 --- a/Source/Core/VideoBackends/Vulkan/CommandBufferManager.cpp +++ b/Source/Core/VideoBackends/Vulkan/CommandBufferManager.cpp @@ -352,7 +352,7 @@ void CommandBufferManager::SubmitCommandBuffer(size_t index, VkSemaphore wait_se &present_image_index, nullptr}; - res = vkQueuePresentKHR(g_vulkan_context->GetGraphicsQueue(), &present_info); + res = vkQueuePresentKHR(g_vulkan_context->GetPresentQueue(), &present_info); if (res != VK_SUCCESS && res != VK_ERROR_OUT_OF_DATE_KHR && res != VK_SUBOPTIMAL_KHR) LOG_VULKAN_ERROR(res, "vkQueuePresentKHR failed: "); } diff --git a/Source/Core/VideoBackends/Vulkan/SwapChain.cpp b/Source/Core/VideoBackends/Vulkan/SwapChain.cpp index 21009510bb..c5b3791637 100644 --- a/Source/Core/VideoBackends/Vulkan/SwapChain.cpp +++ b/Source/Core/VideoBackends/Vulkan/SwapChain.cpp @@ -327,7 +327,6 @@ bool SwapChain::CreateSwapChain() VkSwapchainKHR old_swap_chain = m_swap_chain; // Now we can actually create the swap chain - // TODO: Handle case where the present queue is not the graphics queue. VkSwapchainCreateInfoKHR swap_chain_info = {VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, nullptr, 0, @@ -346,6 +345,17 @@ bool SwapChain::CreateSwapChain() m_present_mode, VK_TRUE, old_swap_chain}; + std::array indices = {{ + g_vulkan_context->GetGraphicsQueueFamilyIndex(), + g_vulkan_context->GetPresentQueueFamilyIndex(), + }}; + if (g_vulkan_context->GetGraphicsQueueFamilyIndex() != + g_vulkan_context->GetPresentQueueFamilyIndex()) + { + swap_chain_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + swap_chain_info.queueFamilyIndexCount = 2; + swap_chain_info.pQueueFamilyIndices = indices.data(); + } res = vkCreateSwapchainKHR(g_vulkan_context->GetDevice(), &swap_chain_info, nullptr, &m_swap_chain); diff --git a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp index 8bb7a5088e..9258a28f6f 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp +++ b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp @@ -494,36 +494,41 @@ bool VulkanContext::CreateDevice(VkSurfaceKHR surface, bool enable_validation_la queue_family_properties.data()); INFO_LOG(VIDEO, "%u vulkan queue families", queue_family_count); - // Find a graphics queue - // Currently we only use a single queue for both graphics and presenting. - // TODO: In the future we could do post-processing and presenting on a different queue. + // Find graphics and present queues. m_graphics_queue_family_index = queue_family_count; + m_present_queue_family_index = queue_family_count; for (uint32_t i = 0; i < queue_family_count; i++) { - if (queue_family_properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) + VkBool32 graphics_supported = queue_family_properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT; + if (graphics_supported) { - // Check that it can present to our surface from this queue - if (surface) + m_graphics_queue_family_index = i; + // Quit now, no need for a present queue. + if (!surface) { - VkBool32 present_supported; - VkResult res = - vkGetPhysicalDeviceSurfaceSupportKHR(m_physical_device, i, surface, &present_supported); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceSurfaceSupportKHR failed: "); - return false; - } - - if (present_supported) - { - m_graphics_queue_family_index = i; - break; - } + break; } - else + } + + if (surface) + { + VkBool32 present_supported; + VkResult res = + vkGetPhysicalDeviceSurfaceSupportKHR(m_physical_device, i, surface, &present_supported); + if (res != VK_SUCCESS) + { + LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceSurfaceSupportKHR failed: "); + return false; + } + + if (present_supported) + { + m_present_queue_family_index = i; + } + + // Prefer one queue family index that does both graphics and present. + if (graphics_supported && present_supported) { - // We don't need present, so any graphics queue will do. - m_graphics_queue_family_index = i; break; } } @@ -533,6 +538,11 @@ bool VulkanContext::CreateDevice(VkSurfaceKHR surface, bool enable_validation_la ERROR_LOG(VIDEO, "Vulkan: Failed to find an acceptable graphics queue."); return false; } + if (surface && m_present_queue_family_index == queue_family_count) + { + ERROR_LOG(VIDEO, "Vulkan: Failed to find an acceptable present queue."); + return false; + } VkDeviceCreateInfo device_info = {}; device_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; @@ -540,15 +550,32 @@ bool VulkanContext::CreateDevice(VkSurfaceKHR surface, bool enable_validation_la device_info.flags = 0; static constexpr float queue_priorities[] = {1.0f}; - VkDeviceQueueCreateInfo queue_info = {}; - queue_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queue_info.pNext = nullptr; - queue_info.flags = 0; - queue_info.queueFamilyIndex = m_graphics_queue_family_index; - queue_info.queueCount = 1; - queue_info.pQueuePriorities = queue_priorities; + VkDeviceQueueCreateInfo graphics_queue_info = {}; + graphics_queue_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + graphics_queue_info.pNext = nullptr; + graphics_queue_info.flags = 0; + graphics_queue_info.queueFamilyIndex = m_graphics_queue_family_index; + graphics_queue_info.queueCount = 1; + graphics_queue_info.pQueuePriorities = queue_priorities; + + VkDeviceQueueCreateInfo present_queue_info = {}; + present_queue_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + present_queue_info.pNext = nullptr; + present_queue_info.flags = 0; + present_queue_info.queueFamilyIndex = m_present_queue_family_index; + present_queue_info.queueCount = 1; + present_queue_info.pQueuePriorities = queue_priorities; + + std::array queue_infos = {{ + graphics_queue_info, present_queue_info, + }}; + device_info.queueCreateInfoCount = 1; - device_info.pQueueCreateInfos = &queue_info; + if (m_graphics_queue_family_index != m_present_queue_family_index) + { + device_info.queueCreateInfoCount = 2; + } + device_info.pQueueCreateInfos = queue_infos.data(); ExtensionList enabled_extensions; if (!SelectDeviceExtensions(&enabled_extensions, surface != VK_NULL_HANDLE)) @@ -584,8 +611,12 @@ bool VulkanContext::CreateDevice(VkSurfaceKHR surface, bool enable_validation_la if (!LoadVulkanDeviceFunctions(m_device)) return false; - // Grab the graphics queue (only one we're using at this point). + // Grab the graphics and present queues. vkGetDeviceQueue(m_device, m_graphics_queue_family_index, 0, &m_graphics_queue); + if (surface) + { + vkGetDeviceQueue(m_device, m_present_queue_family_index, 0, &m_present_queue); + } return true; } diff --git a/Source/Core/VideoBackends/Vulkan/VulkanContext.h b/Source/Core/VideoBackends/Vulkan/VulkanContext.h index 17a4a25d2c..fd504560b8 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanContext.h +++ b/Source/Core/VideoBackends/Vulkan/VulkanContext.h @@ -57,6 +57,8 @@ public: VkDevice GetDevice() const { return m_device; } VkQueue GetGraphicsQueue() const { return m_graphics_queue; } u32 GetGraphicsQueueFamilyIndex() const { return m_graphics_queue_family_index; } + VkQueue GetPresentQueue() const { return m_present_queue; } + u32 GetPresentQueueFamilyIndex() const { return m_present_queue_family_index; } const VkQueueFamilyProperties& GetGraphicsQueueProperties() const { return m_graphics_queue_properties; @@ -119,6 +121,8 @@ private: VkQueue m_graphics_queue = VK_NULL_HANDLE; u32 m_graphics_queue_family_index = 0; + VkQueue m_present_queue = VK_NULL_HANDLE; + u32 m_present_queue_family_index = 0; VkQueueFamilyProperties m_graphics_queue_properties = {}; VkDebugReportCallbackEXT m_debug_report_callback = VK_NULL_HANDLE;