aboutsummaryrefslogtreecommitdiff
path: root/src/platform/vulkan/vulkan_swapchain.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/platform/vulkan/vulkan_swapchain.cpp')
-rw-r--r--src/platform/vulkan/vulkan_swapchain.cpp77
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;
}