diff options
Diffstat (limited to 'src/platform/vulkan/vulkan_swapchain.cpp')
| -rw-r--r-- | src/platform/vulkan/vulkan_swapchain.cpp | 77 |
1 files changed, 63 insertions, 14 deletions
diff --git a/src/platform/vulkan/vulkan_swapchain.cpp b/src/platform/vulkan/vulkan_swapchain.cpp index cbd4512..65b510c 100644 --- a/src/platform/vulkan/vulkan_swapchain.cpp +++ b/src/platform/vulkan/vulkan_swapchain.cpp @@ -11,19 +11,44 @@ namespace Donut::RHI const char** glfwExts = glfwGetRequiredInstanceExtensions(&glfwExtCount); if (!glfwExts) { DONUT_ERROR("Vulkan RHI: GLFW reports no surface support"); return false; } std::vector<const char*> exts(glfwExts, glfwExts + glfwExtCount); - exts.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); - exts.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + // the portability bits are how MoltenVK gets listed at all (it's a + // "portability" driver, not a conformant one). a desktop loader may not + // offer the extension, and asking for one it doesn't have fails the whole + // instance, so only ask when it's on the menu. + uint32_t iec = 0; vkEnumerateInstanceExtensionProperties(nullptr, &iec, nullptr); + std::vector<VkExtensionProperties> iexts(iec); + vkEnumerateInstanceExtensionProperties(nullptr, &iec, iexts.data()); + auto has_ext = [&](const char* name) + { + for (const auto& e : iexts) if (std::strcmp(e.extensionName, name) == 0) return true; + return false; + }; + VkInstanceCreateFlags flags = 0; +#ifdef VK_KHR_portability_enumeration // older SDK headers don't have it at all + if (has_ext(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)) + { + exts.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); + flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; + } +#endif + if (has_ext(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) + exts.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + + // validation only in debug builds: it's slow, and with the SDK installed on + // windows it would otherwise load for every release run too. std::vector<const char*> layers; +#ifdef DONUT_DEBUG uint32_t layer_count = 0; vkEnumerateInstanceLayerProperties(&layer_count, nullptr); std::vector<VkLayerProperties> avail(layer_count); vkEnumerateInstanceLayerProperties(&layer_count, avail.data()); for (const auto& l : avail) if (std::strcmp(l.layerName, "VK_LAYER_KHRONOS_validation") == 0) layers.push_back("VK_LAYER_KHRONOS_validation"); +#endif VkInstanceCreateInfo ici{ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO }; - ici.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; + ici.flags = flags; ici.pApplicationInfo = &app; ici.enabledExtensionCount = (uint32_t)exts.size(); ici.ppEnabledExtensionNames = exts.data(); ici.enabledLayerCount = (uint32_t)layers.size(); ici.ppEnabledLayerNames = layers.data(); @@ -31,6 +56,7 @@ namespace Donut::RHI if (r != VK_SUCCESS && !layers.empty()) { DONUT_WARN("Vulkan RHI: validation layer unavailable, continuing without it"); + layers.clear(); ici.enabledLayerCount = 0; ici.ppEnabledLayerNames = nullptr; r = vkCreateInstance(&ici, nullptr, &m_instance); } @@ -46,19 +72,30 @@ namespace Donut::RHI if (count == 0) { DONUT_ERROR("Vulkan RHI: no physical devices"); return false; } std::vector<VkPhysicalDevice> devices(count); vkEnumeratePhysicalDevices(m_instance, &count, devices.data()); - m_physical = devices[0]; - uint32_t q = 0; vkGetPhysicalDeviceQueueFamilyProperties(m_physical, &q, nullptr); - std::vector<VkQueueFamilyProperties> qfams(q); - vkGetPhysicalDeviceQueueFamilyProperties(m_physical, &q, qfams.data()); - bool fg = false, fp = false; - for (uint32_t i = 0; i < q; ++i) + // pick the best gpu that can both draw and present: a discrete card over an + // integrated one (laptops tend to list the igpu first), never one that can't + // present to our surface. + int best = -1; + for (VkPhysicalDevice pd : devices) { - if (!fg && (qfams[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)) { m_graphics_family = i; fg = true; } - VkBool32 present = VK_FALSE; vkGetPhysicalDeviceSurfaceSupportKHR(m_physical, i, m_surface, &present); - if (!fp && present) { m_present_family = i; fp = true; } + uint32_t q = 0; vkGetPhysicalDeviceQueueFamilyProperties(pd, &q, nullptr); + std::vector<VkQueueFamilyProperties> qfams(q); + vkGetPhysicalDeviceQueueFamilyProperties(pd, &q, qfams.data()); + uint32_t gfam = UINT32_MAX, pfam = UINT32_MAX; + for (uint32_t i = 0; i < q; ++i) + { + if (gfam == UINT32_MAX && (qfams[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)) gfam = i; + VkBool32 present = VK_FALSE; vkGetPhysicalDeviceSurfaceSupportKHR(pd, i, m_surface, &present); + if (pfam == UINT32_MAX && present) pfam = i; + } + if (gfam == UINT32_MAX || pfam == UINT32_MAX) continue; + VkPhysicalDeviceProperties pp{}; vkGetPhysicalDeviceProperties(pd, &pp); + const int score = pp.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU ? 3 + : pp.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU ? 2 : 1; + if (score > best) { best = score; m_physical = pd; m_graphics_family = gfam; m_present_family = pfam; } } - if (!fg || !fp) { DONUT_ERROR("Vulkan RHI: no graphics/present queue"); return false; } + if (best < 0) { DONUT_ERROR("Vulkan RHI: no gpu with both a graphics and a present queue"); return false; } std::vector<const char*> dev_exts = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; uint32_t dec = 0; vkEnumerateDeviceExtensionProperties(m_physical, nullptr, &dec, nullptr); @@ -295,14 +332,26 @@ namespace Donut::RHI auto VulkanDevice::recreate_swapchain() -> bool { + // minimized (0x0 on windows): wait it out, but not through a close request, + // or closing a minimized window from the taskbar hangs the process int w = 0, h = 0; glfwGetFramebufferSize(m_window, &w, &h); - while (w == 0 || h == 0) { glfwGetFramebufferSize(m_window, &w, &h); glfwWaitEvents(); } + while ((w == 0 || h == 0) && !glfwWindowShouldClose(m_window)) { glfwWaitEvents(); glfwGetFramebufferSize(m_window, &w, &h); } + if (w == 0 || h == 0) return false; // closing; keep the old swapchain until shutdown m_width = w; m_height = h; vkDeviceWaitIdle(m_device); cleanup_swapchain(); if (!create_swapchain()) return false; if (!create_image_views()) return false; if (!create_depth_and_framebuffers())return false; + // a new swapchain can come back with a different image count, and the + // present semaphores are per image + if (m_render_finished.size() != m_images.size()) + { + for (auto s : m_render_finished) vkDestroySemaphore(m_device, s, nullptr); + m_render_finished.assign(m_images.size(), VK_NULL_HANDLE); + VkSemaphoreCreateInfo sci{ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; + for (auto& s : m_render_finished) VKD_CHECK(vkCreateSemaphore(m_device, &sci, nullptr, &s)); + } m_images_in_flight.assign(m_images.size(), VK_NULL_HANDLE); return true; } |
