Remove vkCreateSwapchainKHR from ICD Dispatch table
authorCharles Giessen <charles@lunarg.com>
Wed, 12 Oct 2022 19:51:17 +0000 (13:51 -0600)
committerCharles Giessen <46324611+charles-lunarg@users.noreply.github.com>
Wed, 26 Oct 2022 03:04:34 +0000 (21:04 -0600)
vkCreateSwapchainKHR and vkGetDeviceGroupSurfacePresentModesKHR were added to
the icd dispatch table then loaded with GIPA. While this is possible, for best
performance and consistency, the loader should use the device's dispatch table
for these functions. This required changing the terminators for both functions
so that they checked that the dev pointer isn't null and its dispatch entry for
each function isn't null.

Additionally, various debug marker/util functions checked if an ICD supported
surfaces by checking the ICD's dispatch table for vkCreateSwapchainKHR. These
have been updated to use the device's dispatch table instead.

loader/generated/vk_loader_extensions.c
loader/generated/vk_loader_extensions.h
loader/wsi.c
scripts/loader_extension_generator.py
tests/framework/icd/test_icd.cpp
tests/framework/test_util.cpp
tests/framework/test_util.h
tests/loader_regression_tests.cpp

index 4960ab81650d8440983b5c536389f8b189cec474..b3287512db594e2b14f6a4e92c9b6075d4ed8dfa 100644 (file)
@@ -98,8 +98,6 @@ VKAPI_ATTR bool VKAPI_CALL loader_icd_init_entries(struct loader_icd_term *icd_t
     LOOKUP_GIPA(GetPhysicalDeviceSurfacePresentModesKHR, false);
 
     // ---- VK_KHR_swapchain extension commands
-    LOOKUP_GIPA(CreateSwapchainKHR, false);
-    LOOKUP_GIPA(GetDeviceGroupSurfacePresentModesKHR, false);
     LOOKUP_GIPA(GetPhysicalDevicePresentRectanglesKHR, false);
 
     // ---- VK_KHR_display extension commands
@@ -3937,7 +3935,7 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_DebugMarkerSetObjectTagEXT(
             local_tag_info.object = (uint64_t)(uintptr_t)phys_dev_term->phys_dev;
         // If this is a KHR_surface, and the ICD has created its own, we have to replace it with the proper one for the next call.
         } else if (pTagInfo->objectType == VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT) {
-            if (NULL != icd_term && NULL != icd_term->dispatch.CreateSwapchainKHR) {
+            if (NULL != dev && NULL != dev->loader_dispatch.core_dispatch.CreateSwapchainKHR) {
                 VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)pTagInfo->object;
                 if (NULL != icd_surface->real_icd_surfaces) {
                     local_tag_info.object = (uint64_t)icd_surface->real_icd_surfaces[icd_index];
@@ -3985,7 +3983,7 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_DebugMarkerSetObjectNameEXT(
             local_name_info.object = (uint64_t)(uintptr_t)phys_dev_term->phys_dev;
         // If this is a KHR_surface, and the ICD has created its own, we have to replace it with the proper one for the next call.
         } else if (pNameInfo->objectType == VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT) {
-            if (NULL != icd_term && NULL != icd_term->dispatch.CreateSwapchainKHR) {
+            if (NULL != dev && NULL != dev->loader_dispatch.core_dispatch.CreateSwapchainKHR) {
                 VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)pNameInfo->object;
                 if (NULL != icd_surface->real_icd_surfaces) {
                     local_name_info.object = (uint64_t)icd_surface->real_icd_surfaces[icd_index];
@@ -4539,7 +4537,7 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_SetDebugUtilsObjectNameEXT(
             local_name_info.objectHandle = (uint64_t)(uintptr_t)phys_dev_term->phys_dev;
         // If this is a KHR_surface, and the ICD has created its own, we have to replace it with the proper one for the next call.
         } else if (pNameInfo->objectType == VK_OBJECT_TYPE_SURFACE_KHR) {
-            if (NULL != icd_term && NULL != icd_term->dispatch.CreateSwapchainKHR) {
+            if (NULL != dev && NULL != dev->loader_dispatch.core_dispatch.CreateSwapchainKHR) {
                 VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)pNameInfo->objectHandle;
                 if (NULL != icd_surface->real_icd_surfaces) {
                     local_name_info.objectHandle = (uint64_t)icd_surface->real_icd_surfaces[icd_index];
@@ -4591,7 +4589,7 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_SetDebugUtilsObjectTagEXT(
             local_tag_info.objectHandle = (uint64_t)(uintptr_t)phys_dev_term->phys_dev;
         // If this is a KHR_surface, and the ICD has created its own, we have to replace it with the proper one for the next call.
         } else if (pTagInfo->objectType == VK_OBJECT_TYPE_SURFACE_KHR) {
-            if (NULL != icd_term && NULL != icd_term->dispatch.CreateSwapchainKHR) {
+            if (NULL != dev && NULL != dev->loader_dispatch.core_dispatch.CreateSwapchainKHR) {
                 VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)pTagInfo->objectHandle;
                 if (NULL != icd_surface->real_icd_surfaces) {
                     local_tag_info.objectHandle = (uint64_t)icd_surface->real_icd_surfaces[icd_index];
index c0bcfa8773a519e586ff1965a55cb9b7c28a1e24..ce19d6c9ea19708cdddbfa65bf944c5706284fd0 100644 (file)
@@ -250,8 +250,6 @@ struct loader_icd_term_dispatch {
     PFN_vkGetPhysicalDeviceSurfacePresentModesKHR GetPhysicalDeviceSurfacePresentModesKHR;
 
     // ---- VK_KHR_swapchain extension commands
-    PFN_vkCreateSwapchainKHR CreateSwapchainKHR;
-    PFN_vkGetDeviceGroupSurfacePresentModesKHR GetDeviceGroupSurfacePresentModesKHR;
     PFN_vkGetPhysicalDevicePresentRectanglesKHR GetPhysicalDevicePresentRectanglesKHR;
 
     // ---- VK_KHR_display extension commands
index e44c37fbf20cfefe03c5daf9d8ad518d8142f2a5..a1dbe0f403846dd228bd25e1234d65ec6630e9be 100644 (file)
@@ -457,25 +457,27 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateSwapchainKHR(VkDevice device, co
     uint32_t icd_index = 0;
     struct loader_device *dev;
     struct loader_icd_term *icd_term = loader_get_icd_and_device(device, &dev, &icd_index);
-    if (NULL != icd_term && NULL != icd_term->dispatch.CreateSwapchainKHR) {
-        VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)pCreateInfo->surface;
-        if (NULL != icd_surface->real_icd_surfaces) {
-            if ((VkSurfaceKHR)(uintptr_t)NULL != icd_surface->real_icd_surfaces[icd_index]) {
-                // We found the ICD, and there is an ICD KHR surface
-                // associated with it, so copy the CreateInfo struct
-                // and point it at the ICD's surface.
-                VkSwapchainCreateInfoKHR *pCreateCopy = loader_stack_alloc(sizeof(VkSwapchainCreateInfoKHR));
-                if (NULL == pCreateCopy) {
-                    return VK_ERROR_OUT_OF_HOST_MEMORY;
-                }
-                memcpy(pCreateCopy, pCreateInfo, sizeof(VkSwapchainCreateInfoKHR));
-                pCreateCopy->surface = icd_surface->real_icd_surfaces[icd_index];
-                return icd_term->dispatch.CreateSwapchainKHR(device, pCreateCopy, pAllocator, pSwapchain);
+    if (NULL == icd_term || NULL == dev || NULL == dev->loader_dispatch.core_dispatch.CreateSwapchainKHR) {
+        loader_log(NULL, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0,
+                   "vkCreateSwapchainKHR: Invalid device [VUID-vkCreateSwapchainKHR-device-parameter]");
+        abort(); /* Intentionally fail so user can correct issue. */
+    }
+    VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)pCreateInfo->surface;
+    if (NULL != icd_surface->real_icd_surfaces) {
+        if ((VkSurfaceKHR)(uintptr_t)NULL != icd_surface->real_icd_surfaces[icd_index]) {
+            // We found the ICD, and there is an ICD KHR surface
+            // associated with it, so copy the CreateInfo struct
+            // and point it at the ICD's surface.
+            VkSwapchainCreateInfoKHR *pCreateCopy = loader_stack_alloc(sizeof(VkSwapchainCreateInfoKHR));
+            if (NULL == pCreateCopy) {
+                return VK_ERROR_OUT_OF_HOST_MEMORY;
             }
+            memcpy(pCreateCopy, pCreateInfo, sizeof(VkSwapchainCreateInfoKHR));
+            pCreateCopy->surface = icd_surface->real_icd_surfaces[icd_index];
+            return dev->loader_dispatch.core_dispatch.CreateSwapchainKHR(device, pCreateCopy, pAllocator, pSwapchain);
         }
-        return icd_term->dispatch.CreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain);
     }
-    return VK_SUCCESS;
+    return dev->loader_dispatch.core_dispatch.CreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain);
 }
 
 // This is the trampoline entrypoint for DestroySwapchainKHR
@@ -2164,15 +2166,18 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_GetDeviceGroupSurfacePresentModesKHR(V
     uint32_t icd_index = 0;
     struct loader_device *dev;
     struct loader_icd_term *icd_term = loader_get_icd_and_device(device, &dev, &icd_index);
-    if (NULL != icd_term && NULL != icd_term->dispatch.GetDeviceGroupSurfacePresentModesKHR) {
-        VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)surface;
-        if (NULL != icd_surface->real_icd_surfaces && (VkSurfaceKHR)(uintptr_t)NULL != icd_surface->real_icd_surfaces[icd_index]) {
-            return icd_term->dispatch.GetDeviceGroupSurfacePresentModesKHR(device, icd_surface->real_icd_surfaces[icd_index],
-                                                                           pModes);
-        }
-        return icd_term->dispatch.GetDeviceGroupSurfacePresentModesKHR(device, surface, pModes);
+    if (NULL == icd_term || NULL == dev || NULL == dev->loader_dispatch.core_dispatch.GetDeviceGroupSurfacePresentModesKHR) {
+        loader_log(NULL, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0,
+                   "vkGetDeviceGroupSurfacePresentModesKHR: Invalid device "
+                   "[VUID-vkGetDeviceGroupSurfacePresentModesKHR-device-parameter]");
+        abort(); /* Intentionally fail so user can correct issue. */
     }
-    return VK_SUCCESS;
+    VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)surface;
+    if (NULL != icd_surface->real_icd_surfaces && (VkSurfaceKHR)(uintptr_t)NULL != icd_surface->real_icd_surfaces[icd_index]) {
+        return dev->loader_dispatch.core_dispatch.GetDeviceGroupSurfacePresentModesKHR(
+            device, icd_surface->real_icd_surfaces[icd_index], pModes);
+    }
+    return dev->loader_dispatch.core_dispatch.GetDeviceGroupSurfacePresentModesKHR(device, surface, pModes);
 }
 
 LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDevicePresentRectanglesKHR(VkPhysicalDevice physicalDevice,
index cee011f2531713c2339aff8e602309f1fd98caa4..eabcc76c3d217caafc1d5afdc19c44820b770345 100644 (file)
@@ -596,6 +596,10 @@ class LoaderExtensionOutputGenerator(OutputGenerator):
         table = ''
         cur_extension_name = ''
 
+        skip_commands = ['vkCreateSwapchainKHR',
+                         'vkGetDeviceGroupSurfacePresentModesKHR'
+                        ]
+
         table += '// ICD function pointer dispatch table\n'
         table += 'struct loader_icd_term_dispatch {\n'
 
@@ -608,7 +612,7 @@ class LoaderExtensionOutputGenerator(OutputGenerator):
             for cur_cmd in commands:
                 is_inst_handle_type = cur_cmd.name in ADD_INST_CMDS or cur_cmd.handle_type == 'VkInstance' or cur_cmd.handle_type == 'VkPhysicalDevice'
                 if ((is_inst_handle_type or cur_cmd.name in DEVICE_CMDS_NEED_TERM) and
-                    (cur_cmd.name != 'vkGetInstanceProcAddr' and cur_cmd.name != 'vkEnumerateDeviceLayerProperties')):
+                    (cur_cmd.name != 'vkGetInstanceProcAddr' and cur_cmd.name != 'vkEnumerateDeviceLayerProperties') and cur_cmd.name not in skip_commands):
 
                     if cur_cmd.ext_name != cur_extension_name:
                         if 'VK_VERSION_' in cur_cmd.ext_name:
@@ -658,6 +662,8 @@ class LoaderExtensionOutputGenerator(OutputGenerator):
                               'vkEnumerateInstanceExtensionProperties',
                               'vkEnumerateInstanceLayerProperties',
                               'vkEnumerateInstanceVersion',
+                              'vkCreateSwapchainKHR',
+                              'vkGetDeviceGroupSurfacePresentModesKHR',
                              ]
 
         for x in range(0, 2):
@@ -918,7 +924,7 @@ class LoaderExtensionOutputGenerator(OutputGenerator):
         return tables
 
     #
-    # Create the appropriate trampoline (and possibly terminator) functinos
+    # Create the appropriate trampoline (and possibly terminator) functions
     def CreateTrampTermFuncs(self):
         entries = []
         funcs = ''
@@ -1254,7 +1260,7 @@ class LoaderExtensionOutputGenerator(OutputGenerator):
                         funcs += '            local_name_info.object = (uint64_t)(uintptr_t)phys_dev_term->phys_dev;\n'
                         funcs += '        // If this is a KHR_surface, and the ICD has created its own, we have to replace it with the proper one for the next call.\n'
                         funcs += '        } else if (pNameInfo->objectType == VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT) {\n'
-                        funcs += '            if (NULL != icd_term && NULL != icd_term->dispatch.CreateSwapchainKHR) {\n'
+                        funcs += '            if (NULL != dev && NULL != dev->loader_dispatch.core_dispatch.CreateSwapchainKHR) {\n'
                         funcs += '                VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)pNameInfo->object;\n'
                         funcs += '                if (NULL != icd_surface->real_icd_surfaces) {\n'
                         funcs += '                    local_name_info.object = (uint64_t)icd_surface->real_icd_surfaces[icd_index];\n'
@@ -1270,7 +1276,7 @@ class LoaderExtensionOutputGenerator(OutputGenerator):
                         funcs += '            local_tag_info.object = (uint64_t)(uintptr_t)phys_dev_term->phys_dev;\n'
                         funcs += '        // If this is a KHR_surface, and the ICD has created its own, we have to replace it with the proper one for the next call.\n'
                         funcs += '        } else if (pTagInfo->objectType == VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT) {\n'
-                        funcs += '            if (NULL != icd_term && NULL != icd_term->dispatch.CreateSwapchainKHR) {\n'
+                        funcs += '            if (NULL != dev && NULL != dev->loader_dispatch.core_dispatch.CreateSwapchainKHR) {\n'
                         funcs += '                VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)pTagInfo->object;\n'
                         funcs += '                if (NULL != icd_surface->real_icd_surfaces) {\n'
                         funcs += '                    local_tag_info.object = (uint64_t)icd_surface->real_icd_surfaces[icd_index];\n'
@@ -1286,7 +1292,7 @@ class LoaderExtensionOutputGenerator(OutputGenerator):
                         funcs += '            local_name_info.objectHandle = (uint64_t)(uintptr_t)phys_dev_term->phys_dev;\n'
                         funcs += '        // If this is a KHR_surface, and the ICD has created its own, we have to replace it with the proper one for the next call.\n'
                         funcs += '        } else if (pNameInfo->objectType == VK_OBJECT_TYPE_SURFACE_KHR) {\n'
-                        funcs += '            if (NULL != icd_term && NULL != icd_term->dispatch.CreateSwapchainKHR) {\n'
+                        funcs += '            if (NULL != dev && NULL != dev->loader_dispatch.core_dispatch.CreateSwapchainKHR) {\n'
                         funcs += '                VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)pNameInfo->objectHandle;\n'
                         funcs += '                if (NULL != icd_surface->real_icd_surfaces) {\n'
                         funcs += '                    local_name_info.objectHandle = (uint64_t)icd_surface->real_icd_surfaces[icd_index];\n'
@@ -1302,7 +1308,7 @@ class LoaderExtensionOutputGenerator(OutputGenerator):
                         funcs += '            local_tag_info.objectHandle = (uint64_t)(uintptr_t)phys_dev_term->phys_dev;\n'
                         funcs += '        // If this is a KHR_surface, and the ICD has created its own, we have to replace it with the proper one for the next call.\n'
                         funcs += '        } else if (pTagInfo->objectType == VK_OBJECT_TYPE_SURFACE_KHR) {\n'
-                        funcs += '            if (NULL != icd_term && NULL != icd_term->dispatch.CreateSwapchainKHR) {\n'
+                        funcs += '            if (NULL != dev && NULL != dev->loader_dispatch.core_dispatch.CreateSwapchainKHR) {\n'
                         funcs += '                VkIcdSurface *icd_surface = (VkIcdSurface *)(uintptr_t)pTagInfo->objectHandle;\n'
                         funcs += '                if (NULL != icd_surface->real_icd_surfaces) {\n'
                         funcs += '                    local_tag_info.objectHandle = (uint64_t)icd_surface->real_icd_surfaces[icd_index];\n'
index 75ccb8191d0bd833454c98485311cbe3c7c7aadb..890394d5d28ee31bf39fae89e82cea8eccd5e9f7 100644 (file)
@@ -593,6 +593,20 @@ VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateSwapchainKHR(VkDevice device, const
     return VK_SUCCESS;
 }
 
+VKAPI_ATTR VkResult VKAPI_CALL test_vkGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain,
+                                                            uint32_t* pSwapchainImageCount, VkImage* pSwapchainImages) {
+    std::vector<uint64_t> handles{123, 234, 345, 345, 456};
+    if (pSwapchainImages == nullptr) {
+        if (pSwapchainImageCount) *pSwapchainImageCount = static_cast<uint32_t>(handles.size());
+    } else if (pSwapchainImageCount) {
+        for (uint32_t i = 0; i < *pSwapchainImageCount && i < handles.size(); i++) {
+            pSwapchainImages[i] = to_nondispatch_handle<VkImage>(handles.back());
+        }
+        if (*pSwapchainImageCount < handles.size()) return VK_INCOMPLETE;
+    }
+    return VK_SUCCESS;
+}
+
 VKAPI_ATTR void VKAPI_CALL test_vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain,
                                                       const VkAllocationCallbacks* pAllocator) {
     if (swapchain != VK_NULL_HANDLE) {
@@ -600,7 +614,7 @@ VKAPI_ATTR void VKAPI_CALL test_vkDestroySwapchainKHR(VkDevice device, VkSwapcha
         auto found_iter = icd.swapchain_handles.erase(
             std::remove(icd.swapchain_handles.begin(), icd.swapchain_handles.end(), fake_swapchain_handle),
             icd.swapchain_handles.end());
-        if (found_iter == icd.swapchain_handles.end()) {
+        if (!icd.swapchain_handles.empty() && found_iter == icd.swapchain_handles.end()) {
             assert(false && "Swapchain not found during destroy!");
         }
     }
@@ -1295,6 +1309,7 @@ PFN_vkVoidFunction get_device_func(VkDevice device, const char* pName) {
     }
     if (string_eq(pName, "vkDestroyDevice")) return to_vkVoidFunction(test_vkDestroyDevice);
     if (string_eq(pName, "vkCreateSwapchainKHR")) return to_vkVoidFunction(test_vkCreateSwapchainKHR);
+    if (string_eq(pName, "vkGetSwapchainImagesKHR")) return to_vkVoidFunction(test_vkGetSwapchainImagesKHR);
     if (string_eq(pName, "vkDestroySwapchainKHR")) return to_vkVoidFunction(test_vkDestroySwapchainKHR);
     if (string_eq(pName, "vkCreateCommandPool")) return to_vkVoidFunction(test_vkCreateCommandPool);
     if (string_eq(pName, "vkAllocateCommandBuffers")) return to_vkVoidFunction(test_vkAllocateCommandBuffers);
index f7acaee190771c5e59b53295976ec9afa0f59b0e..6b47ff820f7e26443e05f2752973ca1e19067f7f 100644 (file)
@@ -637,6 +637,7 @@ DeviceFunctions::DeviceFunctions(const VulkanFunctions& vulkan_functions, VkDevi
     vkAllocateCommandBuffers = load(device, "vkAllocateCommandBuffers");
     vkDestroyCommandPool = load(device, "vkDestroyCommandPool");
     vkCreateSwapchainKHR = load(device, "vkCreateSwapchainKHR");
+    vkGetSwapchainImagesKHR = load(device, "vkGetSwapchainImagesKHR");
     vkDestroySwapchainKHR = load(device, "vkDestroySwapchainKHR");
 }
 
index 37791c26566435238b59808a16b13481aeead1c7..7ff100aaed0c802f819416a6a0fab63ce05e362d 100644 (file)
@@ -769,6 +769,7 @@ struct DeviceFunctions {
     PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers = nullptr;
     PFN_vkDestroyCommandPool vkDestroyCommandPool = nullptr;
     PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR = nullptr;
+    PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR = nullptr;
     PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR = nullptr;
 
     DeviceFunctions() = default;
index 7390596bdca9d77a8a4cca9bf5daccf2221d4f3b..f788089161c0391c575f2783d3361e64dcdd85f9 100644 (file)
@@ -1013,6 +1013,47 @@ TEST(CreateDevice, ConsecutiveCreateWithoutDestruction) {
     }
 }
 
+TEST(CreateDevice, SwapchainFunctional) {
+    FrameworkEnvironment env{};
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    auto& driver = env.get_test_icd();
+
+    MockQueueFamilyProperties family_props{{VK_QUEUE_GRAPHICS_BIT, 1, 0, {1, 1, 1}}, true};
+
+    driver.physical_devices.emplace_back("physical_device_0");
+    driver.physical_devices.back().queue_family_properties.push_back(family_props);
+
+    InstWrapper inst{env.vulkan_functions};
+    inst.CheckCreate();
+
+    VkPhysicalDevice phys_dev = inst.GetPhysDev();
+
+    uint32_t familyCount = 0;
+    inst->vkGetPhysicalDeviceQueueFamilyProperties(phys_dev, &familyCount, nullptr);
+    ASSERT_EQ(familyCount, 1U);
+
+    VkQueueFamilyProperties families;
+    inst->vkGetPhysicalDeviceQueueFamilyProperties(phys_dev, &familyCount, &families);
+    ASSERT_EQ(familyCount, 1U);
+    ASSERT_EQ(families, family_props.properties);
+
+    DeviceWrapper dev{inst};
+    dev.create_info.add_device_queue(DeviceQueueCreateInfo{}.add_priority(0.0f));
+
+    dev.CheckCreate(phys_dev);
+
+    VkSwapchainKHR swapchain{};
+    VkSwapchainCreateInfoKHR swap_create_info{};
+    DeviceFunctions funcs{*inst.functions, dev};
+    ASSERT_EQ(VK_SUCCESS, funcs.vkCreateSwapchainKHR(dev, &swap_create_info, nullptr, &swapchain));
+    uint32_t count = 0;
+    ASSERT_EQ(VK_SUCCESS, funcs.vkGetSwapchainImagesKHR(dev, swapchain, &count, nullptr));
+    ASSERT_GT(count, 0U);
+    std::array<VkImage, 16> images;
+    ASSERT_EQ(VK_SUCCESS, funcs.vkGetSwapchainImagesKHR(dev, swapchain, &count, images.data()));
+    funcs.vkDestroySwapchainKHR(dev, swapchain, nullptr);
+}
+
 TEST(TryLoadWrongBinaries, WrongICD) {
     FrameworkEnvironment env{};
     env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));