Add device extension layer tests
authorMark Young <marky@lunarg.com>
Thu, 30 Dec 2021 22:26:29 +0000 (15:26 -0700)
committerMark Young <marky@lunarg.com>
Fri, 7 Jan 2022 23:56:38 +0000 (16:56 -0700)
Need to make sure the loader behaves appropriately for layers which report
one or more device extensions.

tests/framework/framework_config.h.in
tests/framework/layer/CMakeLists.txt
tests/framework/layer/test_layer.cpp
tests/framework/layer/wrap_objects.cpp
tests/framework/test_environment.cpp
tests/loader_layer_tests.cpp

index 91536e969c34ac2405a0bf68872e27f7b07ad41d..694a90fe0ee2dcd09b1459deac19e0c9deb8c514 100644 (file)
Binary files a/tests/framework/framework_config.h.in and b/tests/framework/framework_config.h.in differ
index 62e53b4b7b47cf8bc996a5d649f6cf3c638b68f7..d1274a76aecfc204ad228ec1157a9c754cf0cb60 100644 (file)
@@ -53,13 +53,32 @@ target_link_libraries(test_layer_export_version_2 PRIVATE test_layer_deps)
 target_compile_definitions(test_layer_export_version_2 PRIVATE
     ${TEST_LAYER_VERSION_0_EXPORTS} ${TEST_LAYER_VERSION_1_EXPORTS} ${TEST_LAYER_VERSION_2_EXPORTS} )
 
-add_library(test_layer_wrap_objects SHARED wrap_objects.cpp)
+set(WRAP_LAYER_VERSION_1_EXPORTS TEST_LAYER_EXPORT_MAINT_1=1)
+set(WRAP_LAYER_VERSION_2_EXPORTS TEST_LAYER_EXPORT_PRESENT_IMAGE=1)
+set(WRAP_LAYER_VERSION_3_EXPORTS TEST_LAYER_EXPORT_MAINT_1=1 TEST_LAYER_EXPORT_PRESENT_IMAGE=1)
+
+add_library(test_layer_wrap_objects SHARED wrap_objects)
 target_link_libraries(test_layer_wrap_objects PRIVATE test_layer_deps)
 
+add_library(test_layer_wrap_objects_1 SHARED wrap_objects)
+target_link_libraries(test_layer_wrap_objects_1 PRIVATE test_layer_deps)
+target_compile_definitions(test_layer_wrap_objects_1 PRIVATE ${WRAP_LAYER_VERSION_1_EXPORTS})
+
+add_library(test_layer_wrap_objects_2 SHARED wrap_objects)
+target_link_libraries(test_layer_wrap_objects_2 PRIVATE test_layer_deps)
+target_compile_definitions(test_layer_wrap_objects_2 PRIVATE ${WRAP_LAYER_VERSION_2_EXPORTS})
+
+add_library(test_layer_wrap_objects_3 SHARED wrap_objects)
+target_link_libraries(test_layer_wrap_objects_3 PRIVATE test_layer_deps)
+target_compile_definitions(test_layer_wrap_objects_3 PRIVATE ${WRAP_LAYER_VERSION_3_EXPORTS})
+
 if(WIN32)
     target_sources(test_layer_export_base PRIVATE export_definitions/test_layer_base.def)
     target_sources(test_layer_export_version_0 PRIVATE export_definitions/test_layer_0.def)
     target_sources(test_layer_export_version_1 PRIVATE export_definitions/test_layer_1.def)
     target_sources(test_layer_export_version_2 PRIVATE export_definitions/test_layer_2.def)
     target_sources(test_layer_wrap_objects PRIVATE export_definitions/test_layer_wrap_objects.def)
+    target_sources(test_layer_wrap_objects_1 PRIVATE export_definitions/test_layer_wrap_objects.def)
+    target_sources(test_layer_wrap_objects_2 PRIVATE export_definitions/test_layer_wrap_objects.def)
+    target_sources(test_layer_wrap_objects_3 PRIVATE export_definitions/test_layer_wrap_objects.def)
 endif()
\ No newline at end of file
index 04905991b825b6e6232b0a82c20007d1d56ddf82..5aa0def75054457aca250a241248c4359726097b 100644 (file)
@@ -194,7 +194,12 @@ VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateDevice(VkPhysicalDevice physicalDevi
     // initialize layer's dispatch table
     layer_init_device_dispatch_table(device.device_handle, &device.dispatch_table, fpGetDeviceProcAddr);
 
-    if (layer.create_device_callback) result = layer.create_device_callback(layer);
+    if (layer.create_device_callback) {
+        result = layer.create_device_callback(layer);
+    }
+
+    // Need to add the created devices to the list so it can be freed
+    layer.created_devices.push_back(device);
 
     return result;
 }
index 8d67bab1ac36f11dd8f014970c5fb4ce88fc1640..76f4fd0fb9ff6ebff14a604bdc563555c571bff4 100644 (file)
@@ -19,7 +19,8 @@
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
+#include <string>
+#include <algorithm>
 #include <assert.h>
 #include <unordered_map>
 #include <memory>
 #include "loader/generated/vk_dispatch_table_helper.h"
 #include "loader/vk_loader_layer.h"
 
+// Export full support of VK_KHR_maintenance1 extension
+#ifndef TEST_LAYER_EXPORT_MAINT_1
+#define TEST_LAYER_EXPORT_MAINT_1 0
+#endif
+
+// Export full support of VK_KHR_shared_presentable_image extension
+#ifndef TEST_LAYER_EXPORT_PRESENT_IMAGE
+#define TEST_LAYER_EXPORT_PRESENT_IMAGE 0
+#endif
+
 #if !defined(VK_LAYER_EXPORT)
 #if defined(__GNUC__) && __GNUC__ >= 4
 #define VK_LAYER_EXPORT __attribute__((visibility("default")))
@@ -50,13 +61,17 @@ struct wrapped_inst_obj {
     PFN_vkSetInstanceLoaderData pfn_inst_init;
     struct wrapped_phys_dev_obj *ptr_phys_devs;  // any enumerated phys devs
     VkInstance obj;
+    bool layer_is_implicit;
 };
 
 struct wrapped_dev_obj {
-    VkLayerDispatchTable *disp;
-    VkLayerInstanceDispatchTable *layer_disp;  // TODO use this
-    PFN_vkSetDeviceLoaderData pfn_dev_init;    // TODO use this
-    void *obj;
+    VkLayerDispatchTable *loader_disp;
+    VkLayerDispatchTable disp;
+    PFN_vkSetDeviceLoaderData pfn_dev_init;
+    PFN_vkGetDeviceProcAddr pfn_get_dev_proc_addr;
+    VkDevice obj;
+    bool maintanence_1_enabled;
+    bool present_image_enabled;
 };
 
 struct wrapped_debutil_mess_obj {
@@ -64,10 +79,6 @@ struct wrapped_debutil_mess_obj {
     VkDebugUtilsMessengerEXT obj;
 };
 
-// typedef std::unordered_map<void *, VkLayerDispatchTable *> device_table_map;
-// typedef std::unordered_map<void *, VkLayerInstanceDispatchTable *> instance_table_map;
-typedef void *dispatch_key;
-
 VkInstance unwrap_instance(const VkInstance instance, wrapped_inst_obj **inst) {
     *inst = reinterpret_cast<wrapped_inst_obj *>(instance);
     return (*inst)->obj;
@@ -78,49 +89,16 @@ VkPhysicalDevice unwrap_phys_dev(const VkPhysicalDevice physical_device, wrapped
     return reinterpret_cast<VkPhysicalDevice>((*phys_dev)->obj);
 }
 
+VkDevice unwrap_device(const VkDevice device, wrapped_dev_obj **dev) {
+    *dev = reinterpret_cast<wrapped_dev_obj *>(device);
+    return (*dev)->obj;
+}
+
 VkDebugUtilsMessengerEXT unwrap_debutil_messenger(const VkDebugUtilsMessengerEXT messenger, wrapped_debutil_mess_obj **mess) {
     *mess = reinterpret_cast<wrapped_debutil_mess_obj *>(messenger);
     return (*mess)->obj;
 }
 
-dispatch_key get_dispatch_key(const void *object) { return (dispatch_key) * (VkLayerDispatchTable **)object; }
-
-template <typename T>
-struct TableMap {
-    using map_type = std::unordered_map<void *, std::unique_ptr<T>>;
-    map_type map;
-
-    template <typename U>
-    T *get_table(U *object) {
-        dispatch_key key = get_dispatch_key(object);
-        typename map_type::const_iterator it = map.find(static_cast<void *>(key));
-        assert(it != map.end() && "Not able to find dispatch entry");
-        return it->second.get();
-    }
-
-    void destroy_table(dispatch_key key) {
-        typename map_type::const_iterator it = map.find(static_cast<void *>(key));
-        if (it != map.end()) {
-            map.erase(it);
-        }
-    }
-
-    VkLayerDispatchTable *initDeviceTable(VkDevice device, const PFN_vkGetDeviceProcAddr gpa) {
-        dispatch_key key = get_dispatch_key(device);
-        typename map_type::const_iterator it = map.find((void *)key);
-
-        if (it == map.end()) {
-            map[(void *)key] = std::unique_ptr<T>(new VkLayerDispatchTable);
-            VkLayerDispatchTable *pTable = map.at((void *)key).get();
-            layer_init_device_dispatch_table(device, pTable, gpa);
-            return pTable;
-        } else {
-            return it->second.get();
-        }
-    }
-};
-TableMap<VkLayerDispatchTable> device_map;
-
 VkLayerInstanceCreateInfo *get_chain_info(const VkInstanceCreateInfo *pCreateInfo, VkLayerFunction func) {
     VkLayerInstanceCreateInfo *chain_info = (VkLayerInstanceCreateInfo *)pCreateInfo->pNext;
     while (chain_info && !(chain_info->sType == VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO && chain_info->function == func)) {
@@ -177,10 +155,22 @@ VKAPI_ATTR VkResult VKAPI_CALL wrap_vkCreateInstance(const VkInstanceCreateInfo
         if (VK_SUCCESS != result) return result;
     } else {
         inst->pfn_inst_init = NULL;
-        inst->loader_disp = *(reinterpret_cast<VkLayerInstanceDispatchTable **>(*pInstance));
+        inst->loader_disp = *(reinterpret_cast<VkLayerInstanceDispatchTable **>(inst->obj));
     }
     layer_init_instance_dispatch_table(*pInstance, &inst->layer_disp, fpGetInstanceProcAddr);
-
+    bool found = false;
+    for (uint32_t layer = 0; layer < pCreateInfo->enabledLayerCount; ++layer) {
+        std::string layer_name = pCreateInfo->ppEnabledLayerNames[layer];
+        std::transform(layer_name.begin(), layer_name.end(), layer_name.begin(), ::tolower);
+        if (layer_name.find("wrap") != std::string::npos &&
+            layer_name.find("obj") != std::string::npos) {
+            found = true;
+            break;
+        }
+    }
+    if (!found) {
+        inst->layer_is_implicit = true;
+    }
     return result;
 }
 
@@ -340,8 +330,8 @@ VKAPI_ATTR VkResult VKAPI_CALL wrap_vkCreateScreenSurfaceQNX(VkInstance instance
 }
 #endif  // VK_USE_PLATFORM_SCREEN_QNX
 
-VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDevices(VkInstance instance, uint32_t *pPhysicalDeviceCount,
-                                                          VkPhysicalDevice *pPhysicalDevices) {
+VKAPI_ATTR VkResult VKAPI_CALL wrap_vkEnumeratePhysicalDevices(VkInstance instance, uint32_t *pPhysicalDeviceCount,
+                                                               VkPhysicalDevice *pPhysicalDevices) {
     wrapped_inst_obj *inst;
     auto vk_inst = unwrap_instance(instance, &inst);
     VkResult result = inst->layer_disp.EnumeratePhysicalDevices(vk_inst, pPhysicalDeviceCount, pPhysicalDevices);
@@ -384,77 +374,164 @@ VKAPI_ATTR void VKAPI_CALL wrap_vkGetPhysicalDeviceQueueFamilyProperties(VkPhysi
                                                                       pQueueFamilyProperties);
 }
 
+VKAPI_ATTR VkResult VKAPI_CALL wrap_vkEnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, const char *pLayerName,
+                                                                         uint32_t *pPropertyCount,
+                                                                         VkExtensionProperties *pProperties) {
+    VkResult result = VK_SUCCESS;
+    wrapped_phys_dev_obj *phys_dev;
+    auto vk_phys_dev = unwrap_phys_dev(physicalDevice, &phys_dev);
+
+    if (phys_dev->inst->layer_is_implicit || (pLayerName && !strcmp(pLayerName, global_layer.layerName))) {
+        uint32_t ext_count = 0;
+#if TEST_LAYER_EXPORT_MAINT_1
+        ext_count++;
+#endif
+#if TEST_LAYER_EXPORT_PRESENT_IMAGE
+        ext_count++;
+#endif
+        if (pPropertyCount) {
+            if (pProperties) {
+                uint32_t count = ext_count;
+                if (count > *pPropertyCount) {
+                    count = *pPropertyCount;
+                    result = VK_INCOMPLETE;
+                }
+
+                ext_count = 0;
+#if TEST_LAYER_EXPORT_MAINT_1
+                if (ext_count < count) {
+                    strcpy(pProperties[ext_count].extensionName, VK_KHR_MAINTENANCE1_EXTENSION_NAME);
+                    pProperties[ext_count].specVersion = 2;
+                    ext_count++;
+                }
+#endif
+#if TEST_LAYER_EXPORT_PRESENT_IMAGE
+                if (ext_count < count) {
+                    strcpy(pProperties[ext_count].extensionName, VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME);
+                    pProperties[ext_count].specVersion = 1;
+                    ext_count++;
+                }
+#endif
+            }
+            *pPropertyCount = ext_count;
+        }
+        return result;
+    } else {
+        return phys_dev->inst->layer_disp.EnumerateDeviceExtensionProperties(vk_phys_dev, pLayerName, pPropertyCount, pProperties);
+    }
+}
+
 VKAPI_ATTR VkResult VKAPI_CALL wrap_vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo *pCreateInfo,
                                                    const VkAllocationCallbacks *pAllocator, VkDevice *pDevice) {
     wrapped_phys_dev_obj *phys_dev;
     auto vk_phys_dev = unwrap_phys_dev(physicalDevice, &phys_dev);
     VkLayerDeviceCreateInfo *chain_info = get_chain_info(pCreateInfo, VK_LAYER_LINK_INFO);
-    PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr;
-    PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr = chain_info->u.pLayerInfo->pfnNextGetDeviceProcAddr;
-    PFN_vkCreateDevice fpCreateDevice = (PFN_vkCreateDevice)fpGetInstanceProcAddr(phys_dev->inst->obj, "vkCreateDevice");
-    if (fpCreateDevice == NULL) {
+    PFN_vkGetInstanceProcAddr pfn_get_inst_proc_addr = chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr;
+    PFN_vkGetDeviceProcAddr pfn_get_dev_proc_addr = chain_info->u.pLayerInfo->pfnNextGetDeviceProcAddr;
+    PFN_vkCreateDevice pfn_create_device = (PFN_vkCreateDevice)pfn_get_inst_proc_addr(phys_dev->inst->obj, "vkCreateDevice");
+    if (pfn_create_device == NULL) {
         return VK_ERROR_INITIALIZATION_FAILED;
     }
     // Advance the link info for the next element on the chain
     chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext;
-    VkResult result = fpCreateDevice(vk_phys_dev, pCreateInfo, pAllocator, pDevice);
+    VkResult result = pfn_create_device(vk_phys_dev, pCreateInfo, pAllocator, pDevice);
     if (result != VK_SUCCESS) {
         return result;
     }
-    device_map.initDeviceTable(*pDevice, fpGetDeviceProcAddr);
+    auto dev = new wrapped_dev_obj;
+    if (!dev) {
+        return VK_ERROR_OUT_OF_HOST_MEMORY;
+    }
+    memset(dev, 0, sizeof(*dev));
+    dev->obj = *pDevice;
+    dev->pfn_get_dev_proc_addr = pfn_get_dev_proc_addr;
+    *pDevice = reinterpret_cast<VkDevice>(dev);
 
-#if 0  // TODO add once device is wrapped
-    // store the loader callback for initializing created dispatchable objects
+    // Store the loader callback for initializing created dispatchable objects
     chain_info = get_chain_info(pCreateInfo, VK_LOADER_DATA_CALLBACK);
     if (chain_info) {
         dev->pfn_dev_init = chain_info->u.pfnSetDeviceLoaderData;
+        result = dev->pfn_dev_init(dev->obj, reinterpret_cast<void *>(dev));
+        if (VK_SUCCESS != result) {
+            return result;
+        }
     } else {
         dev->pfn_dev_init = NULL;
     }
+
+    // Initialize layer's dispatch table
+    layer_init_device_dispatch_table(dev->obj, &dev->disp, pfn_get_dev_proc_addr);
+
+    for (uint32_t ext = 0; ext < pCreateInfo->enabledExtensionCount; ++ext) {
+        if (!strcmp(pCreateInfo->ppEnabledExtensionNames[ext], VK_KHR_MAINTENANCE1_EXTENSION_NAME)) {
+#if TEST_LAYER_EXPORT_MAINT_1
+            dev->maintanence_1_enabled = true;
+#endif
+        }
+        if (!strcmp(pCreateInfo->ppEnabledExtensionNames[ext], VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME)) {
+#if TEST_LAYER_EXPORT_PRESENT_IMAGE
+            dev->present_image_enabled = true;
 #endif
+        }
+    }
+
     return result;
 }
 
 VKAPI_ATTR void VKAPI_CALL wrap_vkDestroyDevice(VkDevice device, const VkAllocationCallbacks *pAllocator) {
-    dispatch_key key = get_dispatch_key(device);
-    VkLayerDispatchTable *pDisp = device_map.get_table(device);
-    pDisp->DestroyDevice(device, pAllocator);
-    device_map.destroy_table(key);
+    wrapped_dev_obj *dev;
+    auto vk_dev = unwrap_device(device, &dev);
+    dev->disp.DestroyDevice(vk_dev, pAllocator);
+    delete dev;
 }
 
-VKAPI_ATTR VkResult VKAPI_CALL wrap_vkEnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, const char *pLayerName,
-                                                                         uint32_t *pPropertyCount,
-                                                                         VkExtensionProperties *pProperties) {
-    wrapped_phys_dev_obj *phys_dev;
-    auto vk_phys_dev = unwrap_phys_dev(physicalDevice, &phys_dev);
+VKAPI_ATTR void VKAPI_CALL wrap_vkTrimCommandPoolKHR(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlags flags) {}
 
-    if (pLayerName && !strcmp(pLayerName, global_layer.layerName)) {
-        *pPropertyCount = 0;
-        return VK_SUCCESS;
-    }
+VKAPI_ATTR VkResult VKAPI_CALL wrap_vkGetSwapchainStatusKHR(VkDevice device, VkSwapchainKHR swapchain) { return VK_SUCCESS; }
 
-    return phys_dev->inst->layer_disp.EnumerateDeviceExtensionProperties(vk_phys_dev, pLayerName, pPropertyCount, pProperties);
-}
-
-PFN_vkVoidFunction layer_intercept_device_proc(const char *name) {
+PFN_vkVoidFunction layer_intercept_device_proc(wrapped_dev_obj *dev, const char *name) {
     if (!name || name[0] != 'v' || name[1] != 'k') return NULL;
 
     name += 2;
-    if (!strcmp(name, "GetPhysicalDeviceQueueFamilyProperties"))
-        return (PFN_vkVoidFunction)wrap_vkGetPhysicalDeviceQueueFamilyProperties;
     if (!strcmp(name, "CreateDevice")) return (PFN_vkVoidFunction)wrap_vkCreateDevice;
     if (!strcmp(name, "DestroyDevice")) return (PFN_vkVoidFunction)wrap_vkDestroyDevice;
 
+    if (dev->maintanence_1_enabled && !strcmp(name, "TrimCommandPoolKHR")) return (PFN_vkVoidFunction)wrap_vkTrimCommandPoolKHR;
+    if (dev->present_image_enabled && !strcmp(name, "GetSwapchainStatusKHR"))
+        return (PFN_vkVoidFunction)wrap_vkGetSwapchainStatusKHR;
+
     return NULL;
 }
 
+VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL wrap_vkGetDeviceProcAddr(VkDevice device, const char *funcName) {
+    PFN_vkVoidFunction addr;
+
+    if (!strcmp("vkGetDeviceProcAddr", funcName)) {
+        return (PFN_vkVoidFunction)wrap_vkGetDeviceProcAddr;
+    }
+
+    if (device == VK_NULL_HANDLE) {
+        return NULL;
+    }
+
+    wrapped_dev_obj *dev;
+    unwrap_device(device, &dev);
+
+    addr = layer_intercept_device_proc(dev, funcName);
+    if (addr) return addr;
+
+    return dev->pfn_get_dev_proc_addr(dev->obj, funcName);
+}
+
 PFN_vkVoidFunction layer_intercept_instance_proc(const char *name) {
     if (!name || name[0] != 'v' || name[1] != 'k') return NULL;
 
     name += 2;
     if (!strcmp(name, "DestroyInstance")) return (PFN_vkVoidFunction)wrap_vkDestroyInstance;
     if (!strcmp(name, "CreateDevice")) return (PFN_vkVoidFunction)wrap_vkCreateDevice;
-    if (!strcmp(name, "EnumeratePhysicalDevices")) return (PFN_vkVoidFunction)vkEnumeratePhysicalDevices;
+    if (!strcmp(name, "EnumeratePhysicalDevices")) return (PFN_vkVoidFunction)wrap_vkEnumeratePhysicalDevices;
+
+    if (!strcmp(name, "EnumerateDeviceExtensionProperties")) return (PFN_vkVoidFunction)wrap_vkEnumerateDeviceExtensionProperties;
 
     if (!strcmp(name, "CreateDebugUtilsMessengerEXT")) return (PFN_vkVoidFunction)wrap_vkCreateDebugUtilsMessengerEXT;
     if (!strcmp(name, "DestroyDebugUtilsMessengerEXT")) return (PFN_vkVoidFunction)wrap_vkDestroyDebugUtilsMessengerEXT;
@@ -463,8 +540,6 @@ PFN_vkVoidFunction layer_intercept_instance_proc(const char *name) {
     if (!strcmp(name, "GetPhysicalDeviceQueueFamilyProperties"))
         return (PFN_vkVoidFunction)wrap_vkGetPhysicalDeviceQueueFamilyProperties;
 
-    if (!strcmp(name, "EnumerateDeviceExtensionProperties")) return (PFN_vkVoidFunction)wrap_vkEnumerateDeviceExtensionProperties;
-
 #ifdef VK_USE_PLATFORM_ANDROID_KHR
     if (!strcmp(name, "CreateAndroidSurfaceKHR")) return (PFN_vkVoidFunction)wrap_vkCreateAndroidSurfaceKHR;
 #endif  // VK_USE_PLATFORM_WIN32_KHR
@@ -513,27 +588,6 @@ PFN_vkVoidFunction layer_intercept_instance_proc(const char *name) {
     return NULL;
 }
 
-VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL wrap_vkGetDeviceProcAddr(VkDevice device, const char *funcName) {
-    PFN_vkVoidFunction addr;
-
-    if (!strcmp("vkGetDeviceProcAddr", funcName)) {
-        return (PFN_vkVoidFunction)wrap_vkGetDeviceProcAddr;
-    }
-
-    addr = layer_intercept_device_proc(funcName);
-    if (addr) return addr;
-    if (device == VK_NULL_HANDLE) {
-        return NULL;
-    }
-
-    VkLayerDispatchTable *pDisp = device_map.get_table(device);
-    if (pDisp->GetDeviceProcAddr == NULL) {
-        return NULL;
-    }
-
-    return pDisp->GetDeviceProcAddr(device, funcName);
-}
-
 VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL wrap_vkGetInstanceProcAddr(VkInstance instance, const char *funcName) {
     PFN_vkVoidFunction addr;
 
index 1becd43b9c597374d831476cb5cd8357c92ed9bf..65cd070b99cd440713c0e29e155a49b5f3cffae1 100644 (file)
@@ -211,6 +211,27 @@ void FrameworkEnvironment::add_explicit_layer(ManifestLayer layer_manifest, cons
 
 void FrameworkEnvironment::add_layer_impl(ManifestLayer& layer_manifest, const std::string& json_name,
                                           fs::FolderManager& folder_manager, ManifestCategory category) {
+    // We have a special case for any of the wrap objects layers so get the string to the path
+    std::string wrap_layer = fs::fixup_backslashes_in_path(fs::path(TEST_LAYER_WRAP_OBJECTS)).str();
+
+    // Strip off ending
+#if defined(WIN32)
+    size_t index = 0;
+    index = wrap_layer.find(".dll", index);
+    assert(index != std::string::npos);
+    wrap_layer.replace(index, 4, "");
+#elif defined(__APPLE__)
+    size_t index = 0;
+    index = wrap_layer.find(".dylib", index);
+    assert(index != std::string::npos);
+    wrap_layer.replace(index, 6, "");
+#else
+    size_t index = 0;
+    index = wrap_layer.find(".so", index);
+    assert(index != std::string::npos);
+    wrap_layer.replace(index, 3, "");
+#endif
+
     for (auto& layer : layer_manifest.layers) {
         size_t cur_layer_index = layers.size();
         if (!layer.lib_path.str().empty()) {
@@ -218,9 +239,9 @@ void FrameworkEnvironment::add_layer_impl(ManifestLayer& layer_manifest, const s
 
             auto new_layer_location = folder_manager.copy_file(layer.lib_path, new_layer_name);
 
-            // Don't load the layer binary if using the wrap objects layer, since it doesn't export the same interface functions
-            if (fs::fixup_backslashes_in_path(layer.lib_path).str() !=
-                fs::fixup_backslashes_in_path(fs::path(TEST_LAYER_WRAP_OBJECTS)).str()) {
+            // Don't load the layer binary if using any of the wrap objects layers, since it doesn't export the same interface
+            // functions
+            if (fs::fixup_backslashes_in_path(layer.lib_path).str().rfind(wrap_layer) != 0) {
                 layers.push_back(TestLayerHandle(new_layer_location));
                 layers.back().reset_layer();
             }
index 8c682d78482b9c13fd8904b9f24864f2c799b91a..fe1c6525e824f9b38591e86152b987dbfebe9bf0 100644 (file)
@@ -41,6 +41,7 @@ class LayerTests : public ::testing::Test {
 // Subtyping for organization
 class ExplicitLayers : public LayerTests {};
 class ImplicitLayers : public LayerTests {};
+class LayerExtensions : public LayerTests {};
 class MetaLayers : public LayerTests {};
 class OverrideMetaLayer : public LayerTests {};
 class LayerCreateInstance : public LayerTests {};
@@ -301,7 +302,7 @@ TEST_F(LayerCreateInstance, GetPhysicalDeviceProperties2KHR) {
 TEST_F(ExplicitLayers, WrapObjects) {
     auto& driver = env->get_test_icd();
 
-    const char* wrap_objects_name = "WrapObjectsLayer";
+    const char* wrap_objects_name = "VK_LAYER_LUNARG_wrap_objects";
     env->add_explicit_layer(
         ManifestLayer{}.add_layer(
             ManifestLayer::LayerDescription{}.set_name(wrap_objects_name).set_lib_path(TEST_LAYER_WRAP_OBJECTS)),
@@ -363,4 +364,526 @@ TEST_F(ExplicitLayers, WrapObjects) {
         dev.create_info.add_device_queue(DeviceQueueCreateInfo{}.add_priority(0.0f));
         dev.CheckCreate(phys_dev);
     }
-}
\ No newline at end of file
+}
+
+TEST_F(LayerExtensions, ImplicitNoAdditionalDeviceExtension) {
+    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);
+
+    const char* implicit_layer_name = "VK_LAYER_LUNARG_wrap_objects";
+    const char* enable_env_var = "ENABLE_ME";
+    const char* disable_env_var = "DISABLE_ME";
+
+    env->add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
+                                                          .set_name(implicit_layer_name)
+                                                          .set_lib_path(TEST_LAYER_WRAP_OBJECTS)
+                                                          .set_disable_environment(disable_env_var)
+                                                          .set_enable_environment(enable_env_var)),
+                            "implicit_wrap_layer_no_ext.json");
+
+    uint32_t count = 0;
+    ASSERT_EQ(VK_SUCCESS, env->vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr));
+    ASSERT_EQ(count, 1);
+
+    // // set enable env-var, layer should load
+    set_env_var(enable_env_var, "1");
+    CheckLogForLayerString(*env, implicit_layer_name, true);
+
+    InstWrapper inst{env->vulkan_functions};
+    inst.CheckCreate();
+    VkPhysicalDevice phys_dev = inst.GetPhysDev();
+
+    uint32_t extension_count = 0;
+    std::vector<VkExtensionProperties> extension_props;
+    ASSERT_EQ(VK_SUCCESS, env->vulkan_functions.vkEnumerateDeviceExtensionProperties(phys_dev, nullptr, &extension_count, nullptr));
+    if (extension_count > 0) {
+        extension_props.resize(extension_count);
+        ASSERT_EQ(VK_SUCCESS, env->vulkan_functions.vkEnumerateDeviceExtensionProperties(phys_dev, nullptr, &extension_count,
+                                                                                         extension_props.data()));
+
+        // Make sure the extensions that are implemented only in the test layers is not present.
+        for (uint32_t ext = 0; ext < extension_count; ++ext) {
+            ASSERT_NE(0, strcmp(extension_props[ext].extensionName, VK_KHR_MAINTENANCE1_EXTENSION_NAME));
+            ASSERT_NE(0, strcmp(extension_props[ext].extensionName, VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME));
+        }
+    }
+
+    DeviceWrapper dev{inst};
+    dev.create_info.add_device_queue(DeviceQueueCreateInfo{}.add_priority(0.0f));
+    dev.CheckCreate(phys_dev);
+
+    // Make sure all the function pointers are NULL as well
+    ASSERT_EQ(nullptr, dev->vkGetDeviceProcAddr(dev.dev, "vkTrimCommandPoolKHR"));
+    ASSERT_EQ(nullptr, dev->vkGetDeviceProcAddr(dev.dev, "vkGetSwapchainStatusKHR"));
+    ASSERT_EQ(nullptr, dev->vkGetDeviceProcAddr(dev.dev, "vkSetDeviceMemoryPriorityEXT"));
+
+    remove_env_var(enable_env_var);
+}
+
+TEST_F(LayerExtensions, ImplicitMaintenanceDeviceExtension) {
+    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);
+
+    const char* implicit_layer_name = "VK_LAYER_LUNARG_wrap_objects";
+    const char* enable_env_var = "ENABLE_ME";
+    const char* disable_env_var = "DISABLE_ME";
+
+    env->add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
+                                                          .set_name(implicit_layer_name)
+                                                          .set_lib_path(TEST_LAYER_WRAP_OBJECTS_1)
+                                                          .set_disable_environment(disable_env_var)
+                                                          .set_enable_environment(enable_env_var)),
+                            "implicit_wrap_layer_maint.json");
+
+    uint32_t count = 0;
+    ASSERT_EQ(VK_SUCCESS, env->vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr));
+    ASSERT_EQ(count, 1);
+
+    // // set enable env-var, layer should load
+    set_env_var(enable_env_var, "1");
+    CheckLogForLayerString(*env, implicit_layer_name, true);
+
+    InstWrapper inst{env->vulkan_functions};
+    inst.CheckCreate();
+    VkPhysicalDevice phys_dev = inst.GetPhysDev();
+
+    uint32_t extension_count = 0;
+    std::vector<VkExtensionProperties> extension_props;
+    ASSERT_EQ(VK_SUCCESS, env->vulkan_functions.vkEnumerateDeviceExtensionProperties(phys_dev, nullptr, &extension_count, nullptr));
+    ASSERT_GE(extension_count, 1);
+    extension_props.resize(extension_count);
+    ASSERT_EQ(VK_SUCCESS, env->vulkan_functions.vkEnumerateDeviceExtensionProperties(phys_dev, nullptr, &extension_count,
+                                                                                     extension_props.data()));
+
+    // Make sure only the one extension implemented by the enabled implicit layer is present.
+    bool found = false;
+    for (uint32_t ext = 0; ext < extension_count; ++ext) {
+        if (!strcmp(extension_props[ext].extensionName, VK_KHR_MAINTENANCE1_EXTENSION_NAME)) {
+            found = true;
+        }
+        ASSERT_NE(0, strcmp(extension_props[ext].extensionName, VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME));
+    }
+    ASSERT_EQ(true, found);
+
+    DeviceWrapper dev{inst};
+    dev.create_info.add_extension(VK_KHR_MAINTENANCE1_EXTENSION_NAME).add_device_queue(DeviceQueueCreateInfo{}.add_priority(0.0f));
+    dev.CheckCreate(phys_dev);
+
+    // Make sure only the appropriate function pointers are NULL as well
+    ASSERT_NE(nullptr, dev->vkGetDeviceProcAddr(dev.dev, "vkTrimCommandPoolKHR"));
+    ASSERT_EQ(nullptr, dev->vkGetDeviceProcAddr(dev.dev, "vkGetSwapchainStatusKHR"));
+
+    remove_env_var(enable_env_var);
+}
+
+TEST_F(LayerExtensions, ImplicitPresentImageDeviceExtension) {
+    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);
+
+    const char* implicit_layer_name = "VK_LAYER_LUNARG_wrap_objects";
+    const char* enable_env_var = "ENABLE_ME";
+    const char* disable_env_var = "DISABLE_ME";
+
+    env->add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
+                                                          .set_name(implicit_layer_name)
+                                                          .set_lib_path(TEST_LAYER_WRAP_OBJECTS_2)
+                                                          .set_disable_environment(disable_env_var)
+                                                          .set_enable_environment(enable_env_var)),
+                            "implicit_wrap_layer_maint.json");
+
+    uint32_t count = 0;
+    ASSERT_EQ(VK_SUCCESS, env->vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr));
+    ASSERT_EQ(count, 1);
+
+    // // set enable env-var, layer should load
+    set_env_var(enable_env_var, "1");
+    CheckLogForLayerString(*env, implicit_layer_name, true);
+
+    InstWrapper inst{env->vulkan_functions};
+    inst.CheckCreate();
+    VkPhysicalDevice phys_dev = inst.GetPhysDev();
+
+    uint32_t extension_count = 0;
+    std::vector<VkExtensionProperties> extension_props;
+    ASSERT_EQ(VK_SUCCESS, env->vulkan_functions.vkEnumerateDeviceExtensionProperties(phys_dev, nullptr, &extension_count, nullptr));
+    ASSERT_GE(extension_count, 1);
+    extension_props.resize(extension_count);
+    ASSERT_EQ(VK_SUCCESS, env->vulkan_functions.vkEnumerateDeviceExtensionProperties(phys_dev, nullptr, &extension_count,
+                                                                                     extension_props.data()));
+
+    // Make sure only the one extension implemented by the enabled implicit layer is present.
+    bool found = false;
+    for (uint32_t ext = 0; ext < extension_count; ++ext) {
+        ASSERT_NE(0, strcmp(extension_props[ext].extensionName, VK_KHR_MAINTENANCE1_EXTENSION_NAME));
+        if (!strcmp(extension_props[ext].extensionName, VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME)) {
+            found = true;
+        }
+    }
+    ASSERT_EQ(true, found);
+
+    DeviceWrapper dev{inst};
+    dev.create_info.add_extension(VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME)
+        .add_device_queue(DeviceQueueCreateInfo{}.add_priority(0.0f));
+    dev.CheckCreate(phys_dev);
+
+    // Make sure only the appropriate function pointers are NULL as well
+    ASSERT_EQ(nullptr, dev->vkGetDeviceProcAddr(dev.dev, "vkTrimCommandPoolKHR"));
+    ASSERT_NE(nullptr, dev->vkGetDeviceProcAddr(dev.dev, "vkGetSwapchainStatusKHR"));
+
+    remove_env_var(enable_env_var);
+}
+
+TEST_F(LayerExtensions, ImplicitBothDeviceExtensions) {
+    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);
+
+    const char* implicit_layer_name = "VK_LAYER_LUNARG_wrap_objects";
+    const char* enable_env_var = "ENABLE_ME";
+    const char* disable_env_var = "DISABLE_ME";
+
+    env->add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
+                                                          .set_name(implicit_layer_name)
+                                                          .set_lib_path(TEST_LAYER_WRAP_OBJECTS_3)
+                                                          .set_disable_environment(disable_env_var)
+                                                          .set_enable_environment(enable_env_var)),
+                            "implicit_wrap_layer_maint.json");
+
+    uint32_t count = 0;
+    ASSERT_EQ(VK_SUCCESS, env->vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr));
+    ASSERT_EQ(count, 1);
+
+    // // set enable env-var, layer should load
+    set_env_var(enable_env_var, "1");
+    CheckLogForLayerString(*env, implicit_layer_name, true);
+
+    InstWrapper inst{env->vulkan_functions};
+    inst.CheckCreate();
+    VkPhysicalDevice phys_dev = inst.GetPhysDev();
+
+    uint32_t extension_count = 0;
+    std::vector<VkExtensionProperties> extension_props;
+    ASSERT_EQ(VK_SUCCESS, env->vulkan_functions.vkEnumerateDeviceExtensionProperties(phys_dev, nullptr, &extension_count, nullptr));
+    ASSERT_GE(extension_count, 2);
+    extension_props.resize(extension_count);
+    ASSERT_EQ(VK_SUCCESS, env->vulkan_functions.vkEnumerateDeviceExtensionProperties(phys_dev, nullptr, &extension_count,
+                                                                                     extension_props.data()));
+
+    // Make sure only the one extension implemented by the enabled implicit layer is present.
+    bool found[2] = {false, false};
+    for (uint32_t ext = 0; ext < extension_count; ++ext) {
+        if (!strcmp(extension_props[ext].extensionName, VK_KHR_MAINTENANCE1_EXTENSION_NAME)) {
+            found[0] = true;
+        }
+        if (!strcmp(extension_props[ext].extensionName, VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME)) {
+            found[1] = true;
+        }
+    }
+    for (uint32_t ext = 0; ext < 2; ++ext) {
+        ASSERT_EQ(true, found[ext]);
+    }
+
+    DeviceWrapper dev{inst};
+    dev.create_info.add_extension(VK_KHR_MAINTENANCE1_EXTENSION_NAME)
+        .add_extension(VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME)
+        .add_device_queue(DeviceQueueCreateInfo{}.add_priority(0.0f));
+    dev.CheckCreate(phys_dev);
+
+    // Make sure only the appropriate function pointers are NULL as well
+    ASSERT_NE(nullptr, dev->vkGetDeviceProcAddr(dev.dev, "vkTrimCommandPoolKHR"));
+    ASSERT_NE(nullptr, dev->vkGetDeviceProcAddr(dev.dev, "vkGetSwapchainStatusKHR"));
+
+    remove_env_var(enable_env_var);
+}
+
+TEST_F(LayerExtensions, ExplicitNoAdditionalDeviceExtension) {
+    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);
+
+    const char* explicit_layer_name = "VK_LAYER_LUNARG_wrap_objects";
+    env->add_explicit_layer(
+        ManifestLayer{}.add_layer(
+            ManifestLayer::LayerDescription{}.set_name(explicit_layer_name).set_lib_path(TEST_LAYER_WRAP_OBJECTS)),
+        "explicit_wrap_layer_no_ext.json");
+
+    uint32_t count = 0;
+    ASSERT_EQ(VK_SUCCESS, env->vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr));
+    ASSERT_EQ(count, 1);
+
+    InstWrapper inst{env->vulkan_functions};
+    inst.create_info.add_layer(explicit_layer_name);
+    inst.CheckCreate();
+    VkPhysicalDevice phys_dev = inst.GetPhysDev();
+
+    uint32_t extension_count = 0;
+    std::vector<VkExtensionProperties> extension_props;
+    ASSERT_EQ(VK_SUCCESS, env->vulkan_functions.vkEnumerateDeviceExtensionProperties(phys_dev, nullptr, &extension_count, nullptr));
+    if (extension_count > 0) {
+        extension_props.resize(extension_count);
+        ASSERT_EQ(VK_SUCCESS, env->vulkan_functions.vkEnumerateDeviceExtensionProperties(phys_dev, nullptr, &extension_count,
+                                                                                         extension_props.data()));
+
+        // Make sure the extensions are not present
+        for (uint32_t ext = 0; ext < extension_count; ++ext) {
+            ASSERT_NE(0, strcmp(extension_props[ext].extensionName, VK_KHR_MAINTENANCE1_EXTENSION_NAME));
+            ASSERT_NE(0, strcmp(extension_props[ext].extensionName, VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME));
+        }
+    }
+
+    // Now query by layer name.
+    extension_count = 0;
+    extension_props.clear();
+    ASSERT_EQ(VK_SUCCESS,
+              env->vulkan_functions.vkEnumerateDeviceExtensionProperties(phys_dev, explicit_layer_name, &extension_count, nullptr));
+    if (extension_count > 0) {
+        extension_props.resize(extension_count);
+        ASSERT_EQ(VK_SUCCESS, env->vulkan_functions.vkEnumerateDeviceExtensionProperties(phys_dev, explicit_layer_name,
+                                                                                         &extension_count, extension_props.data()));
+
+        // Make sure the extensions still aren't present in this layer
+        for (uint32_t ext = 0; ext < extension_count; ++ext) {
+            ASSERT_NE(0, strcmp(extension_props[ext].extensionName, VK_KHR_MAINTENANCE1_EXTENSION_NAME));
+            ASSERT_NE(0, strcmp(extension_props[ext].extensionName, VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME));
+        }
+    }
+
+    DeviceWrapper dev{inst};
+    dev.create_info.add_device_queue(DeviceQueueCreateInfo{}.add_priority(0.0f));
+    dev.CheckCreate(phys_dev);
+
+    // Make sure all the function pointers are NULL as well
+    ASSERT_EQ(nullptr, dev->vkGetDeviceProcAddr(dev.dev, "vkTrimCommandPoolKHR"));
+    ASSERT_EQ(nullptr, dev->vkGetDeviceProcAddr(dev.dev, "vkGetSwapchainStatusKHR"));
+    ASSERT_EQ(nullptr, dev->vkGetDeviceProcAddr(dev.dev, "vkSetDeviceMemoryPriorityEXT"));
+}
+
+TEST_F(LayerExtensions, ExplicitMaintenanceDeviceExtension) {
+    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);
+
+    const char* explicit_layer_name = "VK_LAYER_LUNARG_wrap_objects";
+    env->add_explicit_layer(
+        ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
+                                      .set_name(explicit_layer_name)
+                                      .set_lib_path(TEST_LAYER_WRAP_OBJECTS_1)
+                                      .set_api_version(VK_MAKE_API_VERSION(0, 1, 0, 0))
+                                      .add_device_extension({VK_KHR_MAINTENANCE1_EXTENSION_NAME, 1, {"vkTrimCommandPoolKHR"}})),
+        "explicit_wrap_layer_maint.json");
+
+    uint32_t count = 0;
+    ASSERT_EQ(VK_SUCCESS, env->vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr));
+    ASSERT_EQ(count, 1);
+
+    InstWrapper inst{env->vulkan_functions};
+    inst.create_info.add_layer(explicit_layer_name);
+    inst.CheckCreate();
+    VkPhysicalDevice phys_dev = inst.GetPhysDev();
+
+    uint32_t extension_count = 0;
+    std::vector<VkExtensionProperties> extension_props;
+    ASSERT_EQ(VK_SUCCESS, env->vulkan_functions.vkEnumerateDeviceExtensionProperties(phys_dev, nullptr, &extension_count, nullptr));
+    if (extension_count > 0) {
+        extension_props.resize(extension_count);
+        ASSERT_EQ(VK_SUCCESS, env->vulkan_functions.vkEnumerateDeviceExtensionProperties(phys_dev, nullptr, &extension_count,
+                                                                                         extension_props.data()));
+
+        // Make sure the extensions are not present
+        for (uint32_t ext = 0; ext < extension_count; ++ext) {
+            ASSERT_NE(0, strcmp(extension_props[ext].extensionName, VK_KHR_MAINTENANCE1_EXTENSION_NAME));
+            ASSERT_NE(0, strcmp(extension_props[ext].extensionName, VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME));
+        }
+    }
+
+    // Now query by layer name.
+    extension_count = 0;
+    extension_props.clear();
+    ASSERT_EQ(VK_SUCCESS,
+              env->vulkan_functions.vkEnumerateDeviceExtensionProperties(phys_dev, explicit_layer_name, &extension_count, nullptr));
+    ASSERT_GE(extension_count, 1);
+    extension_props.resize(extension_count);
+    ASSERT_EQ(VK_SUCCESS, env->vulkan_functions.vkEnumerateDeviceExtensionProperties(phys_dev, explicit_layer_name,
+                                                                                     &extension_count, extension_props.data()));
+
+    // Make sure only the one extension implemented by the enabled implicit layer is present.
+    bool found = true;
+    for (uint32_t ext = 0; ext < extension_count; ++ext) {
+        if (!strcmp(extension_props[ext].extensionName, VK_KHR_MAINTENANCE1_EXTENSION_NAME)) {
+            found = true;
+        }
+        ASSERT_NE(0, strcmp(extension_props[ext].extensionName, VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME));
+    }
+    ASSERT_EQ(true, found);
+
+    DeviceWrapper dev{inst};
+    dev.create_info.add_extension(VK_KHR_MAINTENANCE1_EXTENSION_NAME).add_device_queue(DeviceQueueCreateInfo{}.add_priority(0.0f));
+    dev.CheckCreate(phys_dev);
+
+    // Make sure only the appropriate function pointers are NULL as well
+    ASSERT_NE(nullptr, dev->vkGetDeviceProcAddr(dev.dev, "vkTrimCommandPoolKHR"));
+    ASSERT_EQ(nullptr, dev->vkGetDeviceProcAddr(dev.dev, "vkGetSwapchainStatusKHR"));
+}
+
+TEST_F(LayerExtensions, ExplicitPresentImageDeviceExtension) {
+    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);
+
+    const char* explicit_layer_name = "VK_LAYER_LUNARG_wrap_objects";
+    env->add_explicit_layer(
+        ManifestLayer{}.add_layer(
+            ManifestLayer::LayerDescription{}
+                .set_name(explicit_layer_name)
+                .set_lib_path(TEST_LAYER_WRAP_OBJECTS_2)
+                .set_api_version(VK_MAKE_API_VERSION(0, 1, 0, 0))
+                .add_device_extension({VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME, 1, {"vkGetSwapchainStatusKHR"}})),
+        "explicit_wrap_layer_maint.json");
+
+    uint32_t count = 0;
+    ASSERT_EQ(VK_SUCCESS, env->vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr));
+    ASSERT_EQ(count, 1);
+
+    InstWrapper inst{env->vulkan_functions};
+    inst.create_info.add_layer(explicit_layer_name);
+    inst.CheckCreate();
+    VkPhysicalDevice phys_dev = inst.GetPhysDev();
+
+    uint32_t extension_count = 0;
+    std::vector<VkExtensionProperties> extension_props;
+    ASSERT_EQ(VK_SUCCESS, env->vulkan_functions.vkEnumerateDeviceExtensionProperties(phys_dev, nullptr, &extension_count, nullptr));
+    if (extension_count > 0) {
+        extension_props.resize(extension_count);
+        ASSERT_EQ(VK_SUCCESS, env->vulkan_functions.vkEnumerateDeviceExtensionProperties(phys_dev, nullptr, &extension_count,
+                                                                                         extension_props.data()));
+
+        // Make sure the extensions are not present
+        for (uint32_t ext = 0; ext < extension_count; ++ext) {
+            ASSERT_NE(0, strcmp(extension_props[ext].extensionName, VK_KHR_MAINTENANCE1_EXTENSION_NAME));
+            ASSERT_NE(0, strcmp(extension_props[ext].extensionName, VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME));
+        }
+    }
+
+    // Now query by layer name.
+    extension_count = 0;
+    extension_props.clear();
+    ASSERT_EQ(VK_SUCCESS,
+              env->vulkan_functions.vkEnumerateDeviceExtensionProperties(phys_dev, explicit_layer_name, &extension_count, nullptr));
+    ASSERT_GE(extension_count, 1);
+    extension_props.resize(extension_count);
+    ASSERT_EQ(VK_SUCCESS, env->vulkan_functions.vkEnumerateDeviceExtensionProperties(phys_dev, explicit_layer_name,
+                                                                                     &extension_count, extension_props.data()));
+
+    // Make sure only the one extension implemented by the enabled implicit layer is present.
+    bool found = false;
+    for (uint32_t ext = 0; ext < extension_count; ++ext) {
+        ASSERT_NE(0, strcmp(extension_props[ext].extensionName, VK_KHR_MAINTENANCE1_EXTENSION_NAME));
+        if (!strcmp(extension_props[ext].extensionName, VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME)) {
+            found = true;
+        }
+    }
+    ASSERT_EQ(true, found);
+
+    DeviceWrapper dev{inst};
+    dev.create_info.add_extension(VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME)
+        .add_device_queue(DeviceQueueCreateInfo{}.add_priority(0.0f));
+    dev.CheckCreate(phys_dev);
+
+    // Make sure only the appropriate function pointers are NULL as well
+    ASSERT_EQ(nullptr, dev->vkGetDeviceProcAddr(dev.dev, "vkTrimCommandPoolKHR"));
+    ASSERT_NE(nullptr, dev->vkGetDeviceProcAddr(dev.dev, "vkGetSwapchainStatusKHR"));
+}
+
+TEST_F(LayerExtensions, ExplicitBothDeviceExtensions) {
+    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);
+
+    const char* explicit_layer_name = "VK_LAYER_LUNARG_wrap_objects";
+    env->add_explicit_layer(
+        ManifestLayer{}.add_layer(
+            ManifestLayer::LayerDescription{}
+                .set_name(explicit_layer_name)
+                .set_lib_path(TEST_LAYER_WRAP_OBJECTS_3)
+                .set_api_version(VK_MAKE_API_VERSION(0, 1, 0, 0))
+                .add_device_extension({VK_KHR_MAINTENANCE1_EXTENSION_NAME, 1, {"vkTrimCommandPoolKHR"}})
+                .add_device_extension({VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME, 1, {"vkGetSwapchainStatusKHR"}})),
+        "explicit_wrap_layer_maint.json");
+
+    uint32_t count = 0;
+    ASSERT_EQ(VK_SUCCESS, env->vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr));
+    ASSERT_EQ(count, 1);
+
+    InstWrapper inst{env->vulkan_functions};
+    inst.create_info.add_layer(explicit_layer_name);
+    inst.CheckCreate();
+    VkPhysicalDevice phys_dev = inst.GetPhysDev();
+
+    uint32_t extension_count = 0;
+    std::vector<VkExtensionProperties> extension_props;
+    ASSERT_EQ(VK_SUCCESS, env->vulkan_functions.vkEnumerateDeviceExtensionProperties(phys_dev, nullptr, &extension_count, nullptr));
+    if (extension_count > 0) {
+        extension_props.resize(extension_count);
+        ASSERT_EQ(VK_SUCCESS, env->vulkan_functions.vkEnumerateDeviceExtensionProperties(phys_dev, nullptr, &extension_count,
+                                                                                         extension_props.data()));
+
+        // Make sure the extensions are not present
+        for (uint32_t ext = 0; ext < extension_count; ++ext) {
+            ASSERT_NE(0, strcmp(extension_props[ext].extensionName, VK_KHR_MAINTENANCE1_EXTENSION_NAME));
+            ASSERT_NE(0, strcmp(extension_props[ext].extensionName, VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME));
+        }
+    }
+
+    // Now query by layer name.
+    extension_count = 0;
+    extension_props.clear();
+    ASSERT_EQ(VK_SUCCESS,
+              env->vulkan_functions.vkEnumerateDeviceExtensionProperties(phys_dev, explicit_layer_name, &extension_count, nullptr));
+    ASSERT_GE(extension_count, 1);
+    extension_props.resize(extension_count);
+    ASSERT_EQ(VK_SUCCESS, env->vulkan_functions.vkEnumerateDeviceExtensionProperties(phys_dev, explicit_layer_name,
+                                                                                     &extension_count, extension_props.data()));
+
+    // Make sure only the one extension implemented by the enabled implicit layer is present.
+    bool found[2] = {false, false};
+    for (uint32_t ext = 0; ext < extension_count; ++ext) {
+        if (!strcmp(extension_props[ext].extensionName, VK_KHR_MAINTENANCE1_EXTENSION_NAME)) {
+            found[0] = true;
+        }
+        if (!strcmp(extension_props[ext].extensionName, VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME)) {
+            found[1] = true;
+        }
+    }
+    for (uint32_t ext = 0; ext < 2; ++ext) {
+        ASSERT_EQ(true, found[ext]);
+    }
+
+    DeviceWrapper dev{inst};
+    dev.create_info.add_extension(VK_KHR_MAINTENANCE1_EXTENSION_NAME)
+        .add_extension(VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME)
+        .add_device_queue(DeviceQueueCreateInfo{}.add_priority(0.0f));
+    dev.CheckCreate(phys_dev);
+
+    // Make sure only the appropriate function pointers are NULL as well
+    ASSERT_NE(nullptr, dev->vkGetDeviceProcAddr(dev.dev, "vkTrimCommandPoolKHR"));
+    ASSERT_NE(nullptr, dev->vkGetDeviceProcAddr(dev.dev, "vkGetSwapchainStatusKHR"));
+}