Implement VK_LUNARG_direct_driver_loading
authorCharles Giessen <charles@lunarg.com>
Wed, 4 Jan 2023 22:05:02 +0000 (15:05 -0700)
committerCharles Giessen <46324611+charles-lunarg@users.noreply.github.com>
Mon, 30 Jan 2023 21:00:03 +0000 (14:00 -0700)
VK_LUNARG_direct_driver_loading is an instance extension which allows
applictations to include drivers directly to the loader, instead of relying
on system installed drivers or environment variables specifying where the
desired driver is located. This allows applications to 'ship' a driver with
themselves and easily provide it to the system loader.

Changes related to direct driver loading:
* Source implementation
* Documentation of new extension, interactions with existing driver discovery
mechanism, and issues with implicit layers
* Extensive tests covering many feature combinations.

Changes also made in this commit:
* Add EnvVarWrapper which more cleanly handles setting, modifying, and
removing Environment Variables. This has the effect of allowing all tests to
run from the test executable without leaking env-vars, which previously
caused spurious test failure. Note - CTest runs each test in a separate
process which hides this issue.
* Adds -Wshadow=local for the gcc compiler (only for gcc 7 and up).
The MSVC compiler already enables this check with W4 so it was added for consistency.
* Add <!-- omit from toc --> to Documenation pages. This prevents the
"Markdown all in one" extension from adding labels to the table of contents.
* Add ManifestDiscoveryType::null_dir, to separate tests that want to write
a manifest file to a place the loader normally does not search and tests which
do not write any manifest file at all.
* Updated copyright to 2023 for changed files

22 files changed:
CMakeLists.txt
docs/LoaderApplicationInterface.md
docs/LoaderDebugging.md
docs/LoaderDriverInterface.md
docs/LoaderInterfaceArchitecture.md
docs/LoaderLayerInterface.md
loader/loader.c
loader/loader.h
loader/trampoline.c
tests/framework/README.md
tests/framework/icd/test_icd.cpp
tests/framework/icd/test_icd.h
tests/framework/test_environment.cpp
tests/framework/test_environment.h
tests/framework/test_util.cpp
tests/framework/test_util.h
tests/loader_alloc_callback_tests.cpp
tests/loader_envvar_tests.cpp
tests/loader_layer_tests.cpp
tests/loader_regression_tests.cpp
tests/loader_testing_main.cpp
tests/loader_version_tests.cpp

index 2d5bfafd3f8187d09fe3f22697a15386ec0759ec..6b9d5dc6eb50826ac8f5b1db693891f38cd9f487 100644 (file)
@@ -193,6 +193,9 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang
 
     if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
         target_compile_options(loader_common_options INTERFACE -Wno-stringop-truncation -Wno-stringop-overflow)
+        if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 7.1)
+            target_compile_options(loader_common_options INTERFACE -Wshadow=local) #only added in GCC 7
+        endif()
     endif()
 
     if(UNIX)
index 14b856e1e823f7176fc46a0f92deedc0dd530959..1e271274a069f47c6fe195a5dfb96c80fa10aee6 100644 (file)
@@ -4,15 +4,15 @@
 [1]: https://vulkan.lunarg.com/img/Vulkan_100px_Dec16.png "https://www.khronos.org/vulkan/"
 [2]: https://www.khronos.org/vulkan/
 
-# Application Interface to Loader
+# Application Interface to Loader <!-- omit from toc -->
 [![Creative Commons][3]][4]
 
-<!-- Copyright &copy; 2015-2022 LunarG, Inc. -->
+<!-- Copyright &copy; 2015-2023 LunarG, Inc. -->
 
 [3]: https://i.creativecommons.org/l/by-nd/4.0/88x31.png "Creative Commons License"
 [4]: https://creativecommons.org/licenses/by-nd/4.0/
 
-## Table of Contents
+## Table of Contents <!-- omit from toc -->
 
 - [Overview](#overview)
 - [Interfacing with Vulkan Functions](#interfacing-with-vulkan-functions)
index 1704c8f6f0d78452cc702596b8bf9996685899f6..215c3d5d6ea5dd92751abd7e5afe5ee8f3768e7f 100644 (file)
@@ -4,14 +4,14 @@
 [1]: https://vulkan.lunarg.com/img/Vulkan_100px_Dec16.png "https://www.khronos.org/vulkan/"
 [2]: https://www.khronos.org/vulkan/
 
-# Debugging The Vulkan Desktop Loader
+# Debugging The Vulkan Desktop Loader <!-- omit from toc -->
 [![Creative Commons][3]][4]
 
-<!-- Copyright &copy; 2015-2022 LunarG, Inc. -->
+<!-- Copyright &copy; 2015-2023 LunarG, Inc. -->
 
 [3]: https://i.creativecommons.org/l/by-nd/4.0/88x31.png "Creative Commons License"
 [4]: https://creativecommons.org/licenses/by-nd/4.0/
-## Table of Contents
+## Table of Contents <!-- omit from toc -->
 
 - [Debugging Issues](#debugging-issues)
 - [Loader Logging](#loader-logging)
index ccd26134864a90b42f7211e958fe91d3d76844e4..76cca7b8d08555e342a099eaac07a76ba9e627ec 100644 (file)
@@ -4,16 +4,16 @@
 [1]: https://vulkan.lunarg.com/img/Vulkan_100px_Dec16.png "https://www.khronos.org/vulkan/"
 [2]: https://www.khronos.org/vulkan/
 
-# Driver interface to the Vulkan Loader
+# Driver interface to the Vulkan Loader <!-- omit from toc -->
 [![Creative Commons][3]][4]
 
-<!-- Copyright &copy; 2015-2022 LunarG, Inc. -->
+<!-- Copyright &copy; 2015-2023 LunarG, Inc. -->
 
 [3]: https://i.creativecommons.org/l/by-nd/4.0/88x31.png "Creative Commons License"
 [4]: https://creativecommons.org/licenses/by-nd/4.0/
 
 
-## Table of Contents
+## Table of Contents <!-- omit from toc -->
 
 - [Overview](#overview)
 - [Driver Discovery](#driver-discovery)
   - [Driver Discovery on macOS](#driver-discovery-on-macos)
     - [Example macOS Driver Search Path](#example-macos-driver-search-path)
     - [Additional Settings For Driver Debugging](#additional-settings-for-driver-debugging)
+  - [Driver Discovery using the`VK_LUNARG_direct_driver_loading` extension](#driver-discovery-using-thevk_lunarg_direct_driver_loading-extension)
+    - [How to use `VK_LUNARG_direct_driver_loading`](#how-to-use-vk_lunarg_direct_driver_loading)
+    - [Interactions with other driver discovery mechanisms](#interactions-with-other-driver-discovery-mechanisms)
+    - [Limitations of `VK_LUNARG_direct_driver_loading`](#limitations-of-vk_lunarg_direct_driver_loading)
   - [Using Pre-Production ICDs or Software Drivers](#using-pre-production-icds-or-software-drivers)
   - [Driver Discovery on Android](#driver-discovery-on-android)
 - [Driver Manifest File Format](#driver-manifest-file-format)
@@ -546,6 +550,117 @@ will expose it and cause the Vulkan loader to fail on loading the driver.
 It is recommended that `LD_BIND_NOW` along with `VK_LOADER_DEBUG=error,warn`
 to expose any issues.
 
+### Driver Discovery using the`VK_LUNARG_direct_driver_loading` extension
+
+The `VK_LUNARG_direct_driver_loading` extension allows for applications to
+provide a driver or drivers to the Loader during vkCreateInstance.
+This allows drivers to be included with an application without requiring
+installation and is capable of being used in any execution environment, such as
+a process running with elevated privileges.
+
+When calling `vkEnumeratePhysicalDevices` with the
+`VK_LUNARG_direct_driver_loading` extension enabled, the `VkPhysicalDevice`s
+from system installed drivers and environment variable specified drivers will
+appear before any `VkPhysicalDevice`s that originate from drivers from the
+`VkDirectDriverLoadingListLUNARG::pDrivers` list.
+
+#### How to use `VK_LUNARG_direct_driver_loading`
+
+To use this extension, it must first be enabled on the VkInstance.
+This requires enabling the `VK_LUNARG_direct_driver_loading` extension through
+the `enabledExtensionCount` and `ppEnabledExtensionNames`members of
+`VkInstanceCreateInfo`.
+
+```c
+const char* extensions[] = {VK_LUNARG_DIRECT_DRIVER_LOADING_EXTENSION_NAME, <other extensions>};
+VkInstanceCreateInfo instance_create_info = {};
+instance_create_info.enabledExtensionCount = <size of extension list>;
+instance_create_info.ppEnabledExtensionNames = extensions;
+```
+
+The `VkDirectDriverLoadingInfoLUNARG` structure contains a
+`VkDirectDriverLoadingFlagsLUNARG` member (reserved for future use) and a
+`PFN_vkGetInstanceProcAddrLUNARG` member which provides the loader with the
+function pointer for the driver's `vkGetInstanceProcAddr`.
+
+The `VkDirectDriverLoadingListLUNARG` structure contains a count and pointer
+members which provide the size of and pointer to an application provided array of
+`VkDirectDriverLoadingInfoLUNARG` structures.
+
+Creating those structures looks like the following
+```c
+VkDirectDriverLoadingInfoLUNARG direct_loading_info = {};
+direct_loading_info.sType = VK_STRUCTURE_TYPE_DIRECT_DRIVER_LOADING_INFO_LUNARG
+direct_loading_info.pfnGetInstanceProcAddr = <put the PFN_vkGetInstanceProcAddr of the driver here>
+
+VkDirectDriverLoadingListLUNARG direct_driver_list = {};
+direct_driver_list.sType = VK_STRUCTURE_TYPE_DIRECT_DRIVER_LOADING_LIST_LUNARG;
+direct_driver_list.mode = VK_DIRECT_DRIVER_LOADING_MODE_INCLUSIVE_LUNARG; // or VK_DIRECT_DRIVER_LOADING_MODE_EXCLUSIVE_LUNARG
+direct_driver_list.driverCount = 1;
+direct_driver_list.pDrivers = &direct_loading_info; // can include multiple drivers here if so desired
+```
+
+The `VkDirectDriverLoadingListLUNARG` structure contains the enum
+`VkDirectDriverLoadingModeLUNARG`.
+There are two modes:
+* `VK_DIRECT_DRIVER_LOADING_MODE_EXCLUSIVE_LUNARG` - specifies that the only drivers
+to be loaded will come from the `VkDirectDriverLoadingListLUNARG` structure.
+* `VK_DIRECT_DRIVER_LOADING_MODE_INCLUSIVE_LUNARG` - specifies that drivers
+from the `VkDirectDriverLoadingModeLUNARG` structure will be used in addition to
+any system installed drivers and environment variable specified drivers.
+
+
+
+Then, the `VkDirectDriverLoadingListLUNARG` structure *must* be appended to the
+`pNext` chain of `VkInstanceCreateInfo`.
+
+```c
+instance_create_info.pNext = (const void*)&direct_driver_list;
+```
+
+Finally, create the instance like normal.
+
+#### Interactions with other driver discovery mechanisms
+
+If the `VK_DIRECT_DRIVER_LOADING_MODE_EXCLUSIVE_LUNARG` mode is specified in the
+`VkDirectDriverLoadingListLUNARG` structure, then no system installed drivers
+are loaded.
+This applies equally to all platforms.
+Additionally, the following environment variables have no effect:
+
+* `VK_DRIVER_FILES`
+* `VK_ICD_FILENAMES`
+* `VK_ADD_DRIVER_FILES`
+* `VK_LOADER_DRIVERS_SELECT`
+* `VK_LOADER_DRIVERS_DISABLE`
+
+Exclusive mode will also disable MacOS bundle manifest discovery of drivers.
+
+#### Limitations of `VK_LUNARG_direct_driver_loading`
+
+Because `VkDirectDriverLoadingListLUNARG` is provided to the loader at instance
+creation, there is no mechanism for the loader to query the list of instance
+extensions that originate from `VkDirectDriverLoadingListLUNARG` drivers during
+`vkEnumerateInstanceExtensionProperties`.
+Applications can instead manually load the `vkEnumerateInstanceExtensionProperties`
+function pointer directly from the drivers the application provides to the loader
+using the `pfnGetInstanceProcAddrLUNARG` for each driver.
+Then the application can call each driver's
+`vkEnumerateInstanceExtensionProperties` and append non-duplicate entriees to the
+list from the loader's `vkEnumerateInstanceExtensionProperties` to get the full
+list of supported instance extensions.
+Alternatively, because the Application is providing drivers, it is reasonable for
+the application to already know which instance extensions are available with the
+provided drivers, preventing the need to manually query them.
+
+However, there are limitations.
+If there are any active implicit layers which intercept
+`vkEnumerateInstanceExtensionProperties` to remove unsupported extensions, then
+those layers will not be able to remove unsupported extensions from drivers that
+are provided by the application.
+This is due to `vkEnumerateInstanceExtensionProperties` not having a mechanism
+to extend it.
+
 
 ### Using Pre-Production ICDs or Software Drivers
 
index 543fc71fbada1b54bb73b6b65219f828a4cd1f07..d874e74f96e7da1657e468c61d61057af4d998b9 100644 (file)
@@ -4,14 +4,14 @@
 [1]: https://vulkan.lunarg.com/img/Vulkan_100px_Dec16.png "https://www.khronos.org/vulkan/"
 [2]: https://www.khronos.org/vulkan/
 
-# Architecture of the Vulkan Loader Interfaces
+# Architecture of the Vulkan Loader Interfaces <!-- omit from toc -->
 [![Creative Commons][3]][4]
 
-<!-- Copyright &copy; 2015-2022 LunarG, Inc. -->
+<!-- Copyright &copy; 2015-2023 LunarG, Inc. -->
 
 [3]: https://i.creativecommons.org/l/by-nd/4.0/88x31.png "Creative Commons License"
 [4]: https://creativecommons.org/licenses/by-nd/4.0/
-## Table of Contents
+## Table of Contents <!-- omit from toc -->
 
 - [Overview](#overview)
   - [Who Should Read This Document](#who-should-read-this-document)
index eeae72cadae329197ca4c75b5cb4ef9a6dc29058..790b4e9be7febe136f0450938b5fa87c6af01945 100644 (file)
@@ -4,16 +4,16 @@
 [1]: https://vulkan.lunarg.com/img/Vulkan_100px_Dec16.png "https://www.khronos.org/vulkan/"
 [2]: https://www.khronos.org/vulkan/
 
-# Layer Interface to the Loader
+# Layer Interface to the Loader <!-- omit from toc -->
 [![Creative Commons][3]][4]
 
-<!-- Copyright &copy; 2015-2022 LunarG, Inc. -->
+<!-- Copyright &copy; 2015-2023 LunarG, Inc. -->
 
 [3]: https://i.creativecommons.org/l/by-nd/4.0/88x31.png "Creative Commons License"
 [4]: https://creativecommons.org/licenses/by-nd/4.0/
 
 
-## Table of Contents
+## Table of Contents <!-- omit from toc -->
 
 - [Overview](#overview)
 - [Layer Discovery](#layer-discovery)
@@ -30,6 +30,7 @@
     - [Layer Disable Filtering](#layer-disable-filtering)
     - [Layer Special Case Disable](#layer-special-case-disable)
     - [Layer Disable Warning](#layer-disable-warning)
+      - [`VK_INSTANCE_LAYERS`](#vk_instance_layers)
   - [Exception for Elevated Privileges](#exception-for-elevated-privileges)
 - [Layer Version Negotiation](#layer-version-negotiation)
 - [Layer Call Chains and Distributed Dispatch](#layer-call-chains-and-distributed-dispatch)
@@ -53,6 +54,7 @@
   - [Versioning and Activation Interactions](#versioning-and-activation-interactions)
 - [Layer Manifest File Format](#layer-manifest-file-format)
   - [Layer Manifest File Version History](#layer-manifest-file-version-history)
+  - [Layer Manifest File Version 1.2.1](#layer-manifest-file-version-121)
     - [Layer Manifest File Version 1.2.0](#layer-manifest-file-version-120)
     - [Layer Manifest File Version 1.1.2](#layer-manifest-file-version-112)
     - [Layer Manifest File Version 1.1.1](#layer-manifest-file-version-111)
@@ -504,7 +506,7 @@ Disabling layers, whether just through normal usage of
 `~all~` or `~explicit~` could cause application breakage if the application is
 relying on features provided by one or more explicit layers.
 
-##### VK_INSTANCE_LAYERS
+##### `VK_INSTANCE_LAYERS`
 
 The original `VK_INSTANCE_LAYERS` can be viewed as a special case of the new
 `VK_LOADER_LAYERS_ENABLE`.
index f32c90ca3920d8ef9a6402bd985882c1d5e0fc48..c98d37cfecfe5d4844d0d419928ae2c3ee1d1c1c 100644 (file)
@@ -1,8 +1,8 @@
 /*
  *
- * Copyright (c) 2014-2022 The Khronos Group Inc.
- * Copyright (c) 2014-2022 Valve Corporation
- * Copyright (c) 2014-2022 LunarG, Inc.
+ * Copyright (c) 2014-2023 The Khronos Group Inc.
+ * Copyright (c) 2014-2023 Valve Corporation
+ * Copyright (c) 2014-2023 LunarG, Inc.
  * Copyright (C) 2015 Google Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -1177,6 +1177,12 @@ VkResult loader_get_icd_loader_instance_extensions(const struct loader_instance
     loader_add_to_ext_list(inst, inst_exts, sizeof(portability_enumeration_extension_info) / sizeof(VkExtensionProperties),
                            portability_enumeration_extension_info);
 
+    static const VkExtensionProperties direct_driver_loading_extension_info[] = {
+        {VK_LUNARG_DIRECT_DRIVER_LOADING_EXTENSION_NAME, VK_LUNARG_DIRECT_DRIVER_LOADING_SPEC_VERSION}};
+
+    // Add VK_LUNARG_direct_driver_loading
+    loader_add_to_ext_list(inst, inst_exts, sizeof(direct_driver_loading_extension_info) / sizeof(VkExtensionProperties),
+                           direct_driver_loading_extension_info);
 out:
     return res;
 }
@@ -1325,27 +1331,244 @@ bool loader_get_icd_interface_version(PFN_vkNegotiateLoaderICDInterfaceVersion f
 void loader_scanned_icd_clear(const struct loader_instance *inst, struct loader_icd_tramp_list *icd_tramp_list) {
     if (0 != icd_tramp_list->capacity) {
         for (uint32_t i = 0; i < icd_tramp_list->count; i++) {
-            loader_platform_close_library(icd_tramp_list->scanned_list[i].handle);
+            if (icd_tramp_list->scanned_list[i].handle) {
+                loader_platform_close_library(icd_tramp_list->scanned_list[i].handle);
+                icd_tramp_list->scanned_list[i].handle = NULL;
+            }
             loader_instance_heap_free(inst, icd_tramp_list->scanned_list[i].lib_name);
         }
         loader_instance_heap_free(inst, icd_tramp_list->scanned_list);
-        icd_tramp_list->capacity = 0;
-        icd_tramp_list->count = 0;
-        icd_tramp_list->scanned_list = NULL;
     }
+    icd_tramp_list->capacity = 0;
+    icd_tramp_list->count = 0;
+    icd_tramp_list->scanned_list = NULL;
 }
 
-static VkResult loader_scanned_icd_init(const struct loader_instance *inst, struct loader_icd_tramp_list *icd_tramp_list) {
-    VkResult err = VK_SUCCESS;
+VkResult loader_scanned_icd_init(const struct loader_instance *inst, struct loader_icd_tramp_list *icd_tramp_list) {
+    VkResult res = VK_SUCCESS;
     loader_scanned_icd_clear(inst, icd_tramp_list);
     icd_tramp_list->capacity = 8 * sizeof(struct loader_scanned_icd);
     icd_tramp_list->scanned_list = loader_instance_heap_alloc(inst, icd_tramp_list->capacity, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
     if (NULL == icd_tramp_list->scanned_list) {
         loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0,
                    "loader_scanned_icd_init: Realloc failed for layer list when attempting to add new layer");
-        err = VK_ERROR_OUT_OF_HOST_MEMORY;
+        res = VK_ERROR_OUT_OF_HOST_MEMORY;
     }
-    return err;
+    return res;
+}
+
+VkResult loader_add_direct_driver(const struct loader_instance *inst, uint32_t index,
+                                  const VkDirectDriverLoadingInfoLUNARG *pDriver, struct loader_icd_tramp_list *icd_tramp_list) {
+    // Assume pDriver is valid, since there is no real way to check it. Calling code should make sure the pointer to the array of
+    // VkDirectDriverLoadingInfoLUNARG structures is non-null.
+    if (NULL == pDriver->pfnGetInstanceProcAddr) {
+        loader_log(
+            inst, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_DRIVER_BIT, 0,
+            "loader_add_direct_driver: VkDirectDriverLoadingInfoLUNARG structure at index %d contains a NULL pointer for the "
+            "pfnGetInstanceProcAddr member, skipping.",
+            index);
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+
+    PFN_vkGetInstanceProcAddr fp_get_proc_addr = pDriver->pfnGetInstanceProcAddr;
+    PFN_vkCreateInstance fp_create_inst = NULL;
+    PFN_vkEnumerateInstanceExtensionProperties fp_get_inst_ext_props = NULL;
+    PFN_GetPhysicalDeviceProcAddr fp_get_phys_dev_proc_addr = NULL;
+    PFN_vkNegotiateLoaderICDInterfaceVersion fp_negotiate_icd_version = NULL;
+#if defined(VK_USE_PLATFORM_WIN32_KHR)
+    PFN_vk_icdEnumerateAdapterPhysicalDevices fp_enum_dxgi_adapter_phys_devs = NULL;
+#endif
+    struct loader_scanned_icd *new_scanned_icd;
+    uint32_t interface_version = 0;
+
+    // Try to get the negotiate ICD interface version function
+    fp_negotiate_icd_version = (PFN_vk_icdNegotiateLoaderICDInterfaceVersion)pDriver->pfnGetInstanceProcAddr(
+        NULL, "vk_icdNegotiateLoaderICDInterfaceVersion");
+
+    if (NULL == fp_negotiate_icd_version) {
+        loader_log(inst, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_DRIVER_BIT, 0,
+                   "loader_add_direct_driver: Could not get 'vk_icdNegotiateLoaderICDInterfaceVersion' from "
+                   "VkDirectDriverLoadingInfoLUNARG structure at "
+                   "index %d, skipping.",
+                   index);
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+
+    if (!loader_get_icd_interface_version(fp_negotiate_icd_version, &interface_version)) {
+        loader_log(
+            inst, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_DRIVER_BIT, 0,
+            "loader_add_direct_driver: VkDirectDriverLoadingInfoLUNARG structure at index %d supports interface version %d, "
+            "which is incompatible with the Loader Driver Interface version that supports the VK_LUNARG_direct_driver_loading "
+            "extension, skipping.",
+            index, interface_version);
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+
+    if (interface_version < 7) {
+        loader_log(
+            inst, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_DRIVER_BIT, 0,
+            "loader_add_direct_driver: VkDirectDriverLoadingInfoLUNARG structure at index %d supports interface version %d, "
+            "which is incompatible with the Loader Driver Interface version that supports the VK_LUNARG_direct_driver_loading "
+            "extension, skipping.",
+            index, interface_version);
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+
+    fp_create_inst = (PFN_vkCreateInstance)pDriver->pfnGetInstanceProcAddr(NULL, "vkCreateInstance");
+    if (NULL == fp_create_inst) {
+        loader_log(inst, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_DRIVER_BIT, 0,
+                   "loader_add_direct_driver: Could not get 'vkCreateInstance' from VkDirectDriverLoadingInfoLUNARG structure at "
+                   "index %d, skipping.",
+                   index);
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+    fp_get_inst_ext_props =
+        (PFN_vkEnumerateInstanceExtensionProperties)pDriver->pfnGetInstanceProcAddr(NULL, "vkEnumerateInstanceExtensionProperties");
+    if (NULL == fp_get_inst_ext_props) {
+        loader_log(inst, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_DRIVER_BIT, 0,
+                   "loader_add_direct_driver: Could not get 'vkEnumerateInstanceExtensionProperties' from "
+                   "VkDirectDriverLoadingInfoLUNARG structure at index %d, skipping.",
+                   index);
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+
+    fp_get_phys_dev_proc_addr =
+        (PFN_vk_icdGetPhysicalDeviceProcAddr)pDriver->pfnGetInstanceProcAddr(NULL, "vk_icdGetPhysicalDeviceProcAddr");
+#if defined(VK_USE_PLATFORM_WIN32_KHR)
+    // Query "vk_icdEnumerateAdapterPhysicalDevices" with vk_icdGetInstanceProcAddr if the library reports interface version
+    // 7 or greater, otherwise fallback to loading it from the platform dynamic linker
+    fp_enum_dxgi_adapter_phys_devs =
+        (PFN_vk_icdEnumerateAdapterPhysicalDevices)pDriver->pfnGetInstanceProcAddr(NULL, "vk_icdEnumerateAdapterPhysicalDevices");
+#endif
+
+    // check for enough capacity
+    if ((icd_tramp_list->count * sizeof(struct loader_scanned_icd)) >= icd_tramp_list->capacity) {
+        void *new_ptr = loader_instance_heap_realloc(inst, icd_tramp_list->scanned_list, icd_tramp_list->capacity,
+                                                     icd_tramp_list->capacity * 2, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
+        if (NULL == new_ptr) {
+            loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_add_direct_driver: Realloc failed on icd library list for ICD %s");
+            return VK_ERROR_OUT_OF_HOST_MEMORY;
+        }
+        icd_tramp_list->scanned_list = new_ptr;
+
+        // double capacity
+        icd_tramp_list->capacity *= 2;
+    }
+
+    // Driver must be 1.1 to support version 7
+    uint32_t api_version = VK_API_VERSION_1_1;
+    PFN_vkEnumerateInstanceVersion icd_enumerate_instance_version =
+        (PFN_vkEnumerateInstanceVersion)pDriver->pfnGetInstanceProcAddr(NULL, "vkEnumerateInstanceVersion");
+
+    if (icd_enumerate_instance_version) {
+        VkResult res = icd_enumerate_instance_version(&api_version);
+        if (res != VK_SUCCESS) {
+            return res;
+        }
+    }
+
+    new_scanned_icd = &(icd_tramp_list->scanned_list[icd_tramp_list->count]);
+    new_scanned_icd->handle = NULL;
+    new_scanned_icd->api_version = api_version;
+    new_scanned_icd->GetInstanceProcAddr = fp_get_proc_addr;
+    new_scanned_icd->GetPhysicalDeviceProcAddr = fp_get_phys_dev_proc_addr;
+    new_scanned_icd->EnumerateInstanceExtensionProperties = fp_get_inst_ext_props;
+    new_scanned_icd->CreateInstance = fp_create_inst;
+#if defined(VK_USE_PLATFORM_WIN32_KHR)
+    new_scanned_icd->EnumerateAdapterPhysicalDevices = fp_enum_dxgi_adapter_phys_devs;
+#endif
+    new_scanned_icd->interface_version = interface_version;
+
+    new_scanned_icd->lib_name = NULL;
+    icd_tramp_list->count++;
+
+    loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0,
+               "loader_add_direct_driver: Adding driver found in index %d of "
+               "VkDirectDriverLoadingListLUNARG::pDrivers structure. pfnGetInstanceProcAddr was set to %p",
+               index, pDriver->pfnGetInstanceProcAddr);
+
+    return VK_SUCCESS;
+}
+
+// Search through VkInstanceCreateInfo's pNext chain for any drivers from the direct driver loading extension and load them.
+VkResult loader_scan_for_direct_drivers(const struct loader_instance *inst, const VkInstanceCreateInfo *pCreateInfo,
+                                        struct loader_icd_tramp_list *icd_tramp_list, bool *direct_driver_loading_exclusive_mode) {
+    if (NULL == pCreateInfo) {
+        // Don't do this logic unless we are being called from vkCreateInstance, when pCreateInfo will be non-null
+        return VK_SUCCESS;
+    }
+    bool direct_driver_loading_enabled = false;
+    // Try to if VK_LUNARG_direct_driver_loading is enabled and if we are using it exclusively
+    // Skip this step if inst is NULL, aka when this function is being called before instance creation
+    if (inst != NULL && pCreateInfo->ppEnabledExtensionNames && pCreateInfo->enabledExtensionCount > 0) {
+        // Look through the enabled extension list, make sure VK_LUNARG_direct_driver_loading is present
+        for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) {
+            if (strcmp(pCreateInfo->ppEnabledExtensionNames[i], VK_LUNARG_DIRECT_DRIVER_LOADING_EXTENSION_NAME) == 0) {
+                direct_driver_loading_enabled = true;
+                break;
+            }
+        }
+    }
+    const VkDirectDriverLoadingListLUNARG *ddl_list = NULL;
+    // Find the VkDirectDriverLoadingListLUNARG struct in the pNext chain of vkInstanceCreateInfo
+    const VkBaseOutStructure *chain = pCreateInfo->pNext;
+    while (chain) {
+        if (chain->sType == VK_STRUCTURE_TYPE_DIRECT_DRIVER_LOADING_LIST_LUNARG) {
+            ddl_list = (VkDirectDriverLoadingListLUNARG *)chain;
+            break;
+        }
+        chain = (const VkBaseOutStructure *)chain->pNext;
+    }
+    if (NULL == ddl_list) {
+        if (direct_driver_loading_enabled) {
+            loader_log(
+                inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0,
+                "loader_scan_for_direct_drivers: The VK_LUNARG_direct_driver_loading extension was enabled but the pNext chain of "
+                "VkInstanceCreateInfo did not contain the "
+                "VkDirectDriverLoadingListLUNARG structure.");
+        }
+        // Always want to exit early if there was no VkDirectDriverLoadingListLUNARG in the pNext chain
+        return VK_SUCCESS;
+    }
+
+    if (!direct_driver_loading_enabled) {
+        loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0,
+                   "loader_scan_for_direct_drivers: The pNext chain of VkInstanceCreateInfo contained the "
+                   "VkDirectDriverLoadingListLUNARG structure, but the VK_LUNARG_direct_driver_loading extension was "
+                   "not enabled.");
+        return VK_SUCCESS;
+    }
+    // If we are using exclusive mode, skip looking for any more drivers from system or environment variables
+    if (ddl_list->mode == VK_DIRECT_DRIVER_LOADING_MODE_EXCLUSIVE_LUNARG) {
+        *direct_driver_loading_exclusive_mode = true;
+        loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0,
+                   "loader_scan_for_direct_drivers: The VK_LUNARG_direct_driver_loading extension is active and specified "
+                   "VK_DIRECT_DRIVER_LOADING_MODE_EXCLUSIVE_LUNARG, skipping system and environment "
+                   "variable driver search mechanisms.");
+    }
+    if (NULL == ddl_list->pDrivers) {
+        loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0,
+                   "loader_scan_for_direct_drivers: The VkDirectDriverLoadingListLUNARG structure in the pNext chain of "
+                   "VkInstanceCreateInfo has a NULL pDrivers member.");
+        return VK_SUCCESS;
+    }
+    if (ddl_list->driverCount == 0) {
+        loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0,
+                   "loader_scan_for_direct_drivers: The VkDirectDriverLoadingListLUNARG structure in the pNext chain of "
+                   "VkInstanceCreateInfo has a non-null pDrivers member but a driverCount member with a value "
+                   "of zero.");
+        return VK_SUCCESS;
+    }
+    // Go through all VkDirectDriverLoadingInfoLUNARG entries and add each driver
+    // Because icd_tramp's are prepended, this will result in the drivers appearing at the end
+    for (uint32_t i = 0; i < ddl_list->driverCount; i++) {
+        VkResult res = loader_add_direct_driver(inst, i, &ddl_list->pDrivers[i], icd_tramp_list);
+        if (res == VK_ERROR_OUT_OF_HOST_MEMORY) {
+            return res;
+        }
+    }
+
+    return VK_SUCCESS;
 }
 
 static VkResult loader_scanned_icd_add(const struct loader_instance *inst, struct loader_icd_tramp_list *icd_tramp_list,
@@ -1596,8 +1819,7 @@ void loader_preload_icds(void) {
         return;
     }
 
-    memset(&scanned_icds, 0, sizeof(scanned_icds));
-    VkResult result = loader_icd_scan(NULL, &scanned_icds, NULL);
+    VkResult result = loader_icd_scan(NULL, &scanned_icds, NULL, NULL);
     if (result != VK_SUCCESS) {
         loader_scanned_icd_clear(NULL, &scanned_icds);
     }
@@ -3513,7 +3735,7 @@ out:
 // Vulkan result
 // (on result == VK_SUCCESS) a list of icds that were discovered
 VkResult loader_icd_scan(const struct loader_instance *inst, struct loader_icd_tramp_list *icd_tramp_list,
-                         bool *skipped_portability_drivers) {
+                         const VkInstanceCreateInfo *pCreateInfo, bool *skipped_portability_drivers) {
     struct loader_data_files manifest_files;
     VkResult res = VK_SUCCESS;
     bool lockedMutex = false;
@@ -3524,6 +3746,23 @@ VkResult loader_icd_scan(const struct loader_instance *inst, struct loader_icd_t
     // a failure occurs before allocating the manifest filename_list.
     memset(&manifest_files, 0, sizeof(struct loader_data_files));
 
+    // Set up the ICD Trampoline list so elements can be written into it.
+    res = loader_scanned_icd_init(inst, icd_tramp_list);
+    if (res == VK_ERROR_OUT_OF_HOST_MEMORY) {
+        return res;
+    }
+
+    bool direct_driver_loading_exclusive_mode = false;
+    res = loader_scan_for_direct_drivers(inst, pCreateInfo, icd_tramp_list, &direct_driver_loading_exclusive_mode);
+    if (res == VK_ERROR_OUT_OF_HOST_MEMORY) {
+        goto out;
+    }
+    if (direct_driver_loading_exclusive_mode) {
+        // Make sure to jump over the system & env-var driver discovery mechanisms if exclusive mode is set, even if no drivers
+        // were successfully found through the direct driver loading mechanism
+        goto out;
+    }
+
     // Parse the filter environment variables to determine if we have any special behavior
     res = parse_generic_filter_environment_var(inst, VK_DRIVERS_SELECT_ENV_VAR, &select_filter);
     if (VK_SUCCESS != res) {
@@ -3534,14 +3773,9 @@ VkResult loader_icd_scan(const struct loader_instance *inst, struct loader_icd_t
         goto out;
     }
 
-    res = loader_scanned_icd_init(inst, icd_tramp_list);
-    if (VK_SUCCESS != res) {
-        goto out;
-    }
-
     // Get a list of manifest files for ICDs
     res = loader_get_data_files(inst, LOADER_DATA_FILE_MANIFEST_DRIVER, NULL, &manifest_files);
-    if (VK_SUCCESS != res || manifest_files.count == 0) {
+    if (VK_SUCCESS != res) {
         goto out;
     }
 
@@ -4012,9 +4246,9 @@ static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL loader_gpa_instance_terminator(V
         return (PFN_vkVoidFunction)terminator_CreateDevice;
     }
 
-    // The VK_EXT_debug_utils functions need a special case here so the terminators can still be found from vkGetInstanceProcAddr
-    // This is because VK_EXT_debug_utils is an instance level extension with device level functions, and is 'supported' by the
-    // loader. There needs to be a terminator in case a driver doesn't support VK_EXT_debug_utils.
+    // The VK_EXT_debug_utils functions need a special case here so the terminators can still be found from
+    // vkGetInstanceProcAddr This is because VK_EXT_debug_utils is an instance level extension with device level functions, and
+    // is 'supported' by the loader. There needs to be a terminator in case a driver doesn't support VK_EXT_debug_utils.
     if (!strcmp(pName, "vkSetDebugUtilsObjectNameEXT")) {
         return (PFN_vkVoidFunction)terminator_SetDebugUtilsObjectNameEXT;
     }
@@ -6014,8 +6248,8 @@ bool is_linux_sort_enabled(struct loader_instance *inst) {
 }
 #endif  // LOADER_ENABLE_LINUX_SORT
 
-// Look for physical_device in the provided phys_devs list, return true if found and put the index into out_idx, otherwise return
-// false
+// Look for physical_device in the provided phys_devs list, return true if found and put the index into out_idx, otherwise
+// return false
 bool find_phys_dev(VkPhysicalDevice physical_device, uint32_t phys_devs_count, struct loader_physical_device_term **phys_devs,
                    uint32_t *out_idx) {
     if (NULL == phys_devs) return false;
@@ -6046,8 +6280,8 @@ VkResult check_and_add_to_new_phys_devs(struct loader_instance *inst, VkPhysical
         return VK_SUCCESS;
     }
 
-    // Exit in case something is already present - this shouldn't happen but better to be safe than overwrite existing data since
-    // this code has been refactored a half dozen times.
+    // Exit in case something is already present - this shouldn't happen but better to be safe than overwrite existing data
+    // since this code has been refactored a half dozen times.
     if (NULL != new_phys_devs[idx]) {
         return VK_SUCCESS;
     }
@@ -6610,7 +6844,7 @@ terminator_EnumerateInstanceExtensionProperties(const VkEnumerateInstanceExtensi
         loader_preload_icds();
 
         // Scan/discover all ICD libraries
-        res = loader_icd_scan(NULL, &icd_tramp_list, NULL);
+        res = loader_icd_scan(NULL, &icd_tramp_list, NULL, NULL);
         // EnumerateInstanceExtensionProperties can't return anything other than OOM or VK_ERROR_LAYER_NOT_PRESENT
         if ((VK_SUCCESS != res && icd_tramp_list.count > 0) || res == VK_ERROR_OUT_OF_HOST_MEMORY) {
             goto out;
index a7fe0481c3b504fc0bb15f3e3cc0aaa5fb3e5e74..a73633cc24decc192bc9290c1be2b0670b564597 100644 (file)
@@ -1,8 +1,8 @@
 /*
  *
- * Copyright (c) 2014-2022 The Khronos Group Inc.
- * Copyright (c) 2014-2022 Valve Corporation
- * Copyright (c) 2014-2022 LunarG, Inc.
+ * Copyright (c) 2014-2023 The Khronos Group Inc.
+ * Copyright (c) 2014-2023 Valve Corporation
+ * Copyright (c) 2014-2023 LunarG, Inc.
  * Copyright (C) 2015 Google Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -122,9 +122,10 @@ void loader_destroy_generic_list(const struct loader_instance *inst, struct load
 void loader_destroy_layer_list(const struct loader_instance *inst, struct loader_device *device,
                                struct loader_layer_list *layer_list);
 void loader_delete_layer_list_and_properties(const struct loader_instance *inst, struct loader_layer_list *layer_list);
+VkResult loader_scanned_icd_init(const struct loader_instance *inst, struct loader_icd_tramp_list *icd_tramp_list);
 void loader_scanned_icd_clear(const struct loader_instance *inst, struct loader_icd_tramp_list *icd_tramp_list);
 VkResult loader_icd_scan(const struct loader_instance *inst, struct loader_icd_tramp_list *icd_tramp_list,
-                         bool *skipped_portability_drivers);
+                         const VkInstanceCreateInfo *pCreateInfo, bool *skipped_portability_drivers);
 void loader_icd_destroy(struct loader_instance *ptr_inst, struct loader_icd_term *icd_term,
                         const VkAllocationCallbacks *pAllocator);
 VkResult loader_scan_for_layers(struct loader_instance *inst, struct loader_layer_list *instance_layers);
index 740898ff6e244c21d047787d889b43b7e24c1182..7a3901a78f65430c0044ca355844138908c1a7a3 100644 (file)
@@ -1,8 +1,8 @@
 /*
  *
- * Copyright (c) 2015-2022 The Khronos Group Inc.
- * Copyright (c) 2015-2022 Valve Corporation
- * Copyright (c) 2015-2022 LunarG, Inc.
+ * Copyright (c) 2015-2023 The Khronos Group Inc.
+ * Copyright (c) 2015-2023 Valve Corporation
+ * Copyright (c) 2015-2023 LunarG, Inc.
  * Copyright (C) 2015 Google Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -521,13 +521,14 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance(const VkInstanceCr
         }
     }
 
-    // Scan/discover all ICD libraries
-    memset(&ptr_instance->icd_tramp_list, 0, sizeof(ptr_instance->icd_tramp_list));
+    // Scan/discover all System and Environment Variable ICD libraries
     bool skipped_portability_drivers = false;
-    res = loader_icd_scan(ptr_instance, &ptr_instance->icd_tramp_list, &skipped_portability_drivers);
+    res = loader_icd_scan(ptr_instance, &ptr_instance->icd_tramp_list, pCreateInfo, &skipped_portability_drivers);
     if (res == VK_ERROR_OUT_OF_HOST_MEMORY) {
         goto out;
-    } else if (ptr_instance->icd_tramp_list.count == 0) {
+    }
+
+    if (ptr_instance->icd_tramp_list.count == 0) {
         // No drivers found
         if (skipped_portability_drivers) {
             loader_log(
@@ -700,8 +701,8 @@ LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkDestroyInstance(VkInstance instance,
     loader_instance_heap_free(ptr_instance, ptr_instance);
     loader_platform_thread_unlock_mutex(&loader_lock);
 
-    // Unload preloaded layers, so if vkEnumerateInstanceExtensionProperties or vkCreateInstance is called again, the ICD's are up
-    // to date
+    // Unload preloaded layers, so if vkEnumerateInstanceExtensionProperties or vkCreateInstance is called again, the ICD's are
+    // up to date
     loader_unload_preloaded_icds();
 }
 
@@ -793,9 +794,9 @@ LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceProperties(VkPhysica
     const VkLayerInstanceDispatchTable *disp;
     VkPhysicalDevice unwrapped_phys_dev = loader_unwrap_physical_device(physicalDevice);
     if (VK_NULL_HANDLE == unwrapped_phys_dev) {
-        loader_log(
-            NULL, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0,
-            "vkGetPhysicalDeviceProperties: Invalid physicalDevice [VUID-vkGetPhysicalDeviceProperties-physicalDevice-parameter]");
+        loader_log(NULL, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0,
+                   "vkGetPhysicalDeviceProperties: Invalid physicalDevice "
+                   "[VUID-vkGetPhysicalDeviceProperties-physicalDevice-parameter]");
         abort(); /* Intentionally fail so user can correct issue. */
     }
     disp = loader_get_instance_layer_dispatch(physicalDevice);
@@ -2930,9 +2931,9 @@ LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndexedIndirectCount(VkCommand
                                                                        uint32_t stride) {
     const VkLayerDispatchTable *disp = loader_get_dispatch(commandBuffer);
     if (NULL == disp) {
-        loader_log(
-            NULL, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0,
-            "vkCmdDrawIndexedIndirectCount: Invalid commandBuffer [VUID-vkCmdDrawIndexedIndirectCount-commandBuffer-parameter]");
+        loader_log(NULL, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0,
+                   "vkCmdDrawIndexedIndirectCount: Invalid commandBuffer "
+                   "[VUID-vkCmdDrawIndexedIndirectCount-commandBuffer-parameter]");
         abort(); /* Intentionally fail so user can correct issue. */
     }
     disp->CmdDrawIndexedIndirectCount(commandBuffer, buffer, offset, countBuffer, countBufferOffset, maxDrawCount, stride);
@@ -2995,9 +2996,9 @@ LOADER_EXPORT VKAPI_ATTR uint64_t VKAPI_CALL
 vkGetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo *pInfo) {
     const VkLayerDispatchTable *disp = loader_get_dispatch(device);
     if (NULL == disp) {
-        loader_log(
-            NULL, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0,
-            "vkGetDeviceMemoryOpaqueCaptureAddress: Invalid device [VUID-vkGetDeviceMemoryOpaqueCaptureAddress-device-parameter]");
+        loader_log(NULL, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0,
+                   "vkGetDeviceMemoryOpaqueCaptureAddress: Invalid device "
+                   "[VUID-vkGetDeviceMemoryOpaqueCaptureAddress-device-parameter]");
         abort(); /* Intentionally fail so user can correct issue. */
     }
     return disp->GetDeviceMemoryOpaqueCaptureAddress(device, pInfo);
index 3b11698f91cf1ba429885568044499ebd4e60e58..3148e81fcfe90f9f80381b3c84a39697645bdb4b 100644 (file)
@@ -133,7 +133,7 @@ This is due to some functions being used to query other functions, which the shi
 There are many utilities that the test framework and tests have access to. These include:
 * Including common C and C++ headers
 * `FRAMEWORK_EXPORT` - macro used for exporting shared library funtions
-* Environment Variable helpers: `get_env_var`, `set_env_var`, `remove_env_var`
+* Environment Variable Wrapper: `EnvVarWrapper` for creating, setting, getting, and removing environment variables in a RAII manner
 * Windows API error handling helpers
 * filesystem abstractions:
   * `fs::path` - wrapper around std::string that has a similar API to C++17's `filesystem::path` library
index b1743553a00fbdfff4d26b380493354811a2414d..4e0f9344cdc65daffefb6dc5f7fcf4e66c132883 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2021-2022 The Khronos Group Inc.
- * Copyright (c) 2021-2022 Valve Corporation
- * Copyright (c) 2021-2022 LunarG, Inc.
+ * Copyright (c) 2021-2023 The Khronos Group Inc.
+ * Copyright (c) 2021-2023 Valve Corporation
+ * Copyright (c) 2021-2023 LunarG, Inc.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and/or associated documentation files (the "Materials"), to
@@ -1089,6 +1089,21 @@ VkResult test_vk_icdNegotiateLoaderICDInterfaceVersion(uint32_t* pSupportedVersi
     return VK_SUCCESS;
 }
 
+// Forward declarations for trampolines
+extern "C" {
+#if TEST_ICD_EXPOSE_VERSION_7
+FRAMEWORK_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vk_icdNegotiateLoaderICDInterfaceVersion(uint32_t* pSupportedVersion);
+#if TEST_ICD_EXPORT_ICD_GPDPA
+FRAMEWORK_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vk_icdGetPhysicalDeviceProcAddr(VkInstance instance, const char* pName);
+#endif
+#if defined(WIN32) && TEST_ICD_EXPORT_ICD_ENUMERATE_ADAPTER_PHYSICAL_DEVICES
+FRAMEWORK_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vk_icdEnumerateAdapterPhysicalDevices(VkInstance instance, LUID adapterLUID,
+                                                                                      uint32_t* pPhysicalDeviceCount,
+                                                                                      VkPhysicalDevice* pPhysicalDevices);
+#endif
+#endif
+}
+
 //// trampolines
 
 PFN_vkVoidFunction get_instance_func_ver_1_1(VkInstance instance, const char* pName) {
@@ -1362,10 +1377,6 @@ PFN_vkVoidFunction get_physical_device_func(VkInstance instance, const char* pNa
 }
 
 PFN_vkVoidFunction get_instance_func(VkInstance instance, const char* pName) {
-    if (string_eq(pName, "vkEnumerateInstanceExtensionProperties"))
-        return to_vkVoidFunction(test_vkEnumerateInstanceExtensionProperties);
-    if (string_eq(pName, "vkEnumerateInstanceLayerProperties")) return to_vkVoidFunction(test_vkEnumerateInstanceLayerProperties);
-    if (string_eq(pName, "vkCreateInstance")) return to_vkVoidFunction(test_vkCreateInstance);
     if (string_eq(pName, "vkDestroyInstance")) return to_vkVoidFunction(test_vkDestroyInstance);
     if (string_eq(pName, "vkEnumeratePhysicalDevices")) return to_vkVoidFunction(test_vkEnumeratePhysicalDevices);
 
@@ -1466,24 +1477,33 @@ VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL test_vkGetDeviceProcAddr(VkDevice devic
 }
 
 PFN_vkVoidFunction base_get_instance_proc_addr(VkInstance instance, const char* pName) {
+    if (pName == nullptr) return nullptr;
+    if (instance == NULL) {
 #if TEST_ICD_EXPOSE_VERSION_7
-    if (string_eq(pName, "vk_icdNegotiateLoaderICDInterfaceVersion"))
-        return to_vkVoidFunction(test_vk_icdNegotiateLoaderICDInterfaceVersion);
-    if (string_eq(pName, "vk_icdGetPhysicalDeviceProcAddr")) return to_vkVoidFunction(get_physical_device_func);
-#if defined(WIN32)
-    if (string_eq(pName, "vk_icdEnumerateAdapterPhysicalDevices"))
-        return to_vkVoidFunction(test_vk_icdEnumerateAdapterPhysicalDevices);
+        if (string_eq(pName, "vk_icdNegotiateLoaderICDInterfaceVersion"))
+            return icd.exposes_vk_icdNegotiateLoaderICDInterfaceVersion
+                       ? to_vkVoidFunction(vk_icdNegotiateLoaderICDInterfaceVersion)
+                       : NULL;
+#if TEST_ICD_EXPORT_ICD_GPDPA
+        if (string_eq(pName, "vk_icdGetPhysicalDeviceProcAddr"))
+            return icd.exposes_vk_icdGetPhysicalDeviceProcAddr ? to_vkVoidFunction(vk_icdGetPhysicalDeviceProcAddr) : NULL;
+#endif
+#if defined(WIN32) && TEST_ICD_EXPORT_ICD_ENUMERATE_ADAPTER_PHYSICAL_DEVICES
+        if (string_eq(pName, "vk_icdEnumerateAdapterPhysicalDevices"))
+            return icd.exposes_vk_icdEnumerateAdapterPhysicalDevices ? to_vkVoidFunction(vk_icdEnumerateAdapterPhysicalDevices)
+                                                                     : NULL;
 #endif  // defined(WIN32)
 #endif  // TEST_ICD_EXPOSE_VERSION_7
 
-    if (pName == nullptr) return nullptr;
-    if (instance == NULL) {
         if (string_eq(pName, "vkGetInstanceProcAddr")) return to_vkVoidFunction(test_vkGetInstanceProcAddr);
         if (string_eq(pName, "vkEnumerateInstanceExtensionProperties"))
-            return to_vkVoidFunction(test_vkEnumerateInstanceExtensionProperties);
+            return icd.exposes_vkEnumerateInstanceExtensionProperties
+                       ? to_vkVoidFunction(test_vkEnumerateInstanceExtensionProperties)
+                       : NULL;
         if (string_eq(pName, "vkEnumerateInstanceLayerProperties"))
             return to_vkVoidFunction(test_vkEnumerateInstanceLayerProperties);
         if (string_eq(pName, "vkEnumerateInstanceVersion")) return to_vkVoidFunction(test_vkEnumerateInstanceVersion);
+        if (string_eq(pName, "vkCreateInstance")) return to_vkVoidFunction(test_vkCreateInstance);
     }
     if (string_eq(pName, "vkGetDeviceProcAddr")) return to_vkVoidFunction(test_vkGetDeviceProcAddr);
 
@@ -1499,7 +1519,7 @@ PFN_vkVoidFunction base_get_instance_proc_addr(VkInstance instance, const char*
 // Exported functions
 extern "C" {
 #if TEST_ICD_EXPORT_NEGOTIATE_INTERFACE_VERSION
-extern FRAMEWORK_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vk_icdNegotiateLoaderICDInterfaceVersion(uint32_t* pSupportedVersion) {
+FRAMEWORK_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vk_icdNegotiateLoaderICDInterfaceVersion(uint32_t* pSupportedVersion) {
     return test_vk_icdNegotiateLoaderICDInterfaceVersion(pSupportedVersion);
 }
 #endif  // TEST_ICD_EXPORT_NEGOTIATE_INTERFACE_VERSION
index 274541365e5fafeda4a159952df06e8950595f49..8ca76910547a0c31a24ae3df559a46bd9da68b73 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2021-2022 The Khronos Group Inc.
- * Copyright (c) 2021-2022 Valve Corporation
- * Copyright (c) 2021-2022 LunarG, Inc.
+ * Copyright (c) 2021-2023 The Khronos Group Inc.
+ * Copyright (c) 2021-2023 Valve Corporation
+ * Copyright (c) 2021-2023 LunarG, Inc.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and/or associated documentation files (the "Materials"), to
@@ -77,6 +77,14 @@ inline std::ostream& operator<<(std::ostream& os, const InterfaceVersionCheck& r
 struct TestICD {
     fs::path manifest_file_path;
 
+    BUILDER_VALUE(TestICD, bool, exposes_vk_icdNegotiateLoaderICDInterfaceVersion, true)
+    BUILDER_VALUE(TestICD, bool, exposes_vkEnumerateInstanceExtensionProperties, true)
+    BUILDER_VALUE(TestICD, bool, exposes_vkCreateInstance, true)
+    BUILDER_VALUE(TestICD, bool, exposes_vk_icdGetPhysicalDeviceProcAddr, true)
+#if defined(WIN32)
+    BUILDER_VALUE(TestICD, bool, exposes_vk_icdEnumerateAdapterPhysicalDevices, true)
+#endif
+
     CalledICDGIPA called_vk_icd_gipa = CalledICDGIPA::not_called;
     CalledNegotiateInterface called_negotiate_interface = CalledNegotiateInterface::not_called;
 
index 079bbcd928f82b58887aec75167a61da03062f57..505283504f307a374d4b045deb4d52b5e3e1161a 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2021 The Khronos Group Inc.
- * Copyright (c) 2021 Valve Corporation
- * Copyright (c) 2021 LunarG, Inc.
+ * Copyright (c) 2021-2023 The Khronos Group Inc.
+ * Copyright (c) 2021-2023 Valve Corporation
+ * Copyright (c) 2021-2023 LunarG, Inc.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and/or associated documentation files (the "Materials"), to
@@ -296,7 +296,8 @@ bool FindPrefixPostfixStringOnLine(DebugUtilsLogger const& env_log, const char*
     return env_log.find_prefix_then_postfix(prefix, postfix);
 }
 
-PlatformShimWrapper::PlatformShimWrapper(std::vector<fs::FolderManager>* folders, bool enable_log) noexcept {
+PlatformShimWrapper::PlatformShimWrapper(std::vector<fs::FolderManager>* folders, bool enable_log) noexcept
+    : loader_logging{"VK_LOADER_DEBUG"} {
 #if defined(WIN32) || defined(__APPLE__)
     shim_library = LibraryWrapper(SHIM_LIBRARY_NAME);
     PFN_get_platform_shim get_platform_shim_func = shim_library.get_symbol(GET_PLATFORM_SHIM_STR);
@@ -308,7 +309,7 @@ PlatformShimWrapper::PlatformShimWrapper(std::vector<fs::FolderManager>* folders
     platform_shim->reset();
 
     if (enable_log) {
-        set_env_var("VK_LOADER_DEBUG", "all");
+        loader_logging.set_new_value("all");
     }
 }
 
@@ -349,7 +350,12 @@ fs::path TestLayerHandle::get_layer_manifest_path() noexcept { return manifest_p
 FrameworkEnvironment::FrameworkEnvironment() noexcept : FrameworkEnvironment(true, true) {}
 FrameworkEnvironment::FrameworkEnvironment(bool enable_log) noexcept : FrameworkEnvironment(enable_log, true) {}
 FrameworkEnvironment::FrameworkEnvironment(bool enable_log, bool set_default_search_paths) noexcept
-    : platform_shim(&folders, enable_log), vulkan_functions() {
+    : platform_shim(&folders, enable_log),
+      vulkan_functions(),
+      env_var_vk_icd_filenames("VK_DRIVER_FILES"),
+      add_env_var_vk_icd_filenames("VK_ADD_DRIVER_FILES"),
+      env_var_vk_layer_paths("VK_LAYER_PATH"),
+      add_env_var_vk_layer_paths("VK_ADD_LAYER_PATH") {
     // This order is important, it matches the enum ManifestLocation, used to index the folders vector
     folders.emplace_back(FRAMEWORK_BUILD_DIRECTORY, std::string("null_dir"));
     folders.emplace_back(FRAMEWORK_BUILD_DIRECTORY, std::string("icd_manifests"));
@@ -377,7 +383,7 @@ FrameworkEnvironment::FrameworkEnvironment(bool enable_log, bool set_default_sea
 #endif
 }
 
-void FrameworkEnvironment::add_icd(TestICDDetails icd_details) noexcept {
+TestICDHandle& FrameworkEnvironment::add_icd(TestICDDetails icd_details) noexcept {
     size_t cur_icd_index = icds.size();
     fs::FolderManager* folder = &get_folder(ManifestLocation::driver);
     if (icd_details.discovery_type == ManifestDiscoveryType::env_var ||
@@ -390,6 +396,10 @@ void FrameworkEnvironment::add_icd(TestICDDetails icd_details) noexcept {
     if (icd_details.discovery_type == ManifestDiscoveryType::macos_bundle) {
         folder = &get_folder(ManifestLocation::macos_bundle);
     }
+    if (icd_details.discovery_type == ManifestDiscoveryType::null_dir ||
+        icd_details.discovery_type == ManifestDiscoveryType::none) {
+        folder = &get_folder(ManifestLocation::null);
+    }
     if (!icd_details.is_fake) {
         fs::path new_driver_name = fs::path(icd_details.icd_manifest.lib_path).stem() + "_" + std::to_string(cur_icd_index) +
                                    fs::path(icd_details.icd_manifest.lib_path).extension();
@@ -400,42 +410,36 @@ void FrameworkEnvironment::add_icd(TestICDDetails icd_details) noexcept {
         icds.back().reset_icd();
         icd_details.icd_manifest.lib_path = new_driver_location.str();
     }
-    std::string full_json_name = icd_details.json_name;
-    if (!icd_details.disable_icd_inc) {
-        full_json_name += "_" + std::to_string(cur_icd_index);
-    }
-    full_json_name += ".json";
-
-    icds.back().manifest_path = folder->write_manifest(full_json_name, icd_details.icd_manifest.get_manifest_str());
-    switch (icd_details.discovery_type) {
-        default:
-        case (ManifestDiscoveryType::generic):
-            platform_shim->add_manifest(ManifestCategory::icd, icds.back().manifest_path);
-            break;
-        case (ManifestDiscoveryType::env_var):
-            if (!env_var_vk_icd_filenames.empty()) {
-                env_var_vk_icd_filenames += OS_ENV_VAR_LIST_SEPARATOR;
-            }
-            env_var_vk_icd_filenames += (folder->location() / full_json_name).str();
-            set_env_var("VK_DRIVER_FILES", env_var_vk_icd_filenames);
-            break;
-        case (ManifestDiscoveryType::add_env_var):
-            if (!add_env_var_vk_icd_filenames.empty()) {
-                add_env_var_vk_icd_filenames += OS_ENV_VAR_LIST_SEPARATOR;
-            }
-            add_env_var_vk_icd_filenames += (folder->location() / full_json_name).str();
-            set_env_var("VK_ADD_DRIVER_FILES", add_env_var_vk_icd_filenames);
-            break;
-        case (ManifestDiscoveryType::macos_bundle):
-            platform_shim->add_manifest(ManifestCategory::icd, icds.back().manifest_path);
-        case (ManifestDiscoveryType::none):
-            break;
+    if (icd_details.discovery_type != ManifestDiscoveryType::none) {
+        std::string full_json_name = icd_details.json_name;
+        if (!icd_details.disable_icd_inc) {
+            full_json_name += "_" + std::to_string(cur_icd_index);
+        }
+        full_json_name += ".json";
+        icds.back().manifest_path = folder->write_manifest(full_json_name, icd_details.icd_manifest.get_manifest_str());
+        switch (icd_details.discovery_type) {
+            default:
+            case (ManifestDiscoveryType::generic):
+                platform_shim->add_manifest(ManifestCategory::icd, icds.back().manifest_path);
+                break;
+            case (ManifestDiscoveryType::env_var):
+                env_var_vk_icd_filenames.add_to_list((folder->location() / full_json_name).str());
+                break;
+            case (ManifestDiscoveryType::add_env_var):
+                add_env_var_vk_icd_filenames.add_to_list((folder->location() / full_json_name).str());
+                break;
+            case (ManifestDiscoveryType::macos_bundle):
+                platform_shim->add_manifest(ManifestCategory::icd, icds.back().manifest_path);
+            case (ManifestDiscoveryType::null_dir):
+                break;
 #ifdef _WIN32
-        case (ManifestDiscoveryType::windows_app_package):
-            platform_shim->set_app_package_path(folder->location());
-            break;
+            case (ManifestDiscoveryType::windows_app_package):
+                platform_shim->set_app_package_path(folder->location());
+                break;
 #endif
+        }
     }
+    return icds.back();
 }
 
 void FrameworkEnvironment::add_implicit_layer(ManifestLayer layer_manifest, const std::string& json_name) noexcept {
@@ -466,24 +470,16 @@ void FrameworkEnvironment::add_layer_impl(TestLayerDetails layer_details, Manife
             break;
         case (ManifestDiscoveryType::env_var):
             fs_ptr = &get_folder(ManifestLocation::explicit_layer_env_var);
-            if (!env_var_vk_layer_paths.empty()) {
-                env_var_vk_layer_paths += OS_ENV_VAR_LIST_SEPARATOR;
-            }
             if (layer_details.is_dir) {
-                env_var_vk_layer_paths += fs_ptr->location().str();
+                env_var_vk_layer_paths.add_to_list(fs_ptr->location().str());
             } else {
-                env_var_vk_layer_paths += fs_ptr->location().str() + OS_ENV_VAR_LIST_SEPARATOR + layer_details.json_name;
+                env_var_vk_layer_paths.add_to_list(fs_ptr->location().str());
+                env_var_vk_layer_paths.add_to_list(layer_details.json_name);
             }
-            env_var_vk_layer_paths += fs_ptr->location().str();
-            set_env_var("VK_LAYER_PATH", env_var_vk_layer_paths);
             break;
         case (ManifestDiscoveryType::add_env_var):
             fs_ptr = &get_folder(ManifestLocation::explicit_layer_add_env_var);
-            if (!add_env_var_vk_layer_paths.empty()) {
-                add_env_var_vk_layer_paths += OS_ENV_VAR_LIST_SEPARATOR;
-            }
-            add_env_var_vk_layer_paths += fs_ptr->location().str();
-            set_env_var("VK_ADD_LAYER_PATH", add_env_var_vk_layer_paths);
+            add_env_var_vk_layer_paths.add_to_list(fs_ptr->location().str());
             break;
         case (ManifestDiscoveryType::override_folder):
             fs_ptr = &get_folder(ManifestLocation::override_layer);
@@ -492,6 +488,8 @@ void FrameworkEnvironment::add_layer_impl(TestLayerDetails layer_details, Manife
             fs_ptr = &(get_folder(ManifestLocation::macos_bundle));
             break;
         case (ManifestDiscoveryType::none):
+        case (ManifestDiscoveryType::null_dir):
+            fs_ptr = &(get_folder(ManifestLocation::null));
             break;
     }
     auto& folder = *fs_ptr;
index a04c9e2e5c230e0e0eaa0dac26bd43758e7c6d2c..78577e3109e05c1612c6934c6420c2d551989ea8 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2021 The Khronos Group Inc.
- * Copyright (c) 2021 Valve Corporation
- * Copyright (c) 2021 LunarG, Inc.
+ * Copyright (c) 2021-2023 The Khronos Group Inc.
+ * Copyright (c) 2021-2023 Valve Corporation
+ * Copyright (c) 2021-2023 LunarG, Inc.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and/or associated documentation files (the "Materials"), to
@@ -418,6 +418,7 @@ struct PlatformShimWrapper {
 
     LibraryWrapper shim_library;
     PlatformShim* platform_shim;
+    EnvVarWrapper loader_logging;
 };
 
 struct TestICDHandle {
@@ -449,9 +450,11 @@ struct TestLayerHandle {
     fs::path manifest_path;
 };
 
+// Controls whether to create a manifest and where to put it
 enum class ManifestDiscoveryType {
     generic,              // put the manifest in the regular locations
-    none,                 // don't add to regular locations - eg D3DKMT
+    none,                 // Do not write the manifest anywhere (for Direct Driver Loading)
+    null_dir,             // put the manifest in the 'null_dir' which the loader does not search in (D3DKMT for instance)
     env_var,              // use the corresponding env-var for it
     add_env_var,          // use the corresponding add-env-var for it
     override_folder,      // add to a special folder for the override layer to use
@@ -499,7 +502,7 @@ struct FrameworkEnvironment {
     FrameworkEnvironment(bool enable_log) noexcept;
     FrameworkEnvironment(bool enable_log, bool enable_default_search_paths) noexcept;
 
-    void add_icd(TestICDDetails icd_details) noexcept;
+    TestICDHandle& add_icd(TestICDDetails icd_details) noexcept;
     void add_implicit_layer(ManifestLayer layer_manifest, const std::string& json_name) noexcept;
     void add_implicit_layer(TestLayerDetails layer_details) noexcept;
     void add_explicit_layer(ManifestLayer layer_manifest, const std::string& json_name) noexcept;
@@ -531,11 +534,11 @@ struct FrameworkEnvironment {
     std::vector<TestICDHandle> icds;
     std::vector<TestLayerHandle> layers;
 
-    std::string env_var_vk_icd_filenames;
-    std::string add_env_var_vk_icd_filenames;
+    EnvVarWrapper env_var_vk_icd_filenames;      //"VK_DRIVER_FILES"
+    EnvVarWrapper add_env_var_vk_icd_filenames;  //"VK_ADD_DRIVER_FILES"
 
-    std::string env_var_vk_layer_paths;
-    std::string add_env_var_vk_layer_paths;
+    EnvVarWrapper env_var_vk_layer_paths;      //"VK_LAYER_PATH"
+    EnvVarWrapper add_env_var_vk_layer_paths;  //"VK_ADD_LAYER_PATH"
 
    private:
     void add_layer_impl(TestLayerDetails layer_details, ManifestCategory category);
@@ -555,13 +558,3 @@ void setup_WSI_in_create_instance(InstWrapper& inst, const char* api_selection =
 //    defaults to Metal on macOS and XCB on linux if not provided
 // Returns an assertion failure if the surface failed to be created
 testing::AssertionResult create_surface(InstWrapper& inst, VkSurfaceKHR& out_surface, const char* api_selection = nullptr);
-
-struct EnvVarCleaner {
-    std::string env_var;
-    EnvVarCleaner(std::string env_var) noexcept : env_var(env_var) {}
-    ~EnvVarCleaner() noexcept { remove_env_var(env_var); }
-
-    // delete copy operators
-    EnvVarCleaner(const EnvVarCleaner&) = delete;
-    EnvVarCleaner& operator=(const EnvVarCleaner&) = delete;
-};
index c98ec1ff96f15f2c43796f9bf977e046e2e816b7..647463ca7dab9860f2c10d68679ab793ec000325 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2021 The Khronos Group Inc.
- * Copyright (c) 2021 Valve Corporation
- * Copyright (c) 2021 LunarG, Inc.
+ * Copyright (c) 2021-2023 The Khronos Group Inc.
+ * Copyright (c) 2021-2023 Valve Corporation
+ * Copyright (c) 2021-2023 LunarG, Inc.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and/or associated documentation files (the "Materials"), to
@@ -57,13 +57,13 @@ void print_error_message(LSTATUS status, const char* function_name, std::string
     LocalFree(lpMsgBuf);
 }
 
-void set_env_var(std::string const& name, std::string const& value) {
-    BOOL ret = SetEnvironmentVariableW(widen(name).c_str(), widen(value).c_str());
+void EnvVarWrapper::set_env_var() {
+    BOOL ret = SetEnvironmentVariableW(widen(name).c_str(), widen(cur_value).c_str());
     if (ret == 0) {
         print_error_message(ERROR_SETENV_FAILED, "SetEnvironmentVariableW");
     }
 }
-void remove_env_var(std::string const& name) { SetEnvironmentVariableW(widen(name).c_str(), nullptr); }
+void EnvVarWrapper::remove_env_var() const { SetEnvironmentVariableW(widen(name).c_str(), nullptr); }
 std::string get_env_var(std::string const& name, bool report_failure) {
     std::wstring name_utf16 = widen(name);
     DWORD value_size = GetEnvironmentVariableW(name_utf16.c_str(), nullptr, 0);
@@ -79,8 +79,8 @@ std::string get_env_var(std::string const& name, bool report_failure) {
 }
 #elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
 
-void set_env_var(std::string const& name, std::string const& value) { setenv(name.c_str(), value.c_str(), 1); }
-void remove_env_var(std::string const& name) { unsetenv(name.c_str()); }
+void EnvVarWrapper::set_env_var() { setenv(name.c_str(), cur_value.c_str(), 1); }
+void EnvVarWrapper::remove_env_var() const { unsetenv(name.c_str()); }
 std::string get_env_var(std::string const& name, bool report_failure) {
     char* ret = getenv(name.c_str());
     if (ret == nullptr) {
index cf1b22ee9cb0a13e2e73a8c3bf640981cec48690..9966311f7d5f7649c668ff86576d2461bdd98711 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2021 The Khronos Group Inc.
- * Copyright (c) 2021 Valve Corporation
- * Copyright (c) 2021 LunarG, Inc.
+ * Copyright (c) 2021-2023 The Khronos Group Inc.
+ * Copyright (c) 2021-2023 Valve Corporation
+ * Copyright (c) 2021-2023 LunarG, Inc.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and/or associated documentation files (the "Materials"), to
 #endif
 
 /*
- * Common Environment Variable operations
- * These operate on the actual environemnt, they are not shims.
- * set_env_var - sets the env-var with `name` to `value`.
- * remove_env_var - unsets the env-var `name`. Different than set_env_var(name, "");
- * get_env_var - returns a std::string of `name`. if report_failure is true, then it will log to stderr that it didn't find the
- *     env-var
+ * Wrapper around Environment Variables with common operations
+ * Since Environment Variables leak between tests, there needs to be RAII code to remove them during test cleanup
+
  */
 
+// Wrapper to set & remove env-vars automatically
+struct EnvVarWrapper {
+    // Constructor which does NOT set the env-var
+    EnvVarWrapper(std::string const& name) noexcept : name(name) {}
+    // Constructor which DOES set the env-var
+    EnvVarWrapper(std::string const& name, std::string const& value) noexcept : name(name), cur_value(value) { set_env_var(); }
+    ~EnvVarWrapper() noexcept { remove_env_var(); }
+
+    // delete copy operators
+    EnvVarWrapper(const EnvVarWrapper&) = delete;
+    EnvVarWrapper& operator=(const EnvVarWrapper&) = delete;
+
+    void set_new_value(std::string const& value) {
+        cur_value = value;
+        set_env_var();
+    }
+    void add_to_list(std::string const& list_item) {
+        if (!cur_value.empty()) {
+            cur_value += OS_ENV_VAR_LIST_SEPARATOR;
+        }
+        cur_value += list_item;
+        set_env_var();
+    }
+    void remove_value() const { remove_env_var(); }
+    const char* get() const { return name.c_str(); }
+    const char* value() const { return cur_value.c_str(); }
+
+   private:
+    std::string name;
+    std::string cur_value;
+
 #if defined(WIN32)
-void set_env_var(std::string const& name, std::string const& value);
-void remove_env_var(std::string const& name);
-std::string get_env_var(std::string const& name, bool report_failure = true);
+    void set_env_var();
+    void remove_env_var() const;
+    // Environment variable list separator - not for filesystem paths
+    const char OS_ENV_VAR_LIST_SEPARATOR = ';';
 
 #elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
-void set_env_var(std::string const& name, std::string const& value);
-void remove_env_var(std::string const& name);
+    void set_env_var();
+    void remove_env_var() const;
+    // Environment variable list separator - not for filesystem paths
+    const char OS_ENV_VAR_LIST_SEPARATOR = ':';
+#endif
+};
+
+// get_env_var() - returns a std::string of `name`. if report_failure is true, then it will log to stderr that it didn't find the
+//     env-var
+// NOTE: This is only intended for test framework code, all test code MUST use EnvVarWrapper
+#if defined(WIN32)
+std::string get_env_var(std::string const& name, bool report_failure = true);
+#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
 std::string get_env_var(std::string const& name, bool report_failure = true);
 #endif
 
@@ -125,14 +165,6 @@ void print_error_message(LSTATUS status, const char* function_name, std::string
 struct ManifestICD;    // forward declaration for FolderManager::write
 struct ManifestLayer;  // forward declaration for FolderManager::write
 
-#ifdef _WIN32
-// Environment variable list separator - not for filesystem paths
-const char OS_ENV_VAR_LIST_SEPARATOR = ';';
-#else
-// Environment variable list separator - not for filesystem paths
-const char OS_ENV_VAR_LIST_SEPARATOR = ':';
-#endif
-
 namespace fs {
 std::string make_native(std::string const&);
 
index c7a4a1c454299183ee75dfa7cd07021dad08c5a7..27102205e5a3e363d31f5e50ba590fa9521767db 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2021 The Khronos Group Inc.
- * Copyright (c) 2021 Valve Corporation
- * Copyright (c) 2021 LunarG, Inc.
+ * Copyright (c) 2021-2023 The Khronos Group Inc.
+ * Copyright (c) 2021-2023 Valve Corporation
+ * Copyright (c) 2021-2023 LunarG, Inc.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and/or associated documentation files (the "Materials"), to
@@ -435,11 +435,7 @@ TEST(Allocation, DriverEnvVarIntentionalAllocFail) {
                            "test_layer.json");
     env.get_test_layer().set_do_spurious_allocations_in_create_instance(true).set_do_spurious_allocations_in_create_device(true);
 
-    auto driver_files = get_env_var("VK_DRIVER_FILES");
-    driver_files += OS_ENV_VAR_LIST_SEPARATOR;
-    driver_files += (fs::path("totally_made_up") / "path_to_fake" / "jason_file.json").str();
-    set_env_var("VK_DRIVER_FILES", driver_files);
-    EnvVarCleaner cleaner("VK_DRIVER_FILES");
+    env.env_var_vk_icd_filenames.add_to_list((fs::path("totally_made_up") / "path_to_fake" / "jason_file.json").str());
     size_t fail_index = 0;
     VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY;
     while (result == VK_ERROR_OUT_OF_HOST_MEMORY && fail_index <= 10000) {
@@ -525,6 +521,7 @@ TEST(Allocation, CreateDeviceIntentionalAllocFail) {
 
 // Test failure during vkCreateInstance and vkCreateDevice to make sure we don't
 // leak memory if one of the out-of-memory conditions trigger.
+// Includes drivers with several instance extensions, drivers that will fail to load, directly loaded drivers
 TEST(Allocation, CreateInstanceDeviceIntentionalAllocFail) {
     FrameworkEnvironment env{};
     uint32_t num_physical_devices = 4;
@@ -543,6 +540,18 @@ TEST(Allocation, CreateInstanceDeviceIntentionalAllocFail) {
 
     env.add_icd(TestICDDetails(CURRENT_PLATFORM_DUMMY_BINARY_WRONG_TYPE).set_is_fake(true));
 
+    auto& direct_driver_icd = env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_7).set_discovery_type(ManifestDiscoveryType::none));
+
+    VkDirectDriverLoadingInfoLUNARG ddl_info{};
+    ddl_info.sType = VK_STRUCTURE_TYPE_DIRECT_DRIVER_LOADING_INFO_LUNARG;
+    ddl_info.pfnGetInstanceProcAddr = direct_driver_icd.icd_library.get_symbol("vk_icdGetInstanceProcAddr");
+
+    VkDirectDriverLoadingListLUNARG ddl_list{};
+    ddl_list.sType = VK_STRUCTURE_TYPE_DIRECT_DRIVER_LOADING_LIST_LUNARG;
+    ddl_list.mode = VK_DIRECT_DRIVER_LOADING_MODE_INCLUSIVE_LUNARG;
+    ddl_list.driverCount = 1;
+    ddl_list.pDrivers = &ddl_info;
+
     const char* layer_name = "VK_LAYER_ImplicitAllocFail";
     env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                          .set_name(layer_name)
@@ -574,6 +583,8 @@ TEST(Allocation, CreateInstanceDeviceIntentionalAllocFail) {
 
         VkInstance instance;
         InstanceCreateInfo inst_create_info{};
+        inst_create_info.add_extension(VK_LUNARG_DIRECT_DRIVER_LOADING_EXTENSION_NAME);
+        inst_create_info.instance_info.pNext = reinterpret_cast<const void*>(&ddl_list);
         result = env.vulkan_functions.vkCreateInstance(inst_create_info.get(), tracker.get(), &instance);
         if (result == VK_ERROR_OUT_OF_HOST_MEMORY) {
             ASSERT_TRUE(tracker.empty());
@@ -779,7 +790,7 @@ TEST(Allocation, EnumeratePhysicalDevicesIntentionalAllocFail) {
 // leak memory if one of the out-of-memory conditions trigger.
 TEST(Allocation, CreateInstanceDeviceWithDXGIDriverIntentionalAllocFail) {
     FrameworkEnvironment env{};
-    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_6).set_discovery_type(ManifestDiscoveryType::none));
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_6).set_discovery_type(ManifestDiscoveryType::null_dir));
     env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
 
     for (uint32_t i = 0; i < 2; i++) {
index d5682c0b2299c53c407a3a65b10b0dd1444867b2..b7a4983149c8e7b507d41bd9dc2783bb4b9a1f7e 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2021-2022 The Khronos Group Inc.
- * Copyright (c) 2021-2022 Valve Corporation
- * Copyright (c) 2021-2022 LunarG, Inc.
+ * Copyright (c) 2021-2023 The Khronos Group Inc.
+ * Copyright (c) 2021-2023 Valve Corporation
+ * Copyright (c) 2021-2023 LunarG, Inc.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and/or associated documentation files (the "Materials"), to
 
 #include "test_environment.h"
 
-class EnvVarICDOverrideSetup : public ::testing::Test {
-   protected:
-    virtual void SetUp() {
-        remove_env_var("VK_ICD_FILENAMES");
-        remove_env_var("VK_DRIVER_FILES");
-        remove_env_var("VK_ADD_DRIVER_FILES");
-    }
-
-    virtual void TearDown() {
-        remove_env_var("VK_ICD_FILENAMES");
-        remove_env_var("VK_DRIVER_FILES");
-        remove_env_var("VK_ADD_DRIVER_FILES");
-    }
-};
-
 // Don't support vk_icdNegotiateLoaderICDInterfaceVersion
 // Loader calls vk_icdGetInstanceProcAddr second
 // does not support vk_icdGetInstanceProcAddr
@@ -174,11 +159,11 @@ TEST(EnvVarICDOverrideSetup, XDG) {
     // so that the test app can find them.  Include some badly specified elements as well.
     // Need to redirect the 'home' directory
     fs::path HOME = "/home/fake_home";
-    set_env_var("HOME", HOME.str());
-    set_env_var("XDG_CONFIG_DIRS", ":/tmp/goober:::::/tmp/goober/::::");
-    set_env_var("XDG_CONFIG_HOME", ":/tmp/goober:::::/tmp/goober2/::::");
-    set_env_var("XDG_DATA_DIRS", "::::/tmp/goober3:/tmp/goober4/with spaces:::");
-    set_env_var("XDG_DATA_HOME", "::::/tmp/goober3:/tmp/goober4/with spaces:::");
+    EnvVarWrapper home_env_var{"HOME", HOME.str()};
+    EnvVarWrapper xdg_config_dirs_env_var{"XDG_CONFIG_DIRS", ":/tmp/goober:::::/tmp/goober/::::"};
+    EnvVarWrapper xdg_config_home_env_var{"XDG_CONFIG_HOME", ":/tmp/goober:::::/tmp/goober2/::::"};
+    EnvVarWrapper xdg_data_dirs_env_var{"XDG_DATA_DIRS", "::::/tmp/goober3:/tmp/goober4/with spaces:::"};
+    EnvVarWrapper xdg_data_home_env_var{"XDG_DATA_HOME", "::::/tmp/goober3:/tmp/goober4/with spaces:::"};
 
     FrameworkEnvironment env{};
     env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA));
@@ -188,22 +173,22 @@ TEST(EnvVarICDOverrideSetup, XDG) {
     FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
     inst.CheckCreate();
 
-    auto check_paths = [](DebugUtilsLogger const& debug_log, ManifestCategory category, fs::path const& HOME) {
+    auto check_paths = [](DebugUtilsLogger const& debug_log, ManifestCategory category) {
         EXPECT_TRUE(debug_log.find((fs::path("/tmp/goober/vulkan") / category_path_name(category)).str()));
         EXPECT_TRUE(debug_log.find((fs::path("/tmp/goober2/vulkan") / category_path_name(category)).str()));
         EXPECT_TRUE(debug_log.find((fs::path("/tmp/goober3/vulkan") / category_path_name(category)).str()));
         EXPECT_TRUE(debug_log.find((fs::path("/tmp/goober4/with spaces/vulkan") / category_path_name(category)).str()));
     };
-    check_paths(env.debug_log, ManifestCategory::icd, HOME);
-    check_paths(env.debug_log, ManifestCategory::implicit_layer, HOME);
-    check_paths(env.debug_log, ManifestCategory::explicit_layer, HOME);
+    check_paths(env.debug_log, ManifestCategory::icd);
+    check_paths(env.debug_log, ManifestCategory::implicit_layer);
+    check_paths(env.debug_log, ManifestCategory::explicit_layer);
 }
 // Check that a json file in the paths don't cause the loader to crash
 TEST(EnvVarICDOverrideSetup, XDGContainsJsonFile) {
     // Set up a layer path that includes default and user-specified locations,
     // so that the test app can find them.  Include some badly specified elements as well.
     // Need to redirect the 'home' directory
-    set_env_var("XDG_CONFIG_DIRS", "bad_file.json");
+    EnvVarWrapper xdg_config_dirs_env_var{"XDG_CONFIG_DIRS", "bad_file.json"};
 
     FrameworkEnvironment env{};
     env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA));
@@ -282,11 +267,10 @@ TEST(EnvVarICDOverrideSetup, TestOnlyLayerEnvVar) {
     // so that the test app can find them.  Include some badly specified elements as well.
     // Need to redirect the 'home' directory
     fs::path HOME = "/home/fake_home";
-    set_env_var("HOME", HOME.str());
+    EnvVarWrapper home_env_var{"HOME", HOME.str()};
     std::string vk_layer_path = ":/tmp/carol::::/:";
     vk_layer_path += (HOME / "/ with spaces/:::::/tandy:").str();
-    set_env_var("VK_LAYER_PATH", vk_layer_path);
-    EnvVarCleaner layer_path_cleaner("VK_LAYER_PATH");
+    EnvVarWrapper layer_path_env_var{"VK_LAYER_PATH", vk_layer_path};
     InstWrapper inst1{env.vulkan_functions};
     inst1.create_info.add_layer(layer_name);
     FillDebugUtilsCreateDetails(inst1.create_info, env.debug_log);
@@ -329,11 +313,10 @@ TEST(EnvVarICDOverrideSetup, TestOnlyAddLayerEnvVar) {
     // so that the test app can find them.  Include some badly specified elements as well.
     // Need to redirect the 'home' directory
     fs::path HOME = "/home/fake_home";
-    set_env_var("HOME", HOME.str());
+    EnvVarWrapper home_env_var{"HOME", HOME.str()};
     std::string vk_layer_path = ":/tmp/carol::::/:";
     vk_layer_path += (HOME / "/ with spaces/:::::/tandy:").str();
-    set_env_var("VK_ADD_LAYER_PATH", vk_layer_path);
-    EnvVarCleaner add_layer_path_cleaner("VK_ADD_LAYER_PATH");
+    EnvVarWrapper add_layer_path_env_var{"VK_ADD_LAYER_PATH", vk_layer_path};
 
     InstWrapper inst1{env.vulkan_functions};
     inst1.create_info.add_layer(layer_name);
@@ -364,7 +347,7 @@ TEST(EnvVarICDOverrideSetup, TestOnlyAddLayerEnvVar) {
 // Test that the driver filter select will only enable driver manifest files that match the filter
 TEST(EnvVarICDOverrideSetup, FilterSelectDriver) {
     FrameworkEnvironment env{};
-    const char* filter_select_env_var = "VK_LOADER_DRIVERS_SELECT";
+    EnvVarWrapper filter_select_env_var{"VK_LOADER_DRIVERS_SELECT"};
 
     env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_6).set_disable_icd_inc(true).set_json_name("ABC_ICD"));
     env.add_icd(TestICDDetails{TEST_ICD_PATH_VERSION_6, VK_API_VERSION_1_2}.set_disable_icd_inc(true).set_json_name("BCD_ICD"));
@@ -386,7 +369,7 @@ TEST(EnvVarICDOverrideSetup, FilterSelectDriver) {
 
     // Match full-name
     env.debug_log.clear();
-    set_env_var(filter_select_env_var, "ABC_ICD.json");
+    filter_select_env_var.set_new_value("ABC_ICD.json");
 
     InstWrapper inst2{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst2.create_info, env.debug_log);
@@ -404,7 +387,7 @@ TEST(EnvVarICDOverrideSetup, FilterSelectDriver) {
 
     // Match prefix
     env.debug_log.clear();
-    set_env_var(filter_select_env_var, "ABC*");
+    filter_select_env_var.set_new_value("ABC*");
 
     InstWrapper inst3{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst3.create_info, env.debug_log);
@@ -422,7 +405,7 @@ TEST(EnvVarICDOverrideSetup, FilterSelectDriver) {
 
     // Match suffix
     env.debug_log.clear();
-    set_env_var(filter_select_env_var, "*C_ICD.json");
+    filter_select_env_var.set_new_value("*C_ICD.json");
 
     InstWrapper inst4{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst4.create_info, env.debug_log);
@@ -440,7 +423,7 @@ TEST(EnvVarICDOverrideSetup, FilterSelectDriver) {
 
     // Match sub-string
     env.debug_log.clear();
-    set_env_var(filter_select_env_var, "*BC*");
+    filter_select_env_var.set_new_value("*BC*");
 
     InstWrapper inst5{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst5.create_info, env.debug_log);
@@ -458,7 +441,7 @@ TEST(EnvVarICDOverrideSetup, FilterSelectDriver) {
 
     // Match all with star '*'
     env.debug_log.clear();
-    set_env_var(filter_select_env_var, "*");
+    filter_select_env_var.set_new_value("*");
 
     InstWrapper inst6{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst6.create_info, env.debug_log);
@@ -476,7 +459,7 @@ TEST(EnvVarICDOverrideSetup, FilterSelectDriver) {
 
     // Match all with special name
     env.debug_log.clear();
-    set_env_var(filter_select_env_var, "~all~");
+    filter_select_env_var.set_new_value("~all~");
 
     InstWrapper inst7{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst7.create_info, env.debug_log);
@@ -496,8 +479,7 @@ TEST(EnvVarICDOverrideSetup, FilterSelectDriver) {
 // Test that the driver filter disable disables driver manifest files that match the filter
 TEST(EnvVarICDOverrideSetup, FilterDisableDriver) {
     FrameworkEnvironment env{};
-    const char* filter_disable_env_var = "VK_LOADER_DRIVERS_DISABLE";
-    EnvVarCleaner filter_disable_cleaner(filter_disable_env_var);
+    EnvVarWrapper filter_disable_env_var{"VK_LOADER_DRIVERS_DISABLE"};
 
     env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_6).set_disable_icd_inc(true).set_json_name("ABC_ICD"));
     env.add_icd(TestICDDetails{TEST_ICD_PATH_VERSION_6, VK_API_VERSION_1_2}.set_disable_icd_inc(true).set_json_name("BCD_ICD"));
@@ -519,7 +501,7 @@ TEST(EnvVarICDOverrideSetup, FilterDisableDriver) {
 
     // Match full-name
     env.debug_log.clear();
-    set_env_var(filter_disable_env_var, "ABC_ICD.json");
+    filter_disable_env_var.set_new_value("ABC_ICD.json");
 
     InstWrapper inst2{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst2.create_info, env.debug_log);
@@ -537,7 +519,7 @@ TEST(EnvVarICDOverrideSetup, FilterDisableDriver) {
 
     // Match prefix
     env.debug_log.clear();
-    set_env_var(filter_disable_env_var, "ABC_*");
+    filter_disable_env_var.set_new_value("ABC_*");
 
     InstWrapper inst3{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst3.create_info, env.debug_log);
@@ -555,7 +537,7 @@ TEST(EnvVarICDOverrideSetup, FilterDisableDriver) {
 
     // Match suffix
     env.debug_log.clear();
-    set_env_var(filter_disable_env_var, "*C_ICD.json");
+    filter_disable_env_var.set_new_value("*C_ICD.json");
 
     InstWrapper inst4{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst4.create_info, env.debug_log);
@@ -573,7 +555,7 @@ TEST(EnvVarICDOverrideSetup, FilterDisableDriver) {
 
     // Match substring
     env.debug_log.clear();
-    set_env_var(filter_disable_env_var, "*BC*");
+    filter_disable_env_var.set_new_value("*BC*");
 
     InstWrapper inst5{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst5.create_info, env.debug_log);
@@ -591,7 +573,7 @@ TEST(EnvVarICDOverrideSetup, FilterDisableDriver) {
 
     // Match all with star '*'
     env.debug_log.clear();
-    set_env_var(filter_disable_env_var, "*");
+    filter_disable_env_var.set_new_value("*");
 
     InstWrapper inst6{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst6.create_info, env.debug_log);
@@ -609,7 +591,7 @@ TEST(EnvVarICDOverrideSetup, FilterDisableDriver) {
 
     // Match all with special name
     env.debug_log.clear();
-    set_env_var(filter_disable_env_var, "~all~");
+    filter_disable_env_var.set_new_value("~all~");
 
     InstWrapper inst7{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst7.create_info, env.debug_log);
@@ -630,10 +612,8 @@ TEST(EnvVarICDOverrideSetup, FilterDisableDriver) {
 // appropriate drivers are enabled and disabled
 TEST(EnvVarICDOverrideSetup, FilterSelectAndDisableDriver) {
     FrameworkEnvironment env{};
-    const char* filter_select_env_var = "VK_LOADER_DRIVERS_SELECT";
-    const char* filter_disable_env_var = "VK_LOADER_DRIVERS_DISABLE";
-    EnvVarCleaner filter_select_cleaner(filter_select_env_var);
-    EnvVarCleaner filter_disable_cleaner(filter_disable_env_var);
+    EnvVarWrapper filter_disable_env_var{"VK_LOADER_DRIVERS_DISABLE"};
+    EnvVarWrapper filter_select_env_var{"VK_LOADER_DRIVERS_SELECT"};
 
     env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_6).set_disable_icd_inc(true).set_json_name("ABC_ICD"));
     env.add_icd(TestICDDetails{TEST_ICD_PATH_VERSION_6, VK_API_VERSION_1_2}.set_disable_icd_inc(true).set_json_name("BCD_ICD"));
@@ -655,8 +635,8 @@ TEST(EnvVarICDOverrideSetup, FilterSelectAndDisableDriver) {
 
     // Disable two, but enable one
     env.debug_log.clear();
-    set_env_var(filter_disable_env_var, "*BC*");
-    set_env_var(filter_select_env_var, "BCD*");
+    filter_disable_env_var.set_new_value("*BC*");
+    filter_select_env_var.set_new_value("BCD*");
 
     InstWrapper inst2{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst2.create_info, env.debug_log);
@@ -674,8 +654,8 @@ TEST(EnvVarICDOverrideSetup, FilterSelectAndDisableDriver) {
 
     // Disable all, but enable two
     env.debug_log.clear();
-    set_env_var(filter_disable_env_var, "*");
-    set_env_var(filter_select_env_var, "*BC*");
+    filter_disable_env_var.set_new_value("*");
+    filter_select_env_var.set_new_value("*BC*");
 
     InstWrapper inst3{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst3.create_info, env.debug_log);
@@ -693,8 +673,8 @@ TEST(EnvVarICDOverrideSetup, FilterSelectAndDisableDriver) {
 
     // Disable all, but enable all
     env.debug_log.clear();
-    set_env_var(filter_disable_env_var, "*");
-    set_env_var(filter_select_env_var, "*");
+    filter_disable_env_var.set_new_value("*");
+    filter_select_env_var.set_new_value("*");
 
     InstWrapper inst4{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst4.create_info, env.debug_log);
index fce9bd24ba8c967471d325d4e78cfb8f0e389220..a0c9bff443dcf7e4fa2d57329e53a8df46c0a69c 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2021-2022 The Khronos Group Inc.
- * Copyright (c) 2021-2022 Valve Corporation
- * Copyright (c) 2021-2022 LunarG, Inc.
+ * Copyright (c) 2021-2023 The Khronos Group Inc.
+ * Copyright (c) 2021-2023 Valve Corporation
+ * Copyright (c) 2021-2023 LunarG, Inc.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and/or associated documentation files (the "Materials"), to
@@ -47,17 +47,15 @@ TEST(ImplicitLayers, WithEnableAndDisableEnvVar) {
     FrameworkEnvironment env;
     env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA));
     const char* implicit_layer_name = "VK_LAYER_ImplicitTestLayer";
-    const char* enable_env_var = "ENABLE_ME";
-    const char* disable_env_var = "DISABLE_ME";
 
-    EnvVarCleaner enable_cleaner(enable_env_var);
-    EnvVarCleaner disable_cleaner(disable_env_var);
+    EnvVarWrapper enable_env_var{"ENABLE_ME"};
+    EnvVarWrapper disable_env_var{"DISABLE_ME"};
 
     env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                          .set_name(implicit_layer_name)
                                                          .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
-                                                         .set_disable_environment(disable_env_var)
-                                                         .set_enable_environment(enable_env_var)),
+                                                         .set_disable_environment(disable_env_var.get())
+                                                         .set_enable_environment(enable_env_var.get())),
                            "implicit_test_layer.json");
 
     uint32_t count = 0;
@@ -68,27 +66,27 @@ TEST(ImplicitLayers, WithEnableAndDisableEnvVar) {
     CheckLogForLayerString(env, implicit_layer_name, false);
 
     // set enable env-var to 0, no layer should be found
-    set_env_var(enable_env_var, "0");
+    enable_env_var.set_new_value("0");
     CheckLogForLayerString(env, implicit_layer_name, false);
 
     // set enable env-var, layer should load
-    set_env_var(enable_env_var, "1");
+    enable_env_var.set_new_value("1");
     CheckLogForLayerString(env, implicit_layer_name, true);
 
     // remove enable env var, so we can check what happens when only disable is present
-    remove_env_var(enable_env_var);
+    enable_env_var.remove_value();
 
     // set disable env-var to 0, layer should not load
-    set_env_var(disable_env_var, "0");
+    disable_env_var.set_new_value("0");
     CheckLogForLayerString(env, implicit_layer_name, false);
 
     // set disable env-var to 1, layer should not load
-    set_env_var(disable_env_var, "1");
+    disable_env_var.set_new_value("1");
     CheckLogForLayerString(env, implicit_layer_name, false);
 
     // set both enable and disable env-var, layer should not load
-    set_env_var(enable_env_var, "1");
-    set_env_var(disable_env_var, "1");
+    enable_env_var.set_new_value("1");
+    disable_env_var.set_new_value("1");
     CheckLogForLayerString(env, implicit_layer_name, false);
 }
 
@@ -96,12 +94,12 @@ TEST(ImplicitLayers, OnlyDisableEnvVar) {
     FrameworkEnvironment env;
     env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA));
     const char* implicit_layer_name = "ImplicitTestLayer";
-    const char* disable_env_var = "DISABLE_ME";
-    EnvVarCleaner disable_cleaner(disable_env_var);
+    EnvVarWrapper disable_env_var{"DISABLE_ME"};
+
     env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                          .set_name(implicit_layer_name)
                                                          .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
-                                                         .set_disable_environment(disable_env_var)),
+                                                         .set_disable_environment(disable_env_var.get())),
                            "implicit_test_layer.json");
 
     uint32_t count = 0;
@@ -112,11 +110,11 @@ TEST(ImplicitLayers, OnlyDisableEnvVar) {
     CheckLogForLayerString(env, implicit_layer_name, true);
 
     // set disable env-var to 0, layer should load
-    set_env_var(disable_env_var, "0");
+    disable_env_var.set_new_value("0");
     CheckLogForLayerString(env, implicit_layer_name, false);
 
     // set disable env-var to 1, layer should not load
-    set_env_var(disable_env_var, "1");
+    disable_env_var.set_new_value("1");
     CheckLogForLayerString(env, implicit_layer_name, false);
 
     {
@@ -132,8 +130,7 @@ TEST(ImplicitLayers, PreInstanceEnumInstLayerProps) {
     FrameworkEnvironment env;
     env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA));
     const char* implicit_layer_name = "ImplicitTestLayer";
-    const char* disable_env_var = "DISABLE_ME";
-    EnvVarCleaner disable_cleaner(disable_env_var);
+    EnvVarWrapper disable_env_var{"DISABLE_ME"};
 
     env.add_implicit_layer(
         ManifestLayer{}
@@ -141,7 +138,7 @@ TEST(ImplicitLayers, PreInstanceEnumInstLayerProps) {
             .add_layer(ManifestLayer::LayerDescription{}
                            .set_name(implicit_layer_name)
                            .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
-                           .set_disable_environment(disable_env_var)
+                           .set_disable_environment(disable_env_var.get())
                            .add_pre_instance_function(ManifestLayer::LayerDescription::FunctionOverride{}
                                                           .set_vk_func("vkEnumerateInstanceLayerProperties")
                                                           .set_override_name("test_preinst_vkEnumerateInstanceLayerProperties"))),
@@ -156,7 +153,7 @@ TEST(ImplicitLayers, PreInstanceEnumInstLayerProps) {
     ASSERT_EQ(count, layer_props);
 
     // set disable env-var to 1, layer should not load
-    set_env_var(disable_env_var, "1");
+    disable_env_var.set_new_value("1");
 
     count = 0;
     ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr));
@@ -168,15 +165,14 @@ TEST(ImplicitLayers, PreInstanceEnumInstExtProps) {
     FrameworkEnvironment env;
     env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA));
     const char* implicit_layer_name = "ImplicitTestLayer";
-    const char* disable_env_var = "DISABLE_ME";
-    EnvVarCleaner disable_cleaner(disable_env_var);
+    EnvVarWrapper disable_env_var{"DISABLE_ME"};
 
     env.add_implicit_layer(ManifestLayer{}
                                .set_file_format_version(ManifestVersion(1, 1, 2))
                                .add_layer(ManifestLayer::LayerDescription{}
                                               .set_name(implicit_layer_name)
                                               .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
-                                              .set_disable_environment(disable_env_var)
+                                              .set_disable_environment(disable_env_var.get())
                                               .add_pre_instance_function(
                                                   ManifestLayer::LayerDescription::FunctionOverride{}
                                                       .set_vk_func("vkEnumerateInstanceExtensionProperties")
@@ -192,7 +188,7 @@ TEST(ImplicitLayers, PreInstanceEnumInstExtProps) {
     ASSERT_EQ(count, ext_props);
 
     // set disable env-var to 1, layer should not load
-    set_env_var(disable_env_var, "1");
+    disable_env_var.set_new_value("1");
 
     count = 0;
     ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr));
@@ -207,8 +203,7 @@ TEST(ImplicitLayers, PreInstanceVersion) {
     env.get_test_icd().icd_api_version = VK_MAKE_API_VERSION(0, 1, 2, 3);
 
     const char* implicit_layer_name = "ImplicitTestLayer";
-    const char* disable_env_var = "DISABLE_ME";
-    EnvVarCleaner disable_cleaner(disable_env_var);
+    EnvVarWrapper disable_env_var{"DISABLE_ME"};
 
     env.add_implicit_layer(
         ManifestLayer{}
@@ -217,7 +212,7 @@ TEST(ImplicitLayers, PreInstanceVersion) {
                            .set_name(implicit_layer_name)
                            .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                            .set_api_version(VK_MAKE_API_VERSION(0, 1, 2, 3))
-                           .set_disable_environment(disable_env_var)
+                           .set_disable_environment(disable_env_var.get())
                            .add_pre_instance_function(ManifestLayer::LayerDescription::FunctionOverride{}
                                                           .set_vk_func("vkEnumerateInstanceVersion")
                                                           .set_override_name("test_preinst_vkEnumerateInstanceVersion"))),
@@ -232,7 +227,7 @@ TEST(ImplicitLayers, PreInstanceVersion) {
     ASSERT_EQ(version, layer_version);
 
     // set disable env-var to 1, layer should not load
-    set_env_var(disable_env_var, "1");
+    disable_env_var.set_new_value("1");
 
     version = 0;
     ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceVersion(&version));
@@ -250,15 +245,14 @@ TEST(ImplicitLayers, OverrideGetInstanceProcAddr) {
     env.get_test_icd().physical_devices.push_back({});
 
     const char* implicit_layer_name = "ImplicitTestLayer";
-    const char* disable_env_var = "DISABLE_ME";
-    EnvVarCleaner disable_cleaner(disable_env_var);
+    EnvVarWrapper disable_env_var{"DISABLE_ME"};
 
     env.add_implicit_layer(ManifestLayer{}
                                .set_file_format_version(ManifestVersion(1, 0, 0))
                                .add_layer(ManifestLayer::LayerDescription{}
                                               .set_name(implicit_layer_name)
                                               .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_1)
-                                              .set_disable_environment(disable_env_var)
+                                              .set_disable_environment(disable_env_var.get())
                                               .add_function(ManifestLayer::LayerDescription::FunctionOverride{}
                                                                 .set_vk_func("vkGetInstanceProcAddr")
                                                                 .set_override_name("test_override_vkGetInstanceProcAddr"))),
@@ -271,7 +265,7 @@ TEST(ImplicitLayers, OverrideGetInstanceProcAddr) {
 
     {
         // set disable env-var to 1, layer should not load
-        set_env_var(disable_env_var, "1");
+        disable_env_var.set_new_value("1");
         InstWrapper inst2{env.vulkan_functions};
         inst2.CheckCreate();
     }
@@ -319,8 +313,8 @@ TEST(ImplicitLayers, EnableWithFilter) {
                                                          .set_api_version(VK_MAKE_API_VERSION(0, 1, 0, 0))),
                            implicit_json_name_3);
 
-    EnvVarCleaner layers_enable_cleaner("VK_LOADER_LAYERS_ENABLE");
-    EnvVarCleaner layer_1_enable_cleaner(enable_layer_name_1);
+    EnvVarWrapper layers_enable_env_var{"VK_LOADER_LAYERS_ENABLE"};
+    EnvVarWrapper layer_1_enable_env_var{enable_layer_name_1};
 
     // First, test an instance/device without the layer forced on.
     InstWrapper inst1{env.vulkan_functions};
@@ -343,7 +337,7 @@ TEST(ImplicitLayers, EnableWithFilter) {
     // Now force on one layer with its full name
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_ENABLE", implicit_layer_name_1);
+    layers_enable_env_var.set_new_value(implicit_layer_name_1);
 
     InstWrapper inst2{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst2.create_info, env.debug_log);
@@ -365,7 +359,7 @@ TEST(ImplicitLayers, EnableWithFilter) {
     // Match prefix
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_ENABLE", "VK_LAYER_LUNARG_*");
+    layers_enable_env_var.set_new_value("VK_LAYER_LUNARG_*");
 
     InstWrapper inst3{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst3.create_info, env.debug_log);
@@ -387,7 +381,7 @@ TEST(ImplicitLayers, EnableWithFilter) {
     // Match suffix
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_ENABLE", "*Second_layer");
+    layers_enable_env_var.set_new_value("*Second_layer");
 
     InstWrapper inst4{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst4.create_info, env.debug_log);
@@ -409,7 +403,7 @@ TEST(ImplicitLayers, EnableWithFilter) {
     // Match substring
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_ENABLE", "*Second*");
+    layers_enable_env_var.set_new_value("*Second*");
 
     InstWrapper inst5{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst5.create_info, env.debug_log);
@@ -431,7 +425,7 @@ TEST(ImplicitLayers, EnableWithFilter) {
     // Match all with star '*'
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_ENABLE", "*");
+    layers_enable_env_var.set_new_value("*");
 
     InstWrapper inst6{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst6.create_info, env.debug_log);
@@ -453,7 +447,7 @@ TEST(ImplicitLayers, EnableWithFilter) {
     // Match all with special name
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_ENABLE", "~all~");
+    layers_enable_env_var.set_new_value("~all~");
 
     InstWrapper inst7{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst7.create_info, env.debug_log);
@@ -475,8 +469,8 @@ TEST(ImplicitLayers, EnableWithFilter) {
     // Match substring, but enable the other layer manually
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var(enable_layer_name_1, "1");
-    set_env_var("VK_LOADER_LAYERS_ENABLE", "*Second*");
+    layer_1_enable_env_var.set_new_value("1");
+    layers_enable_env_var.set_new_value("*Second*");
 
     InstWrapper inst8{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst8.create_info, env.debug_log);
@@ -532,7 +526,7 @@ TEST(ImplicitLayers, DisableWithFilter) {
                                                          .set_api_version(VK_MAKE_API_VERSION(0, 1, 0, 0))),
                            implicit_json_name_3);
 
-    EnvVarCleaner layers_disable_cleaner("VK_LOADER_LAYERS_DISABLE");
+    EnvVarWrapper layers_disable_env_var{"VK_LOADER_LAYERS_DISABLE"};
 
     // First, test an instance/device
     InstWrapper inst1{env.vulkan_functions};
@@ -555,7 +549,7 @@ TEST(ImplicitLayers, DisableWithFilter) {
     // Now force off one layer with its full name
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", implicit_layer_name_1);
+    layers_disable_env_var.set_new_value(implicit_layer_name_1);
 
     InstWrapper inst2{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst2.create_info, env.debug_log);
@@ -577,7 +571,7 @@ TEST(ImplicitLayers, DisableWithFilter) {
     // Match prefix
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", "VK_LAYER_LUNARG_*");
+    layers_disable_env_var.set_new_value("VK_LAYER_LUNARG_*");
 
     InstWrapper inst3{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst3.create_info, env.debug_log);
@@ -599,7 +593,7 @@ TEST(ImplicitLayers, DisableWithFilter) {
     // Match suffix
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", "*Second_layer");
+    layers_disable_env_var.set_new_value("*Second_layer");
 
     InstWrapper inst4{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst4.create_info, env.debug_log);
@@ -621,7 +615,7 @@ TEST(ImplicitLayers, DisableWithFilter) {
     // Match substring
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", "*Second*");
+    layers_disable_env_var.set_new_value("*Second*");
 
     InstWrapper inst5{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst5.create_info, env.debug_log);
@@ -643,7 +637,7 @@ TEST(ImplicitLayers, DisableWithFilter) {
     // Match all with star '*'
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", "*");
+    layers_disable_env_var.set_new_value("*");
 
     InstWrapper inst6{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst6.create_info, env.debug_log);
@@ -665,7 +659,7 @@ TEST(ImplicitLayers, DisableWithFilter) {
     // Match all with special name
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", "~all~");
+    layers_disable_env_var.set_new_value("~all~");
 
     InstWrapper inst7{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst7.create_info, env.debug_log);
@@ -715,9 +709,9 @@ TEST(ImplicitLayers, DisableWithFilterWhenLayersEnableEnvVarIsActive) {
                                                          .set_api_version(VK_MAKE_API_VERSION(0, 1, 0, 0))),
                            implicit_json_name_2);
 
-    EnvVarCleaner layers_disable_cleaner("VK_LOADER_LAYERS_DISABLE");
-    EnvVarCleaner layer_1_enable_cleaner(enable_layer_name_1);
-    EnvVarCleaner layer_2_enable_cleaner(enable_layer_name_2);
+    EnvVarWrapper layers_disable_env_var{"VK_LOADER_LAYERS_DISABLE"};
+    EnvVarWrapper layer_1_enable_env_var{enable_layer_name_1};
+    EnvVarWrapper layer_2_enable_env_var{enable_layer_name_2};
 
     // First, test an instance/device
     InstWrapper inst1{env.vulkan_functions};
@@ -736,8 +730,8 @@ TEST(ImplicitLayers, DisableWithFilterWhenLayersEnableEnvVarIsActive) {
     // Set the layers enable env-var
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var(enable_layer_name_1, "1");
-    set_env_var(enable_layer_name_2, "1");
+    layer_1_enable_env_var.set_new_value("1");
+    layer_2_enable_env_var.set_new_value("1");
 
     InstWrapper inst2{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst2.create_info, env.debug_log);
@@ -755,7 +749,7 @@ TEST(ImplicitLayers, DisableWithFilterWhenLayersEnableEnvVarIsActive) {
     // Now force off one layer with its full name
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", implicit_layer_name_1);
+    layers_disable_env_var.set_new_value(implicit_layer_name_1);
 
     InstWrapper inst3{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst3.create_info, env.debug_log);
@@ -773,7 +767,7 @@ TEST(ImplicitLayers, DisableWithFilterWhenLayersEnableEnvVarIsActive) {
     // Now force off both layers
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", "~implicit~");
+    layers_disable_env_var.set_new_value("~implicit~");
 
     InstWrapper inst4{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst4.create_info, env.debug_log);
@@ -826,14 +820,14 @@ TEST(ImplicitLayers, EnableAndDisableWithFilter) {
                                                          .set_api_version(VK_MAKE_API_VERSION(0, 1, 0, 0))),
                            implicit_json_name_3);
 
-    EnvVarCleaner layers_enable_cleaner("VK_LOADER_LAYERS_ENABLE");
-    EnvVarCleaner layers_disable_cleaner("VK_LOADER_LAYERS_DISABLE");
+    EnvVarWrapper layers_disable_env_var{"VK_LOADER_LAYERS_DISABLE"};
+    EnvVarWrapper layers_enable_env_var{"VK_LOADER_LAYERS_ENABLE"};
 
     // Disable 2 but enable 1
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", "*Second*");
-    set_env_var("VK_LOADER_LAYERS_ENABLE", "*test_layer");
+    layers_disable_env_var.set_new_value("*Second*");
+    layers_enable_env_var.set_new_value("*test_layer");
 
     InstWrapper inst1{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst1.create_info, env.debug_log);
@@ -855,8 +849,8 @@ TEST(ImplicitLayers, EnableAndDisableWithFilter) {
     // Disable all but enable 2
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", "*");
-    set_env_var("VK_LOADER_LAYERS_ENABLE", "*Second*");
+    layers_disable_env_var.set_new_value("*");
+    layers_enable_env_var.set_new_value("*Second*");
 
     InstWrapper inst2{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst2.create_info, env.debug_log);
@@ -878,8 +872,8 @@ TEST(ImplicitLayers, EnableAndDisableWithFilter) {
     // Disable all but enable 2
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", "~all~");
-    set_env_var("VK_LOADER_LAYERS_ENABLE", "*Second*");
+    layers_disable_env_var.set_new_value("~all~");
+    layers_enable_env_var.set_new_value("*Second*");
 
     InstWrapper inst3{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst3.create_info, env.debug_log);
@@ -901,8 +895,8 @@ TEST(ImplicitLayers, EnableAndDisableWithFilter) {
     // Disable implicit but enable 2
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", "~implicit~");
-    set_env_var("VK_LOADER_LAYERS_ENABLE", "*Second*");
+    layers_disable_env_var.set_new_value("~implicit~");
+    layers_enable_env_var.set_new_value("*Second*");
 
     InstWrapper inst4{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst4.create_info, env.debug_log);
@@ -924,8 +918,8 @@ TEST(ImplicitLayers, EnableAndDisableWithFilter) {
     // Disable explicit but enable 2 (should still be everything)
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", "~explicit~");
-    set_env_var("VK_LOADER_LAYERS_ENABLE", "*Second*");
+    layers_disable_env_var.set_new_value("~explicit~");
+    layers_enable_env_var.set_new_value("*Second*");
 
     InstWrapper inst5{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst5.create_info, env.debug_log);
@@ -947,8 +941,8 @@ TEST(ImplicitLayers, EnableAndDisableWithFilter) {
     // Disable implicit but enable all
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", "~implicit~");
-    set_env_var("VK_LOADER_LAYERS_ENABLE", "*");
+    layers_disable_env_var.set_new_value("~implicit~");
+    layers_enable_env_var.set_new_value("*");
 
     InstWrapper inst6{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst6.create_info, env.debug_log);
@@ -1002,13 +996,13 @@ TEST(MetaLayers, InvalidComponentLayer) {
     EXPECT_TRUE(string_eq(layer_props.layerName, regular_layer_name));
 
     uint32_t extension_count = 0;
-    std::array<VkExtensionProperties, 3> extensions;
+    std::array<VkExtensionProperties, 4> extensions;
     EXPECT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr));
-    EXPECT_EQ(extension_count, 3U);  // return debug report & debug utils & portability enumeration
+    EXPECT_EQ(extension_count, 4U);  // debug report & debug utils & portability enumeration & direct driver loading
 
     EXPECT_EQ(VK_SUCCESS,
               env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, extensions.data()));
-    EXPECT_EQ(extension_count, 3U);
+    EXPECT_EQ(extension_count, 4U);  // debug report & debug utils & portability enumeration & direct driver loading
 
     InstWrapper inst{env.vulkan_functions};
     inst.create_info.add_layer(meta_layer_name);
@@ -1048,13 +1042,13 @@ TEST(MetaLayers, ExplicitMetaLayer) {
         EXPECT_TRUE(check_permutation({regular_layer_name, meta_layer_name}, layer_props));
 
         uint32_t extension_count = 0;
-        std::array<VkExtensionProperties, 3> extensions;
+        std::array<VkExtensionProperties, 4> extensions;
         EXPECT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr));
-        EXPECT_EQ(extension_count, 3U);  // return debug report & debug utils & portability enumeration
+        EXPECT_EQ(extension_count, 4U);  // debug report, debug utils, portability enumeration, direct driver loading
 
         EXPECT_EQ(VK_SUCCESS,
                   env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, extensions.data()));
-        EXPECT_EQ(extension_count, 3U);
+        EXPECT_EQ(extension_count, 4U);  // debug report, debug utils, portability enumeration, direct driver loading
     }
     {  // don't enable the layer, shouldn't find any layers when calling vkEnumerateDeviceLayerProperties
         InstWrapper inst{env.vulkan_functions};
@@ -1111,13 +1105,13 @@ TEST(MetaLayers, MetaLayerNameInComponentLayers) {
     EXPECT_TRUE(string_eq(layer_props.layerName, regular_layer_name));
 
     uint32_t extension_count = 0;
-    std::array<VkExtensionProperties, 3> extensions;
+    std::array<VkExtensionProperties, 4> extensions;
     EXPECT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr));
-    EXPECT_EQ(extension_count, 3U);  // return debug report & debug utils & portability enumeration
+    EXPECT_EQ(extension_count, 4U);  // debug report & debug utils & portability enumeration
 
     EXPECT_EQ(VK_SUCCESS,
               env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, extensions.data()));
-    EXPECT_EQ(extension_count, 3U);
+    EXPECT_EQ(extension_count, 4U);  // debug report, debug utils, portability enumeration, direct driver loading
 
     InstWrapper inst{env.vulkan_functions};
     inst.create_info.add_layer(meta_layer_name);
@@ -1160,13 +1154,13 @@ TEST(MetaLayers, MetaLayerWhichAddsMetaLayer) {
     EXPECT_TRUE(check_permutation({regular_layer_name, meta_layer_name, meta_meta_layer_name}, layer_props));
 
     uint32_t extension_count = 0;
-    std::array<VkExtensionProperties, 3> extensions;
+    std::array<VkExtensionProperties, 4> extensions;
     EXPECT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr));
-    EXPECT_EQ(extension_count, 3U);  // return debug report & debug utils & portability enumeration
+    EXPECT_EQ(extension_count, 4U);  // debug report & debug utils & portability enumeration & direct driver loading
 
     EXPECT_EQ(VK_SUCCESS,
               env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, extensions.data()));
-    EXPECT_EQ(extension_count, 3U);
+    EXPECT_EQ(extension_count, 4U);  // debug report, debug utils, portability enumeration, direct driver loading
 
     InstWrapper inst{env.vulkan_functions};
     inst.create_info.add_layer(meta_layer_name);
@@ -1766,13 +1760,12 @@ TEST(OverrideMetaLayer, OverridePathsInteractionWithVK_LAYER_PATH) {
     FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
     inst.CheckCreate(VK_ERROR_LAYER_NOT_PRESENT);
     ASSERT_FALSE(env.debug_log.find(std::string("Insert instance layer \"") + env_var_layer_name));
-    ASSERT_TRUE(
-        env.debug_log.find("Ignoring VK_LAYER_PATH. The Override layer is active and has override paths set, which takes priority. "
-                           "VK_LAYER_PATH is set to " +
-                           env.env_var_vk_layer_paths));
+    ASSERT_TRUE(env.debug_log.find(
+        std::string("Ignoring VK_LAYER_PATH. The Override layer is active and has override paths set, which takes priority. "
+                    "VK_LAYER_PATH is set to ") +
+        env.env_var_vk_layer_paths.value()));
 
     env.layers.clear();
-    remove_env_var("VK_LAYER_PATH");
 }
 
 // Make sure that implicit layers not in the override paths aren't found by mistake
@@ -1967,8 +1960,8 @@ TEST(LayerCreateInstance, GetPhysicalDeviceProperties2) {
                 ManifestLayer::LayerDescription{}.set_name(regular_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
         "regular_test_layer.json");
 
-    auto& layer = env.get_test_layer(0);
-    layer.set_create_instance_callback([](TestLayer& layer) -> VkResult {
+    auto& layer_handle = env.get_test_layer(0);
+    layer_handle.set_create_instance_callback([](TestLayer& layer) -> VkResult {
         uint32_t phys_dev_count = 0;
         VkResult res = layer.instance_dispatch_table.EnumeratePhysicalDevices(layer.instance_handle, &phys_dev_count, nullptr);
         if (res != VK_SUCCESS || phys_dev_count > 1) {
@@ -2006,8 +1999,8 @@ TEST(LayerCreateInstance, GetPhysicalDeviceProperties2KHR) {
             ManifestLayer::LayerDescription{}.set_name(regular_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
         "regular_test_layer.json");
 
-    auto& layer = env.get_test_layer(0);
-    layer.set_create_instance_callback([](TestLayer& layer) -> VkResult {
+    auto& layer_handle = env.get_test_layer(0);
+    layer_handle.set_create_instance_callback([](TestLayer& layer) -> VkResult {
         uint32_t phys_dev_count = 1;
         VkPhysicalDevice phys_dev{};
         layer.instance_dispatch_table.EnumeratePhysicalDevices(layer.instance_handle, &phys_dev_count, &phys_dev);
@@ -2196,7 +2189,6 @@ TEST(LayerExtensions, ImplicitNoAdditionalInstanceExtension) {
     const char* implicit_layer_name = "VK_LAYER_LUNARG_wrap_objects";
     const char* enable_env_var = "ENABLE_ME";
     const char* disable_env_var = "DISABLE_ME";
-    EnvVarCleaner enable_cleaner(enable_env_var);
 
     env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                          .set_name(implicit_layer_name)
@@ -2210,7 +2202,7 @@ TEST(LayerExtensions, ImplicitNoAdditionalInstanceExtension) {
     ASSERT_EQ(count, 1U);
 
     // set enable env-var, layer should load
-    set_env_var(enable_env_var, "1");
+    EnvVarWrapper wrap_enable_env_var{enable_env_var, "1"};
     CheckLogForLayerString(env, implicit_layer_name, true);
 
     uint32_t extension_count = 0;
@@ -2220,7 +2212,7 @@ TEST(LayerExtensions, ImplicitNoAdditionalInstanceExtension) {
     extension_props.resize(extension_count);
     ASSERT_EQ(VK_SUCCESS,
               env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, extension_props.data()));
-    ASSERT_EQ(extension_count, 3U);  // debug_utils, debug_report, and portability enumeration
+    ASSERT_EQ(extension_count, 4U);  // debug_utils, debug_report, portability enumeration, direct driver loading
 
     // Make sure the extensions that are implemented only in the test layers is not present.
     ASSERT_FALSE(contains(extension_props, VK_EXT_DIRECT_MODE_DISPLAY_EXTENSION_NAME));
@@ -2246,7 +2238,6 @@ TEST(LayerExtensions, ImplicitDirDispModeInstanceExtension) {
     const char* implicit_layer_name = "VK_LAYER_LUNARG_wrap_objects";
     const char* enable_env_var = "ENABLE_ME";
     const char* disable_env_var = "DISABLE_ME";
-    EnvVarCleaner enable_cleaner(enable_env_var);
 
     env.add_implicit_layer(
         ManifestLayer{}.add_layer(
@@ -2262,18 +2253,19 @@ TEST(LayerExtensions, ImplicitDirDispModeInstanceExtension) {
     ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr));
     ASSERT_EQ(count, 1U);
 
-    // // set enable env-var, layer should load
-    set_env_var(enable_env_var, "1");
+    // set enable env-var, layer should load
+    EnvVarWrapper wrap_enable_env_var{enable_env_var, "1"};
     CheckLogForLayerString(env, implicit_layer_name, true);
 
     uint32_t extension_count = 0;
     std::vector<VkExtensionProperties> extension_props;
     ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr));
-    ASSERT_EQ(extension_count, 4U);  // the instance extension, debug_utils, debug_report, and portability enumeration
+    ASSERT_EQ(extension_count,
+              5U);  // the instance extension, debug_utils, debug_report, portability enumeration, direct driver loading
     extension_props.resize(extension_count);
     ASSERT_EQ(VK_SUCCESS,
               env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, extension_props.data()));
-    ASSERT_EQ(extension_count, 4U);
+    ASSERT_EQ(extension_count, 5U);
 
     // Make sure the extensions that are implemented only in the test layers is not present.
     ASSERT_TRUE(contains(extension_props, VK_EXT_DIRECT_MODE_DISPLAY_EXTENSION_NAME));
@@ -2300,7 +2292,6 @@ TEST(LayerExtensions, ImplicitDispSurfCountInstanceExtension) {
     const char* implicit_layer_name = "VK_LAYER_LUNARG_wrap_objects";
     const char* enable_env_var = "ENABLE_ME";
     const char* disable_env_var = "DISABLE_ME";
-    EnvVarCleaner enable_cleaner(enable_env_var);
 
     env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                          .set_name(implicit_layer_name)
@@ -2316,18 +2307,19 @@ TEST(LayerExtensions, ImplicitDispSurfCountInstanceExtension) {
     ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr));
     ASSERT_EQ(count, 1U);
 
-    // // set enable env-var, layer should load
-    set_env_var(enable_env_var, "1");
+    // set enable env-var, layer should load
+    EnvVarWrapper wrap_enable_env_var{enable_env_var, "1"};
     CheckLogForLayerString(env, implicit_layer_name, true);
 
     uint32_t extension_count = 0;
     std::vector<VkExtensionProperties> extension_props;
     ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr));
-    ASSERT_EQ(extension_count, 4U);  // the instance extension, debug_utils, debug_report, and portability enumeration
+    ASSERT_EQ(extension_count,
+              5U);  // the instance extension, debug_utils, debug_report, portability enumeration, direct driver loading
     extension_props.resize(extension_count);
     ASSERT_EQ(VK_SUCCESS,
               env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, extension_props.data()));
-    ASSERT_EQ(extension_count, 4U);
+    ASSERT_EQ(extension_count, 5U);
 
     // Make sure the extensions that are implemented only in the test layers is not present.
     ASSERT_FALSE(contains(extension_props, VK_EXT_DIRECT_MODE_DISPLAY_EXTENSION_NAME));
@@ -2354,7 +2346,6 @@ TEST(LayerExtensions, ImplicitBothInstanceExtensions) {
     const char* implicit_layer_name = "VK_LAYER_LUNARG_wrap_objects";
     const char* enable_env_var = "ENABLE_ME";
     const char* disable_env_var = "DISABLE_ME";
-    EnvVarCleaner enable_cleaner(enable_env_var);
 
     env.add_implicit_layer(
         ManifestLayer{}.add_layer(
@@ -2372,18 +2363,19 @@ TEST(LayerExtensions, ImplicitBothInstanceExtensions) {
     ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr));
     ASSERT_EQ(count, 1U);
 
-    // // set enable env-var, layer should load
-    set_env_var(enable_env_var, "1");
+    // set enable env-var, layer should load
+    EnvVarWrapper wrap_enable_env_var{enable_env_var, "1"};
     CheckLogForLayerString(env, implicit_layer_name, true);
 
     uint32_t extension_count = 0;
     std::vector<VkExtensionProperties> extension_props;
     ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr));
-    ASSERT_EQ(extension_count, 5U);  // the two instance extension plus debug_utils, debug_report, portability enumeration
+    ASSERT_EQ(extension_count,
+              6U);  // the two instance extension plus debug_utils, debug_report, portability enumeration, direct driver loading
     extension_props.resize(extension_count);
     ASSERT_EQ(VK_SUCCESS,
               env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, extension_props.data()));
-    ASSERT_EQ(extension_count, 5U);
+    ASSERT_EQ(extension_count, 6U);
 
     // Make sure the extensions that are implemented only in the test layers is not present.
     ASSERT_TRUE(contains(extension_props, VK_EXT_DIRECT_MODE_DISPLAY_EXTENSION_NAME));
@@ -2421,11 +2413,11 @@ TEST(LayerExtensions, ExplicitNoAdditionalInstanceExtension) {
     uint32_t extension_count = 0;
     std::vector<VkExtensionProperties> extension_props;
     ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr));
-    ASSERT_EQ(extension_count, 3U);  // debug utils, debug report, portability enumeration
+    ASSERT_EQ(extension_count, 4U);  // debug utils, debug report, portability enumeration, direct driver loading
     extension_props.resize(extension_count);
     ASSERT_EQ(VK_SUCCESS,
               env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, extension_props.data()));
-    ASSERT_EQ(extension_count, 3U);
+    ASSERT_EQ(extension_count, 4U);  // debug report, debug utils, portability enumeration, direct driver loading
 
     // Make sure the extensions are not present
     for (const auto& ext : extension_props) {
@@ -2473,11 +2465,11 @@ TEST(LayerExtensions, ExplicitDirDispModeInstanceExtension) {
     uint32_t extension_count = 0;
     std::vector<VkExtensionProperties> extension_props;
     ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr));
-    ASSERT_EQ(extension_count, 3U);  // debug utils, debug report, portability enumeration
+    ASSERT_EQ(extension_count, 4U);  // debug utils, debug report, portability enumeration
     extension_props.resize(extension_count);
     ASSERT_EQ(VK_SUCCESS,
               env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, extension_props.data()));
-    ASSERT_EQ(extension_count, 3U);
+    ASSERT_EQ(extension_count, 4U);
     // Make sure the extensions are not present
     for (const auto& ext : extension_props) {
         ASSERT_FALSE(string_eq(ext.extensionName, VK_EXT_DIRECT_MODE_DISPLAY_EXTENSION_NAME));
@@ -2714,9 +2706,8 @@ TEST(LayerExtensions, ImplicitNoAdditionalDeviceExtension) {
     ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr));
     ASSERT_EQ(count, 1U);
 
-    // // set enable env-var, layer should load
-    set_env_var(enable_env_var, "1");
-    EnvVarCleaner enable_cleaner(enable_env_var);
+    // set enable env-var, layer should load
+    EnvVarWrapper wrap_enable_env_var{enable_env_var, "1"};
     CheckLogForLayerString(env, implicit_layer_name, true);
 
     InstWrapper inst{env.vulkan_functions};
@@ -2788,7 +2779,6 @@ TEST(LayerExtensions, ImplicitMaintenanceDeviceExtension) {
     const char* implicit_layer_name = "VK_LAYER_LUNARG_wrap_objects";
     const char* enable_env_var = "ENABLE_ME";
     const char* disable_env_var = "DISABLE_ME";
-    EnvVarCleaner enable_cleaner(enable_env_var);
 
     env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                          .set_name(implicit_layer_name)
@@ -2801,8 +2791,8 @@ TEST(LayerExtensions, ImplicitMaintenanceDeviceExtension) {
     ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr));
     ASSERT_EQ(count, 1U);
 
-    // // set enable env-var, layer should load
-    set_env_var(enable_env_var, "1");
+    // set enable env-var, layer should load
+    EnvVarWrapper wrap_enable_env_var{enable_env_var, "1"};
     CheckLogForLayerString(env, implicit_layer_name, true);
 
     InstWrapper inst{env.vulkan_functions};
@@ -2848,7 +2838,6 @@ TEST(LayerExtensions, ImplicitPresentImageDeviceExtension) {
     const char* implicit_layer_name = "VK_LAYER_LUNARG_wrap_objects";
     const char* enable_env_var = "ENABLE_ME";
     const char* disable_env_var = "DISABLE_ME";
-    EnvVarCleaner enable_cleaner(enable_env_var);
 
     env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                          .set_name(implicit_layer_name)
@@ -2861,8 +2850,8 @@ TEST(LayerExtensions, ImplicitPresentImageDeviceExtension) {
     ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr));
     ASSERT_EQ(count, 1U);
 
-    // // set enable env-var, layer should load
-    set_env_var(enable_env_var, "1");
+    // set enable env-var, layer should load
+    EnvVarWrapper wrap_enable_env_var{enable_env_var, "1"};
     CheckLogForLayerString(env, implicit_layer_name, true);
 
     InstWrapper inst{env.vulkan_functions};
@@ -2909,7 +2898,6 @@ TEST(LayerExtensions, ImplicitBothDeviceExtensions) {
     const char* implicit_layer_name = "VK_LAYER_LUNARG_wrap_objects";
     const char* enable_env_var = "ENABLE_ME";
     const char* disable_env_var = "DISABLE_ME";
-    EnvVarCleaner enable_cleaner(enable_env_var);
 
     env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
                                                          .set_name(implicit_layer_name)
@@ -2922,8 +2910,8 @@ TEST(LayerExtensions, ImplicitBothDeviceExtensions) {
     ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr));
     ASSERT_EQ(count, 1U);
 
-    // // set enable env-var, layer should load
-    set_env_var(enable_env_var, "1");
+    // set enable env-var, layer should load
+    EnvVarWrapper wrap_enable_env_var{enable_env_var, "1"};
     CheckLogForLayerString(env, implicit_layer_name, true);
 
     InstWrapper inst{env.vulkan_functions};
@@ -3517,8 +3505,7 @@ TEST(TestLayers, InstEnvironEnableExplicitLayer) {
     handle_assert_null(pfn_GetSwapchainStatusBefore);
 
     // Now setup the instance layer
-    set_env_var("VK_INSTANCE_LAYERS", explicit_layer_name);
-    EnvVarCleaner instance_layers_cleaner("VK_INSTANCE_LAYERS");
+    EnvVarWrapper instance_layers_env_var{"VK_INSTANCE_LAYERS", explicit_layer_name};
 
     // Now, test an instance/device with the layer forced on.  The extensions should be present and
     // the function pointers should be valid.
@@ -3588,7 +3575,7 @@ TEST(TestLayers, EnvironLayerEnableExplicitLayer) {
                                                          .set_api_version(VK_MAKE_API_VERSION(0, 1, 0, 0))),
                            explicit_json_name_3);
 
-    EnvVarCleaner layers_enable_cleaner("VK_LOADER_LAYERS_ENABLE");
+    EnvVarWrapper layers_enable_env_var{"VK_LOADER_LAYERS_ENABLE"};
 
     // First, test an instance/device without the layer forced on.
     InstWrapper inst1{env.vulkan_functions};
@@ -3611,7 +3598,7 @@ TEST(TestLayers, EnvironLayerEnableExplicitLayer) {
     // Now force on one layer with its full name
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_ENABLE", explicit_layer_name_1);
+    layers_enable_env_var.set_new_value(explicit_layer_name_1);
 
     InstWrapper inst2{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst2.create_info, env.debug_log);
@@ -3633,7 +3620,7 @@ TEST(TestLayers, EnvironLayerEnableExplicitLayer) {
     // Match prefix
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_ENABLE", "VK_LAYER_LUNARG_*");
+    layers_enable_env_var.set_new_value("VK_LAYER_LUNARG_*");
 
     InstWrapper inst3{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst3.create_info, env.debug_log);
@@ -3655,7 +3642,7 @@ TEST(TestLayers, EnvironLayerEnableExplicitLayer) {
     // Match suffix
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_ENABLE", "*Second_layer");
+    layers_enable_env_var.set_new_value("*Second_layer");
 
     InstWrapper inst4{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst4.create_info, env.debug_log);
@@ -3677,7 +3664,7 @@ TEST(TestLayers, EnvironLayerEnableExplicitLayer) {
     // Match substring
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_ENABLE", "*Second*");
+    layers_enable_env_var.set_new_value("*Second*");
 
     InstWrapper inst5{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst5.create_info, env.debug_log);
@@ -3699,7 +3686,7 @@ TEST(TestLayers, EnvironLayerEnableExplicitLayer) {
     // Match all with star '*'
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_ENABLE", "*");
+    layers_enable_env_var.set_new_value("*");
 
     InstWrapper inst6{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst6.create_info, env.debug_log);
@@ -3721,7 +3708,7 @@ TEST(TestLayers, EnvironLayerEnableExplicitLayer) {
     // Match all with special name
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_ENABLE", "~all~");
+    layers_enable_env_var.set_new_value("~all~");
 
     InstWrapper inst7{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst7.create_info, env.debug_log);
@@ -3771,7 +3758,7 @@ TEST(TestLayers, EnvironLayerDisableExplicitLayer) {
                                                          .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
                                                          .set_api_version(VK_MAKE_API_VERSION(0, 1, 0, 0))),
                            explicit_json_name_3);
-    EnvVarCleaner layers_enable_cleaner("VK_LOADER_LAYERS_ENABLE");
+    EnvVarWrapper layers_disable_env_var{"VK_LOADER_LAYERS_DISABLE"};
 
     // First, test an instance/device without the layer forced on.
     InstWrapper inst1{env.vulkan_functions};
@@ -3795,7 +3782,7 @@ TEST(TestLayers, EnvironLayerDisableExplicitLayer) {
     // Now force on one layer with its full name
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", explicit_layer_name_1);
+    layers_disable_env_var.set_new_value(explicit_layer_name_1);
 
     InstWrapper inst2{env.vulkan_functions};
     inst2.create_info.add_layer(explicit_layer_name_1).add_layer(explicit_layer_name_2).add_layer(explicit_layer_name_3);
@@ -3818,7 +3805,7 @@ TEST(TestLayers, EnvironLayerDisableExplicitLayer) {
     // Match prefix
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", "VK_LAYER_LUNARG_*");
+    layers_disable_env_var.set_new_value("VK_LAYER_LUNARG_*");
 
     InstWrapper inst3{env.vulkan_functions};
     inst3.create_info.add_layer(explicit_layer_name_1).add_layer(explicit_layer_name_2).add_layer(explicit_layer_name_3);
@@ -3841,7 +3828,7 @@ TEST(TestLayers, EnvironLayerDisableExplicitLayer) {
     // Match suffix
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", "*Second_layer");
+    layers_disable_env_var.set_new_value("*Second_layer");
 
     InstWrapper inst4{env.vulkan_functions};
     inst4.create_info.add_layer(explicit_layer_name_1).add_layer(explicit_layer_name_2).add_layer(explicit_layer_name_3);
@@ -3864,7 +3851,7 @@ TEST(TestLayers, EnvironLayerDisableExplicitLayer) {
     // Match substring
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", "*Second*");
+    layers_disable_env_var.set_new_value("*Second*");
 
     InstWrapper inst5{env.vulkan_functions};
     inst5.create_info.add_layer(explicit_layer_name_1).add_layer(explicit_layer_name_2).add_layer(explicit_layer_name_3);
@@ -3887,7 +3874,7 @@ TEST(TestLayers, EnvironLayerDisableExplicitLayer) {
     // Match all with star '*'
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", "*");
+    layers_disable_env_var.set_new_value("*");
 
     InstWrapper inst6{env.vulkan_functions};
     inst6.create_info.add_layer(explicit_layer_name_1).add_layer(explicit_layer_name_2).add_layer(explicit_layer_name_3);
@@ -3910,7 +3897,7 @@ TEST(TestLayers, EnvironLayerDisableExplicitLayer) {
     // Match all with special name
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", "~all~");
+    layers_disable_env_var.set_new_value("~all~");
 
     InstWrapper inst7{env.vulkan_functions};
     inst7.create_info.add_layer(explicit_layer_name_1).add_layer(explicit_layer_name_2).add_layer(explicit_layer_name_3);
@@ -3933,7 +3920,7 @@ TEST(TestLayers, EnvironLayerDisableExplicitLayer) {
     // Match explicit special name
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", "~explicit~");
+    layers_disable_env_var.set_new_value("~explicit~");
 
     InstWrapper inst8{env.vulkan_functions};
     inst8.create_info.add_layer(explicit_layer_name_1).add_layer(explicit_layer_name_2).add_layer(explicit_layer_name_3);
@@ -3956,7 +3943,7 @@ TEST(TestLayers, EnvironLayerDisableExplicitLayer) {
     // No match implicit special name
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", "~implicit~");
+    layers_disable_env_var.set_new_value("~implicit~");
 
     InstWrapper inst9{env.vulkan_functions};
     inst9.create_info.add_layer(explicit_layer_name_1).add_layer(explicit_layer_name_2).add_layer(explicit_layer_name_3);
@@ -4008,8 +3995,8 @@ TEST(TestLayers, EnvironLayerEnableDisableExplicitLayer) {
                                                          .set_api_version(VK_MAKE_API_VERSION(0, 1, 0, 0))),
                            explicit_json_name_3);
 
-    EnvVarCleaner layers_enable_cleaner("VK_LOADER_LAYERS_ENABLE");
-    EnvVarCleaner layers_disable_cleaner("VK_LOADER_LAYERS_DISABLE");
+    EnvVarWrapper layers_enable_env_var{"VK_LOADER_LAYERS_ENABLE"};
+    EnvVarWrapper layers_disable_env_var{"VK_LOADER_LAYERS_DISABLE"};
 
     // First, test an instance/device without the layer forced on.
     InstWrapper inst1{env.vulkan_functions};
@@ -4033,8 +4020,8 @@ TEST(TestLayers, EnvironLayerEnableDisableExplicitLayer) {
     // Disable 2 but enable 1
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", "*Second*");
-    set_env_var("VK_LOADER_LAYERS_ENABLE", "*test_layer");
+    layers_disable_env_var.set_new_value("*Second*");
+    layers_enable_env_var.set_new_value("*test_layer");
 
     InstWrapper inst2{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst2.create_info, env.debug_log);
@@ -4056,8 +4043,8 @@ TEST(TestLayers, EnvironLayerEnableDisableExplicitLayer) {
     // Disable all but enable 2
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", "*");
-    set_env_var("VK_LOADER_LAYERS_ENABLE", "*Second*");
+    layers_disable_env_var.set_new_value("*");
+    layers_enable_env_var.set_new_value("*Second*");
 
     InstWrapper inst3{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst3.create_info, env.debug_log);
@@ -4079,8 +4066,8 @@ TEST(TestLayers, EnvironLayerEnableDisableExplicitLayer) {
     // Disable all but enable 2
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", "~all~");
-    set_env_var("VK_LOADER_LAYERS_ENABLE", "*Second*");
+    layers_disable_env_var.set_new_value("~all~");
+    layers_enable_env_var.set_new_value("*Second*");
 
     InstWrapper inst4{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst4.create_info, env.debug_log);
@@ -4102,8 +4089,8 @@ TEST(TestLayers, EnvironLayerEnableDisableExplicitLayer) {
     // Disable explicit but enable 2
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", "~explicit~");
-    set_env_var("VK_LOADER_LAYERS_ENABLE", "*Second*");
+    layers_disable_env_var.set_new_value("~explicit~");
+    layers_enable_env_var.set_new_value("*Second*");
 
     InstWrapper inst5{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst5.create_info, env.debug_log);
@@ -4125,8 +4112,8 @@ TEST(TestLayers, EnvironLayerEnableDisableExplicitLayer) {
     // Disable implicit but enable 2 (should still be everything)
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", "~implicit~");
-    set_env_var("VK_LOADER_LAYERS_ENABLE", "*Second*");
+    layers_disable_env_var.set_new_value("~implicit~");
+    layers_enable_env_var.set_new_value("*Second*");
 
     InstWrapper inst6{env.vulkan_functions};
     inst6.create_info.add_layer(explicit_layer_name_1).add_layer(explicit_layer_name_2).add_layer(explicit_layer_name_3);
@@ -4149,8 +4136,8 @@ TEST(TestLayers, EnvironLayerEnableDisableExplicitLayer) {
     // Disable explicit but enable all
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", "~explicit~");
-    set_env_var("VK_LOADER_LAYERS_ENABLE", "*");
+    layers_disable_env_var.set_new_value("~explicit~");
+    layers_enable_env_var.set_new_value("*");
 
     InstWrapper inst7{env.vulkan_functions};
     inst7.create_info.add_layer(explicit_layer_name_1).add_layer(explicit_layer_name_2).add_layer(explicit_layer_name_3);
@@ -4194,8 +4181,8 @@ TEST(TestLayers, EnvironVkInstanceLayersAndDisableFilters) {
                                                          .set_api_version(VK_MAKE_API_VERSION(0, 1, 0, 0))),
                            explicit_json_name_2);
 
-    EnvVarCleaner instance_layers_cleaner("VK_INSTANCE_LAYERS");
-    EnvVarCleaner layers_disable_cleaner("VK_LOADER_LAYERS_DISABLE");
+    EnvVarWrapper layers_enable_env_var{"VK_INSTANCE_LAYERS"};
+    EnvVarWrapper layers_disable_env_var{"VK_LOADER_LAYERS_DISABLE"};
 
     // First, test an instance/device without the layer forced on.
     InstWrapper inst1{env.vulkan_functions};
@@ -4215,7 +4202,7 @@ TEST(TestLayers, EnvironVkInstanceLayersAndDisableFilters) {
     // Enable the non-default enabled layer with VK_INSTANCE_LAYERS
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_INSTANCE_LAYERS", explicit_layer_name_2);
+    layers_enable_env_var.set_new_value(explicit_layer_name_2);
 
     InstWrapper inst2{env.vulkan_functions};
     inst2.create_info.add_layer(explicit_layer_name_1);
@@ -4234,7 +4221,7 @@ TEST(TestLayers, EnvironVkInstanceLayersAndDisableFilters) {
     // Try to disable all
     // ------------------------------------------
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", "*");
+    layers_disable_env_var.set_new_value("*");
 
     InstWrapper inst3{env.vulkan_functions};
     inst3.create_info.add_layer(explicit_layer_name_1);
@@ -4265,8 +4252,7 @@ TEST(TestLayers, AppEnabledExplicitLayerFails) {
                            explicit_json_name_1);
 
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", explicit_layer_name_1);
-    EnvVarCleaner layers_disable_cleaner("VK_LOADER_LAYERS_DISABLE");
+    EnvVarWrapper layers_disable_env_var{"VK_LOADER_LAYERS_DISABLE", explicit_layer_name_1};
 
     uint32_t count = 0;
     env.vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr);
@@ -4309,8 +4295,7 @@ TEST(TestLayers, OverrideEnabledExplicitLayerWithDisableFilter) {
                            "meta_test_layer.json");
 
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", explicit_layer_name_1);
-    EnvVarCleaner layers_disable_cleaner("VK_LOADER_LAYERS_DISABLE");
+    EnvVarWrapper layers_disable_env_var{"VK_LOADER_LAYERS_DISABLE", explicit_layer_name_1};
 
     uint32_t count = 0;
     env.vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr);
@@ -4367,8 +4352,7 @@ TEST(TestLayers, OverrideEnabledExplicitLayerWithDisableFilterForOverrideLayer)
                            "meta_test_layer.json");
 
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_DISABLE", lunarg_meta_layer_name);
-    EnvVarCleaner layers_disable_cleaner("VK_LOADER_LAYERS_DISABLE");
+    EnvVarWrapper layers_disable_env_var{"VK_LOADER_LAYERS_DISABLE", lunarg_meta_layer_name};
 
     uint32_t count = 0;
     env.vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr);
@@ -4425,8 +4409,7 @@ TEST(TestLayers, OverrideBlacklistedLayerWithEnableFilter) {
                            "meta_test_layer.json");
 
     env.debug_log.clear();
-    set_env_var("VK_LOADER_LAYERS_ENABLE", explicit_layer_name_1);
-    EnvVarCleaner layers_enable_cleaner("VK_LOADER_LAYERS_ENABLE");
+    EnvVarWrapper layers_enable_env_var{"VK_LOADER_LAYERS_ENABLE", explicit_layer_name_1};
 
     uint32_t count = 0;
     env.vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr);
index 0cbfc1430891686b0521787499ba07a62c70cb09..ed0cffb6af95359119bc8df50aa6031001512350 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2021-2022 The Khronos Group Inc.
- * Copyright (c) 2021-2022 Valve Corporation
- * Copyright (c) 2021-2022 LunarG, Inc.
+ * Copyright (c) 2021-2023 The Khronos Group Inc.
+ * Copyright (c) 2021-2023 Valve Corporation
+ * Copyright (c) 2021-2023 LunarG, Inc.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and/or associated documentation files (the "Materials"), to
@@ -189,11 +189,11 @@ TEST(EnumerateInstanceExtensionProperties, UsageChecks) {
     env.reset_icd().add_instance_extensions({first_ext, second_ext});
 
     {  // One Pass
-        uint32_t extension_count = 5;
-        std::array<VkExtensionProperties, 5> extensions;
+        uint32_t extension_count = 6;
+        std::array<VkExtensionProperties, 6> extensions;
         ASSERT_EQ(VK_SUCCESS,
                   env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, extensions.data()));
-        ASSERT_EQ(extension_count, 5U);  // return debug report & debug utils & portability enumeration + our two extensions
+        ASSERT_EQ(extension_count, 6U);  // default extensions + our two extensions
 
         // loader always adds the debug report & debug utils extensions
         ASSERT_TRUE(first_ext.extensionName == extensions[0].extensionName);
@@ -204,13 +204,13 @@ TEST(EnumerateInstanceExtensionProperties, UsageChecks) {
     }
     {  // Two Pass
         uint32_t extension_count = 0;
-        std::array<VkExtensionProperties, 5> extensions;
+        std::array<VkExtensionProperties, 6> extensions;
         ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr));
-        ASSERT_EQ(extension_count, 5U);  // return debug report & debug utils + our two extensions
+        ASSERT_EQ(extension_count, 6U);  // return default extensions + our extension
 
         ASSERT_EQ(VK_SUCCESS,
                   env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, extensions.data()));
-        ASSERT_EQ(extension_count, 5U);
+        ASSERT_EQ(extension_count, 6U);
         // loader always adds the debug report & debug utils extensions
         ASSERT_TRUE(first_ext.extensionName == extensions[0].extensionName);
         ASSERT_TRUE(second_ext.extensionName == extensions[1].extensionName);
@@ -225,10 +225,10 @@ TEST(EnumerateInstanceExtensionProperties, PropertyCountLessThanAvailable) {
     env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
 
     uint32_t extension_count = 0;
-    std::array<VkExtensionProperties, 2> extensions;
+    std::array<VkExtensionProperties, 4> extensions;
     {  // use nullptr for null string
         ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr));
-        ASSERT_EQ(extension_count, 3U);  // return debug report & debug utils & portability enumeration
+        ASSERT_EQ(extension_count, 4U);  // return debug report & debug utils & portability enumeration & direct driver loading
         extension_count = 1;             // artificially remove one extension
 
         ASSERT_EQ(VK_INCOMPLETE,
@@ -239,7 +239,7 @@ TEST(EnumerateInstanceExtensionProperties, PropertyCountLessThanAvailable) {
     }
     {  // use "" for null string
         ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceExtensionProperties("", &extension_count, nullptr));
-        ASSERT_EQ(extension_count, 3U);  // return debug report & debug utils & portability enumeration
+        ASSERT_EQ(extension_count, 4U);  // return debug report & debug utils & portability enumeration & direct driver loading
         extension_count = 1;             // artificially remove one extension
 
         ASSERT_EQ(VK_INCOMPLETE,
@@ -260,26 +260,27 @@ TEST(EnumerateInstanceExtensionProperties, FilterUnkownInstanceExtensions) {
     {
         uint32_t extension_count = 0;
         ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceExtensionProperties("", &extension_count, nullptr));
-        ASSERT_EQ(extension_count, 3U);  // return debug report & debug utils & portability enumeration
+        ASSERT_EQ(extension_count, 4U);  // debug report & debug utils & portability enumeration & direct driver loading
 
-        std::array<VkExtensionProperties, 3> extensions;
+        std::array<VkExtensionProperties, 4> extensions;
         ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceExtensionProperties("", &extension_count, extensions.data()));
-        ASSERT_EQ(extension_count, 3U);
+        ASSERT_EQ(extension_count, 4U);  // debug report & debug utils & portability enumeration & direct driver loading
         // loader always adds the debug report & debug utils extensions
         ASSERT_TRUE(string_eq(extensions[0].extensionName, "VK_EXT_debug_report"));
         ASSERT_TRUE(string_eq(extensions[1].extensionName, "VK_EXT_debug_utils"));
         ASSERT_TRUE(string_eq(extensions[2].extensionName, "VK_KHR_portability_enumeration"));
+        ASSERT_TRUE(string_eq(extensions[3].extensionName, "VK_LUNARG_direct_driver_loading"));
     }
     {  // Disable unknown instance extension filtering
-        set_env_var("VK_LOADER_DISABLE_INST_EXT_FILTER", "1");
+        EnvVarWrapper disable_inst_ext_filter_env_var{"VK_LOADER_DISABLE_INST_EXT_FILTER", "1"};
 
         uint32_t extension_count = 0;
         ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceExtensionProperties("", &extension_count, nullptr));
-        ASSERT_EQ(extension_count, 5U);
+        ASSERT_EQ(extension_count, 6U);
 
-        std::array<VkExtensionProperties, 5> extensions;
+        std::array<VkExtensionProperties, 6> extensions;
         ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceExtensionProperties("", &extension_count, extensions.data()));
-        ASSERT_EQ(extension_count, 5U);
+        ASSERT_EQ(extension_count, 6U);
 
         ASSERT_EQ(extensions[0], first_ext.get());
         ASSERT_EQ(extensions[1], second_ext.get());
@@ -287,6 +288,7 @@ TEST(EnumerateInstanceExtensionProperties, FilterUnkownInstanceExtensions) {
         ASSERT_TRUE(string_eq(extensions[2].extensionName, "VK_EXT_debug_report"));
         ASSERT_TRUE(string_eq(extensions[3].extensionName, "VK_EXT_debug_utils"));
         ASSERT_TRUE(string_eq(extensions[4].extensionName, "VK_KHR_portability_enumeration"));
+        ASSERT_TRUE(string_eq(extensions[5].extensionName, "VK_LUNARG_direct_driver_loading"));
     }
 }
 
@@ -2814,8 +2816,7 @@ TEST(SortedPhysicalDevices, DevicesSortEnabled11) {
 TEST(SortedPhysicalDevices, DevicesSortedDisabled) {
     FrameworkEnvironment env{};
 
-    set_env_var("VK_LOADER_DISABLE_SELECT", "1");
-    EnvVarCleaner disable_select_cleaner("VK_LOADER_DISABLE_SELECT");
+    EnvVarWrapper disable_select_env_var{"VK_LOADER_DISABLE_SELECT", "1"};
 
     env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2, VK_API_VERSION_1_0));
     env.get_test_icd(0).add_instance_extension({VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME});
@@ -3107,8 +3108,7 @@ TEST(SortedPhysicalDevices, DeviceGroupsSortedEnabled) {
 TEST(SortedPhysicalDevices, DeviceGroupsSortedDisabled) {
     FrameworkEnvironment env{};
 
-    set_env_var("VK_LOADER_DISABLE_SELECT", "1");
-    EnvVarCleaner disable_select_cleaner("VK_LOADER_DISABLE_SELECT");
+    EnvVarWrapper disable_select_env_var{"VK_LOADER_DISABLE_SELECT", "1"};
 
     // ICD 0: Vulkan 1.1
     //   PhysDev 0: pd0, Discrete, Vulkan 1.1, Bus 7
@@ -3472,19 +3472,21 @@ TEST(PortabilityICDConfiguration, PortabilityAndRegularICDPreInstanceFunctions)
     {
         // check that enumerating instance extensions work with a portability driver present
         uint32_t extension_count = 0;
-        std::array<VkExtensionProperties, 5> extensions;
+        std::array<VkExtensionProperties, 6> extensions;
         ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr));
-        ASSERT_EQ(extension_count, 5U);  // return debug report & debug utils + our two extensions
+        // loader always adds the debug report, debug utils extensions, portability enumeration, direct driver loading
+        ASSERT_EQ(extension_count, 6U);
 
         ASSERT_EQ(VK_SUCCESS,
                   env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, extensions.data()));
-        ASSERT_EQ(extension_count, 5U);
-        // loader always adds the debug report & debug utils extensions
+        ASSERT_EQ(extension_count, 6U);
+        // loader always adds the debug report, debug utils extensions, portability enumeration, direct driver loading
         ASSERT_TRUE(first_ext.extensionName == extensions[0].extensionName);
         ASSERT_TRUE(second_ext.extensionName == extensions[1].extensionName);
         ASSERT_TRUE(string_eq("VK_EXT_debug_report", extensions[2].extensionName));
         ASSERT_TRUE(string_eq("VK_EXT_debug_utils", extensions[3].extensionName));
         ASSERT_TRUE(string_eq("VK_KHR_portability_enumeration", extensions[4].extensionName));
+        ASSERT_TRUE(string_eq("VK_LUNARG_direct_driver_loading", extensions[5].extensionName));
     }
 
     const char* layer_name = "TestLayer";
@@ -3555,7 +3557,7 @@ TEST(DuplicateRegistryEntries, Drivers) {
     auto null_path = env.get_folder(ManifestLocation::null).location() / "test_icd_0.json";
     env.platform_shim->add_manifest(ManifestCategory::icd, null_path.str());
 
-    env.add_icd(TestICDDetails{TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA}.set_discovery_type(ManifestDiscoveryType::none));
+    env.add_icd(TestICDDetails{TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA}.set_discovery_type(ManifestDiscoveryType::null_dir));
     auto& driver = env.get_test_icd();
     driver.physical_devices.emplace_back("physical_device_0");
     driver.adapterLUID = _LUID{10, 1000};
@@ -3574,7 +3576,8 @@ TEST(DuplicateRegistryEntries, Drivers) {
 // Check that valid symlinks do not cause the loader to crash when directly in an XDG env-var
 TEST(ManifestDiscovery, ValidSymlinkInXDGEnvVar) {
     FrameworkEnvironment env{true, false};
-    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA).set_discovery_type(ManifestDiscoveryType::none));
+    env.add_icd(
+        TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA).set_discovery_type(ManifestDiscoveryType::override_folder));
     env.get_test_icd().physical_devices.push_back({});
     auto driver_path = env.get_icd_manifest_path(0);
     std::string symlink_name = "symlink_to_driver.json";
@@ -3582,7 +3585,7 @@ TEST(ManifestDiscovery, ValidSymlinkInXDGEnvVar) {
     env.get_folder(ManifestLocation::driver_env_var).add_existing_file(symlink_name);
     int res = symlink(driver_path.c_str(), symlink_path.c_str());
     ASSERT_EQ(res, 0);
-    set_env_var("XDG_CONFIG_DIRS", symlink_path.str());
+    EnvVarWrapper xdg_config_dirs_env_var{"XDG_CONFIG_DIRS", symlink_path.str()};
 
     InstWrapper inst{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
@@ -3592,7 +3595,8 @@ TEST(ManifestDiscovery, ValidSymlinkInXDGEnvVar) {
 // Check that valid symlinks do not cause the loader to crash
 TEST(ManifestDiscovery, ValidSymlink) {
     FrameworkEnvironment env{true, false};
-    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA).set_discovery_type(ManifestDiscoveryType::none));
+    env.add_icd(
+        TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA).set_discovery_type(ManifestDiscoveryType::override_folder));
     env.get_test_icd().physical_devices.push_back({});
 
     auto driver_path = env.get_icd_manifest_path(0);
@@ -3619,7 +3623,7 @@ TEST(ManifestDiscovery, InvalidSymlinkXDGEnvVar) {
     ASSERT_EQ(res, 0);
     env.get_folder(ManifestLocation::driver_env_var).add_existing_file(symlink_name);
 
-    set_env_var("XDG_CONFIG_DIRS", symlink_path.str());
+    EnvVarWrapper xdg_config_dirs_env_var{symlink_path.str()};
 
     InstWrapper inst{env.vulkan_functions};
     FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
index 6fe3f4540f001b1553ea1095b4140d8eb5e33a75..e6caeefdd40621035c098c0f0e9db03a69d75c10 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2021 The Khronos Group Inc.
- * Copyright (c) 2021 Valve Corporation
- * Copyright (c) 2021 LunarG, Inc.
+ * Copyright (c) 2021-2023 The Khronos Group Inc.
+ * Copyright (c) 2021-2023 Valve Corporation
+ * Copyright (c) 2021-2023 LunarG, Inc.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and/or associated documentation files (the "Materials"), to
@@ -44,32 +44,49 @@ int main(int argc, char** argv) {
     // clean up folders from old test
     fs::delete_folder(fs::path(FRAMEWORK_BUILD_DIRECTORY) / "null_dir");
     fs::delete_folder(fs::path(FRAMEWORK_BUILD_DIRECTORY) / "icd_manifests");
-    fs::delete_folder(fs::path(FRAMEWORK_BUILD_DIRECTORY) / "portability_icd_manifests");
+    fs::delete_folder(fs::path(FRAMEWORK_BUILD_DIRECTORY) / "icd_env_vars_manifests");
     fs::delete_folder(fs::path(FRAMEWORK_BUILD_DIRECTORY) / "explicit_layer_manifests");
+    fs::delete_folder(fs::path(FRAMEWORK_BUILD_DIRECTORY) / "explicit_env_var_layer_folder");
+    fs::delete_folder(fs::path(FRAMEWORK_BUILD_DIRECTORY) / "explicit_add_env_var_layer_folder");
     fs::delete_folder(fs::path(FRAMEWORK_BUILD_DIRECTORY) / "implicit_layer_manifests");
+    fs::delete_folder(fs::path(FRAMEWORK_BUILD_DIRECTORY) / "override_layer_manifests");
+    fs::delete_folder(fs::path(FRAMEWORK_BUILD_DIRECTORY) / "app_package_manifests");
+    fs::delete_folder(fs::path(FRAMEWORK_BUILD_DIRECTORY) / "macos_bundle");
 
     // make sure the tests don't find these env-vars if they were set on the system
-    remove_env_var("VK_ICD_FILENAMES");
-    remove_env_var("VK_DRIVER_FILES");
-    remove_env_var("VK_ADD_DRIVER_FILES");
-    remove_env_var("VK_LAYER_PATH");
-    remove_env_var("VK_ADD_LAYER_PATH");
-    remove_env_var("VK_INSTANCE_LAYERS");
-    remove_env_var("VK_LOADER_DRIVERS_SELECT");
-    remove_env_var("VK_LOADER_DRIVERS_DISABLE");
-    remove_env_var("VK_LOADER_LAYERS_ENABLE");
-    remove_env_var("VK_LOADER_LAYERS_DISABLE");
-    remove_env_var("VK_LOADER_DEBUG");
-    remove_env_var("VK_LOADER_DISABLE_INST_EXT_FILTER");
+    EnvVarWrapper vk_icd_filenames_env_var{"VK_ICD_FILENAMES"};
+    EnvVarWrapper vk_driver_files_env_var{"VK_DRIVER_FILES"};
+    EnvVarWrapper vk_add_driver_files_env_var{"VK_ADD_DRIVER_FILES"};
+    EnvVarWrapper vk_layer_path_env_var{"VK_LAYER_PATH"};
+    EnvVarWrapper vk_add_layer_path_env_var{"VK_ADD_LAYER_PATH"};
+    EnvVarWrapper vk_instance_layers_env_var{"VK_INSTANCE_LAYERS"};
+    EnvVarWrapper vk_loader_drivers_select_env_var{"VK_LOADER_DRIVERS_SELECT"};
+    EnvVarWrapper vk_loader_drivers_disable_env_var{"VK_LOADER_DRIVERS_DISABLE"};
+    EnvVarWrapper vk_loader_layers_enable_env_var{"VK_LOADER_LAYERS_ENABLE"};
+    EnvVarWrapper vk_loader_layers_disable_env_var{"VK_LOADER_LAYERS_DISABLE"};
+    EnvVarWrapper vk_loader_debug_env_var{"VK_LOADER_DEBUG"};
+    EnvVarWrapper vk_loader_disable_inst_ext_filter_env_var{"VK_LOADER_DISABLE_INST_EXT_FILTER"};
+    vk_icd_filenames_env_var.remove_value();
+    vk_driver_files_env_var.remove_value();
+    vk_add_driver_files_env_var.remove_value();
+    vk_layer_path_env_var.remove_value();
+    vk_add_layer_path_env_var.remove_value();
+    vk_instance_layers_env_var.remove_value();
+    vk_loader_drivers_select_env_var.remove_value();
+    vk_loader_drivers_disable_env_var.remove_value();
+    vk_loader_layers_enable_env_var.remove_value();
+    vk_loader_layers_disable_env_var.remove_value();
+    vk_loader_debug_env_var.remove_value();
+    vk_loader_disable_inst_ext_filter_env_var.remove_value();
 
 #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__)
-    set_env_var("XDG_CONFIG_HOME", "/etc");
-    set_env_var("XDG_CONFIG_DIRS", "/etc");
-    set_env_var("XDG_DATA_HOME", "/etc");
-    set_env_var("XDG_DATA_DIRS", "/etc");
+    EnvVarWrapper xdg_config_home_env_var{"XDG_CONFIG_HOME", "/etc"};
+    EnvVarWrapper xdg_config_dirs_env_var{"XDG_CONFIG_DIRS", "/etc"};
+    EnvVarWrapper xdg_data_home_env_var{"XDG_DATA_HOME", "/etc"};
+    EnvVarWrapper xdg_data_dirs_env_var{"XDG_DATA_DIRS", "/etc"};
 #endif
 #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
-    set_env_var("HOME", "/home/fake_home");
+    EnvVarWrapper home_env_var{"HOME", "/home/fake_home"};
 #endif
     ::testing::InitGoogleTest(&argc, argv);
     int result = RUN_ALL_TESTS();
index 2e24743d1a99d7701362374139515ef66cd78744..9cd5f031eb8a489c6ca786a697d9b519c592978e 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2021-2022 The Khronos Group Inc.
- * Copyright (c) 2021-2022 Valve Corporation
- * Copyright (c) 2021-2022 LunarG, Inc.
+ * Copyright (c) 2021-2023 The Khronos Group Inc.
+ * Copyright (c) 2021-2023 Valve Corporation
+ * Copyright (c) 2021-2023 LunarG, Inc.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and/or associated documentation files (the "Materials"), to
@@ -133,7 +133,7 @@ TEST(ICDInterfaceVersion2PlusEnumerateAdapterPhysicalDevices, version_6_in_drive
 // Make the version_6 driver found through the D3DKMT driver discovery mechanism of the loader
 TEST(ICDInterfaceVersion2PlusEnumerateAdapterPhysicalDevices, version_6) {
     FrameworkEnvironment env{};
-    env.add_icd(TestICDDetails{TEST_ICD_PATH_VERSION_6, VK_API_VERSION_1_3}.set_discovery_type(ManifestDiscoveryType::none));
+    env.add_icd(TestICDDetails{TEST_ICD_PATH_VERSION_6, VK_API_VERSION_1_3}.set_discovery_type(ManifestDiscoveryType::null_dir));
     // Version 6 provides a mechanism to allow the loader to sort physical devices.
     // The loader will only attempt to sort physical devices on an ICD if version 6 of the interface is supported.
     // This version provides the vk_icdEnumerateAdapterPhysicalDevices function.
@@ -186,7 +186,7 @@ TEST(ICDInterfaceVersion2PlusEnumerateAdapterPhysicalDevices, version_6) {
 // EnumerateAdapterPhysicalDevices
 TEST(ICDInterfaceVersion2, EnumAdapters2) {
     FrameworkEnvironment env{};
-    env.add_icd(TestICDDetails{TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA}.set_discovery_type(ManifestDiscoveryType::none));
+    env.add_icd(TestICDDetails{TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA}.set_discovery_type(ManifestDiscoveryType::null_dir));
     InstWrapper inst{env.vulkan_functions};
     auto& driver = env.get_test_icd();
     driver.physical_devices.emplace_back("physical_device_1");
@@ -212,7 +212,7 @@ TEST(ICDInterfaceVersion2, EnumAdapters2) {
 TEST(ICDInterfaceVersion2PlusEnumerateAdapterPhysicalDevices, VerifyPhysDevResults) {
     FrameworkEnvironment env{};
     env.add_icd(TestICDDetails{TEST_ICD_PATH_VERSION_2_EXPORT_ICD_ENUMERATE_ADAPTER_PHYSICAL_DEVICES}.set_discovery_type(
-        ManifestDiscoveryType::none));
+        ManifestDiscoveryType::null_dir));
     auto& driver = env.get_test_icd();
     driver.min_icd_interface_version = 6;
     driver.set_icd_api_version(VK_API_VERSION_1_1);
@@ -256,7 +256,7 @@ TEST(ICDInterfaceVersion2PlusEnumerateAdapterPhysicalDevices, VerifyPhysDevResul
 TEST(ICDInterfaceVersion2PlusEnumerateAdapterPhysicalDevices, VerifyGroupResults) {
     FrameworkEnvironment env{};
     env.add_icd(TestICDDetails{TEST_ICD_PATH_VERSION_2_EXPORT_ICD_ENUMERATE_ADAPTER_PHYSICAL_DEVICES}.set_discovery_type(
-        ManifestDiscoveryType::none));
+        ManifestDiscoveryType::null_dir));
     auto& driver = env.get_test_icd();
     driver.min_icd_interface_version = 6;
     driver.set_icd_api_version(VK_API_VERSION_1_1);
@@ -943,3 +943,455 @@ TEST(LayerManifest, UnknownManifestVersion) {
     // log prints the path to the file, don't look for it since it is hard to determine inside the test what the path should be.
     ASSERT_TRUE(log.find("has unknown layer manifest file version 3.2.1.  May cause errors."));
 }
+
+struct DriverInfo {
+    DriverInfo(TestICDDetails icd_details, uint32_t driver_version, bool expect_to_find) noexcept
+        : icd_details(icd_details), driver_version(driver_version), expect_to_find(expect_to_find) {}
+    TestICDDetails icd_details;
+    uint32_t driver_version = 0;
+    bool expect_to_find = false;
+};
+
+void CheckDirectDriverLoading(FrameworkEnvironment& env, std::vector<DriverInfo> const& normal_drivers,
+                              std::vector<DriverInfo> const& direct_drivers, bool exclusive) {
+    std::vector<VkDirectDriverLoadingInfoLUNARG> ddl_infos;
+    uint32_t expected_driver_count = 0;
+
+    for (auto const& driver : direct_drivers) {
+        auto& direct_driver_icd = env.add_icd(driver.icd_details);
+        direct_driver_icd.get_test_icd().physical_devices.push_back({});
+        direct_driver_icd.get_test_icd().physical_devices.at(0).properties.driverVersion = driver.driver_version;
+        VkDirectDriverLoadingInfoLUNARG ddl_info{};
+        ddl_info.sType = VK_STRUCTURE_TYPE_DIRECT_DRIVER_LOADING_INFO_LUNARG;
+        ddl_info.pfnGetInstanceProcAddr = direct_driver_icd.icd_library.get_symbol("vk_icdGetInstanceProcAddr");
+        ddl_infos.push_back(ddl_info);
+        if (driver.expect_to_find) {
+            expected_driver_count++;
+        }
+    }
+
+    for (auto const& driver : normal_drivers) {
+        auto& direct_driver_icd = env.add_icd(driver.icd_details);
+        direct_driver_icd.get_test_icd().physical_devices.push_back({});
+        direct_driver_icd.get_test_icd().physical_devices.at(0).properties.driverVersion = driver.driver_version;
+        if (!exclusive && driver.expect_to_find) {
+            expected_driver_count++;
+        }
+    }
+
+    VkDirectDriverLoadingListLUNARG ddl_list{};
+    ddl_list.sType = VK_STRUCTURE_TYPE_DIRECT_DRIVER_LOADING_LIST_LUNARG;
+    ddl_list.mode = exclusive ? VK_DIRECT_DRIVER_LOADING_MODE_EXCLUSIVE_LUNARG : VK_DIRECT_DRIVER_LOADING_MODE_INCLUSIVE_LUNARG;
+    ddl_list.driverCount = static_cast<uint32_t>(ddl_infos.size());
+    ddl_list.pDrivers = ddl_infos.data();
+
+    DebugUtilsLogger log;
+    InstWrapper inst{env.vulkan_functions};
+    FillDebugUtilsCreateDetails(inst.create_info, log);
+    log.get()->pNext = reinterpret_cast<const void*>(&ddl_list);
+    inst.create_info.add_extension(VK_LUNARG_DIRECT_DRIVER_LOADING_EXTENSION_NAME);
+    inst.create_info.set_api_version(VK_MAKE_API_VERSION(0, 1, 0, 0));
+    ASSERT_NO_FATAL_FAILURE(inst.CheckCreate());
+
+    if (exclusive) {
+        ASSERT_TRUE(
+            log.find("loader_scan_for_direct_drivers: The VK_LUNARG_direct_driver_loading extension is active and specified "
+                     "VK_DIRECT_DRIVER_LOADING_MODE_EXCLUSIVE_LUNARG, skipping system and environment "
+                     "variable driver search mechanisms."));
+    }
+
+    // Make sure all drivers we expect to load were found - including checking that the pfn matches exactly.
+    for (uint32_t i = 0; i < direct_drivers.size(); i++) {
+        if (direct_drivers.at(i).expect_to_find) {
+            std::stringstream ss;
+            ss << "loader_add_direct_driver: Adding driver found in index " << i
+               << " of VkDirectDriverLoadingListLUNARG::pDrivers structure. pfnGetInstanceProcAddr was set to "
+               << reinterpret_cast<const void*>(ddl_infos.at(i).pfnGetInstanceProcAddr);
+            std::string log_message = ss.str();
+            ASSERT_TRUE(log.find(log_message));
+        }
+    }
+
+    auto phys_devs = inst.GetPhysDevs();
+    ASSERT_EQ(phys_devs.size(), expected_driver_count);
+
+    // We have to iterate through the driver lists backwards because the loader *prepends* icd's, so the last found ICD is found
+    // first in the driver list
+    uint32_t driver_index = 0;
+    for (size_t i = normal_drivers.size() - 1; i == 0; i--) {
+        if (normal_drivers.at(i).expect_to_find) {
+            VkPhysicalDeviceProperties props{};
+            inst.functions->vkGetPhysicalDeviceProperties(phys_devs.at(driver_index), &props);
+            ASSERT_EQ(props.driverVersion, normal_drivers.at(i).driver_version);
+            driver_index++;
+        }
+    }
+    for (size_t i = direct_drivers.size() - 1; i == 0; i--) {
+        if (direct_drivers.at(i).expect_to_find) {
+            VkPhysicalDeviceProperties props{};
+            inst.functions->vkGetPhysicalDeviceProperties(phys_devs.at(driver_index), &props);
+            ASSERT_EQ(props.driverVersion, direct_drivers.at(i).driver_version);
+            driver_index++;
+        }
+    }
+}
+
+// Only 1 direct driver
+TEST(DirectDriverLoading, Individual) {
+    FrameworkEnvironment env{};
+    std::vector<DriverInfo> normal_drivers;
+    std::vector<DriverInfo> direct_drivers;
+    direct_drivers.emplace_back(TestICDDetails(TEST_ICD_PATH_VERSION_7).set_discovery_type(ManifestDiscoveryType::none), 10, true);
+
+    ASSERT_NO_FATAL_FAILURE(CheckDirectDriverLoading(env, normal_drivers, direct_drivers, false));
+}
+
+// 2 direct drivers
+TEST(DirectDriverLoading, MultipleDirectDrivers) {
+    FrameworkEnvironment env{};
+    std::vector<DriverInfo> normal_drivers;
+    std::vector<DriverInfo> direct_drivers;
+    direct_drivers.emplace_back(TestICDDetails(TEST_ICD_PATH_VERSION_7).set_discovery_type(ManifestDiscoveryType::none), 13, true);
+    direct_drivers.emplace_back(TestICDDetails(TEST_ICD_PATH_VERSION_7).set_discovery_type(ManifestDiscoveryType::none), 7, true);
+    ASSERT_NO_FATAL_FAILURE(CheckDirectDriverLoading(env, normal_drivers, direct_drivers, false));
+}
+
+// Multiple direct drivers with a normal driver in the middle
+TEST(DirectDriverLoading, MultipleDirectDriversAndNormalDrivers) {
+    FrameworkEnvironment env{};
+    std::vector<DriverInfo> normal_drivers;
+    std::vector<DriverInfo> direct_drivers;
+    normal_drivers.emplace_back(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA), 90, true);
+    direct_drivers.emplace_back(TestICDDetails(TEST_ICD_PATH_VERSION_7).set_discovery_type(ManifestDiscoveryType::none), 80, true);
+    direct_drivers.emplace_back(TestICDDetails(TEST_ICD_PATH_VERSION_7).set_discovery_type(ManifestDiscoveryType::none), 70, true);
+    ASSERT_NO_FATAL_FAILURE(CheckDirectDriverLoading(env, normal_drivers, direct_drivers, false));
+}
+
+// Normal driver and direct driver with direct driver exclusivity
+TEST(DirectDriverLoading, ExclusiveWithNormalDriver) {
+    FrameworkEnvironment env{};
+    std::vector<DriverInfo> normal_drivers;
+    std::vector<DriverInfo> direct_drivers;
+    direct_drivers.emplace_back(TestICDDetails(TEST_ICD_PATH_VERSION_7).set_discovery_type(ManifestDiscoveryType::none), 33, true);
+    normal_drivers.emplace_back(TestICDDetails(TEST_ICD_PATH_VERSION_2), 44, false);
+    ASSERT_NO_FATAL_FAILURE(CheckDirectDriverLoading(env, normal_drivers, direct_drivers, true));
+}
+
+TEST(DirectDriverLoading, ExclusiveWithMultipleNormalDriver) {
+    FrameworkEnvironment env{};
+    std::vector<DriverInfo> normal_drivers;
+    std::vector<DriverInfo> direct_drivers;
+    normal_drivers.emplace_back(TestICDDetails(TEST_ICD_PATH_VERSION_2), 55, true);
+    normal_drivers.emplace_back(TestICDDetails(TEST_ICD_PATH_VERSION_2), 66, true);
+    direct_drivers.emplace_back(TestICDDetails(TEST_ICD_PATH_VERSION_7).set_discovery_type(ManifestDiscoveryType::none), 77, true);
+    ASSERT_NO_FATAL_FAILURE(CheckDirectDriverLoading(env, normal_drivers, direct_drivers, true));
+}
+
+TEST(DirectDriverLoading, ExclusiveWithDriverEnvVar) {
+    FrameworkEnvironment env{};
+    std::vector<DriverInfo> normal_drivers;
+    std::vector<DriverInfo> direct_drivers;
+    normal_drivers.emplace_back(
+        TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA).set_discovery_type(ManifestDiscoveryType::env_var), 4, false);
+    direct_drivers.emplace_back(TestICDDetails(TEST_ICD_PATH_VERSION_7).set_discovery_type(ManifestDiscoveryType::none), 5, true);
+    ASSERT_NO_FATAL_FAILURE(CheckDirectDriverLoading(env, normal_drivers, direct_drivers, true));
+}
+
+TEST(DirectDriverLoading, ExclusiveWithAddDriverEnvVar) {
+    FrameworkEnvironment env{};
+    std::vector<DriverInfo> normal_drivers;
+    std::vector<DriverInfo> direct_drivers;
+
+    normal_drivers.emplace_back(
+        TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA).set_discovery_type(ManifestDiscoveryType::add_env_var), 6, false);
+    direct_drivers.emplace_back(TestICDDetails(TEST_ICD_PATH_VERSION_7).set_discovery_type(ManifestDiscoveryType::none), 7, true);
+    ASSERT_NO_FATAL_FAILURE(CheckDirectDriverLoading(env, normal_drivers, direct_drivers, true));
+}
+
+TEST(DirectDriverLoading, InclusiveWithFilterSelect) {
+    FrameworkEnvironment env{};
+    std::vector<DriverInfo> normal_drivers;
+    std::vector<DriverInfo> direct_drivers;
+
+    EnvVarWrapper driver_filter_select_env_var{"VK_LOADER_DRIVERS_SELECT", "normal_driver.json"};
+
+    normal_drivers.emplace_back(
+        TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA).set_disable_icd_inc(true).set_json_name("normal_driver"), 8, true);
+    direct_drivers.emplace_back(TestICDDetails(TEST_ICD_PATH_VERSION_7).set_discovery_type(ManifestDiscoveryType::none), 9, true);
+
+    ASSERT_NO_FATAL_FAILURE(CheckDirectDriverLoading(env, normal_drivers, direct_drivers, false));
+}
+
+TEST(DirectDriverLoading, ExclusiveWithFilterSelect) {
+    FrameworkEnvironment env{};
+    std::vector<DriverInfo> normal_drivers;
+    std::vector<DriverInfo> direct_drivers;
+
+    EnvVarWrapper driver_filter_select_env_var{"VK_LOADER_DRIVERS_SELECT", "normal_driver.json"};
+
+    normal_drivers.emplace_back(
+        TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA).set_disable_icd_inc(true).set_json_name("normal_driver"), 10,
+        false);
+    direct_drivers.emplace_back(TestICDDetails(TEST_ICD_PATH_VERSION_7).set_discovery_type(ManifestDiscoveryType::none), 11, true);
+
+    ASSERT_NO_FATAL_FAILURE(CheckDirectDriverLoading(env, normal_drivers, direct_drivers, true));
+}
+
+TEST(DirectDriverLoading, InclusiveWithFilterDisable) {
+    FrameworkEnvironment env{};
+    std::vector<DriverInfo> normal_drivers;
+    std::vector<DriverInfo> direct_drivers;
+
+    EnvVarWrapper driver_filter_disable_env_var{"VK_LOADER_DRIVERS_DISABLE", "normal_driver.json"};
+
+    normal_drivers.emplace_back(
+        TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA).set_disable_icd_inc(true).set_json_name("normal_driver"), 12,
+        false);
+    direct_drivers.emplace_back(TestICDDetails(TEST_ICD_PATH_VERSION_7).set_discovery_type(ManifestDiscoveryType::none), 13, true);
+    ASSERT_NO_FATAL_FAILURE(CheckDirectDriverLoading(env, normal_drivers, direct_drivers, false));
+}
+
+TEST(DirectDriverLoading, ExclusiveWithFilterDisable) {
+    FrameworkEnvironment env{};
+    std::vector<DriverInfo> normal_drivers;
+    std::vector<DriverInfo> direct_drivers;
+
+    EnvVarWrapper driver_filter_disable_env_var{"VK_LOADER_DRIVERS_DISABLE", "normal_driver.json"};
+
+    normal_drivers.emplace_back(
+        TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA).set_disable_icd_inc(true).set_json_name("normal_driver"), 14,
+        false);
+    direct_drivers.emplace_back(TestICDDetails(TEST_ICD_PATH_VERSION_7).set_discovery_type(ManifestDiscoveryType::none), 15, true);
+    ASSERT_NO_FATAL_FAILURE(CheckDirectDriverLoading(env, normal_drivers, direct_drivers, true));
+}
+
+// The VK_LUNARG_direct_driver_loading extension is not enabled
+TEST(DirectDriverLoading, ExtensionNotEnabled) {
+    FrameworkEnvironment env{};
+
+    auto& direct_driver_icd = env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_7).set_discovery_type(ManifestDiscoveryType::none));
+    direct_driver_icd.get_test_icd().physical_devices.push_back({});
+
+    VkDirectDriverLoadingInfoLUNARG ddl_info{};
+    ddl_info.sType = VK_STRUCTURE_TYPE_DIRECT_DRIVER_LOADING_INFO_LUNARG;
+    ddl_info.pfnGetInstanceProcAddr = direct_driver_icd.icd_library.get_symbol("vk_icdGetInstanceProcAddr");
+
+    VkDirectDriverLoadingListLUNARG ddl_list{};
+    ddl_list.sType = VK_STRUCTURE_TYPE_DIRECT_DRIVER_LOADING_LIST_LUNARG;
+    ddl_list.mode = VK_DIRECT_DRIVER_LOADING_MODE_INCLUSIVE_LUNARG;
+    ddl_list.driverCount = 1U;
+    ddl_list.pDrivers = &ddl_info;
+
+    DebugUtilsLogger log;
+    InstWrapper inst{env.vulkan_functions};
+    FillDebugUtilsCreateDetails(inst.create_info, log);
+    log.get()->pNext = reinterpret_cast<const void*>(&ddl_list);
+    inst.create_info.set_api_version(VK_MAKE_API_VERSION(0, 1, 0, 0));
+    ASSERT_NO_FATAL_FAILURE(inst.CheckCreate(VK_ERROR_INCOMPATIBLE_DRIVER));
+
+    ASSERT_TRUE(
+        log.find("loader_scan_for_direct_drivers: The pNext chain of VkInstanceCreateInfo contained the "
+                 "VkDirectDriverLoadingListLUNARG structure, but the VK_LUNARG_direct_driver_loading extension was "
+                 "not enabled."));
+}
+
+// VkDirectDriverLoadingListLUNARG is not in the pNext chain of VkInstanceCreateInfo
+TEST(DirectDriverLoading, DriverListNotInPnextChain) {
+    FrameworkEnvironment env{};
+
+    auto& direct_driver_icd = env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_7).set_discovery_type(ManifestDiscoveryType::none));
+    direct_driver_icd.get_test_icd().physical_devices.push_back({});
+
+    DebugUtilsLogger log;
+    InstWrapper inst{env.vulkan_functions};
+    FillDebugUtilsCreateDetails(inst.create_info, log);
+    inst.create_info.add_extension(VK_LUNARG_DIRECT_DRIVER_LOADING_EXTENSION_NAME);
+    inst.create_info.set_api_version(VK_MAKE_API_VERSION(0, 1, 0, 0));
+    ASSERT_NO_FATAL_FAILURE(inst.CheckCreate(VK_ERROR_INCOMPATIBLE_DRIVER));
+
+    ASSERT_TRUE(
+        log.find("loader_scan_for_direct_drivers: The VK_LUNARG_direct_driver_loading extension was enabled but the pNext chain of "
+                 "VkInstanceCreateInfo did not contain the "
+                 "VkDirectDriverLoadingListLUNARG structure."));
+}
+
+// User sets the pDrivers pointer in VkDirectDriverLoadingListLUNARG to nullptr
+TEST(DirectDriverLoading, DriverListHasNullDriverPointer) {
+    FrameworkEnvironment env{};
+
+    auto& direct_driver_icd = env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_7).set_discovery_type(ManifestDiscoveryType::none));
+    direct_driver_icd.get_test_icd().physical_devices.push_back({});
+
+    VkDirectDriverLoadingListLUNARG ddl_list{};
+    ddl_list.sType = VK_STRUCTURE_TYPE_DIRECT_DRIVER_LOADING_LIST_LUNARG;
+    ddl_list.mode = VK_DIRECT_DRIVER_LOADING_MODE_INCLUSIVE_LUNARG;
+    ddl_list.driverCount = 1U;
+    ddl_list.pDrivers = nullptr;  // user forgot to set the pDrivers
+
+    DebugUtilsLogger log;
+    InstWrapper inst{env.vulkan_functions};
+    FillDebugUtilsCreateDetails(inst.create_info, log);
+    log.get()->pNext = reinterpret_cast<const void*>(&ddl_list);
+    inst.create_info.add_extension(VK_LUNARG_DIRECT_DRIVER_LOADING_EXTENSION_NAME);
+    inst.create_info.set_api_version(VK_MAKE_API_VERSION(0, 1, 0, 0));
+    ASSERT_NO_FATAL_FAILURE(inst.CheckCreate(VK_ERROR_INCOMPATIBLE_DRIVER));
+
+    ASSERT_TRUE(
+        log.find("loader_scan_for_direct_drivers: The VkDirectDriverLoadingListLUNARG structure in the pNext chain of "
+                 "VkInstanceCreateInfo has a NULL pDrivers member."));
+}
+
+// User sets the driverCount in VkDirectDriverLoadingListLUNARG to zero
+TEST(DirectDriverLoading, DriverListHasZeroInfoCount) {
+    FrameworkEnvironment env{};
+
+    auto& direct_driver_icd = env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_7).set_discovery_type(ManifestDiscoveryType::none));
+    direct_driver_icd.get_test_icd().physical_devices.push_back({});
+
+    VkDirectDriverLoadingInfoLUNARG ddl_info{};
+    ddl_info.sType = VK_STRUCTURE_TYPE_DIRECT_DRIVER_LOADING_INFO_LUNARG;
+    ddl_info.pfnGetInstanceProcAddr = direct_driver_icd.icd_library.get_symbol("vk_icdGetInstanceProcAddr");
+
+    VkDirectDriverLoadingListLUNARG ddl_list{};
+    ddl_list.sType = VK_STRUCTURE_TYPE_DIRECT_DRIVER_LOADING_LIST_LUNARG;
+    ddl_list.mode = VK_DIRECT_DRIVER_LOADING_MODE_INCLUSIVE_LUNARG;
+    ddl_list.driverCount = 0;  // user set 0 for the info list
+    ddl_list.pDrivers = &ddl_info;
+
+    DebugUtilsLogger log;
+    InstWrapper inst{env.vulkan_functions};
+    FillDebugUtilsCreateDetails(inst.create_info, log);
+    log.get()->pNext = reinterpret_cast<const void*>(&ddl_list);
+    inst.create_info.add_extension(VK_LUNARG_DIRECT_DRIVER_LOADING_EXTENSION_NAME);
+    inst.create_info.set_api_version(VK_MAKE_API_VERSION(0, 1, 0, 0));
+    ASSERT_NO_FATAL_FAILURE(inst.CheckCreate(VK_ERROR_INCOMPATIBLE_DRIVER));
+
+    ASSERT_TRUE(
+        log.find("loader_scan_for_direct_drivers: The VkDirectDriverLoadingListLUNARG structure in the pNext chain of "
+                 "VkInstanceCreateInfo has a non-null pDrivers member but a driverCount member with a value "
+                 "of zero."));
+}
+
+// pfnGetInstanceProcAddr in VkDirectDriverLoadingInfoLUNARG is nullptr
+TEST(DirectDriverLoading, DriverInfoMissingGetInstanceProcAddr) {
+    FrameworkEnvironment env{};
+
+    auto& direct_driver_icd = env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_7).set_discovery_type(ManifestDiscoveryType::none));
+    direct_driver_icd.get_test_icd().physical_devices.push_back({});
+
+    std::array<VkDirectDriverLoadingInfoLUNARG, 2> ddl_infos{};
+    ddl_infos[0].sType = VK_STRUCTURE_TYPE_DIRECT_DRIVER_LOADING_INFO_LUNARG;
+    ddl_infos[0].pfnGetInstanceProcAddr = nullptr;  // user didn't set the pfnGetInstanceProcAddr to the driver's handle
+
+    ddl_infos[1].sType = VK_STRUCTURE_TYPE_DIRECT_DRIVER_LOADING_INFO_LUNARG;
+    ddl_infos[1].pfnGetInstanceProcAddr = nullptr;  // user didn't set the pfnGetInstanceProcAddr to the driver's handle
+
+    VkDirectDriverLoadingListLUNARG ddl_list{};
+    ddl_list.sType = VK_STRUCTURE_TYPE_DIRECT_DRIVER_LOADING_LIST_LUNARG;
+    ddl_list.mode = VK_DIRECT_DRIVER_LOADING_MODE_INCLUSIVE_LUNARG;
+    ddl_list.driverCount = static_cast<uint32_t>(ddl_infos.size());
+    ddl_list.pDrivers = ddl_infos.data();
+
+    DebugUtilsLogger log;
+    InstWrapper inst{env.vulkan_functions};
+    FillDebugUtilsCreateDetails(inst.create_info, log);
+    log.get()->pNext = reinterpret_cast<const void*>(&ddl_list);
+    inst.create_info.add_extension(VK_LUNARG_DIRECT_DRIVER_LOADING_EXTENSION_NAME);
+    inst.create_info.set_api_version(VK_MAKE_API_VERSION(0, 1, 0, 0));
+    ASSERT_NO_FATAL_FAILURE(inst.CheckCreate(VK_ERROR_INCOMPATIBLE_DRIVER));
+
+    ASSERT_TRUE(
+        log.find("loader_add_direct_driver: VkDirectDriverLoadingInfoLUNARG structure at index 0 contains a NULL pointer for the "
+                 "pfnGetInstanceProcAddr member, skipping."));
+    ASSERT_TRUE(
+        log.find("loader_add_direct_driver: VkDirectDriverLoadingInfoLUNARG structure at index 1 contains a NULL pointer for the "
+                 "pfnGetInstanceProcAddr member, skipping."));
+}
+
+// test the various error paths in loader_add_direct_driver
+TEST(DirectDriverLoading, DriverDoesNotExportNegotiateFunction) {
+    FrameworkEnvironment env{};
+
+    auto& direct_driver_icd = env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_7).set_discovery_type(ManifestDiscoveryType::none));
+    direct_driver_icd.get_test_icd().physical_devices.push_back({});
+    direct_driver_icd.get_test_icd()
+        .set_exposes_vk_icdNegotiateLoaderICDInterfaceVersion(false)
+        .set_exposes_vkCreateInstance(false)
+        .set_exposes_vkEnumerateInstanceExtensionProperties(false);
+
+    VkDirectDriverLoadingInfoLUNARG ddl_info{};
+    ddl_info.sType = VK_STRUCTURE_TYPE_DIRECT_DRIVER_LOADING_INFO_LUNARG;
+    ddl_info.pfnGetInstanceProcAddr = direct_driver_icd.icd_library.get_symbol("vk_icdGetInstanceProcAddr");
+
+    VkDirectDriverLoadingListLUNARG ddl_list{};
+    ddl_list.sType = VK_STRUCTURE_TYPE_DIRECT_DRIVER_LOADING_LIST_LUNARG;
+    ddl_list.mode = VK_DIRECT_DRIVER_LOADING_MODE_INCLUSIVE_LUNARG;
+    ddl_list.driverCount = 1;
+    ddl_list.pDrivers = &ddl_info;
+
+    {
+        DebugUtilsLogger log;
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, log);
+        log.get()->pNext = reinterpret_cast<const void*>(&ddl_list);
+        inst.create_info.add_extension(VK_LUNARG_DIRECT_DRIVER_LOADING_EXTENSION_NAME);
+        inst.create_info.set_api_version(VK_MAKE_API_VERSION(0, 1, 0, 0));
+        ASSERT_NO_FATAL_FAILURE(inst.CheckCreate(VK_ERROR_INCOMPATIBLE_DRIVER));
+
+        ASSERT_TRUE(
+            log.find("loader_add_direct_driver: Could not get 'vk_icdNegotiateLoaderICDInterfaceVersion' from "
+                     "VkDirectDriverLoadingInfoLUNARG structure at "
+                     "index 0, skipping."));
+    }
+
+    // Allow the negotiate function to be found, now it should fail to find instance creation function
+    direct_driver_icd.get_test_icd().set_exposes_vk_icdNegotiateLoaderICDInterfaceVersion(true);
+    direct_driver_icd.get_test_icd().set_max_icd_interface_version(4);
+
+    {
+        DebugUtilsLogger log;
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, log);
+        log.get()->pNext = reinterpret_cast<const void*>(&ddl_list);
+        inst.create_info.add_extension(VK_LUNARG_DIRECT_DRIVER_LOADING_EXTENSION_NAME);
+        inst.create_info.set_api_version(VK_MAKE_API_VERSION(0, 1, 0, 0));
+        ASSERT_NO_FATAL_FAILURE(inst.CheckCreate(VK_ERROR_INCOMPATIBLE_DRIVER));
+
+        ASSERT_TRUE(log.find(
+            "loader_add_direct_driver: VkDirectDriverLoadingInfoLUNARG structure at index 0 supports interface version 4, "
+            "which is incompatible with the Loader Driver Interface version that supports the VK_LUNARG_direct_driver_loading "
+            "extension, skipping."));
+    }
+    direct_driver_icd.get_test_icd().set_max_icd_interface_version(7);
+
+    {
+        DebugUtilsLogger log;
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, log);
+        log.get()->pNext = reinterpret_cast<const void*>(&ddl_list);
+        inst.create_info.add_extension(VK_LUNARG_DIRECT_DRIVER_LOADING_EXTENSION_NAME);
+        inst.create_info.set_api_version(VK_MAKE_API_VERSION(0, 1, 0, 0));
+        ASSERT_NO_FATAL_FAILURE(inst.CheckCreate(VK_ERROR_INCOMPATIBLE_DRIVER));
+
+        ASSERT_TRUE(
+            log.find("loader_add_direct_driver: Could not get 'vkEnumerateInstanceExtensionProperties' from "
+                     "VkDirectDriverLoadingInfoLUNARG structure at index 0, skipping."));
+    }
+
+    // Allow the instance creation function to be found, now it should fail to find EnumInstExtProps
+    direct_driver_icd.get_test_icd().set_exposes_vkCreateInstance(true);
+
+    {
+        DebugUtilsLogger log;
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, log);
+        log.get()->pNext = reinterpret_cast<const void*>(&ddl_list);
+        inst.create_info.add_extension(VK_LUNARG_DIRECT_DRIVER_LOADING_EXTENSION_NAME);
+        inst.create_info.set_api_version(VK_MAKE_API_VERSION(0, 1, 0, 0));
+        ASSERT_NO_FATAL_FAILURE(inst.CheckCreate(VK_ERROR_INCOMPATIBLE_DRIVER));
+
+        ASSERT_TRUE(
+            log.find("loader_add_direct_driver: Could not get 'vkEnumerateInstanceExtensionProperties' from "
+                     "VkDirectDriverLoadingInfoLUNARG structure at index 0, skipping."));
+    }
+}