Add loader settings file
authorCharles Giessen <charles@lunarg.com>
Sat, 6 May 2023 05:26:52 +0000 (23:26 -0600)
committerCharles Giessen <46324611+charles-lunarg@users.noreply.github.com>
Mon, 29 May 2023 23:45:08 +0000 (17:45 -0600)
The loader settings file is a json file that enables external tools, such as
VkConfig to precisely enable and order the layers. This mirrors the
functionality of the override meta layer but by not being a layer, is able to
be much more cleanly implemented.

The primary capability of the settings file is to be able to dictate the
order of all layers regardless of their type. If the loader detects a settings
file, it will use the order provided in it. It has similar functionality for
applying a setting per-application as well as globally. A new feature is the
ability to dictate the 'global' logging behavior, eg overriding the
VK_LOADER_DEBUG environment variable.

Settings are per-instance, so if an application destroys its instance and
creates a new one, it will get the most up to date settings file. There is
a 'global' settings file used for pre-instance functions, and is updated on
every call that makes use of the settings (aka all global functions except for
vkGetInstanceProcAddr).

Most of the new code lives in settings.h and settings.c but changes were made
to loader_scan_for_layers, loader_scan_for_implicit_layers,
loader_enable_instance_layers, and loader_validate_instance_extensions.

22 files changed:
BUILD.gn
loader/CMakeLists.txt
loader/cJSON.c
loader/loader.c
loader/loader.h
loader/loader_common.h
loader/loader_windows.c
loader/loader_windows.h
loader/log.c
loader/log.h
loader/settings.c [new file with mode: 0644]
loader/settings.h [new file with mode: 0644]
loader/trampoline.c
loader/vk_loader_platform.h
tests/CMakeLists.txt
tests/framework/shim/shim.h
tests/framework/shim/shim_common.cpp
tests/framework/shim/windows_shim.cpp
tests/framework/test_environment.cpp
tests/framework/test_environment.h
tests/loader_alloc_callback_tests.cpp
tests/loader_settings_tests.cpp [new file with mode: 0644]

index c3d412763c44fbf4f740267f079f944b5d0e5e81..7bbd39316393f9c5cbcdeaea23da03c6c98f7e09 100644 (file)
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -127,6 +127,8 @@ if (!is_android) {
       "loader/log.c",
       "loader/log.h",
       "loader/phys_dev_ext.c",
+      "loader/settings.c",
+      "loader/settings.h",
       "loader/stack_allocation.h",
       "loader/terminator.c",
       "loader/trampoline.c",
index 9a5cae74bf9601416a9e9f3e776b46a6955739b4..d015b54889ef86d089827a6c5b76b4c423d4cef7 100644 (file)
@@ -112,6 +112,8 @@ set(NORMAL_LOADER_SRCS
     loader.h
     log.c
     log.h
+    settings.c
+    settings.h
     terminator.c
     trampoline.c
     unknown_function_handling.c
index 00a121ee96f5f1512beb397ab5e4aeeadbd682ab..9933144be9dd90eb2c189216507b162db0335e38 100644 (file)
@@ -1264,7 +1264,7 @@ VkResult loader_get_json(const struct loader_instance *inst, const char *filenam
     } while (fread_ret_count == 256 && !feof(file));
     len = ftell(file);
     fseek(file, 0, SEEK_SET);
-    json_buf = (char *)loader_instance_heap_alloc(inst, len + 1, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
+    json_buf = (char *)loader_instance_heap_calloc(inst, len + 1, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
     if (json_buf == NULL) {
         loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0,
                    "loader_get_json: Failed to allocate space for JSON file %s buffer of length %d", filename, len);
index afd0a88438b6117691333b4d86cbdaf9ae896fe5..3243a73e33a63fb4d7a7e9b31355b9191220913b 100644 (file)
@@ -286,6 +286,8 @@ VkResult append_str_to_string_list(const struct loader_instance *inst, struct lo
         if (NULL == string_list->list) {
             return VK_ERROR_OUT_OF_HOST_MEMORY;
         }
+        // Null out the new space
+        memset(string_list->list + string_list->allocated_count, 0, string_list->allocated_count);
         string_list->allocated_count *= 2;
     }
     string_list->list[string_list->count++] = str;
@@ -432,18 +434,14 @@ bool has_vk_dev_ext_property(const VkExtensionProperties *ext_prop, const struct
     return false;
 }
 
-// Get the next unused layer property in the list. Init the property to zero.
-struct loader_layer_properties *loader_get_next_layer_property_slot(const struct loader_instance *inst,
-                                                                    struct loader_layer_list *layer_list) {
+VkResult loader_append_layer_property(const struct loader_instance *inst, struct loader_layer_list *layer_list,
+                                      struct loader_layer_properties *layer_property) {
+    VkResult res = VK_SUCCESS;
     if (layer_list->capacity == 0) {
-        layer_list->list =
-            loader_instance_heap_calloc(inst, sizeof(struct loader_layer_properties) * 64, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
-        if (layer_list->list == NULL) {
-            loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0,
-                       "loader_get_next_layer_property_slot: Out of memory can not add any layer properties to list");
-            return NULL;
+        res = loader_init_generic_list(inst, (struct loader_generic_list *)layer_list, sizeof(struct loader_layer_properties));
+        if (VK_SUCCESS != res) {
+            goto out;
         }
-        layer_list->capacity = sizeof(struct loader_layer_properties) * 64;
     }
 
     // Ensure enough room to add an entry
@@ -451,16 +449,22 @@ struct loader_layer_properties *loader_get_next_layer_property_slot(const struct
         void *new_ptr = loader_instance_heap_realloc(inst, layer_list->list, layer_list->capacity, layer_list->capacity * 2,
                                                      VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
         if (NULL == new_ptr) {
-            loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_next_layer_property_slot: realloc failed for layer list");
-            return NULL;
+            loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_append_layer_property: realloc failed for layer list");
+            res = VK_ERROR_OUT_OF_HOST_MEMORY;
+            goto out;
         }
         layer_list->list = new_ptr;
         memset((uint8_t *)layer_list->list + layer_list->capacity, 0, layer_list->capacity);
         layer_list->capacity *= 2;
     }
-
+    memcpy(&layer_list->list[layer_list->count], layer_property, sizeof(struct loader_layer_properties));
     layer_list->count++;
-    return &(layer_list->list[layer_list->count - 1]);
+    memset(layer_property, 0, sizeof(struct loader_layer_properties));
+out:
+    if (res != VK_SUCCESS) {
+        loader_free_layer_properties(inst, layer_property);
+    }
+    return res;
 }
 
 // Search the given layer list for a layer property matching the given layer name
@@ -799,8 +803,8 @@ VkResult loader_add_to_ext_list(const struct loader_instance *inst, struct loade
 
 // Append one extension property defined in props with entrypoints defined in entries to the given
 // ext_list. Do not append if a duplicate.
-// If this is a duplicate, this function free's the passed in entries - as in it takes ownership over that list (if it is not NULL)
-// Return - Vk_SUCCESS on success
+// If this is a duplicate, this function free's the passed in entries - as in it takes ownership over that list (if it is not
+// NULL) Return - Vk_SUCCESS on success
 VkResult loader_add_to_dev_ext_list(const struct loader_instance *inst, struct loader_device_extension_list *ext_list,
                                     const VkExtensionProperties *props, struct loader_string_list *entrys) {
     VkResult res = VK_SUCCESS;
@@ -1059,7 +1063,8 @@ bool loader_implicit_layer_is_enabled(const struct loader_instance *inst, const
 }
 
 // Check the individual implicit layer for the enable/disable environment variable settings.  Only add it after
-// every check has passed indicating it should be used, including making sure a layer of the same name hasn't already been added.
+// every check has passed indicating it should be used, including making sure a layer of the same name hasn't already been
+// added.
 VkResult loader_add_implicit_layer(const struct loader_instance *inst, struct loader_layer_properties *prop,
                                    const struct loader_envvar_filter *enable_filter,
                                    const struct loader_envvar_disable_layers_filter *disable_filter,
@@ -1444,8 +1449,8 @@ VkResult loader_scanned_icd_init(const struct loader_instance *inst, struct load
 
 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.
+    // 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,
@@ -1606,11 +1611,11 @@ VkResult loader_scan_for_direct_drivers(const struct loader_instance *inst, cons
     }
     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.");
+            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;
@@ -1730,8 +1735,8 @@ VkResult loader_scanned_icd_add(const struct loader_instance *inst, struct loade
 
     // If vk_icdGetInstanceProcAddr is NULL, this ICD is using version 0 and so we should respond accordingly.
     if (NULL == fp_get_proc_addr) {
-        // Exporting vk_icdNegotiateLoaderICDInterfaceVersion but not vk_icdGetInstanceProcAddr violates Version 2's requirements,
-        // as for Version 2 to be supported Version 1 must also be supported
+        // Exporting vk_icdNegotiateLoaderICDInterfaceVersion but not vk_icdGetInstanceProcAddr violates Version 2's
+        // requirements, as for Version 2 to be supported Version 1 must also be supported
         if (interface_vers != 0) {
             loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0,
                        "loader_scanned_icd_add: ICD %s reports an interface version of %d but doesn't export "
@@ -1769,7 +1774,8 @@ VkResult loader_scanned_icd_add(const struct loader_instance *inst, struct loade
         }
     } else {
         // vk_icdGetInstanceProcAddr was successfully found, we can assume the version is at least one
-        // If vk_icdNegotiateLoaderICDInterfaceVersion was also found, interface_vers must be 2 or greater, so this check is fine
+        // If vk_icdNegotiateLoaderICDInterfaceVersion was also found, interface_vers must be 2 or greater, so this check is
+        // fine
         if (interface_vers == 0) {
             interface_vers = 1;
         }
@@ -1800,8 +1806,8 @@ VkResult loader_scanned_icd_add(const struct loader_instance *inst, struct loade
             fp_get_phys_dev_proc_addr = loader_platform_get_proc_address(handle, "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
+        // Query "vk_icdEnumerateAdapterPhysicalDevices" with vk_icdGetInstanceProcAddr if the library reports interface version
+        // or greater, otherwise fallback to loading it from the platform dynamic linker
         if (interface_vers >= 7) {
             fp_enum_dxgi_adapter_phys_devs =
                 (PFN_vk_icdEnumerateAdapterPhysicalDevices)fp_get_proc_addr(NULL, "vk_icdEnumerateAdapterPhysicalDevices");
@@ -1865,6 +1871,7 @@ void loader_initialize(void) {
     loader_platform_thread_create_mutex(&loader_lock);
     loader_platform_thread_create_mutex(&loader_preload_icd_lock);
     loader_platform_thread_create_mutex(&loader_global_instance_list_lock);
+    init_global_loader_settings();
 
     // initialize logging
     loader_init_global_debug_level();
@@ -1893,6 +1900,7 @@ void loader_release() {
     loader_unload_preloaded_icds();
 
     // release mutexes
+    teardown_global_loader_settings();
     loader_platform_thread_delete_mutex(&loader_lock);
     loader_platform_thread_delete_mutex(&loader_preload_icd_lock);
     loader_platform_thread_delete_mutex(&loader_global_instance_list_lock);
@@ -2167,14 +2175,11 @@ void remove_all_non_valid_override_layers(struct loader_instance *inst, struct l
         return;
     }
 
-    char cur_path[MAX_STRING_SIZE];
-    char *ret = loader_platform_executable_path(cur_path, sizeof(cur_path));
-    if (ret == NULL) {
-        loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0,
-                   "remove_all_non_valid_override_layers: Failed to get executable path and name");
+    char cur_path[1024];
+    char *ret = loader_platform_executable_path(cur_path, 1024);
+    if (NULL == ret) {
         return;
     }
-
     // Find out if there is an override layer with same the app_key_path as the path to the current executable.
     // If more than one is found, remove it and use the first layer
     // Remove any layers which aren't global and do not have the same app_key_path as the path to the current executable.
@@ -2634,17 +2639,12 @@ VkResult loader_read_layer_json(const struct loader_instance *inst, struct loade
     result = VK_SUCCESS;
 
 out:
-
+    // Try to append the layer property
     if (VK_SUCCESS == result) {
-        struct loader_layer_properties *dest_props = loader_get_next_layer_property_slot(inst, layer_instance_list);
-        if (NULL == dest_props) {
-            // Error already triggered in loader_get_next_layer_property_slot.
-            result = VK_ERROR_OUT_OF_HOST_MEMORY;
-            loader_free_layer_properties(inst, &props);
-        } else {
-            *dest_props = props;
-        }
-    } else {
+        result = loader_append_layer_property(inst, layer_instance_list, &props);
+    }
+    // If appending fails - free all the memory allocated in it
+    if (VK_SUCCESS != result) {
         loader_free_layer_properties(inst, &props);
     }
     loader_instance_heap_free(inst, type);
@@ -3756,11 +3756,27 @@ VkResult get_override_layer_override_paths(struct loader_instance *inst, struct
 
 VkResult loader_scan_for_layers(struct loader_instance *inst, struct loader_layer_list *instance_layers) {
     VkResult res = VK_SUCCESS;
+    struct loader_layer_list settings_layers = {0};
+    struct loader_layer_list regular_instance_layers = {0};
     bool override_layer_valid = false;
     char *override_paths = NULL;
     struct loader_envvar_filter enable_filter;
     struct loader_envvar_disable_layers_filter disable_filter;
 
+    bool should_search_for_other_layers = true;
+    res = get_settings_layers(inst, &settings_layers, &should_search_for_other_layers);
+    if (VK_SUCCESS != res) {
+        goto out;
+    }
+
+    // If we should not look for layers using other mechanisms, assing settings_layers to instance_layers and jump to the
+    // output
+    if (!should_search_for_other_layers) {
+        *instance_layers = settings_layers;
+        memset(&settings_layers, 0, sizeof(struct loader_layer_list));
+        goto out;
+    }
+
     // Parse the filter environment variables to determine if we have any special behavior
     res = parse_generic_filter_environment_var(inst, VK_LAYERS_ENABLE_ENV_VAR, &enable_filter);
     if (VK_SUCCESS != res) {
@@ -3771,17 +3787,17 @@ VkResult loader_scan_for_layers(struct loader_instance *inst, struct loader_laye
         goto out;
     }
 
-    res = loader_parse_instance_layers(inst, LOADER_DATA_FILE_MANIFEST_IMPLICIT_LAYER, NULL, instance_layers);
+    res = loader_parse_instance_layers(inst, LOADER_DATA_FILE_MANIFEST_IMPLICIT_LAYER, NULL, &regular_instance_layers);
     if (VK_SUCCESS != res) {
         goto out;
     }
 
     // Remove any extraneous override layers.
-    remove_all_non_valid_override_layers(inst, instance_layers);
+    remove_all_non_valid_override_layers(inst, &regular_instance_layers);
 
     // Check to see if the override layer is present, and use it's override paths.
-    for (int32_t i = 0; i < (int32_t)instance_layers->count; i++) {
-        struct loader_layer_properties *prop = &instance_layers->list[i];
+    for (uint32_t i = 0; i < regular_instance_layers.count; i++) {
+        struct loader_layer_properties *prop = &regular_instance_layers.list[i];
         if (prop->is_override && loader_implicit_layer_is_enabled(inst, &enable_filter, &disable_filter, prop) &&
             prop->override_paths.count > 0) {
             res = get_override_layer_override_paths(inst, prop, &override_paths);
@@ -3793,31 +3809,35 @@ VkResult loader_scan_for_layers(struct loader_instance *inst, struct loader_laye
     }
 
     // Get a list of manifest files for explicit layers
-    res = loader_parse_instance_layers(inst, LOADER_DATA_FILE_MANIFEST_EXPLICIT_LAYER, override_paths, instance_layers);
+    res = loader_parse_instance_layers(inst, LOADER_DATA_FILE_MANIFEST_EXPLICIT_LAYER, override_paths, &regular_instance_layers);
     if (VK_SUCCESS != res) {
         goto out;
     }
 
     // Verify any meta-layers in the list are valid and all the component layers are
     // actually present in the available layer list
-    verify_all_meta_layers(inst, &enable_filter, &disable_filter, instance_layers, &override_layer_valid);
+    verify_all_meta_layers(inst, &enable_filter, &disable_filter, &regular_instance_layers, &override_layer_valid);
 
     if (override_layer_valid) {
-        loader_remove_layers_in_blacklist(inst, instance_layers);
+        loader_remove_layers_in_blacklist(inst, &regular_instance_layers);
         if (NULL != inst) {
             inst->override_layer_present = true;
         }
     }
 
     // Remove disabled layers
-    for (uint32_t i = 0; i < instance_layers->count; ++i) {
-        if (!loader_layer_is_available(inst, &enable_filter, &disable_filter, &instance_layers->list[i])) {
-            loader_remove_layer_in_list(inst, instance_layers, i);
+    for (uint32_t i = 0; i < regular_instance_layers.count; ++i) {
+        if (!loader_layer_is_available(inst, &enable_filter, &disable_filter, &regular_instance_layers.list[i])) {
+            loader_remove_layer_in_list(inst, &regular_instance_layers, i);
             i--;
         }
     }
 
+    res = combine_settings_layers_with_regular_layers(inst, &settings_layers, &regular_instance_layers, instance_layers);
+
 out:
+    loader_delete_layer_list_and_properties(inst, &settings_layers);
+    loader_delete_layer_list_and_properties(inst, &regular_instance_layers);
 
     loader_instance_heap_free(inst, override_paths);
     return res;
@@ -3825,12 +3845,28 @@ out:
 
 VkResult loader_scan_for_implicit_layers(struct loader_instance *inst, struct loader_layer_list *instance_layers) {
     VkResult res = VK_SUCCESS;
+    struct loader_layer_list settings_layers = {0};
+    struct loader_layer_list regular_instance_layers = {0};
     bool override_layer_valid = false;
     char *override_paths = NULL;
     bool implicit_metalayer_present = false;
     struct loader_envvar_filter enable_filter;
     struct loader_envvar_disable_layers_filter disable_filter;
 
+    bool should_search_for_other_layers = true;
+    res = get_settings_layers(inst, &settings_layers, &should_search_for_other_layers);
+    if (VK_SUCCESS != res) {
+        goto out;
+    }
+
+    // If we should not look for layers using other mechanisms, assing settings_layers to instance_layers and jump to the
+    // output
+    if (!should_search_for_other_layers) {
+        *instance_layers = settings_layers;
+        memset(&settings_layers, 0, sizeof(struct loader_layer_list));
+        goto out;
+    }
+
     // Parse the filter environment variables to determine if we have any special behavior
     res = parse_generic_filter_environment_var(inst, VK_LAYERS_ENABLE_ENV_VAR, &enable_filter);
     if (VK_SUCCESS != res) {
@@ -3841,18 +3877,18 @@ VkResult loader_scan_for_implicit_layers(struct loader_instance *inst, struct lo
         goto out;
     }
 
-    res = loader_parse_instance_layers(inst, LOADER_DATA_FILE_MANIFEST_IMPLICIT_LAYER, NULL, instance_layers);
+    res = loader_parse_instance_layers(inst, LOADER_DATA_FILE_MANIFEST_IMPLICIT_LAYER, NULL, &regular_instance_layers);
     if (VK_SUCCESS != res) {
         goto out;
     }
 
     // Remove any extraneous override layers.
-    remove_all_non_valid_override_layers(inst, instance_layers);
+    remove_all_non_valid_override_layers(inst, &regular_instance_layers);
 
     // Check to see if either the override layer is present, or another implicit meta-layer.
     // Each of these may require explicit layers to be enabled at this time.
-    for (int32_t i = 0; i < (int32_t)instance_layers->count; i++) {
-        struct loader_layer_properties *prop = &instance_layers->list[i];
+    for (uint32_t i = 0; i < regular_instance_layers.count; i++) {
+        struct loader_layer_properties *prop = &regular_instance_layers.list[i];
         if (prop->is_override && loader_implicit_layer_is_enabled(inst, &enable_filter, &disable_filter, prop)) {
             override_layer_valid = true;
             res = get_override_layer_override_paths(inst, prop, &override_paths);
@@ -3868,7 +3904,8 @@ VkResult loader_scan_for_implicit_layers(struct loader_instance *inst, struct lo
     // explicit layer info as well.  Not to worry, though, all explicit layers not included
     // in the override layer will be removed below in loader_remove_layers_in_blacklist().
     if (override_layer_valid || implicit_metalayer_present) {
-        res = loader_parse_instance_layers(inst, LOADER_DATA_FILE_MANIFEST_EXPLICIT_LAYER, override_paths, instance_layers);
+        res =
+            loader_parse_instance_layers(inst, LOADER_DATA_FILE_MANIFEST_EXPLICIT_LAYER, override_paths, &regular_instance_layers);
         if (VK_SUCCESS != res) {
             goto out;
         }
@@ -3876,24 +3913,28 @@ VkResult loader_scan_for_implicit_layers(struct loader_instance *inst, struct lo
 
     // Verify any meta-layers in the list are valid and all the component layers are
     // actually present in the available layer list
-    verify_all_meta_layers(inst, &enable_filter, &disable_filter, instance_layers, &override_layer_valid);
+    verify_all_meta_layers(inst, &enable_filter, &disable_filter, &regular_instance_layers, &override_layer_valid);
 
     if (override_layer_valid || implicit_metalayer_present) {
-        loader_remove_layers_not_in_implicit_meta_layers(inst, instance_layers);
+        loader_remove_layers_not_in_implicit_meta_layers(inst, &regular_instance_layers);
         if (override_layer_valid && inst != NULL) {
             inst->override_layer_present = true;
         }
     }
 
     // Remove disabled layers
-    for (uint32_t i = 0; i < instance_layers->count; ++i) {
-        if (!loader_implicit_layer_is_enabled(inst, &enable_filter, &disable_filter, &instance_layers->list[i])) {
-            loader_remove_layer_in_list(inst, instance_layers, i);
+    for (uint32_t i = 0; i < regular_instance_layers.count; ++i) {
+        if (!loader_implicit_layer_is_enabled(inst, &enable_filter, &disable_filter, &regular_instance_layers.list[i])) {
+            loader_remove_layer_in_list(inst, &regular_instance_layers, i);
             i--;
         }
     }
 
+    res = combine_settings_layers_with_regular_layers(inst, &settings_layers, &regular_instance_layers, instance_layers);
+
 out:
+    loader_delete_layer_list_and_properties(inst, &settings_layers);
+    loader_delete_layer_list_and_properties(inst, &regular_instance_layers);
 
     loader_instance_heap_free(inst, override_paths);
     return res;
@@ -4113,6 +4154,22 @@ VkResult loader_add_implicit_layers(const struct loader_instance *inst, const st
     return VK_SUCCESS;
 }
 
+void warn_if_layers_are_older_than_application(struct loader_instance *inst) {
+    for (uint32_t i = 0; i < inst->expanded_activated_layer_list.count; i++) {
+        // Verify that the layer api version is at least that of the application's request, if not, throw a warning since
+        // undefined behavior could occur.
+        struct loader_layer_properties *prop = inst->expanded_activated_layer_list.list[i];
+        loader_api_version prop_spec_version = loader_make_version(prop->info.specVersion);
+        if (!loader_check_version_meets_required(inst->app_api_version, prop_spec_version)) {
+            loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0,
+                       "Layer %s uses API version %u.%u which is older than the application specified "
+                       "API version of %u.%u. May cause issues.",
+                       prop->info.layerName, prop_spec_version.major, prop_spec_version.minor, inst->app_api_version.major,
+                       inst->app_api_version.minor);
+        }
+    }
+}
+
 VkResult loader_enable_instance_layers(struct loader_instance *inst, const VkInstanceCreateInfo *pCreateInfo,
                                        const struct loader_layer_list *instance_layers) {
     VkResult res = VK_SUCCESS;
@@ -4145,6 +4202,15 @@ VkResult loader_enable_instance_layers(struct loader_instance *inst, const VkIns
         goto out;
     }
 
+    if (inst->settings.settings_active) {
+        res = enable_correct_layers_from_settings(
+            inst, &layers_enable_filter, &layers_disable_filter, pCreateInfo->enabledLayerCount, pCreateInfo->ppEnabledLayerNames,
+            &inst->instance_layer_list, &inst->app_activated_layer_list, &inst->expanded_activated_layer_list);
+        warn_if_layers_are_older_than_application(inst);
+
+        goto out;
+    }
+
     // Add any implicit layers first
     res = loader_add_implicit_layers(inst, &layers_enable_filter, &layers_disable_filter, &inst->app_activated_layer_list,
                                      &inst->expanded_activated_layer_list, instance_layers);
@@ -4164,19 +4230,7 @@ VkResult loader_enable_instance_layers(struct loader_instance *inst, const VkIns
                                          &inst->expanded_activated_layer_list, pCreateInfo->enabledLayerCount,
                                          pCreateInfo->ppEnabledLayerNames, instance_layers);
 
-    for (uint32_t i = 0; i < inst->expanded_activated_layer_list.count; i++) {
-        // Verify that the layer api version is at least that of the application's request, if not, throw a warning since
-        // undefined behavior could occur.
-        struct loader_layer_properties *prop = inst->expanded_activated_layer_list.list[i];
-        loader_api_version prop_spec_version = loader_make_version(prop->info.specVersion);
-        if (!loader_check_version_meets_required(inst->app_api_version, prop_spec_version)) {
-            loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0,
-                       "Layer %s uses API version %u.%u which is older than the application specified "
-                       "API version of %u.%u. May cause issues.",
-                       prop->info.layerName, prop_spec_version.major, prop_spec_version.minor, inst->app_api_version.major,
-                       inst->app_api_version.minor);
-        }
-    }
+    warn_if_layers_are_older_than_application(inst);
 out:
     return res;
 }
@@ -4456,7 +4510,7 @@ VkResult loader_create_instance_chain(const VkInstanceCreateInfo *pCreateInfo, c
 
                 if (!functions_in_interface) {
                     if ((cur_gipa = layer_prop->functions.get_instance_proc_addr) == NULL) {
-                        if (strlen(layer_prop->functions.str_gipa) == 0) {
+                        if (layer_prop->functions.str_gipa == NULL || strlen(layer_prop->functions.str_gipa) == 0) {
                             cur_gipa =
                                 (PFN_vkGetInstanceProcAddr)loader_platform_get_proc_address(lib_handle, "vkGetInstanceProcAddr");
                             layer_prop->functions.get_instance_proc_addr = cur_gipa;
@@ -4794,7 +4848,7 @@ VkResult loader_create_device_chain(const VkPhysicalDevice pd, const VkDeviceCre
             // The Get*ProcAddr pointers will already be filled in if they were received from either the json file or the
             // version negotiation
             if ((fpGIPA = layer_prop->functions.get_instance_proc_addr) == NULL) {
-                if (strlen(layer_prop->functions.str_gipa) == 0) {
+                if (layer_prop->functions.str_gipa == NULL || strlen(layer_prop->functions.str_gipa) == 0) {
                     fpGIPA = (PFN_vkGetInstanceProcAddr)loader_platform_get_proc_address(lib_handle, "vkGetInstanceProcAddr");
                     layer_prop->functions.get_instance_proc_addr = fpGIPA;
                 } else
@@ -4819,7 +4873,7 @@ VkResult loader_create_device_chain(const VkPhysicalDevice pd, const VkDeviceCre
             }
 
             if ((fpGDPA = layer_prop->functions.get_device_proc_addr) == NULL) {
-                if (strlen(layer_prop->functions.str_gdpa) == 0) {
+                if (layer_prop->functions.str_gdpa == NULL || strlen(layer_prop->functions.str_gdpa) == 0) {
                     fpGDPA = (PFN_vkGetDeviceProcAddr)loader_platform_get_proc_address(lib_handle, "vkGetDeviceProcAddr");
                     layer_prop->functions.get_device_proc_addr = fpGDPA;
                 } else
@@ -4936,7 +4990,7 @@ VkResult loader_validate_layers(const struct loader_instance *inst, const uint32
 
     if (layer_count > 0 && ppEnabledLayerNames == NULL) {
         loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0,
-                   "loader_validate_instance_layers: ppEnabledLayerNames is NULL but enabledLayerCount is greater than zero");
+                   "loader_validate_layers: ppEnabledLayerNames is NULL but enabledLayerCount is greater than zero");
         return VK_ERROR_LAYER_NOT_PRESENT;
     }
 
@@ -4954,6 +5008,13 @@ VkResult loader_validate_layers(const struct loader_instance *inst, const uint32
                        "loader_validate_layers: Layer %d does not exist in the list of available layers", i);
             return VK_ERROR_LAYER_NOT_PRESENT;
         }
+        if (inst->settings.settings_active && prop->settings_control_value != LOADER_SETTINGS_LAYER_CONTROL_ON &&
+            prop->settings_control_value != LOADER_SETTINGS_LAYER_CONTROL_DEFAULT) {
+            loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0,
+                       "loader_validate_layers: Layer %d was explicitly prevented from being enabled by the loader settings file",
+                       i);
+            return VK_ERROR_LAYER_NOT_PRESENT;
+        }
     }
     return VK_SUCCESS;
 }
@@ -4996,24 +5057,32 @@ VkResult loader_validate_instance_extensions(struct loader_instance *inst, const
         goto out;
     }
 
-    // Build the lists of active layers (including metalayers) and expanded layers (with metalayers resolved to their
-    // components)
-    res = loader_add_implicit_layers(inst, &layers_enable_filter, &layers_disable_filter, &active_layers, &expanded_layers,
-                                     instance_layers);
-    if (res != VK_SUCCESS) {
-        goto out;
-    }
-    res = loader_add_environment_layers(inst, VK_LAYER_TYPE_FLAG_EXPLICIT_LAYER, &layers_enable_filter, &layers_disable_filter,
-                                        &active_layers, &expanded_layers, instance_layers);
-    if (res != VK_SUCCESS) {
-        goto out;
-    }
-    res = loader_add_layer_names_to_list(inst, &layers_enable_filter, &layers_disable_filter, &active_layers, &expanded_layers,
-                                         pCreateInfo->enabledLayerCount, pCreateInfo->ppEnabledLayerNames, instance_layers);
-    if (VK_SUCCESS != res) {
-        goto out;
+    if (inst->settings.settings_active) {
+        res = enable_correct_layers_from_settings(inst, &layers_enable_filter, &layers_disable_filter,
+                                                  pCreateInfo->enabledLayerCount, pCreateInfo->ppEnabledLayerNames, instance_layers,
+                                                  &active_layers, &expanded_layers);
+        if (res != VK_SUCCESS) {
+            goto out;
+        }
+    } else {
+        // Build the lists of active layers (including metalayers) and expanded layers (with metalayers resolved to their
+        // components)
+        res = loader_add_implicit_layers(inst, &layers_enable_filter, &layers_disable_filter, &active_layers, &expanded_layers,
+                                         instance_layers);
+        if (res != VK_SUCCESS) {
+            goto out;
+        }
+        res = loader_add_environment_layers(inst, VK_LAYER_TYPE_FLAG_EXPLICIT_LAYER, &layers_enable_filter, &layers_disable_filter,
+                                            &active_layers, &expanded_layers, instance_layers);
+        if (res != VK_SUCCESS) {
+            goto out;
+        }
+        res = loader_add_layer_names_to_list(inst, &layers_enable_filter, &layers_disable_filter, &active_layers, &expanded_layers,
+                                             pCreateInfo->enabledLayerCount, pCreateInfo->ppEnabledLayerNames, instance_layers);
+        if (VK_SUCCESS != res) {
+            goto out;
+        }
     }
-
     for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) {
         VkStringErrorFlags result = vk_string_validate(MaxLoaderStringLength, pCreateInfo->ppEnabledExtensionNames[i]);
         if (result != VK_STRING_ERROR_NONE) {
@@ -6624,14 +6693,27 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_EnumerateInstanceLayerProperties(const
         goto out;
     }
 
+    uint32_t active_layer_count = 0;
+    for (uint32_t i = 0; i < instance_layer_list.count; i++) {
+        if (instance_layer_list.list[i].settings_control_value == LOADER_SETTINGS_LAYER_CONTROL_ON ||
+            instance_layer_list.list[i].settings_control_value == LOADER_SETTINGS_LAYER_CONTROL_DEFAULT) {
+            active_layer_count++;
+        }
+    }
+
     if (pProperties == NULL) {
-        *pPropertyCount = instance_layer_list.count;
+        *pPropertyCount = active_layer_count;
         goto out;
     }
 
-    copy_size = (*pPropertyCount < instance_layer_list.count) ? *pPropertyCount : instance_layer_list.count;
+    copy_size = (*pPropertyCount < active_layer_count) ? *pPropertyCount : active_layer_count;
+    uint32_t output_properties_index = 0;
     for (uint32_t i = 0; i < copy_size; i++) {
-        memcpy(&pProperties[i], &instance_layer_list.list[i].info, sizeof(VkLayerProperties));
+        if (instance_layer_list.list[i].settings_control_value == LOADER_SETTINGS_LAYER_CONTROL_ON ||
+            instance_layer_list.list[i].settings_control_value == LOADER_SETTINGS_LAYER_CONTROL_DEFAULT) {
+            memcpy(&pProperties[output_properties_index], &instance_layer_list.list[i].info, sizeof(VkLayerProperties));
+            output_properties_index++;
+        }
     }
 
     *pPropertyCount = copy_size;
index 99a969054908b2918653d88b34aa28d5a01645ac..34262a17cb08cc135e1286d8bcf0e87d502d1dd8 100644 (file)
@@ -30,6 +30,7 @@
 #pragma once
 
 #include "loader_common.h"
+#include "cJSON.h"
 
 // Declare the once_init variable
 LOADER_PLATFORM_THREAD_ONCE_EXTERN_DEFINITION(once_init)
@@ -115,14 +116,22 @@ VkResult copy_str_to_string_list(const struct loader_instance *inst, struct load
 // Free any string inside of loader_string_list and then free the list itself
 void free_string_list(const struct loader_instance *inst, struct loader_string_list *string_list);
 
+VkResult loader_init_generic_list(const struct loader_instance *inst, struct loader_generic_list *list_info, size_t element_size);
 bool has_vk_extension_property_array(const VkExtensionProperties *vk_ext_prop, const uint32_t count,
                                      const VkExtensionProperties *ext_array);
 bool has_vk_extension_property(const VkExtensionProperties *vk_ext_prop, const struct loader_extension_list *ext_list);
-
+// This function takes ownership of layer_property in the case that allocation fails
+VkResult loader_append_layer_property(const struct loader_instance *inst, struct loader_layer_list *layer_list,
+                                      struct loader_layer_properties *layer_property);
+VkResult loader_add_layer_properties(const struct loader_instance *inst, struct loader_layer_list *layer_instance_list, cJSON *json,
+                                     bool is_implicit, char *filename);
 bool loader_find_layer_name_in_list(const char *name, const struct loader_pointer_layer_list *layer_list);
 VkResult loader_add_layer_properties_to_list(const struct loader_instance *inst, struct loader_pointer_layer_list *list,
                                              struct loader_layer_properties *props);
 void loader_free_layer_properties(const struct loader_instance *inst, struct loader_layer_properties *layer_properties);
+bool loader_implicit_layer_is_enabled(const struct loader_instance *inst, const struct loader_envvar_filter *enable_filter,
+                                      const struct loader_envvar_disable_layers_filter *disable_filter,
+                                      const struct loader_layer_properties *prop);
 VkResult loader_add_meta_layer(const struct loader_instance *inst, const struct loader_envvar_filter *enable_filter,
                                const struct loader_envvar_disable_layers_filter *disable_filter,
                                struct loader_layer_properties *prop, struct loader_pointer_layer_list *target_list,
@@ -138,6 +147,8 @@ VkResult loader_init_generic_list(const struct loader_instance *inst, struct loa
 void loader_destroy_generic_list(const struct loader_instance *inst, struct loader_generic_list *list);
 void loader_destroy_pointer_layer_list(const struct loader_instance *inst, struct loader_pointer_layer_list *layer_list);
 void loader_delete_layer_list_and_properties(const struct loader_instance *inst, struct loader_layer_list *layer_list);
+void loader_remove_layer_in_list(const struct loader_instance *inst, struct loader_layer_list *layer_list,
+                                 uint32_t layer_to_remove);
 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,
index de1434f442c1f7755dbf5b4b72abeb068d15326c..249d96faa18bc036e60a1a090f21edac90ba9355 100644 (file)
@@ -39,6 +39,8 @@
 #include "vk_layer_dispatch_table.h"
 #include "vk_loader_extensions.h"
 
+#include "settings.h"
+
 typedef enum VkStringErrorFlagBits {
     VK_STRING_ERROR_NONE = 0x00000000,
     VK_STRING_ERROR_LENGTH = 0x00000001,
@@ -130,6 +132,8 @@ enum layer_type_flags {
 struct loader_layer_properties {
     VkLayerProperties info;
     enum layer_type_flags type_flags;
+    enum loader_settings_layer_control settings_control_value;
+
     uint32_t interface_version;  // PFN_vkNegotiateLoaderLayerInterfaceVersion
     char *manifest_file_name;
     char *lib_name;
@@ -304,6 +308,8 @@ struct loader_instance {
     // Set to true after vkCreateInstance has returned - necessary for loader_gpa_instance_terminator()
     bool instance_finished_creation;
 
+    loader_settings settings;
+
     bool portability_enumeration_enabled;
 
     bool wsi_surface_enabled;
index 8e3915ca251e957d1e36aa3990f66613e488c109..0f9861d801567f39b0bb72a7d94595662e9570b5 100644 (file)
@@ -1069,4 +1069,96 @@ cleanup:
     loader_instance_heap_free(inst, packages);
     return ret;
 }
+
+VkResult get_settings_path_if_exists_in_registry_key(const struct loader_instance *inst, char **out_path, HKEY key) {
+    VkResult result = VK_ERROR_INITIALIZATION_FAILED;
+
+    char name[MAX_STRING_SIZE] = {0};
+    DWORD name_size = sizeof(name);
+
+    *out_path = NULL;
+
+    LONG rtn_value = ERROR_SUCCESS;
+    for (DWORD idx = 0; rtn_value == ERROR_SUCCESS; idx++) {
+        DWORD value = 0;
+        DWORD value_size = sizeof(value);
+        rtn_value = RegEnumValue(key, idx, name, &name_size, NULL, NULL, (LPBYTE)&value, &value_size);
+
+        if (ERROR_SUCCESS != rtn_value) {
+            break;
+        }
+
+        uint32_t start_of_path_filename = 0;
+        for (uint32_t last_char = name_size; last_char > 0; last_char--) {
+            if (name[last_char] == '\\') {
+                start_of_path_filename = last_char + 1;
+                break;
+            }
+        }
+
+        // Make sure the path exists first
+        if (*out_path && !loader_platform_file_exists(name)) {
+            return VK_ERROR_INITIALIZATION_FAILED;
+        }
+
+        if (strcmp(VK_LOADER_SETTINGS_FILENAME, &(name[start_of_path_filename])) == 0) {
+            *out_path = loader_instance_heap_alloc(inst, name_size, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
+            if (*out_path == NULL) {
+                return VK_ERROR_OUT_OF_HOST_MEMORY;
+            }
+            strcpy(*out_path, name);
+            result = VK_SUCCESS;
+            break;
+        }
+    }
+
+    return result;
+}
+
+VkResult windows_get_loader_settings_file_path(const struct loader_instance *inst, char **out_path) {
+    VkResult result = VK_SUCCESS;
+    DWORD access_flags = KEY_QUERY_VALUE;
+    HKEY key = NULL;
+
+    *out_path = NULL;
+
+    // if we are running with admin privileges, only check HKEY_LOCAL_MACHINE.
+    // Otherwise check HKEY_CURRENT_USER, and if nothing is there, look in HKEY_LOCAL_MACHINE
+
+    if (is_high_integrity()) {
+        LONG rtn_value = RegOpenKeyEx(HKEY_LOCAL_MACHINE, VK_SETTINGS_INFO_REGISTRY_LOC, 0, access_flags, &key);
+        if (ERROR_SUCCESS != rtn_value) {
+            goto out;
+        }
+        result = get_settings_path_if_exists_in_registry_key(inst, out_path, key);
+    } else {
+        LONG rtn_value = RegOpenKeyEx(HKEY_CURRENT_USER, VK_SETTINGS_INFO_REGISTRY_LOC, 0, access_flags, &key);
+        if (ERROR_SUCCESS == rtn_value) {
+            result = get_settings_path_if_exists_in_registry_key(inst, out_path, key);
+            RegCloseKey(key);
+            // Either we got OOM and *must* exit or we successfully found the settings file and can exit
+            if (result == VK_ERROR_OUT_OF_HOST_MEMORY || result == VK_SUCCESS) {
+                goto out;
+            }
+        }
+
+        rtn_value = RegOpenKeyEx(HKEY_LOCAL_MACHINE, VK_SETTINGS_INFO_REGISTRY_LOC, 0, access_flags, &key);
+        if (ERROR_SUCCESS != rtn_value) {
+            goto out;
+        }
+
+        result = get_settings_path_if_exists_in_registry_key(inst, out_path, key);
+        if (result == VK_ERROR_OUT_OF_HOST_MEMORY) {
+            goto out;
+        }
+    }
+
+out:
+    if (NULL != key) {
+        RegCloseKey(key);
+    }
+
+    return result;
+}
+
 #endif  // _WIN32
index 93cb022bf5a70e4799a15689d08463a01726e00d..f534ea498cc27a8d0d3cbf33aa94ec73350c32c7 100644 (file)
@@ -116,4 +116,10 @@ VkLoaderFeatureFlags windows_initialize_dxgi(void);
 // Retrieve a path to an installed app package that contains Vulkan manifests.
 // When done using the returned string, the caller should free the pointer.
 char *windows_get_app_package_manifest_path(const struct loader_instance *inst);
+
+// Gets the path to the loader settings file, if it exists. If it doesn't exists, writes NULL to out_path.
+// The path is located through the registry as an entry in HKEY_CURRENT_USER/SOFTWARE/Khronos/Vulkan/Settings
+// and if nothing is there, will try to look in HKEY_LOCAL_MACHINE/SOFTWARE/Khronos/Vulkan/Settings.
+// If running with elevated privileges, this function only looks in HKEY_LOCAL_MACHINE.
+VkResult windows_get_loader_settings_file_path(const struct loader_instance *inst, char **out_path);
 #endif  // WIN32
index df13ab726a45fc1d8eabfd69cfe01da48f30db2c..6f868e97db264aa9098e2af1a033bf66292313dd 100644 (file)
 #include <stdarg.h>
 
 #include "debug_utils.h"
+#include "loader_common.h"
 #include "loader_environment.h"
+#include "settings.h"
+#include "vk_loader_platform.h"
 
 uint32_t g_loader_debug = 0;
 
@@ -83,6 +86,8 @@ void loader_init_global_debug_level(void) {
     loader_free_getenv(orig, NULL);
 }
 
+void loader_set_global_debug_level(uint32_t new_loader_debug) { g_loader_debug = new_loader_debug; }
+
 uint32_t loader_get_global_debug_level(void) { return g_loader_debug; }
 
 void loader_log(const struct loader_instance *inst, VkFlags msg_type, int32_t msg_code, const char *format, ...) {
@@ -146,9 +151,12 @@ void loader_log(const struct loader_instance *inst, VkFlags msg_type, int32_t ms
         util_SubmitDebugUtilsMessageEXT(inst, severity, type, &callback_data);
     }
 
-    uint32_t filtered_msg_type = (msg_type & g_loader_debug);
-    if (0 == filtered_msg_type) {
+    // Exit early if the current instance settings do not ask for logging to stderr
+    if (inst && inst->settings.settings_active && 0 == (msg_type & inst->settings.debug_level)) {
         return;
+    } else {
+        // Check the global settings and if that doesn't say to skip, check the environment variable
+        if (0 == (msg_type & g_loader_debug)) return;
     }
 
     // Only need enough space to create the filter description header for log messages
@@ -218,7 +226,6 @@ void loader_log(const struct loader_instance *inst, VkFlags msg_type, int32_t ms
     OutputDebugString(msg);
     OutputDebugString("\n");
 #endif
-
 }
 
 void loader_log_asm_function_not_supported(const struct loader_instance *inst, VkFlags msg_type, int32_t msg_code,
index 16901f58923bbfac8fbcab86bab3bb7ea1b731e8..d38a32359f8c7d8c40b4b70e8f49cae0608dcadb 100644 (file)
@@ -49,6 +49,9 @@ enum vulkan_loader_debug_flags {
 // This should be called before any Vulkan API calls, eg in the initialization of the .dll or .so
 void loader_init_global_debug_level(void);
 
+// Sets the global debug level - used by global settings files
+void loader_set_global_debug_level(uint32_t new_loader_debug);
+
 // Returns a bitmask that indicates the current flags that should be output
 uint32_t loader_get_global_debug_level(void);
 
diff --git a/loader/settings.c b/loader/settings.c
new file mode 100644 (file)
index 0000000..fda2e3c
--- /dev/null
@@ -0,0 +1,800 @@
+/*
+ *
+ * Copyright (c) 2023 The Khronos Group Inc.
+ * Copyright (c) 2023 Valve Corporation
+ * Copyright (c) 2023 LunarG, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ * Author: Charles Giessen <charles@lunarg.com>
+ *
+ */
+
+#include "settings.h"
+
+#include "allocation.h"
+#include "cJSON.h"
+#include "loader.h"
+#include "loader_environment.h"
+#include "loader_windows.h"
+#include "log.h"
+#include "stack_allocation.h"
+#include "vk_loader_platform.h"
+
+loader_platform_thread_mutex global_loader_settings_lock;
+loader_settings global_loader_settings;
+
+void free_layer_configuration(const struct loader_instance* inst, loader_settings_layer_configuration* layer_configuration) {
+    loader_instance_heap_free(inst, layer_configuration->name);
+    layer_configuration->name = NULL;
+    loader_instance_heap_free(inst, layer_configuration->path);
+    layer_configuration->path = NULL;
+}
+
+void free_loader_settings(const struct loader_instance* inst, loader_settings* settings) {
+    if (NULL != settings->layer_configurations) {
+        for (uint32_t i = 0; i < settings->layer_configuration_count; i++) {
+            free_layer_configuration(inst, &settings->layer_configurations[i]);
+        }
+    }
+    loader_instance_heap_free(inst, settings->layer_configurations);
+    loader_instance_heap_free(inst, settings->settings_file_path);
+    settings->layer_configurations = NULL;
+    memset(settings, 0, sizeof(loader_settings));
+}
+
+loader_settings_layer_control parse_control_string(char* control_string) {
+    loader_settings_layer_control layer_control = LOADER_SETTINGS_LAYER_CONTROL_DEFAULT;
+    if (strcmp(control_string, "auto") == 0)
+        layer_control = LOADER_SETTINGS_LAYER_CONTROL_DEFAULT;
+    else if (strcmp(control_string, "on") == 0)
+        layer_control = LOADER_SETTINGS_LAYER_CONTROL_ON;
+    else if (strcmp(control_string, "off") == 0)
+        layer_control = LOADER_SETTINGS_LAYER_CONTROL_OFF;
+    else if (strcmp(control_string, "unordered_layer_location") == 0)
+        layer_control = LOADER_SETTINGS_LAYER_UNORDERED_LAYER_LOCATION;
+    return layer_control;
+}
+
+const char* loader_settings_layer_control_to_string(loader_settings_layer_control control) {
+    switch (control) {
+        case (LOADER_SETTINGS_LAYER_CONTROL_DEFAULT):
+            return "auto";
+        case (LOADER_SETTINGS_LAYER_CONTROL_ON):
+            return "on";
+        case (LOADER_SETTINGS_LAYER_CONTROL_OFF):
+            return "off";
+        case (LOADER_SETTINGS_LAYER_UNORDERED_LAYER_LOCATION):
+            return "unordered_layer_location";
+        default:
+            return "UNKNOWN_LAYER_CONTROl";
+    }
+}
+
+uint32_t parse_log_filters_from_strings(struct loader_string_list* log_filters) {
+    uint32_t filters = 0;
+    for (uint32_t i = 0; i < log_filters->count; i++) {
+        if (strcmp(log_filters->list[i], "all") == 0)
+            filters |= VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_PERF_BIT | VULKAN_LOADER_ERROR_BIT |
+                       VULKAN_LOADER_DEBUG_BIT | VULKAN_LOADER_LAYER_BIT | VULKAN_LOADER_DRIVER_BIT | VULKAN_LOADER_VALIDATION_BIT;
+        else if (strcmp(log_filters->list[i], "info") == 0)
+            filters |= VULKAN_LOADER_INFO_BIT;
+        else if (strcmp(log_filters->list[i], "warn") == 0)
+            filters |= VULKAN_LOADER_WARN_BIT;
+        else if (strcmp(log_filters->list[i], "perf") == 0)
+            filters |= VULKAN_LOADER_PERF_BIT;
+        else if (strcmp(log_filters->list[i], "error") == 0)
+            filters |= VULKAN_LOADER_ERROR_BIT;
+        else if (strcmp(log_filters->list[i], "debug") == 0)
+            filters |= VULKAN_LOADER_DEBUG_BIT;
+        else if (strcmp(log_filters->list[i], "layer") == 0)
+            filters |= VULKAN_LOADER_LAYER_BIT;
+        else if (strcmp(log_filters->list[i], "driver") == 0)
+            filters |= VULKAN_LOADER_DRIVER_BIT;
+        else if (strcmp(log_filters->list[i], "validation") == 0)
+            filters |= VULKAN_LOADER_VALIDATION_BIT;
+    }
+    return filters;
+}
+
+bool parse_json_enable_disable_option(const struct loader_instance* inst, cJSON* object, const char* key) {
+    char* str = NULL;
+    VkResult res = loader_parse_json_string(inst, object, key, &str);
+    if (res != VK_SUCCESS || NULL == str) {
+        return false;
+    }
+    bool enable = false;
+    if (strcmp(str, "enabled") == 0) {
+        enable = true;
+    }
+    loader_instance_heap_free(inst, str);
+    return enable;
+}
+
+VkResult parse_layer_configuration(const struct loader_instance* inst, cJSON* layer_configuration_json,
+                                   loader_settings_layer_configuration* layer_configuration) {
+    char* control_string = NULL;
+    VkResult res = loader_parse_json_string(inst, layer_configuration_json, "control", &control_string);
+    if (res != VK_SUCCESS) {
+        goto out;
+    }
+    layer_configuration->control = parse_control_string(control_string);
+    loader_instance_heap_free(inst, control_string);
+
+    // If that is the only value - do no further parsing
+    if (layer_configuration->control == LOADER_SETTINGS_LAYER_UNORDERED_LAYER_LOCATION) {
+        goto out;
+    }
+
+    res = loader_parse_json_string(inst, layer_configuration_json, "name", &(layer_configuration->name));
+    if (res != VK_SUCCESS) {
+        goto out;
+    }
+
+    res = loader_parse_json_string(inst, layer_configuration_json, "path", &(layer_configuration->path));
+    if (res != VK_SUCCESS) {
+        goto out;
+    }
+
+    cJSON* treat_as_implicit_manifest = cJSON_GetObjectItem(layer_configuration_json, "treat_as_implicit_manifest");
+    if (treat_as_implicit_manifest && treat_as_implicit_manifest->type == cJSON_True) {
+        layer_configuration->treat_as_implicit_manifest = true;
+    }
+out:
+    if (VK_SUCCESS != res) {
+        free_layer_configuration(inst, layer_configuration);
+    }
+    return res;
+}
+
+VkResult parse_layer_configurations(const struct loader_instance* inst, cJSON* settings_object, loader_settings* loader_settings) {
+    VkResult res = VK_SUCCESS;
+
+    cJSON* layer_configurations = cJSON_GetObjectItem(settings_object, "layers");
+    if (NULL == layer_configurations) {
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+
+    uint32_t layer_configurations_count = cJSON_GetArraySize(layer_configurations);
+    if (layer_configurations_count == 0) {
+        return VK_SUCCESS;
+    }
+
+    loader_settings->layer_configuration_count = layer_configurations_count;
+
+    loader_settings->layer_configurations = loader_instance_heap_calloc(
+        inst, sizeof(loader_settings_layer_configuration) * layer_configurations_count, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
+    if (NULL == loader_settings->layer_configurations) {
+        res = VK_ERROR_OUT_OF_HOST_MEMORY;
+        goto out;
+    }
+
+    for (uint32_t i = 0; i < layer_configurations_count; i++) {
+        cJSON* layer = cJSON_GetArrayItem(layer_configurations, i);
+        if (NULL == layer) {
+            res = VK_ERROR_INITIALIZATION_FAILED;
+            goto out;
+        }
+        res = parse_layer_configuration(inst, layer, &(loader_settings->layer_configurations[i]));
+        if (VK_SUCCESS != res) {
+            goto out;
+        }
+    }
+out:
+    if (res != VK_SUCCESS) {
+        if (loader_settings->layer_configurations) {
+            for (uint32_t i = 0; i < layer_configurations_count; i++) {
+                free_layer_configuration(inst, &loader_settings->layer_configurations[i]);
+            }
+        }
+        loader_instance_heap_free(inst, &loader_settings->layer_configurations);
+    }
+
+    return res;
+}
+
+VkResult check_if_settings_path_exists(const struct loader_instance* inst, char* base, char* suffix, char** settings_file_path) {
+    if (NULL == base || NULL == suffix) {
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+    *settings_file_path = loader_instance_heap_calloc(inst, strlen(base) + strlen(suffix) + 1, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
+    if (NULL == *settings_file_path) {
+        return VK_ERROR_OUT_OF_HOST_MEMORY;
+    }
+    strcpy(*settings_file_path, base);
+    strcat(*settings_file_path, suffix);
+
+    if (!loader_platform_file_exists(*settings_file_path)) {
+        loader_instance_heap_free(inst, *settings_file_path);
+        *settings_file_path = NULL;
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+    return VK_SUCCESS;
+}
+VkResult get_unix_settings_path(const struct loader_instance* inst, char** settings_file_path) {
+    VkResult res =
+        check_if_settings_path_exists(inst, loader_secure_getenv("HOME", inst),
+                                      "/.local/share/vulkan/settings.d/" VK_LOADER_SETTINGS_FILENAME, settings_file_path);
+    if (res == VK_SUCCESS) {
+        return res;
+    }
+    // If HOME isn't set, fallback to XDG_DATA_HOME
+    res = check_if_settings_path_exists(inst, loader_secure_getenv("XDG_DATA_HOME", inst),
+                                        "/vulkan/settings.d/" VK_LOADER_SETTINGS_FILENAME, settings_file_path);
+    if (res == VK_SUCCESS) {
+        return res;
+    }
+    // if XDG_DATA_HOME isn't set, fallback to /etc.
+    // note that the settings_fil_path_suffix stays the same since its the same layout as for XDG_DATA_HOME
+    return check_if_settings_path_exists(inst, "/etc", "/vulkan/settings.d/" VK_LOADER_SETTINGS_FILENAME, settings_file_path);
+}
+
+bool check_if_settings_are_equal(loader_settings* a, loader_settings* b) {
+    // If either pointer is null, return true
+    if (NULL == a || NULL == b) return false;
+    bool are_equal = true;
+    are_equal &= a->settings_active == b->settings_active;
+    are_equal &= a->has_unordered_layer_location == b->has_unordered_layer_location;
+    are_equal &= a->debug_level == b->debug_level;
+    are_equal &= a->layer_configuration_count == b->layer_configuration_count;
+    if (!are_equal) return false;
+    for (uint32_t i = 0; i < a->layer_configuration_count && i < b->layer_configuration_count; i++) {
+        if (a->layer_configurations[i].name && b->layer_configurations[i].name) {
+            are_equal &= 0 == strcmp(a->layer_configurations[i].name, b->layer_configurations[i].name);
+        } else {
+            are_equal = false;
+        }
+        if (a->layer_configurations[i].path && b->layer_configurations[i].path) {
+            are_equal &= 0 == strcmp(a->layer_configurations[i].path, b->layer_configurations[i].path);
+        } else {
+            are_equal = false;
+        }
+        are_equal &= a->layer_configurations[i].control == b->layer_configurations[i].control;
+    }
+    return are_equal;
+}
+
+void log_settings(const struct loader_instance* inst, loader_settings* settings) {
+    if (settings == NULL) {
+        return;
+    }
+    loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, "Using layer configurations found in loader settings from %s",
+               settings->settings_file_path);
+
+    loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "Layer Configurations count = %d", settings->layer_configuration_count);
+    for (uint32_t i = 0; i < settings->layer_configuration_count; i++) {
+        loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "---- Layer Configuration [%d] ----", i);
+        if (settings->layer_configurations[i].control != LOADER_SETTINGS_LAYER_UNORDERED_LAYER_LOCATION) {
+            loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "Name: %s", settings->layer_configurations[i].name);
+            loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "Path: %s", settings->layer_configurations[i].path);
+        }
+        loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "Control: %s",
+                   loader_settings_layer_control_to_string(settings->layer_configurations[i].control));
+    }
+    loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "---------------------------------");
+}
+
+// Loads the vk_loader_settings.json file
+// Returns VK_SUCCESS if it was found & was successfully parsed. Otherwise, it returns VK_ERROR_INITIALIZATION_FAILED if it
+// wasn't found or failed to parse, and returns VK_ERROR_OUT_OF_HOST_MEMORY if it was unable to allocate enough memory.
+VkResult get_loader_settings(const struct loader_instance* inst, loader_settings* loader_settings) {
+    VkResult res = VK_SUCCESS;
+    cJSON* json = NULL;
+    char* file_format_version_string = NULL;
+    char* settings_file_path = NULL;
+#if defined(WIN32)
+    res = windows_get_loader_settings_file_path(inst, &settings_file_path);
+    if (res != VK_SUCCESS) {
+        goto out;
+    }
+
+#elif COMMON_UNIX_PLATFORMS
+    res = get_unix_settings_path(inst, &settings_file_path);
+    if (res != VK_SUCCESS) {
+        goto out;
+    }
+#else
+#warning "Unsupported platform - must specify platform specific location for vk_loader_settings.json"
+#endif
+
+    res = loader_get_json(inst, settings_file_path, &json);
+    // Make sure sure the top level json value is an object
+    if (res != VK_SUCCESS || NULL == json || json->type != 6) {
+        goto out;
+    }
+
+    res = loader_parse_json_string(inst, json, "file_format_version", &file_format_version_string);
+    if (res != VK_SUCCESS) {
+        goto out;
+    }
+    uint32_t settings_array_count = 0;
+    bool has_multi_setting_file = false;
+    cJSON* settings_array = cJSON_GetObjectItem(json, "settings_array");
+    cJSON* single_settings_object = cJSON_GetObjectItem(json, "settings");
+    if (NULL != settings_array) {
+        has_multi_setting_file = true;
+        settings_array_count = cJSON_GetArraySize(settings_array);
+    } else if (NULL != single_settings_object) {
+        settings_array_count = 1;
+    }
+
+    // Corresponds to the settings object that has no app keys
+    int global_settings_index = -1;
+    // Corresponds to the settings object which has a matchign app key
+    int index_to_use = -1;
+
+    char current_process_path[1024];
+    bool valid_exe_path = NULL != loader_platform_executable_path(current_process_path, 1024);
+
+    for (int i = 0; i < (int)settings_array_count; i++) {
+        if (has_multi_setting_file) {
+            single_settings_object = cJSON_GetArrayItem(settings_array, i);
+        }
+        cJSON* app_keys = cJSON_GetObjectItem(single_settings_object, "app_keys");
+        if (NULL == app_keys) {
+            if (global_settings_index == -1) {
+                global_settings_index = i;  // use the first 'global' settings that has no app keys as the global one
+            }
+            continue;
+        } else if (valid_exe_path) {
+            int app_key_count = cJSON_GetArraySize(app_keys);
+            if (app_key_count == 0) {
+                continue;  // empty array
+            }
+            for (int j = 0; j < app_key_count; j++) {
+                cJSON* app_key_json = cJSON_GetArrayItem(app_keys, j);
+                if (NULL == app_key_json) {
+                    continue;
+                }
+                char* app_key = cJSON_Print(app_key_json);
+                if (NULL == app_key) {
+                    continue;
+                }
+
+                if (strcmp(current_process_path, app_key) == 0) {
+                    index_to_use = i;
+                }
+                loader_instance_heap_free(inst, app_key);
+                if (index_to_use == i) {
+                    break;  // break only after freeing the app key
+                }
+            }
+        }
+    }
+
+    // No app specific settings match - either use global settings or exit
+    if (index_to_use == -1) {
+        if (global_settings_index == -1) {
+            goto out;  // No global settings were found - exit
+        } else {
+            index_to_use = global_settings_index;  // Global settings are present - use it
+        }
+    }
+
+    // Now get the actual settings object to use - already have it if there is only one settings object
+    // If there are multiple settings, just need to set single_settings_object to the desired settings object
+    if (has_multi_setting_file) {
+        single_settings_object = cJSON_GetArrayItem(settings_array, index_to_use);
+        if (NULL == single_settings_object) {
+            res = VK_ERROR_INITIALIZATION_FAILED;
+            goto out;
+        }
+    }
+
+    // optional
+    cJSON* stderr_filter = cJSON_GetObjectItem(single_settings_object, "stderr_log");
+    if (NULL != stderr_filter) {
+        struct loader_string_list stderr_log = {0};
+        res = loader_parse_json_array_of_strings(inst, single_settings_object, "stderr_log", &stderr_log);
+        if (VK_ERROR_OUT_OF_HOST_MEMORY == res) {
+            goto out;
+        }
+        loader_settings->debug_level = parse_log_filters_from_strings(&stderr_log);
+        free_string_list(inst, &stderr_log);
+    }
+
+    // optional
+    cJSON* logs_to_use = cJSON_GetObjectItem(single_settings_object, "log_locations");
+    if (NULL != logs_to_use) {
+        int log_count = cJSON_GetArraySize(logs_to_use);
+        for (int i = 0; i < log_count; i++) {
+            cJSON* log_element = cJSON_GetArrayItem(logs_to_use, i);
+            // bool is_valid = true;
+            if (NULL != log_element) {
+                struct loader_string_list log_destinations = {0};
+                res = loader_parse_json_array_of_strings(inst, log_element, "destinations", &log_destinations);
+                if (res != VK_SUCCESS) {
+                    // is_valid = false;
+                }
+                free_string_list(inst, &log_destinations);
+                struct loader_string_list log_filters = {0};
+                res = loader_parse_json_array_of_strings(inst, log_element, "filters", &log_filters);
+                if (res != VK_SUCCESS) {
+                    // is_valid = false;
+                }
+                free_string_list(inst, &log_filters);
+            }
+        }
+    }
+
+    res = parse_layer_configurations(inst, single_settings_object, loader_settings);
+    if (res != VK_SUCCESS) {
+        goto out;
+    }
+
+    // Determine if there exists a layer configuration indicating where to put layers not contained in the settings file
+    // LOADER_SETTINGS_LAYER_UNORDERED_LAYER_LOCATION
+    for (uint32_t i = 0; i < loader_settings->layer_configuration_count; i++) {
+        if (loader_settings->layer_configurations[i].control == LOADER_SETTINGS_LAYER_UNORDERED_LAYER_LOCATION) {
+            loader_settings->has_unordered_layer_location = true;
+            break;
+        }
+    }
+
+    loader_settings->settings_file_path = settings_file_path;
+    settings_file_path = NULL;
+    loader_settings->settings_active = true;
+out:
+    if (NULL != json) {
+        cJSON_Delete(json);
+    }
+
+    loader_instance_heap_free(inst, settings_file_path);
+
+    loader_instance_heap_free(inst, file_format_version_string);
+    return res;
+}
+
+VkResult update_global_loader_settings(void) {
+    loader_settings settings = {0};
+    VkResult res = get_loader_settings(NULL, &settings);
+    loader_platform_thread_lock_mutex(&global_loader_settings_lock);
+
+    free_loader_settings(NULL, &global_loader_settings);
+    if (res == VK_SUCCESS) {
+        if (!check_if_settings_are_equal(&settings, &global_loader_settings)) {
+            log_settings(NULL, &settings);
+        }
+
+        memcpy(&global_loader_settings, &settings, sizeof(loader_settings));
+        if (global_loader_settings.settings_active) {
+            loader_set_global_debug_level(global_loader_settings.debug_level);
+        }
+    }
+    loader_platform_thread_unlock_mutex(&global_loader_settings_lock);
+    return res;
+}
+
+void init_global_loader_settings(void) {
+    loader_platform_thread_create_mutex(&global_loader_settings_lock);
+    // Free out the global settings in case the process was loaded & unloaded
+    free_loader_settings(NULL, &global_loader_settings);
+}
+void teardown_global_loader_settings(void) {
+    free_loader_settings(NULL, &global_loader_settings);
+    loader_platform_thread_delete_mutex(&global_loader_settings_lock);
+}
+
+bool should_skip_logging_global_messages(VkFlags msg_type) {
+    loader_platform_thread_lock_mutex(&global_loader_settings_lock);
+    bool should_skip = global_loader_settings.settings_active && 0 != (msg_type & global_loader_settings.debug_level);
+    loader_platform_thread_unlock_mutex(&global_loader_settings_lock);
+    return should_skip;
+}
+
+// Use this function to get the correct settings to use based on the context
+// If inst is NULL - use the global settings and lock the mutex
+// Else return the settings local to the instance - but do nto lock the mutex
+const loader_settings* get_current_settings_and_lock(const struct loader_instance* inst) {
+    if (inst) {
+        return &inst->settings;
+    }
+    loader_platform_thread_lock_mutex(&global_loader_settings_lock);
+    return &global_loader_settings;
+}
+// Release the global settings lock if we are using the global settings - aka if inst is NULL
+void release_current_settings_lock(const struct loader_instance* inst) {
+    if (inst == NULL) {
+        loader_platform_thread_unlock_mutex(&global_loader_settings_lock);
+    }
+}
+
+VkResult get_settings_layers(const struct loader_instance* inst, struct loader_layer_list* settings_layers,
+                             bool* should_search_for_other_layers) {
+    VkResult res = VK_SUCCESS;
+    *should_search_for_other_layers = true;  // default to true
+
+    const loader_settings* settings = get_current_settings_and_lock(inst);
+
+    // Assume the list doesn't contain LOADER_SETTINGS_LAYER_UNORDERED_LAYER_LOCATION at first
+    if (settings != NULL && settings->settings_active) {
+        *should_search_for_other_layers = false;
+    }
+
+    for (uint32_t i = 0; i < settings->layer_configuration_count; i++) {
+        loader_settings_layer_configuration* layer_config = &settings->layer_configurations[i];
+
+        // If we encountered a layer that should be forced off, we add it to the settings_layers list but only
+        // with the data required to compare it with layers not in the settings file (aka name and manifest path)
+        if (layer_config->control == LOADER_SETTINGS_LAYER_CONTROL_OFF) {
+            struct loader_layer_properties props = {0};
+            props.settings_control_value = LOADER_SETTINGS_LAYER_CONTROL_OFF;
+            strncpy(props.info.layerName, layer_config->name, VK_MAX_EXTENSION_NAME_SIZE);
+            props.info.layerName[VK_MAX_EXTENSION_NAME_SIZE - 1] = '\0';
+            res = loader_copy_to_new_str(inst, layer_config->path, &props.manifest_file_name);
+            if (VK_ERROR_OUT_OF_HOST_MEMORY == res) {
+                goto out;
+            }
+            res = loader_append_layer_property(inst, settings_layers, &props);
+            if (VK_ERROR_OUT_OF_HOST_MEMORY == res) {
+                loader_free_layer_properties(inst, &props);
+                goto out;
+            }
+            continue;
+        }
+
+        // The special layer location that indicates where unordered layers should go only should have the
+        // settings_control_value set - everythign else should be NULL
+        if (layer_config->control == LOADER_SETTINGS_LAYER_UNORDERED_LAYER_LOCATION) {
+            struct loader_layer_properties props = {0};
+            props.settings_control_value = LOADER_SETTINGS_LAYER_UNORDERED_LAYER_LOCATION;
+            res = loader_append_layer_property(inst, settings_layers, &props);
+            if (VK_ERROR_OUT_OF_HOST_MEMORY == res) {
+                loader_free_layer_properties(inst, &props);
+                goto out;
+            }
+            *should_search_for_other_layers = true;
+            continue;
+        }
+
+        if (layer_config->path == NULL) {
+            continue;
+        }
+
+        cJSON* json = NULL;
+        VkResult local_res = loader_get_json(inst, layer_config->path, &json);
+        if (VK_ERROR_OUT_OF_HOST_MEMORY == local_res) {
+            res = VK_ERROR_OUT_OF_HOST_MEMORY;
+            goto out;
+        } else if (VK_SUCCESS != local_res || NULL == json) {
+            continue;
+        }
+
+        local_res =
+            loader_add_layer_properties(inst, settings_layers, json, layer_config->treat_as_implicit_manifest, layer_config->path);
+        cJSON_Delete(json);
+
+        // If the error is anything other than out of memory we still want to try to load the other layers
+        if (VK_ERROR_OUT_OF_HOST_MEMORY == local_res) {
+            res = VK_ERROR_OUT_OF_HOST_MEMORY;
+            goto out;
+        }
+        struct loader_layer_properties* newly_added_layer = &settings_layers->list[settings_layers->count - 1];
+        newly_added_layer->settings_control_value = layer_config->control;
+        // If the manifest file found has a name that differs from the one in the settings, remove this layer from consideration
+        bool should_remove = false;
+        if (strncmp(newly_added_layer->info.layerName, layer_config->name, VK_MAX_EXTENSION_NAME_SIZE) != 0) {
+            should_remove = true;
+            loader_remove_layer_in_list(inst, settings_layers, settings_layers->count - 1);
+        }
+        // Make sure the layer isn't already in the list
+        for (uint32_t j = 0; settings_layers->count > 0 && j < settings_layers->count - 1; j++) {
+            if (0 ==
+                strncmp(settings_layers->list[j].info.layerName, newly_added_layer->info.layerName, VK_MAX_EXTENSION_NAME_SIZE)) {
+                if (0 == (newly_added_layer->type_flags & VK_LAYER_TYPE_FLAG_META_LAYER) &&
+                    strcmp(settings_layers->list[j].lib_name, newly_added_layer->lib_name) == 0) {
+                    should_remove = true;
+                    break;
+                }
+            }
+        }
+        if (should_remove) {
+            loader_remove_layer_in_list(inst, settings_layers, settings_layers->count - 1);
+        }
+    }
+
+out:
+    release_current_settings_lock(inst);
+    return res;
+}
+
+// Check if layers has an element with the same name.
+// If layer_property is a regular layer, check if the lib_path is the same.
+// If layer_property is a meta layer, just use the layerName
+bool check_if_layer_is_in_list(struct loader_layer_list* layer_list, struct loader_layer_properties* layer_property) {
+    // If the layer is a meta layer, just check against the name
+    for (uint32_t i = 0; i < layer_list->count; i++) {
+        if (0 == strncmp(layer_list->list[i].info.layerName, layer_property->info.layerName, VK_MAX_EXTENSION_NAME_SIZE)) {
+            if (0 == (layer_property->type_flags & VK_LAYER_TYPE_FLAG_META_LAYER) &&
+                strcmp(layer_list->list[i].lib_name, layer_property->lib_name) == 0) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+VkResult combine_settings_layers_with_regular_layers(const struct loader_instance* inst, struct loader_layer_list* settings_layers,
+                                                     struct loader_layer_list* regular_layers,
+                                                     struct loader_layer_list* output_layers) {
+    VkResult res = VK_SUCCESS;
+    bool has_unordered_layer_location = false;
+    uint32_t unordered_layer_location_index = 0;
+    // Location to put layers that aren't known to the settings file
+    // Find it here so we dont have to pass in a loader_settings struct
+    for (uint32_t i = 0; i < settings_layers->count; i++) {
+        if (settings_layers->list[i].settings_control_value == LOADER_SETTINGS_LAYER_UNORDERED_LAYER_LOCATION) {
+            has_unordered_layer_location = true;
+            unordered_layer_location_index = i;
+            break;
+        }
+    }
+
+    if (settings_layers->count == 0 && regular_layers->count == 0) {
+        // No layers to combine
+        goto out;
+    } else if (settings_layers->count == 0) {
+        // No settings layers - just copy regular to output_layers - memset regular layers to prevent double frees
+        *output_layers = *regular_layers;
+        memset(regular_layers, 0, sizeof(struct loader_layer_list));
+        goto out;
+    } else if (regular_layers->count == 0 || !has_unordered_layer_location) {
+        // No regular layers or has_unordered_layer_location is false - just copy settings to output_layers -
+        // memset settings layers to prevent double frees
+        *output_layers = *settings_layers;
+        memset(settings_layers, 0, sizeof(struct loader_layer_list));
+        goto out;
+    }
+
+    res = loader_init_generic_list(inst, (struct loader_generic_list*)output_layers,
+                                   (settings_layers->count + regular_layers->count) * sizeof(struct loader_layer_properties));
+    if (VK_SUCCESS != res) {
+        goto out;
+    }
+
+    // Insert the settings layers into output_layers up to unordered_layer_index
+    for (uint32_t i = 0; i < unordered_layer_location_index; i++) {
+        if (!check_if_layer_is_in_list(output_layers, &settings_layers->list[i])) {
+            res = loader_append_layer_property(inst, output_layers, &settings_layers->list[i]);
+            if (VK_SUCCESS != res) {
+                goto out;
+            }
+        }
+    }
+
+    for (uint32_t i = 0; i < regular_layers->count; i++) {
+        // Check if its already been put in the output_layers list as well as the remaining settings_layers
+        bool regular_layer_is_ordered = check_if_layer_is_in_list(output_layers, &regular_layers->list[i]) ||
+                                        check_if_layer_is_in_list(settings_layers, &regular_layers->list[i]);
+        // If it isn't found, add it
+        if (!regular_layer_is_ordered) {
+            res = loader_append_layer_property(inst, output_layers, &regular_layers->list[i]);
+            if (VK_SUCCESS != res) {
+                goto out;
+            }
+        } else {
+            // layer is already ordered and can be safely freed
+            loader_free_layer_properties(inst, &regular_layers->list[i]);
+        }
+    }
+
+    // Insert the rest of the settings layers into combined_layers from  unordered_layer_index to the end
+    // start at one after the unordered_layer_index
+    for (uint32_t i = unordered_layer_location_index + 1; i < settings_layers->count; i++) {
+        res = loader_append_layer_property(inst, output_layers, &settings_layers->list[i]);
+        if (VK_SUCCESS != res) {
+            goto out;
+        }
+    }
+
+out:
+    if (res != VK_SUCCESS) {
+        loader_delete_layer_list_and_properties(inst, output_layers);
+    }
+
+    return res;
+}
+
+VkResult enable_correct_layers_from_settings(const struct loader_instance* inst, const struct loader_envvar_filter* enable_filter,
+                                             const struct loader_envvar_disable_layers_filter* disable_filter, uint32_t name_count,
+                                             const char* const* names, const struct loader_layer_list* instance_layers,
+                                             struct loader_pointer_layer_list* target_layer_list,
+                                             struct loader_pointer_layer_list* activated_layer_list) {
+    VkResult res = VK_SUCCESS;
+    char* vk_instance_layers_env = loader_getenv(ENABLED_LAYERS_ENV, inst);
+    if (vk_instance_layers_env != NULL) {
+        loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, "env var \'%s\' defined and adding layers \"%s\"",
+                   ENABLED_LAYERS_ENV, names);
+    }
+    for (uint32_t i = 0; i < instance_layers->count; i++) {
+        bool enable_layer = false;
+        struct loader_layer_properties* props = &instance_layers->list[i];
+
+        // Do not enable the layer if the settings have it set as off
+        if (props->settings_control_value == LOADER_SETTINGS_LAYER_CONTROL_OFF) {
+            continue;
+        }
+        // Force enable it based on settings
+        if (props->settings_control_value == LOADER_SETTINGS_LAYER_CONTROL_ON) {
+            enable_layer = true;
+        }
+
+        // Check if disable filter needs to skip the layer
+        if (NULL != disable_filter &&
+            (disable_filter->disable_all || disable_filter->disable_all_implicit ||
+             check_name_matches_filter_environment_var(inst, props->info.layerName, &disable_filter->additional_filters))) {
+            continue;
+        }
+        // Check the enable filter
+        if (!enable_layer && NULL != enable_filter &&
+            check_name_matches_filter_environment_var(inst, props->info.layerName, enable_filter)) {
+            enable_layer = true;
+        }
+
+        if (!enable_layer && vk_instance_layers_env) {
+            char* name = loader_stack_alloc(strlen(vk_instance_layers_env) + 1);
+            if (name != NULL) {
+                strcpy(name, vk_instance_layers_env);
+                // First look for the old-fashion layers forced on with VK_INSTANCE_LAYERS
+                while (name && *name) {
+                    char* next = loader_get_next_path(name);
+
+                    if (strlen(name) > 0) {
+                        if (0 == strcmp(name, props->info.layerName)) {
+                            enable_layer = true;
+                            break;
+                        }
+                        name = next;
+                    }
+                }
+            }
+        }
+
+        // Check if it should be enabled by the application
+        if (!enable_layer) {
+            for (uint32_t j = 0; j < name_count; j++) {
+                if (strcmp(props->info.layerName, names[j]) == 0) {
+                    enable_layer = true;
+                    break;
+                }
+            }
+        }
+
+        // Check if its an implicit layers and thus enabled by default
+        if (!enable_layer && (0 == (props->type_flags & VK_LAYER_TYPE_FLAG_EXPLICIT_LAYER)) &&
+            loader_implicit_layer_is_enabled(inst, enable_filter, disable_filter, props)) {
+            enable_layer = true;
+        }
+
+        if (enable_layer) {
+            // Check if the layer is a meta layer reuse the existing function to add the meta layer
+            if (props->type_flags & VK_LAYER_TYPE_FLAG_META_LAYER) {
+                res = loader_add_meta_layer(inst, enable_filter, disable_filter, props, target_layer_list, activated_layer_list,
+                                            instance_layers, NULL);
+                if (res == VK_ERROR_OUT_OF_HOST_MEMORY) goto out;
+            } else {
+                res = loader_add_layer_properties_to_list(inst, target_layer_list, props);
+                if (res != VK_SUCCESS) {
+                    goto out;
+                }
+                res = loader_add_layer_properties_to_list(inst, activated_layer_list, props);
+                if (res != VK_SUCCESS) {
+                    goto out;
+                }
+            }
+        }
+    }
+out:
+    return res;
+}
diff --git a/loader/settings.h b/loader/settings.h
new file mode 100644 (file)
index 0000000..02cf81b
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ *
+ * Copyright (c) 2023 The Khronos Group Inc.
+ * Copyright (c) 2023 Valve Corporation
+ * Copyright (c) 2023 LunarG, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ * Author: Charles Giessen <charles@lunarg.com>
+ *
+ */
+
+#pragma once
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "log.h"
+
+typedef enum VkResult VkResult;
+struct loader_instance;
+struct loader_layer_list;
+struct loader_pointer_layer_list;
+struct loader_envvar_filter;
+struct loader_envvar_disable_layers_filter;
+typedef struct log_configuration log_configuration;
+
+typedef enum loader_settings_layer_control {
+    LOADER_SETTINGS_LAYER_CONTROL_DEFAULT,          // layer is not enabled by settings file but can be enabled through other means
+    LOADER_SETTINGS_LAYER_CONTROL_ON,               // layer is enabled by settings file
+    LOADER_SETTINGS_LAYER_CONTROL_OFF,              // layer is prevented from being enabled
+    LOADER_SETTINGS_LAYER_UNORDERED_LAYER_LOCATION  // special control indicating unspecified layers should go here. If this is not
+                                                    // in the settings file, then the loader assume no other layers should be
+                                                    // searched & loaded.
+} loader_settings_layer_control;
+
+// If a loader_settings_layer_configuration has a name of loader_settings_unknown_layers_location, then it specifies that the
+// layer configuation it was found in shall be the location all layers not listed in the settings file that are enabled.
+#define LOADER_SETTINGS_UNKNOWN_LAYERS_LOCATION "loader_settings_unknown_layers_location"
+
+#define LOADER_SETTINGS_MAX_NAME_SIZE 256U;
+
+typedef struct loader_settings_layer_configuration {
+    char* name;
+    char* path;
+    loader_settings_layer_control control;
+    bool treat_as_implicit_manifest;  // whether or not the layer should be parsed as if it is implicit
+
+} loader_settings_layer_configuration;
+
+typedef struct loader_settings {
+    bool settings_active;
+    bool has_unordered_layer_location;
+    enum vulkan_loader_debug_flags debug_level;
+
+    uint32_t layer_configuration_count;
+    loader_settings_layer_configuration* layer_configurations;
+
+    char* settings_file_path;
+} loader_settings;
+
+// Call this function to get the current settings that the loader should use.
+// It will open up the current loader settings file and return a loader_settigns in out_loader_settings if it.
+// It should be called on every call to the global functions excluding vkGetInstanceProcAddr
+// Caller is responsible for cleaning up by calling free_loader_settings()
+VkResult get_loader_settings(const struct loader_instance* inst, loader_settings* out_loader_settings);
+
+void free_loader_settings(const struct loader_instance* inst, loader_settings* loader_settings);
+
+// Log the settings to the console
+void log_settings(const struct loader_instance* inst, loader_settings* settings);
+
+// Every global function needs to call this at startup to insure that
+VkResult update_global_loader_settings(void);
+
+// Needs to be called during startup -
+void init_global_loader_settings(void);
+void teardown_global_loader_settings(void);
+
+// Check the global settings and return true if msg_type does not correspond to the active global loader settings
+bool should_skip_logging_global_messages(VkFlags msg_type);
+
+// Query the current settings (either global or per-instance) and return the list of layers contained within.
+// should_search_for_other_layers tells the caller if the settings file should be used exclusively for layer searching or not
+VkResult get_settings_layers(const struct loader_instance* inst, struct loader_layer_list* settings_layers,
+                             bool* should_search_for_other_layers);
+
+// Take the provided list of settings_layers and add in the layers from regular search paths
+// Only adds layers that aren't already present in the settings_layers and in the location of the
+// layer configuration with LOADER_SETTINGS_LAYER_UNORDERED_LAYER_LOCATION set
+VkResult combine_settings_layers_with_regular_layers(const struct loader_instance* inst, struct loader_layer_list* settings_layers,
+                                                     struct loader_layer_list* regular_layers,
+                                                     struct loader_layer_list* output_layers);
+
+// Fill out activated_layer_list with the layers that should be activated, based on environment variables, VkInstanceCreateInfo, and
+// the settings
+VkResult enable_correct_layers_from_settings(const struct loader_instance* inst, const struct loader_envvar_filter* enable_filter,
+                                             const struct loader_envvar_disable_layers_filter* disable_filter, uint32_t name_count,
+                                             const char* const* names, const struct loader_layer_list* instance_layers,
+                                             struct loader_pointer_layer_list* target_layer_list,
+                                             struct loader_pointer_layer_list* activated_layer_list);
index 8761978f70a3f336ff41ab45f636c1b5dc696b30..51cd17f0bfa496c7f40068f57b857dc9f583bfc1 100644 (file)
@@ -32,6 +32,7 @@
 #include "gpa_helper.h"
 #include "loader.h"
 #include "log.h"
+#include "settings.h"
 #include "vk_loader_extensions.h"
 #include "vk_loader_platform.h"
 #include "wsi.h"
@@ -144,6 +145,8 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceExtensionPropert
                                                                                     VkExtensionProperties *pProperties) {
     LOADER_PLATFORM_THREAD_ONCE(&once_init, loader_initialize);
 
+    update_global_loader_settings();
+
     // We know we need to call at least the terminator
     VkResult res = VK_SUCCESS;
     VkEnumerateInstanceExtensionPropertiesChain chain_tail = {
@@ -242,6 +245,8 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceLayerProperties(
                                                                                 VkLayerProperties *pProperties) {
     LOADER_PLATFORM_THREAD_ONCE(&once_init, loader_initialize);
 
+    update_global_loader_settings();
+
     // We know we need to call at least the terminator
     VkResult res = VK_SUCCESS;
     VkEnumerateInstanceLayerPropertiesChain chain_tail = {
@@ -339,6 +344,8 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceLayerProperties(
 LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceVersion(uint32_t *pApiVersion) {
     LOADER_PLATFORM_THREAD_ONCE(&once_init, loader_initialize);
 
+    update_global_loader_settings();
+
     if (NULL == pApiVersion) {
         loader_log(NULL, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_VALIDATION_BIT, 0,
                    "vkEnumerateInstanceVersion: \'pApiVersion\' must not be NULL "
@@ -444,6 +451,7 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance(const VkInstanceCr
     struct loader_instance *ptr_instance = NULL;
     VkInstance created_instance = VK_NULL_HANDLE;
     VkResult res = VK_ERROR_INITIALIZATION_FAILED;
+    VkInstanceCreateInfo ici = *pCreateInfo;
 
     LOADER_PLATFORM_THREAD_ONCE(&once_init, loader_initialize);
 
@@ -461,8 +469,6 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance(const VkInstanceCr
     ptr_instance =
         (struct loader_instance *)loader_calloc(pAllocator, sizeof(struct loader_instance), VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
 
-    VkInstanceCreateInfo ici = *pCreateInfo;
-
     if (ptr_instance == NULL) {
         res = VK_ERROR_OUT_OF_HOST_MEMORY;
         goto out;
@@ -500,6 +506,15 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance(const VkInstanceCr
         goto out;
     }
 
+    VkResult settings_file_res = get_loader_settings(ptr_instance, &ptr_instance->settings);
+    if (settings_file_res == VK_ERROR_OUT_OF_HOST_MEMORY) {
+        res = settings_file_res;
+        goto out;
+    }
+    if (ptr_instance->settings.settings_active) {
+        log_settings(ptr_instance, &ptr_instance->settings);
+    }
+
     // Check the VkInstanceCreateInfoFlags wether to allow the portability enumeration flag
     if ((pCreateInfo->flags & VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR) == 1) {
         // Make sure the extension has been enabled
@@ -632,6 +647,8 @@ out:
             }
             loader_platform_thread_unlock_mutex(&loader_global_instance_list_lock);
 
+            free_loader_settings(ptr_instance, &ptr_instance->settings);
+
             loader_instance_heap_free(ptr_instance, ptr_instance->disp);
             // Remove any created VK_EXT_debug_report or VK_EXT_debug_utils items
             destroy_debug_callbacks_chain(ptr_instance, pAllocator);
@@ -705,6 +722,8 @@ LOADER_EXPORT VKAPI_ATTR void VKAPI_CALL vkDestroyInstance(VkInstance instance,
     disp = loader_get_instance_layer_dispatch(instance);
     disp->DestroyInstance(ptr_instance->instance, pAllocator);
 
+    free_loader_settings(ptr_instance, &ptr_instance->settings);
+
     loader_destroy_pointer_layer_list(ptr_instance, &ptr_instance->expanded_activated_layer_list);
     loader_destroy_pointer_layer_list(ptr_instance, &ptr_instance->app_activated_layer_list);
 
index 2e0c98edd016ad745c85175643e6cd77745fb8ca..50266bd715f090bb985b9ab61b2c9b3139398040 100644 (file)
 // Override layer information
 #define VK_OVERRIDE_LAYER_NAME "VK_LAYER_LUNARG_override"
 
+// Loader Settings filename
+#define VK_LOADER_SETTINGS_FILENAME "vk_loader_settings.json"
+
 #define LAYERS_PATH_ENV "VK_LAYER_PATH"
 #define ENABLED_LAYERS_ENV "VK_INSTANCE_LAYERS"
 
index 9f899f07a1c464c6dbfbaf2e2a400efb9ddbfff5..1e1783263dfd7c1fda48a955c648e66cac26b67f 100644 (file)
@@ -32,6 +32,7 @@ add_executable(
         loader_layer_tests.cpp
         loader_regression_tests.cpp
         loader_phys_dev_inst_ext_tests.cpp
+        loader_settings_tests.cpp
         loader_version_tests.cpp
         loader_unknown_ext_tests.cpp
         loader_wsi_tests.cpp)
index b58de03b64d1cdb72c4e8b1230ed8b318e2590a8..e4e66d260cdb9a94b5bc25c8608405b61729a704 100644 (file)
@@ -45,7 +45,7 @@
 #include <adapters.h>
 #endif
 
-enum class ManifestCategory { implicit_layer, explicit_layer, icd };
+enum class ManifestCategory { implicit_layer, explicit_layer, icd, settings };
 enum class GpuType { unspecified, integrated, discrete, external };
 
 #if defined(WIN32)
@@ -147,7 +147,8 @@ struct PlatformShim {
     // need to be ordered correctly
     void add_known_path(fs::path const& path);
 
-    void add_manifest(ManifestCategory category, fs::path const& path, bool use_local_machine = true);
+    void add_manifest(ManifestCategory category, fs::path const& path);
+    void add_unsecured_manifest(ManifestCategory category, fs::path const& path);
 
 // platform specific shim interface
 #if defined(WIN32)
@@ -179,6 +180,8 @@ struct PlatformShim {
     std::vector<RegistryEntry> hkey_local_machine_explicit_layers;
     std::vector<RegistryEntry> hkey_local_machine_implicit_layers;
     std::vector<RegistryEntry> hkey_local_machine_drivers;
+    std::vector<RegistryEntry> hkey_local_machine_settings;
+    std::vector<RegistryEntry> hkey_current_user_settings;
 
     std::wstring app_package_path;
 
@@ -199,6 +202,8 @@ struct PlatformShim {
     void redirect_dlopen_name(fs::path const& filename, fs::path const& actual_path);
     bool is_dlopen_redirect_name(fs::path const& filename);
 
+    fs::path query_default_redirect_path(ManifestCategory category);
+
     std::unordered_map<std::string, fs::path> redirection_map;
     std::unordered_map<std::string, fs::path> dlopen_redirection_map;
     std::unordered_set<std::string> known_path_set;
index 15a83c879424245a5b03dabbc9449b3c45d901b7..448f02cf75fcfc05d2ba33a5332a673d7a176119 100644 (file)
@@ -101,26 +101,35 @@ void PlatformShim::reset() {
     hkey_local_machine_explicit_layers.clear();
     hkey_local_machine_implicit_layers.clear();
     hkey_local_machine_drivers.clear();
+    hkey_local_machine_settings.clear();
+    hkey_current_user_settings.clear();
 }
 
 void PlatformShim::set_fake_path(ManifestCategory category, fs::path const& path) {}
 void PlatformShim::add_known_path(fs::path const& path) {}
 
-void PlatformShim::add_manifest(ManifestCategory category, fs::path const& path, bool use_local_machine) {
-    if (category == ManifestCategory::implicit_layer) {
-        if (use_local_machine)
-            hkey_local_machine_implicit_layers.emplace_back(path.str());
-        else
-            hkey_current_user_implicit_layers.emplace_back(path.str());
+void PlatformShim::add_manifest(ManifestCategory category, fs::path const& path) {
+    if (category == ManifestCategory::settings) {
+        hkey_local_machine_settings.emplace_back(path.str());
+    } else if (category == ManifestCategory::implicit_layer) {
+        hkey_local_machine_implicit_layers.emplace_back(path.str());
     } else if (category == ManifestCategory::explicit_layer) {
-        if (use_local_machine)
-            hkey_local_machine_explicit_layers.emplace_back(path.str());
-        else
-            hkey_current_user_explicit_layers.emplace_back(path.str());
+        hkey_local_machine_explicit_layers.emplace_back(path.str());
     } else {
         hkey_local_machine_drivers.emplace_back(path.str());
     }
 }
+
+void PlatformShim::add_unsecured_manifest(ManifestCategory category, fs::path const& path) {
+    if (category == ManifestCategory::settings) {
+        hkey_current_user_settings.emplace_back(path.str());
+    } else if (category == ManifestCategory::implicit_layer) {
+        hkey_current_user_implicit_layers.emplace_back(path.str());
+    } else if (category == ManifestCategory::explicit_layer) {
+        hkey_current_user_explicit_layers.emplace_back(path.str());
+    }
+}
+
 void PlatformShim::add_dxgi_adapter(GpuType gpu_preference, DXGI_ADAPTER_DESC1 desc1) {
     dxgi_adapters.push_back(DXGIAdapter(gpu_preference, desc1, next_adapter_handle++));
 }
@@ -160,6 +169,7 @@ void PlatformShim::redirect_category(fs::path const& new_path, ManifestCategory
 #include <unistd.h>
 
 std::string category_path_name(ManifestCategory category) {
+    if (category == ManifestCategory::settings) return "settings.d";
     if (category == ManifestCategory::implicit_layer) return "implicit_layer.d";
     if (category == ManifestCategory::explicit_layer)
         return "explicit_layer.d";
@@ -178,7 +188,8 @@ bool PlatformShim::is_known_path(fs::path const& path) { return known_path_set.c
 void PlatformShim::add_known_path(fs::path const& path) { known_path_set.insert(path.str()); }
 void PlatformShim::remove_known_path(fs::path const& path) { known_path_set.erase(path.str()); }
 
-void PlatformShim::add_manifest(ManifestCategory category, fs::path const& path, bool use_local_machine) {}
+void PlatformShim::add_manifest(ManifestCategory category, fs::path const& path) {}
+void PlatformShim::add_unsecured_manifest(ManifestCategory category, fs::path const& path) {}
 
 void parse_and_add_env_var_override(std::vector<std::string>& paths, std::string env_var_contents) {
     auto parsed_paths = parse_env_var_list(env_var_contents);
@@ -188,6 +199,11 @@ void parse_and_add_env_var_override(std::vector<std::string>& paths, std::string
 void PlatformShim::redirect_category(fs::path const& new_path, ManifestCategory category) {
     std::vector<std::string> paths;
     auto home = fs::path(get_env_var("HOME"));
+    if (category == ManifestCategory::settings) {
+        redirect_path(home / ".local/share/vulkan" / category_path_name(category), new_path);
+        return;
+    }
+
     if (home.size() != 0) {
         paths.push_back((home / ".config").str());
         paths.push_back((home / ".local/share").str());
@@ -235,4 +251,7 @@ void PlatformShim::redirect_dlopen_name(fs::path const& filename, fs::path const
 
 bool PlatformShim::is_dlopen_redirect_name(fs::path const& filename) { return dlopen_redirection_map.count(filename.str()) == 1; }
 
+fs::path PlatformShim::query_default_redirect_path(ManifestCategory category) {
+    return fs::path(SYSCONFDIR) / "vulkan" / category_path_name(category);
+}
 #endif
index dc975900300b5d8a73515a9e1d28a7502ad18477..b5ce711260bc8dfa6361874bef09330d32bab751 100644 (file)
@@ -341,6 +341,8 @@ std::vector<RegistryEntry> *get_registry_vector(std::string const &path) {
         return &platform_shim.hkey_current_user_explicit_layers;
     if (path == "HKEY_CURRENT_USER\\SOFTWARE\\Khronos\\Vulkan\\ImplicitLayers")
         return &platform_shim.hkey_current_user_implicit_layers;
+    if (path == "HKEY_LOCAL_MACHINE\\SOFTWARE\\Khronos\\Vulkan\\Settings") return &platform_shim.hkey_local_machine_settings;
+    if (path == "HKEY_CURRENT_USER\\SOFTWARE\\Khronos\\Vulkan\\Settings") return &platform_shim.hkey_current_user_settings;
     return nullptr;
 }
 LSTATUS __stdcall ShimRegQueryValueExA(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData,
index 396579622e156b0ddf8feba50fc7734e3b1302e9..d423bca76a61f70e6a11d0cddf34c018dedc00f9 100644 (file)
@@ -347,6 +347,7 @@ TestICD& TestICDHandle::reset_icd() noexcept {
 }
 fs::path TestICDHandle::get_icd_full_path() noexcept { return icd_library.lib_path; }
 fs::path TestICDHandle::get_icd_manifest_path() noexcept { return manifest_path; }
+fs::path TestICDHandle::get_shimmed_manifest_path() noexcept { return shimmed_manifest_path; }
 
 TestLayerHandle::TestLayerHandle() noexcept {}
 TestLayerHandle::TestLayerHandle(fs::path const& layer_path) noexcept : layer_library(layer_path) {
@@ -363,10 +364,11 @@ TestLayer& TestLayerHandle::reset_layer() noexcept {
 }
 fs::path TestLayerHandle::get_layer_full_path() noexcept { return layer_library.lib_path; }
 fs::path TestLayerHandle::get_layer_manifest_path() noexcept { return manifest_path; }
+fs::path TestLayerHandle::get_shimmed_manifest_path() noexcept { return shimmed_manifest_path; }
 
 FrameworkEnvironment::FrameworkEnvironment() noexcept : FrameworkEnvironment(FrameworkSettings{}) {}
 FrameworkEnvironment::FrameworkEnvironment(FrameworkSettings const& settings) noexcept
-    : platform_shim(&folders, settings.log_filter) {
+    : settings(settings), platform_shim(&folders, settings.log_filter) {
     // 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"));
@@ -379,6 +381,7 @@ FrameworkEnvironment::FrameworkEnvironment(FrameworkSettings const& settings) no
     folders.emplace_back(FRAMEWORK_BUILD_DIRECTORY, std::string("app_package_manifests"));
     folders.emplace_back(FRAMEWORK_BUILD_DIRECTORY, std::string("macos_bundle"));
     folders.emplace_back(FRAMEWORK_BUILD_DIRECTORY, std::string("unsecured_location"));
+    folders.emplace_back(FRAMEWORK_BUILD_DIRECTORY, std::string("settings_location"));
 
     platform_shim->redirect_all_paths(get_folder(ManifestLocation::null).location());
     if (settings.enable_default_search_paths) {
@@ -393,6 +396,15 @@ FrameworkEnvironment::FrameworkEnvironment(FrameworkSettings const& settings) no
         platform_shim->redirect_path(home + "/.local/share/vulkan/explicit_layer.d", unsecured_location);
 #endif
     }
+#if COMMON_UNIX_PLATFORMS
+    if (settings.secure_loader_settings) {
+        platform_shim->redirect_path("/etc/vulkan/settings.d", get_folder(ManifestLocation::settings_location).location());
+    } else {
+        platform_shim->redirect_path(get_env_var("HOME") + "/.local/share/vulkan/settings.d",
+                                     get_folder(ManifestLocation::settings_location).location());
+    }
+#endif
+
 #if defined(__APPLE__)
     // Necessary since bundles look in sub folders for manifests, not the test framework folder itself
     auto bundle_location = get_folder(ManifestLocation::macos_bundle).location();
@@ -400,6 +412,10 @@ FrameworkEnvironment::FrameworkEnvironment(FrameworkSettings const& settings) no
     platform_shim->redirect_path(bundle_location / "vulkan/explicit_layer.d", bundle_location);
     platform_shim->redirect_path(bundle_location / "vulkan/implicit_layer.d", bundle_location);
 #endif
+    // only set the settings file if there are elements in the app_specific_settings vector
+    if (!settings.loader_settings.app_specific_settings.empty()) {
+        update_loader_settings(settings.loader_settings);
+    }
 }
 
 FrameworkEnvironment::~FrameworkEnvironment() {
@@ -459,10 +475,15 @@ TestICDHandle& FrameworkEnvironment::add_icd(TestICDDetails icd_details) noexcep
         }
         full_json_name += ".json";
         icds.back().manifest_path = folder->write_manifest(full_json_name, icd_details.icd_manifest.get_manifest_str());
+        icds.back().shimmed_manifest_path = icds.back().manifest_path;
         switch (icd_details.discovery_type) {
             default:
             case (ManifestDiscoveryType::generic):
                 platform_shim->add_manifest(ManifestCategory::icd, icds.back().manifest_path);
+#if COMMON_UNIX_PLATFORMS
+                icds.back().shimmed_manifest_path =
+                    platform_shim->query_default_redirect_path(ManifestCategory::icd) / full_json_name;
+#endif
                 break;
             case (ManifestDiscoveryType::env_var):
                 env_var_vk_icd_filenames.add_to_list((folder->location() / full_json_name).str());
@@ -476,7 +497,7 @@ TestICDHandle& FrameworkEnvironment::add_icd(TestICDDetails icd_details) noexcep
                 platform_shim->add_manifest(ManifestCategory::icd, icds.back().manifest_path);
                 break;
             case (ManifestDiscoveryType::unsecured_generic):
-                platform_shim->add_manifest(ManifestCategory::icd, icds.back().manifest_path, false);
+                platform_shim->add_unsecured_manifest(ManifestCategory::icd, icds.back().manifest_path);
                 break;
             case (ManifestDiscoveryType::null_dir):
                 break;
@@ -581,34 +602,132 @@ void FrameworkEnvironment::add_layer_impl(TestLayerDetails layer_details, Manife
     }
     if (layer_details.discovery_type != ManifestDiscoveryType::none) {
         // Write a manifest file to a folder as long as the discovery type isn't none
-        auto layer_loc = folder.write_manifest(layer_details.json_name, layer_details.layer_manifest.get_manifest_str());
+        auto layer_manifest_loc = folder.write_manifest(layer_details.json_name, layer_details.layer_manifest.get_manifest_str());
         // only add the manifest to the registry if its a generic location (as if it was installed) - both system and user local
         if (layer_details.discovery_type == ManifestDiscoveryType::generic) {
-            platform_shim->add_manifest(category, layer_loc);
+            platform_shim->add_manifest(category, layer_manifest_loc);
         }
         if (layer_details.discovery_type == ManifestDiscoveryType::unsecured_generic) {
-            platform_shim->add_manifest(category, layer_loc, false);
+            platform_shim->add_unsecured_manifest(category, layer_manifest_loc);
         }
         for (size_t i = new_layers_start; i < layers.size(); i++) {
-            layers.at(i).manifest_path = layer_loc;
+            layers.at(i).manifest_path = layer_manifest_loc;
+            layers.at(i).shimmed_manifest_path = layer_manifest_loc;
+#if COMMON_UNIX_PLATFORMS
+            if (layer_details.discovery_type == ManifestDiscoveryType::generic) {
+                layers.at(i).shimmed_manifest_path = platform_shim->query_default_redirect_path(category) / layer_details.json_name;
+            }
+#endif
         }
     }
 }
 
+std::string get_loader_settings_file_contents(const LoaderSettings& loader_settings) noexcept {
+    JsonWriter writer;
+    writer.StartObject();
+    writer.AddKeyedString("file_format_version", loader_settings.file_format_version.get_version_str());
+    bool one_setting_file = true;
+    if (loader_settings.app_specific_settings.size() > 1) {
+        writer.StartKeyedArray("settings_array");
+        one_setting_file = false;
+    }
+    for (const auto& setting : loader_settings.app_specific_settings) {
+        if (one_setting_file) {
+            writer.StartKeyedObject("settings");
+        } else {
+            writer.StartObject();
+        }
+        if (!setting.app_keys.empty()) {
+            writer.StartKeyedArray("app_keys");
+            for (const auto& app_key : setting.app_keys) {
+                writer.AddString(app_key);
+            }
+            writer.EndArray();
+        }
+        if (!setting.layer_configurations.empty()) {
+            writer.StartKeyedArray("layers");
+            for (const auto& config : setting.layer_configurations) {
+                writer.StartObject();
+                writer.AddKeyedString("name", config.name);
+                writer.AddKeyedString("path", fs::fixup_backslashes_in_path(config.path));
+                writer.AddKeyedString("control", config.control);
+                writer.AddKeyedBool("treat_as_implicit_manifest", config.treat_as_implicit_manifest);
+                writer.EndObject();
+            }
+            writer.EndArray();
+        }
+        if (!setting.stderr_log.empty()) {
+            writer.StartKeyedArray("stderr_log");
+            for (const auto& filter : setting.stderr_log) {
+                writer.AddString(filter);
+            }
+            writer.EndArray();
+        }
+        if (!setting.log_configurations.empty()) {
+            writer.StartKeyedArray("log_locations");
+            for (const auto& config : setting.log_configurations) {
+                writer.StartObject();
+                writer.StartKeyedArray("destinations");
+                for (const auto& dest : config.destinations) {
+                    writer.AddString(dest);
+                }
+                writer.EndArray();
+                writer.StartKeyedArray("filter");
+                for (const auto& filter : config.filters) {
+                    writer.AddString(filter);
+                }
+                writer.EndArray();
+                writer.EndObject();
+            }
+            writer.EndArray();
+        }
+        writer.EndObject();
+    }
+    if (!one_setting_file) {
+        writer.EndArray();
+    }
+
+    writer.EndObject();
+    return writer.output;
+}
+void FrameworkEnvironment::write_settings_file(std::string const& file_contents) {
+    auto out_path = get_folder(ManifestLocation::settings_location).write_manifest("vk_loader_settings.json", file_contents);
+#if defined(WIN32)
+    platform_shim->hkey_current_user_settings.clear();
+    platform_shim->hkey_local_machine_settings.clear();
+#endif
+    if (settings.secure_loader_settings)
+        platform_shim->add_manifest(ManifestCategory::settings, out_path);
+    else
+        platform_shim->add_unsecured_manifest(ManifestCategory::settings, out_path);
+}
+void FrameworkEnvironment::update_loader_settings(const LoaderSettings& settings) noexcept {
+    write_settings_file(get_loader_settings_file_contents(settings));
+}
+
 TestICD& FrameworkEnvironment::get_test_icd(size_t index) noexcept { return icds[index].get_test_icd(); }
 TestICD& FrameworkEnvironment::reset_icd(size_t index) noexcept { return icds[index].reset_icd(); }
 fs::path FrameworkEnvironment::get_test_icd_path(size_t index) noexcept { return icds[index].get_icd_full_path(); }
 fs::path FrameworkEnvironment::get_icd_manifest_path(size_t index) noexcept { return icds[index].get_icd_manifest_path(); }
+fs::path FrameworkEnvironment::get_shimmed_icd_manifest_path(size_t index) noexcept {
+    return icds[index].get_shimmed_manifest_path();
+}
 
 TestLayer& FrameworkEnvironment::get_test_layer(size_t index) noexcept { return layers[index].get_test_layer(); }
 TestLayer& FrameworkEnvironment::reset_layer(size_t index) noexcept { return layers[index].reset_layer(); }
 fs::path FrameworkEnvironment::get_test_layer_path(size_t index) noexcept { return layers[index].get_layer_full_path(); }
 fs::path FrameworkEnvironment::get_layer_manifest_path(size_t index) noexcept { return layers[index].get_layer_manifest_path(); }
+fs::path FrameworkEnvironment::get_shimmed_layer_manifest_path(size_t index) noexcept {
+    return layers[index].get_shimmed_manifest_path();
+}
 
 fs::FolderManager& FrameworkEnvironment::get_folder(ManifestLocation location) noexcept {
     // index it directly using the enum location since they will always be in that order
     return folders.at(static_cast<size_t>(location));
 }
+fs::FolderManager const& FrameworkEnvironment::get_folder(ManifestLocation location) const noexcept {
+    return folders.at(static_cast<size_t>(location));
+}
 #if defined(__APPLE__)
 void FrameworkEnvironment::setup_macos_bundle() noexcept {
     platform_shim->bundle_contents = get_folder(ManifestLocation::macos_bundle).location().str();
index ed679455782425381a32bd3dd34d5698ea6715b1..a0724868f7a756608590648d824b823b2c161d1f 100644 (file)
@@ -412,6 +412,41 @@ VkResult CreateDebugUtilsMessenger(DebugUtilsWrapper& debug_utils);
 void FillDebugUtilsCreateDetails(InstanceCreateInfo& create_info, DebugUtilsLogger& logger);
 void FillDebugUtilsCreateDetails(InstanceCreateInfo& create_info, DebugUtilsWrapper& wrapper);
 
+struct LoaderSettingsLayerConfiguration {
+    BUILDER_VALUE(LoaderSettingsLayerConfiguration, std::string, name, {})
+    BUILDER_VALUE(LoaderSettingsLayerConfiguration, std::string, path, {})
+    BUILDER_VALUE(LoaderSettingsLayerConfiguration, std::string, control, {})
+    BUILDER_VALUE(LoaderSettingsLayerConfiguration, bool, treat_as_implicit_manifest, false)
+};
+inline bool operator==(LoaderSettingsLayerConfiguration const& a, LoaderSettingsLayerConfiguration const& b) {
+    return a.name == b.name && a.path == b.path && a.control == b.control &&
+           a.treat_as_implicit_manifest == b.treat_as_implicit_manifest;
+}
+inline bool operator!=(LoaderSettingsLayerConfiguration const& a, LoaderSettingsLayerConfiguration const& b) { return !(a == b); }
+inline bool operator<(LoaderSettingsLayerConfiguration const& a, LoaderSettingsLayerConfiguration const& b) {
+    return a.name < b.name;
+}
+inline bool operator>(LoaderSettingsLayerConfiguration const& a, LoaderSettingsLayerConfiguration const& b) { return (b < a); }
+inline bool operator<=(LoaderSettingsLayerConfiguration const& a, LoaderSettingsLayerConfiguration const& b) { return !(b < a); }
+inline bool operator>=(LoaderSettingsLayerConfiguration const& a, LoaderSettingsLayerConfiguration const& b) { return !(a < b); }
+
+// Log files and their associated filter
+struct LoaderLogConfiguration {
+    BUILDER_VECTOR(LoaderLogConfiguration, std::string, destinations, destination)
+    BUILDER_VECTOR(LoaderLogConfiguration, std::string, filters, filter)
+};
+struct AppSpecificSettings {
+    BUILDER_VECTOR(AppSpecificSettings, std::string, app_keys, app_key)
+    BUILDER_VECTOR(AppSpecificSettings, LoaderSettingsLayerConfiguration, layer_configurations, layer_configuration)
+    BUILDER_VECTOR(AppSpecificSettings, std::string, stderr_log, stderr_log_filter)
+    BUILDER_VECTOR(AppSpecificSettings, LoaderLogConfiguration, log_configurations, log_configuration)
+};
+
+struct LoaderSettings {
+    BUILDER_VALUE(LoaderSettings, ManifestVersion, file_format_version, ManifestVersion())
+    BUILDER_VECTOR(LoaderSettings, AppSpecificSettings, app_specific_settings, app_specific_setting);
+};
+
 struct FrameworkEnvironment;  // forward declaration
 
 struct PlatformShimWrapper {
@@ -435,12 +470,14 @@ struct TestICDHandle {
     TestICD& get_test_icd() noexcept;
     fs::path get_icd_full_path() noexcept;
     fs::path get_icd_manifest_path() noexcept;
+    fs::path get_shimmed_manifest_path() noexcept;
 
     // Must use statically
     LibraryWrapper icd_library;
     GetTestICDFunc proc_addr_get_test_icd = nullptr;
     GetNewTestICDFunc proc_addr_reset_icd = nullptr;
-    fs::path manifest_path;
+    fs::path manifest_path;  // path to the manifest file is on the actual filesystem (aka <build_folder>/tests/framework/<...>)
+    fs::path shimmed_manifest_path;  // path to where the loader will find the manifest file (eg /usr/local/share/vulkan/<...>)
 };
 struct TestLayerHandle {
     TestLayerHandle() noexcept;
@@ -449,12 +486,14 @@ struct TestLayerHandle {
     TestLayer& get_test_layer() noexcept;
     fs::path get_layer_full_path() noexcept;
     fs::path get_layer_manifest_path() noexcept;
+    fs::path get_shimmed_manifest_path() noexcept;
 
     // Must use statically
     LibraryWrapper layer_library;
     GetTestLayerFunc proc_addr_get_test_layer = nullptr;
     GetNewTestLayerFunc proc_addr_reset_layer = nullptr;
-    fs::path manifest_path;
+    fs::path manifest_path;  // path to the manifest file is on the actual filesystem (aka <build_folder>/tests/framework/<...>)
+    fs::path shimmed_manifest_path;  // path to where the loader will find the manifest file (eg /usr/local/share/vulkan/<...>)
 };
 
 // Controls whether to create a manifest and where to put it
@@ -512,11 +551,14 @@ enum class ManifestLocation {
     windows_app_package = 8,
     macos_bundle = 9,
     unsecured_location = 10,
+    settings_location = 11,
 };
 
 struct FrameworkSettings {
     BUILDER_VALUE(FrameworkSettings, const char*, log_filter, "all");
     BUILDER_VALUE(FrameworkSettings, bool, enable_default_search_paths, true);
+    BUILDER_VALUE(FrameworkSettings, LoaderSettings, loader_settings, {});
+    BUILDER_VALUE(FrameworkSettings, bool, secure_loader_settings, false);
 };
 
 struct FrameworkEnvironment {
@@ -535,21 +577,32 @@ struct FrameworkEnvironment {
     void add_fake_implicit_layer(ManifestLayer layer_manifest, const std::string& json_name) noexcept;
     void add_fake_explicit_layer(ManifestLayer layer_manifest, const std::string& json_name) noexcept;
 
+    // resets the current settings with the values contained in loader_settings
+    void write_settings_file(std::string const& file_contents);
+    // apply any changes made to FrameworkEnvironment's loader_settings member
+    void update_loader_settings(const LoaderSettings& loader_settings) noexcept;
+
     TestICD& get_test_icd(size_t index = 0) noexcept;
     TestICD& reset_icd(size_t index = 0) noexcept;
     fs::path get_test_icd_path(size_t index = 0) noexcept;
     fs::path get_icd_manifest_path(size_t index = 0) noexcept;
+    fs::path get_shimmed_icd_manifest_path(size_t index = 0) noexcept;
 
     TestLayer& get_test_layer(size_t index = 0) noexcept;
     TestLayer& reset_layer(size_t index = 0) noexcept;
     fs::path get_test_layer_path(size_t index = 0) noexcept;
     fs::path get_layer_manifest_path(size_t index = 0) noexcept;
+    fs::path get_shimmed_layer_manifest_path(size_t index = 0) noexcept;
 
     fs::FolderManager& get_folder(ManifestLocation location) noexcept;
+    fs::FolderManager const& get_folder(ManifestLocation location) const noexcept;
 #if defined(__APPLE__)
     // Set the path of the app bundle to the appropriate test framework bundle
     void setup_macos_bundle() noexcept;
 #endif
+
+    FrameworkSettings settings;
+
     // Query the global extensions
     // Optional: use layer_name to query the extensions of a specific layer
     std::vector<VkExtensionProperties> GetInstanceExtensions(uint32_t count, const char* layer_name = nullptr);
@@ -570,6 +623,7 @@ struct FrameworkEnvironment {
     EnvVarWrapper env_var_vk_layer_paths{"VK_LAYER_PATH"};
     EnvVarWrapper add_env_var_vk_layer_paths{"VK_ADD_LAYER_PATH"};
 
+    LoaderSettings loader_settings;  // the current settings written to disk
    private:
     void add_layer_impl(TestLayerDetails layer_details, ManifestCategory category);
 };
index dee141933c408ef7a9ce3c0f22c501a62c023cb3..3f64068f159b49e783c9fa2f9f8b92056c76bab0 100644 (file)
@@ -421,6 +421,141 @@ TEST(Allocation, CreateInstanceIntentionalAllocFail) {
     }
 }
 
+// Test failure during vkCreateInstance & surface creation to make sure we don't leak memory if
+// one of the out-of-memory conditions trigger.
+TEST(Allocation, CreateSurfaceIntentionalAllocFail) {
+    FrameworkEnvironment env{FrameworkSettings{}.set_log_filter("error,warn")};
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    setup_WSI_in_ICD(env.get_test_icd());
+
+    const char* layer_name = "VkLayerImplicit0";
+    env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
+                                                         .set_name(layer_name)
+                                                         .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
+                                                         .set_disable_environment("DISABLE_ENV")),
+                           "test_layer.json");
+    env.get_test_layer().set_do_spurious_allocations_in_create_instance(true).set_do_spurious_allocations_in_create_device(true);
+
+    size_t fail_index = 0;
+    VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY;
+    while (result == VK_ERROR_OUT_OF_HOST_MEMORY && fail_index <= 10000) {
+        MemoryTracker tracker(MemoryTrackerSettings{false, 0, true, fail_index});
+
+        VkInstance instance;
+        InstanceCreateInfo inst_create_info{};
+        setup_WSI_in_create_instance(inst_create_info);
+        result = env.vulkan_functions.vkCreateInstance(inst_create_info.get(), tracker.get(), &instance);
+        if (result == VK_ERROR_OUT_OF_HOST_MEMORY) {
+            ASSERT_TRUE(tracker.empty());
+            fail_index++;
+            continue;
+        }
+
+        VkSurfaceKHR surface{};
+        result = create_surface(&env.vulkan_functions, instance, surface);
+        if (result == VK_ERROR_OUT_OF_HOST_MEMORY) {
+            env.vulkan_functions.vkDestroyInstance(instance, tracker.get());
+            ASSERT_TRUE(tracker.empty());
+            fail_index++;
+            continue;
+        }
+        env.vulkan_functions.vkDestroySurfaceKHR(instance, surface, tracker.get());
+
+        env.vulkan_functions.vkDestroyInstance(instance, tracker.get());
+        ASSERT_TRUE(tracker.empty());
+        fail_index++;
+    }
+}
+
+// Test failure during vkCreateInstance to make sure we don't leak memory if
+// one of the out-of-memory conditions trigger.
+TEST(Allocation, CreateInstanceIntentionalAllocFailWithSettingsFilePresent) {
+    FrameworkEnvironment env{FrameworkSettings{}.set_log_filter("error,warn")};
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+
+    const char* layer_name = "VkLayerImplicit0";
+    env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
+                                                         .set_name(layer_name)
+                                                         .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
+                                                         .set_disable_environment("DISABLE_ENV")),
+                           "test_layer.json");
+    env.get_test_layer().set_do_spurious_allocations_in_create_instance(true).set_do_spurious_allocations_in_create_device(true);
+
+    env.update_loader_settings(
+        env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(
+            LoaderSettingsLayerConfiguration{}
+                .set_name(layer_name)
+                .set_control("auto")
+                .set_path(env.get_shimmed_layer_manifest_path(0).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) {
+        MemoryTracker tracker(MemoryTrackerSettings{false, 0, true, fail_index});
+
+        VkInstance instance;
+        InstanceCreateInfo inst_create_info{};
+        result = env.vulkan_functions.vkCreateInstance(inst_create_info.get(), tracker.get(), &instance);
+        if (result == VK_SUCCESS) {
+            env.vulkan_functions.vkDestroyInstance(instance, tracker.get());
+        }
+        ASSERT_TRUE(tracker.empty());
+        fail_index++;
+    }
+}
+
+// Test failure during vkCreateInstance & surface creation to make sure we don't leak memory if
+// one of the out-of-memory conditions trigger.
+TEST(Allocation, CreateSurfaceIntentionalAllocFailWithSettingsFilePresent) {
+    FrameworkEnvironment env{FrameworkSettings{}.set_log_filter("error,warn")};
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    setup_WSI_in_ICD(env.get_test_icd());
+
+    const char* layer_name = "VkLayerImplicit0";
+    env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
+                                                         .set_name(layer_name)
+                                                         .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
+                                                         .set_disable_environment("DISABLE_ENV")),
+                           "test_layer.json");
+    env.get_test_layer().set_do_spurious_allocations_in_create_instance(true).set_do_spurious_allocations_in_create_device(true);
+    env.update_loader_settings(
+        env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(
+            LoaderSettingsLayerConfiguration{}
+                .set_name(layer_name)
+                .set_control("auto")
+                .set_path(env.get_shimmed_layer_manifest_path(0).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) {
+        MemoryTracker tracker(MemoryTrackerSettings{false, 0, true, fail_index});
+
+        VkInstance instance;
+        InstanceCreateInfo inst_create_info{};
+        setup_WSI_in_create_instance(inst_create_info);
+        result = env.vulkan_functions.vkCreateInstance(inst_create_info.get(), tracker.get(), &instance);
+        if (result == VK_ERROR_OUT_OF_HOST_MEMORY) {
+            ASSERT_TRUE(tracker.empty());
+            fail_index++;
+            continue;
+        }
+
+        VkSurfaceKHR surface{};
+        result = create_surface(&env.vulkan_functions, instance, surface);
+        if (result == VK_ERROR_OUT_OF_HOST_MEMORY) {
+            env.vulkan_functions.vkDestroyInstance(instance, tracker.get());
+            ASSERT_TRUE(tracker.empty());
+            fail_index++;
+            continue;
+        }
+        env.vulkan_functions.vkDestroySurfaceKHR(instance, surface, tracker.get());
+
+        env.vulkan_functions.vkDestroyInstance(instance, tracker.get());
+        ASSERT_TRUE(tracker.empty());
+        fail_index++;
+    }
+}
+
 // Test failure during vkCreateInstance to make sure we don't leak memory if
 // one of the out-of-memory conditions trigger.
 TEST(Allocation, DriverEnvVarIntentionalAllocFail) {
diff --git a/tests/loader_settings_tests.cpp b/tests/loader_settings_tests.cpp
new file mode 100644 (file)
index 0000000..6e56530
--- /dev/null
@@ -0,0 +1,1723 @@
+/*
+ * Copyright (c) 2023 The Khronos Group Inc.
+ * Copyright (c) 2023 Valve Corporation
+ * Copyright (c) 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
+ * deal in the Materials without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Materials, and to permit persons to whom the Materials are
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice(s) and this permission notice shall be included in
+ * all copies or substantial portions of the Materials.
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ *
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE
+ * USE OR OTHER DEALINGS IN THE MATERIALS.
+ *
+ * Author: Charles Giessen <charles@lunarg.com>
+ */
+
+#include "test_environment.h"
+
+std::string get_settings_location_log_message(FrameworkEnvironment const& env, bool use_secure = false) {
+    std::string s = "Using layer configurations found in loader settings from ";
+#if defined(WIN32)
+    return s + env.get_folder(ManifestLocation::settings_location).location().str() + "\\vk_loader_settings.json";
+#elif COMMON_UNIX_PLATFORMS
+    if (use_secure)
+        return s + "/etc/vulkan/settings.d/vk_loader_settings.json";
+    else
+        return s + "/home/fake_home/.local/share/vulkan/settings.d/vk_loader_settings.json";
+#endif
+}
+
+// Make sure settings layer is found and that a layer defined in it is loaded
+TEST(SettingsFile, FileExist) {
+    FrameworkEnvironment env{};
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    env.get_test_icd().add_physical_device({});
+    const char* regular_layer_name = "VK_LAYER_TestLayer_0";
+    env.add_explicit_layer(TestLayerDetails{
+        ManifestLayer{}.add_layer(
+            ManifestLayer::LayerDescription{}.set_name(regular_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+        "regular_test_layer.json"}
+                               .set_discovery_type(ManifestDiscoveryType::override_folder));
+    env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
+        AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(
+            LoaderSettingsLayerConfiguration{}
+                .set_name(regular_layer_name)
+                .set_path(env.get_shimmed_layer_manifest_path().str())
+                .set_control("on"))));
+    {
+        auto layer_props = env.GetLayerProperties(1);
+        EXPECT_TRUE(string_eq(layer_props.at(0).layerName, regular_layer_name));
+
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+
+        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
+        auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1);
+        EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, regular_layer_name));
+    }
+}
+
+// Make sure that if the settings file is in a user local path, that it isn't used when running with elevated privileges
+TEST(SettingsFile, SettingsInUnsecuredLocation) {
+    FrameworkEnvironment env{};
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    env.get_test_icd().add_physical_device({});
+    const char* regular_layer_name = "VK_LAYER_TestLayer_0";
+    env.add_explicit_layer(TestLayerDetails{
+        ManifestLayer{}.add_layer(
+            ManifestLayer::LayerDescription{}.set_name(regular_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+        "regular_test_layer.json"}
+                               .set_discovery_type(ManifestDiscoveryType::override_folder));
+    env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
+        AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(
+            LoaderSettingsLayerConfiguration{}
+                .set_name(regular_layer_name)
+                .set_path(env.get_layer_manifest_path().str())
+                .set_control("on"))));
+    {
+        auto layer_props = env.GetLayerProperties(1);
+        EXPECT_TRUE(string_eq(layer_props.at(0).layerName, regular_layer_name));
+
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+
+        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
+        env.debug_log.clear();
+        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
+        ASSERT_TRUE(string_eq(layers.at(0).layerName, regular_layer_name));
+    }
+    env.platform_shim->set_elevated_privilege(true);
+    {
+        ASSERT_NO_FATAL_FAILURE(env.GetLayerProperties(0));
+
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+
+        ASSERT_FALSE(env.debug_log.find(get_settings_location_log_message(env)));
+        ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0));
+    }
+}
+
+TEST(SettingsFile, SettingsInSecuredLocation) {
+    FrameworkEnvironment env{FrameworkSettings{}.set_secure_loader_settings(true)};
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    env.get_test_icd().add_physical_device({});
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    const char* regular_layer_name = "VK_LAYER_TestLayer_0";
+    env.add_explicit_layer(TestLayerDetails{
+        ManifestLayer{}.add_layer(
+            ManifestLayer::LayerDescription{}.set_name(regular_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+        "regular_test_layer.json"}
+                               .set_discovery_type(ManifestDiscoveryType::override_folder));
+    env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
+        AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(
+            LoaderSettingsLayerConfiguration{}
+                .set_name(regular_layer_name)
+                .set_path(env.get_layer_manifest_path().str())
+                .set_control("on"))));
+    {
+        auto layer_props = env.GetLayerProperties(1);
+        EXPECT_TRUE(string_eq(layer_props.at(0).layerName, regular_layer_name));
+
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+
+        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env, true)));
+        env.debug_log.clear();
+        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
+        ASSERT_TRUE(string_eq(layers.at(0).layerName, regular_layer_name));
+    }
+    env.platform_shim->set_elevated_privilege(true);
+    {
+        auto layer_props = env.GetLayerProperties(1);
+        EXPECT_TRUE(string_eq(layer_props.at(0).layerName, regular_layer_name));
+
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+
+        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env, true)));
+        env.debug_log.clear();
+        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
+        ASSERT_TRUE(string_eq(layers.at(0).layerName, regular_layer_name));
+    }
+}
+
+// Make sure settings file can have multiple sets of settings
+TEST(SettingsFile, SupportsMultipleSetingsSimultaneously) {
+    FrameworkEnvironment env{};
+    const char* app_specific_layer_name = "VK_LAYER_TestLayer_0";
+    env.add_explicit_layer(TestLayerDetails{
+        ManifestLayer{}.add_layer(
+            ManifestLayer::LayerDescription{}.set_name(app_specific_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+        "VK_LAYER_app_specific.json"}
+                               .set_discovery_type(ManifestDiscoveryType::override_folder));
+    const char* global_layer_name = "VK_LAYER_TestLayer_1";
+    env.add_explicit_layer(TestLayerDetails{
+        ManifestLayer{}.add_layer(
+            ManifestLayer::LayerDescription{}.set_name(global_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+        "VK_LAYER_global.json"}
+                               .set_discovery_type(ManifestDiscoveryType::override_folder));
+    env.update_loader_settings(
+        env.loader_settings
+            // configuration that matches the current executable path - but dont set the app-key just yet
+            .add_app_specific_setting(AppSpecificSettings{}
+                                          .add_stderr_log_filter("all")
+                                          .add_layer_configuration(LoaderSettingsLayerConfiguration{}
+                                                                       .set_name(app_specific_layer_name)
+                                                                       .set_path(env.get_layer_manifest_path(0).str())
+                                                                       .set_control("on"))
+                                          .add_app_key("key0"))
+            // configuration that should never be used
+            .add_app_specific_setting(
+                AppSpecificSettings{}
+                    .add_stderr_log_filter("all")
+                    .add_layer_configuration(
+                        LoaderSettingsLayerConfiguration{}.set_name("VK_LAYER_haha").set_path("/made/up/path").set_control("auto"))
+                    .add_layer_configuration(LoaderSettingsLayerConfiguration{}
+                                                 .set_name("VK_LAYER_haha2")
+                                                 .set_path("/made/up/path2")
+                                                 .set_control("auto"))
+                    .add_app_key("key1")
+                    .add_app_key("key2"))
+            // Add a global configuration
+            .add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(
+                LoaderSettingsLayerConfiguration{}
+                    .set_name(global_layer_name)
+                    .set_path(env.get_layer_manifest_path(1).str())
+                    .set_control("on"))));
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    env.get_test_icd().add_physical_device({});
+    {
+        auto layer_props = env.GetLayerProperties(1);
+        EXPECT_TRUE(string_eq(layer_props.at(0).layerName, global_layer_name));
+
+        // Check that the global config is used
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
+        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
+        ASSERT_TRUE(string_eq(layers.at(0).layerName, global_layer_name));
+    }
+    env.debug_log.clear();
+    // Set one set to contain the current executable path
+    env.loader_settings.app_specific_settings.at(0).add_app_key(fs::fixup_backslashes_in_path(test_platform_executable_path()));
+    env.update_loader_settings(env.loader_settings);
+    {
+        auto layer_props = env.GetLayerProperties(1);
+        EXPECT_TRUE(string_eq(layer_props.at(0).layerName, app_specific_layer_name));
+
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
+        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
+        ASSERT_TRUE(string_eq(layers.at(0).layerName, app_specific_layer_name));
+    }
+}
+
+// Make sure layers found through the settings file are enableable by environment variables
+TEST(SettingsFile, LayerAutoEnabledByEnvVars) {
+    FrameworkEnvironment env{};
+    env.loader_settings.set_file_format_version({1, 0, 0});
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    env.get_test_icd().add_physical_device({});
+
+    const char* layer_name = "VK_LAYER_automatic";
+    env.add_explicit_layer(
+        TestLayerDetails{ManifestLayer{}.add_layer(
+                             ManifestLayer::LayerDescription{}.set_name(layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+                         "layer_name.json"}
+            .set_discovery_type(ManifestDiscoveryType::override_folder));
+
+    env.update_loader_settings(
+        env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(
+            LoaderSettingsLayerConfiguration{}
+                .set_name(layer_name)
+                .set_path(env.get_layer_manifest_path(0).str())
+                .set_control("auto"))));
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    {
+        EnvVarWrapper instance_layers{"VK_INSTANCE_LAYERS", layer_name};
+        auto layer_props = env.GetLayerProperties(1);
+        EXPECT_TRUE(string_eq(layer_props.at(0).layerName, layer_name));
+
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
+        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
+        ASSERT_TRUE(string_eq(layers.at(0).layerName, layer_name));
+    }
+    env.debug_log.clear();
+
+    {
+        EnvVarWrapper loader_layers_enable{"VK_LOADER_LAYERS_ENABLE", layer_name};
+        auto layer_props = env.GetLayerProperties(1);
+        EXPECT_TRUE(string_eq(layer_props.at(0).layerName, layer_name));
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
+        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
+        ASSERT_TRUE(string_eq(layers.at(0).layerName, layer_name));
+    }
+}
+
+// Make sure layers are disallowed from loading if the settings file says so
+TEST(SettingsFile, LayerDisablesImplicitLayer) {
+    FrameworkEnvironment env{};
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    env.get_test_icd().add_physical_device({});
+    const char* implicit_layer_name = "VK_LAYER_Implicit_TestLayer";
+    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("oof")),
+                           "implicit_test_layer.json");
+
+    env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
+        AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(
+            LoaderSettingsLayerConfiguration{}
+                .set_name(implicit_layer_name)
+                .set_path(env.get_shimmed_layer_manifest_path(0).str())
+                .set_control("off")
+                .set_treat_as_implicit_manifest(true))));
+    {
+        ASSERT_NO_FATAL_FAILURE(env.GetLayerProperties(0));
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+
+        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
+        ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0));
+    }
+}
+
+// Implicit layers should be reordered by the settings file
+TEST(SettingsFile, ImplicitLayersDontInterfere) {
+    FrameworkEnvironment env{};
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    env.get_test_icd().add_physical_device({});
+    const char* implicit_layer_name1 = "VK_LAYER_Implicit_TestLayer1";
+    env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
+                                                         .set_name(implicit_layer_name1)
+                                                         .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
+                                                         .set_disable_environment("oof")),
+                           "implicit_test_layer1.json");
+    const char* implicit_layer_name2 = "VK_LAYER_Implicit_TestLayer2";
+    env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
+                                                         .set_name(implicit_layer_name2)
+                                                         .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
+                                                         .set_disable_environment("oof")),
+                           "implicit_test_layer2.json");
+    // validate order when settings file is not present
+    {
+        auto layer_props = env.GetLayerProperties(2);
+        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, implicit_layer_name1));
+        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, implicit_layer_name2));
+
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+        ASSERT_FALSE(env.debug_log.find(get_settings_location_log_message(env)));
+        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2);
+        ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_name1));
+        ASSERT_TRUE(string_eq(layers.at(1).layerName, implicit_layer_name2));
+    }
+    // Now setup the settings file to contain a specific order
+    env.update_loader_settings(LoaderSettings{}.add_app_specific_setting(
+        AppSpecificSettings{}
+            .add_stderr_log_filter("all")
+            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
+                                         .set_name(implicit_layer_name1)
+                                         .set_path(env.get_shimmed_layer_manifest_path(0).str())
+                                         .set_control("auto")
+                                         .set_treat_as_implicit_manifest(true))
+            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
+                                         .set_name(implicit_layer_name2)
+                                         .set_path(env.get_shimmed_layer_manifest_path(1).str())
+                                         .set_control("auto")
+                                         .set_treat_as_implicit_manifest(true))));
+    {
+        auto layer_props = env.GetLayerProperties(2);
+        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, implicit_layer_name1));
+        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, implicit_layer_name2));
+
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
+        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2);
+        ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_name1));
+        ASSERT_TRUE(string_eq(layers.at(1).layerName, implicit_layer_name2));
+    }
+
+    // Flip the order and store the settings in the env for later use in the test
+    env.loader_settings = LoaderSettings{}.add_app_specific_setting(
+        AppSpecificSettings{}
+            .add_stderr_log_filter("all")
+            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
+                                         .set_name(implicit_layer_name2)
+                                         .set_path(env.get_shimmed_layer_manifest_path(1).str())
+                                         .set_control("auto")
+                                         .set_treat_as_implicit_manifest(true))
+            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
+                                         .set_name(implicit_layer_name1)
+                                         .set_path(env.get_shimmed_layer_manifest_path(0).str())
+                                         .set_control("auto")
+                                         .set_treat_as_implicit_manifest(true)));
+    env.update_loader_settings(env.loader_settings);
+
+    {
+        auto layer_props = env.GetLayerProperties(2);
+        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, implicit_layer_name2));
+        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, implicit_layer_name1));
+
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
+        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2);
+        ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_name2));
+        ASSERT_TRUE(string_eq(layers.at(1).layerName, implicit_layer_name1));
+    }
+
+    // Now add an explicit layer into the middle and verify that is in the correct location
+    const char* explicit_layer_name3 = "VK_LAYER_Explicit_TestLayer3";
+    env.add_explicit_layer(
+        ManifestLayer{}.add_layer(
+            ManifestLayer::LayerDescription{}.set_name(explicit_layer_name3).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+        "explicit_test_layer3.json");
+    env.loader_settings.app_specific_settings.at(0).layer_configurations.insert(
+        env.loader_settings.app_specific_settings.at(0).layer_configurations.begin() + 1,
+        LoaderSettingsLayerConfiguration{}
+            .set_name(explicit_layer_name3)
+            .set_path(env.get_shimmed_layer_manifest_path(2).str())
+            .set_control("on"));
+    env.update_loader_settings(env.loader_settings);
+    {
+        auto layer_props = env.GetLayerProperties(3);
+        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, implicit_layer_name2));
+        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, explicit_layer_name3));
+        ASSERT_TRUE(string_eq(layer_props.at(2).layerName, implicit_layer_name1));
+
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
+        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 3);
+        ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_name2));
+        ASSERT_TRUE(string_eq(layers.at(1).layerName, explicit_layer_name3));
+        ASSERT_TRUE(string_eq(layers.at(2).layerName, implicit_layer_name1));
+    }
+}
+
+// Make sure layers that are disabled can't be enabled by the application
+TEST(SettingsFile, ApplicationEnablesIgnored) {
+    FrameworkEnvironment env{};
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    env.get_test_icd().add_physical_device({});
+    const char* explicit_layer_name = "VK_LAYER_TestLayer";
+    env.add_explicit_layer(
+        ManifestLayer{}.add_layer(
+            ManifestLayer::LayerDescription{}.set_name(explicit_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+        "regular_test_layer.json");
+
+    env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
+        AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(
+            LoaderSettingsLayerConfiguration{}
+                .set_name(explicit_layer_name)
+                .set_path(env.get_shimmed_layer_manifest_path(0).str())
+                .set_control("off"))));
+    {
+        ASSERT_NO_FATAL_FAILURE(env.GetLayerProperties(0));
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.create_info.add_layer(explicit_layer_name);
+        ASSERT_NO_FATAL_FAILURE(inst.CheckCreate(VK_ERROR_LAYER_NOT_PRESENT));
+    }
+}
+
+TEST(SettingsFile, LayerListIsEmpty) {
+    FrameworkEnvironment env{};
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    env.get_test_icd().add_physical_device({});
+    const char* implicit_layer_name = "VK_LAYER_TestLayer";
+    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("HeeHee")),
+                           "regular_test_layer.json");
+
+    JsonWriter writer{};
+    writer.StartObject();
+    writer.AddKeyedString("file_format_version", "1.0.0");
+    writer.StartKeyedObject("settings");
+    writer.StartKeyedObject("layers");
+    writer.EndObject();
+    writer.EndObject();
+    writer.EndObject();
+    env.write_settings_file(writer.output);
+
+    ASSERT_NO_FATAL_FAILURE(env.GetLayerProperties(0));
+
+    InstWrapper inst{env.vulkan_functions};
+    FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+    inst.CheckCreate();
+    ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
+    ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0));
+}
+
+// If a settings file exists but contains no valid settings - don't consider it
+TEST(SettingsFile, InvalidSettingsFile) {
+    FrameworkEnvironment env{};
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    env.get_test_icd().add_physical_device({});
+    const char* explicit_layer_name = "VK_LAYER_TestLayer";
+    env.add_explicit_layer(
+        ManifestLayer{}.add_layer(
+            ManifestLayer::LayerDescription{}.set_name(explicit_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+        "regular_test_layer.json");
+    const char* implicit_layer_name = "VK_LAYER_ImplicitTestLayer";
+    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("foobarbaz")),
+                           "implicit_test_layer.json");
+    auto check_integrity = [&env, explicit_layer_name, implicit_layer_name]() {
+        auto layer_props = env.GetLayerProperties(2);
+        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, implicit_layer_name));
+        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, explicit_layer_name));
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.create_info.add_layer(explicit_layer_name);
+        inst.CheckCreate();
+        ASSERT_FALSE(env.debug_log.find(get_settings_location_log_message(env)));
+        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2);
+        ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_name));
+        ASSERT_TRUE(string_eq(layers.at(1).layerName, explicit_layer_name));
+    };
+
+    // No actual settings
+    {
+        JsonWriter writer{};
+        writer.StartObject();
+        writer.AddKeyedString("file_format_version", "0.0.0");
+        writer.EndObject();
+        env.write_settings_file(writer.output);
+
+        check_integrity();
+    }
+
+    {
+        JsonWriter writer{};
+        writer.StartObject();
+        writer.AddKeyedString("file_format_version", "0.0.0");
+        writer.StartKeyedArray("settings_array");
+        writer.EndArray();
+        writer.StartKeyedObject("settings");
+        writer.EndObject();
+        writer.EndObject();
+        env.write_settings_file(writer.output);
+
+        check_integrity();
+    }
+
+    {
+        JsonWriter writer{};
+        writer.StartObject();
+        for (uint32_t i = 0; i < 3; i++) {
+            writer.StartKeyedArray("settings_array");
+            writer.EndArray();
+            writer.StartKeyedObject("boogabooga");
+            writer.EndObject();
+            writer.StartKeyedObject("settings");
+            writer.EndObject();
+        }
+        writer.EndObject();
+        env.write_settings_file(writer.output);
+
+        check_integrity();
+    }
+}
+
+// Unknown layers are put in the correct location
+TEST(SettingsFile, UnknownLayersInRightPlace) {
+    FrameworkEnvironment env{};
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    env.get_test_icd().add_physical_device({});
+    const char* explicit_layer_name1 = "VK_LAYER_TestLayer1";
+    env.add_explicit_layer(
+        ManifestLayer{}.add_layer(
+            ManifestLayer::LayerDescription{}.set_name(explicit_layer_name1).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+        "regular_test_layer1.json");
+    const char* implicit_layer_name1 = "VK_LAYER_ImplicitTestLayer1";
+    env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
+                                                         .set_name(implicit_layer_name1)
+                                                         .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
+                                                         .set_disable_environment("foobarbaz")),
+                           "implicit_test_layer1.json");
+    const char* explicit_layer_name2 = "VK_LAYER_TestLayer2";
+    env.add_explicit_layer(
+        ManifestLayer{}.add_layer(
+            ManifestLayer::LayerDescription{}.set_name(explicit_layer_name2).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+        "regular_test_layer2.json");
+    const char* implicit_layer_name2 = "VK_LAYER_ImplicitTestLayer2";
+    env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
+                                                         .set_name(implicit_layer_name2)
+                                                         .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
+                                                         .set_disable_environment("foobarbaz")),
+                           "implicit_test_layer2.json");
+
+    env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
+        AppSpecificSettings{}
+            .add_stderr_log_filter("all")
+            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
+                                         .set_name(explicit_layer_name2)
+                                         .set_path(env.get_shimmed_layer_manifest_path(2).str())
+                                         .set_control("on"))
+            .add_layer_configuration(LoaderSettingsLayerConfiguration{}.set_control("unordered_layer_location"))
+            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
+                                         .set_name(implicit_layer_name2)
+                                         .set_path(env.get_shimmed_layer_manifest_path(3).str())
+                                         .set_control("on")
+                                         .set_treat_as_implicit_manifest(true))));
+
+    auto layer_props = env.GetLayerProperties(4);
+    ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name2));
+    ASSERT_TRUE(string_eq(layer_props.at(1).layerName, implicit_layer_name1));
+    ASSERT_TRUE(string_eq(layer_props.at(2).layerName, explicit_layer_name1));
+    ASSERT_TRUE(string_eq(layer_props.at(3).layerName, implicit_layer_name2));
+    InstWrapper inst{env.vulkan_functions};
+    FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+    inst.create_info.add_layer(explicit_layer_name1);
+    inst.CheckCreate();
+    ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
+    auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 4);
+    ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name2));
+    ASSERT_TRUE(string_eq(layer_props.at(1).layerName, implicit_layer_name1));
+    ASSERT_TRUE(string_eq(layer_props.at(2).layerName, explicit_layer_name1));
+    ASSERT_TRUE(string_eq(layer_props.at(3).layerName, implicit_layer_name2));
+}
+
+// Settings file allows loading multiple layers with the same name - as long as the path is different
+TEST(SettingsFile, MultipleLayersWithSameName) {
+    FrameworkEnvironment env{};
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    env.get_test_icd().add_physical_device({});
+
+    const char* explicit_layer_name = "VK_LAYER_TestLayer";
+    env.add_explicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
+                                                         .set_name(explicit_layer_name)
+                                                         .set_description("0000")
+                                                         .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+                           "regular_test_layer1.json");
+
+    env.add_explicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
+                                                         .set_name(explicit_layer_name)
+                                                         .set_description("1111")
+                                                         .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+                           "regular_test_layer2.json");
+
+    env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
+        AppSpecificSettings{}
+            .add_stderr_log_filter("all")
+            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
+                                         .set_name(explicit_layer_name)
+                                         .set_path(env.get_shimmed_layer_manifest_path(0).str())
+                                         .set_control("on"))
+            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
+                                         .set_name(explicit_layer_name)
+                                         .set_path(env.get_shimmed_layer_manifest_path(1).str())
+                                         .set_control("on"))));
+    auto layer_props = env.GetLayerProperties(2);
+    ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name));
+    ASSERT_TRUE(string_eq(layer_props.at(0).description, "0000"));
+    ASSERT_TRUE(string_eq(layer_props.at(1).layerName, explicit_layer_name));
+    ASSERT_TRUE(string_eq(layer_props.at(1).description, "1111"));
+    InstWrapper inst{env.vulkan_functions};
+    FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+    inst.CheckCreate();
+    ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
+    auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2);
+    ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name));
+    ASSERT_TRUE(string_eq(layers.at(0).description, "0000"));
+    ASSERT_TRUE(string_eq(layers.at(1).layerName, explicit_layer_name));
+    ASSERT_TRUE(string_eq(layers.at(1).description, "1111"));
+}
+
+// Settings file shouldn't be able to cause the same layer from the same path twice
+TEST(SettingsFile, MultipleLayersWithSamePath) {
+    FrameworkEnvironment env{};
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    env.get_test_icd().add_physical_device({});
+
+    const char* explicit_layer_name = "VK_LAYER_TestLayer";
+    env.add_explicit_layer(
+        ManifestLayer{}.add_layer(
+            ManifestLayer::LayerDescription{}.set_name(explicit_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+        "regular_test_layer1.json");
+
+    env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
+        AppSpecificSettings{}
+            .add_stderr_log_filter("all")
+            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
+                                         .set_name(explicit_layer_name)
+                                         .set_path(env.get_shimmed_layer_manifest_path(0).str())
+                                         .set_control("on"))
+            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
+                                         .set_name(explicit_layer_name)
+                                         .set_path(env.get_shimmed_layer_manifest_path(0).str())
+                                         .set_control("on"))));
+
+    auto layer_props = env.GetLayerProperties(1);
+    ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name));
+
+    InstWrapper inst{env.vulkan_functions};
+    FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+    inst.CheckCreate();
+    ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
+    auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
+    ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name));
+}
+
+// Settings contains a layer whose name doesn't match the one found in the layer manifest - make sure the layer from the settings
+// file is removed
+TEST(SettingsFile, MismatchedLayerNameAndManifestPath) {
+    FrameworkEnvironment env{};
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    env.get_test_icd().add_physical_device({});
+
+    const char* manifest_explicit_layer_name = "VK_LAYER_MANIFEST_TestLayer";
+    const char* settings_explicit_layer_name = "VK_LAYER_Settings_TestLayer";
+    env.add_explicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
+                                                         .set_name(manifest_explicit_layer_name)
+                                                         .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+                           "regular_test_layer1.json");
+
+    const char* implicit_layer_name = "VK_LAYER_Implicit_TestLayer";
+    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("oof")),
+                           "implicit_test_layer.json");
+
+    env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
+        AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(
+            LoaderSettingsLayerConfiguration{}
+                .set_name(settings_explicit_layer_name)
+                .set_path(env.get_shimmed_layer_manifest_path(0).str())
+                .set_control("on"))));
+
+    ASSERT_NO_FATAL_FAILURE(env.GetLayerProperties(0));
+
+    InstWrapper inst{env.vulkan_functions};
+    FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+    inst.CheckCreate();
+    ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
+    ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0));
+}
+
+// Settings file should take precedence over the meta layer, if present
+TEST(SettingsFile, MetaLayerAlsoActivates) {
+    FrameworkEnvironment env{};
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    env.get_test_icd().add_physical_device({});
+
+    const char* settings_explicit_layer_name = "VK_LAYER_Regular_TestLayer";
+    env.add_explicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
+                                                         .set_name(settings_explicit_layer_name)
+                                                         .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+                           "explicit_test_layer.json");
+
+    const char* settings_implicit_layer_name = "VK_LAYER_RegularImplicit_TestLayer";
+    env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
+                                                         .set_name(settings_implicit_layer_name)
+                                                         .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
+                                                         .set_disable_environment("AndISaidHey")
+                                                         .set_enable_environment("WhatsGoingOn")),
+                           "implicit_layer.json");
+
+    const char* component_explicit_layer_name1 = "VK_LAYER_Component_TestLayer1";
+    env.add_explicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
+                                                                          .set_name(component_explicit_layer_name1)
+                                                                          .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+                                            "component_test_layer1.json"));
+
+    const char* component_explicit_layer_name2 = "VK_LAYER_Component_TestLayer2";
+    env.add_explicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
+                                                                          .set_name(component_explicit_layer_name2)
+                                                                          .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+                                            "component_test_layer2.json"));
+
+    const char* meta_layer_name1 = "VK_LAYER_meta_layer1";
+    env.add_implicit_layer(ManifestLayer{}
+                               .set_file_format_version(ManifestVersion(1, 1, 2))
+                               .add_layer(ManifestLayer::LayerDescription{}
+                                              .set_name(meta_layer_name1)
+                                              .add_component_layer(component_explicit_layer_name2)
+                                              .add_component_layer(component_explicit_layer_name1)
+                                              .set_disable_environment("NotGonnaWork")),
+                           "meta_test_layer.json");
+
+    const char* meta_layer_name2 = "VK_LAYER_meta_layer2";
+    env.add_implicit_layer(ManifestLayer{}
+                               .set_file_format_version(ManifestVersion(1, 1, 2))
+                               .add_layer(ManifestLayer::LayerDescription{}
+                                              .set_name(meta_layer_name2)
+                                              .add_component_layer(component_explicit_layer_name1)
+                                              .set_disable_environment("ILikeTrains")
+                                              .set_enable_environment("BakedBeans")),
+                           "not_automatic_meta_test_layer.json");
+
+    env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
+        AppSpecificSettings{}
+            .add_stderr_log_filter("all")
+            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
+                                         .set_name(settings_explicit_layer_name)
+                                         .set_path(env.get_shimmed_layer_manifest_path(0).str())
+                                         .set_control("on")
+                                         .set_treat_as_implicit_manifest(false))
+            .add_layer_configuration(LoaderSettingsLayerConfiguration{}.set_control("unordered_layer_location"))
+            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
+                                         .set_name(settings_implicit_layer_name)
+                                         .set_path(env.get_shimmed_layer_manifest_path(1).str())
+                                         .set_control("auto")
+                                         .set_treat_as_implicit_manifest(true))));
+    {
+        EnvVarWrapper enable_meta_layer{"WhatsGoingOn", "1"};
+        auto layer_props = env.GetLayerProperties(6);
+        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, settings_explicit_layer_name));
+        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, meta_layer_name1));
+        ASSERT_TRUE(string_eq(layer_props.at(2).layerName, meta_layer_name2));
+        ASSERT_TRUE(string_eq(layer_props.at(3).layerName, component_explicit_layer_name1));
+        ASSERT_TRUE(string_eq(layer_props.at(4).layerName, component_explicit_layer_name2));
+        ASSERT_TRUE(string_eq(layer_props.at(5).layerName, settings_implicit_layer_name));
+
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
+        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 5);
+        ASSERT_TRUE(string_eq(layers.at(0).layerName, settings_explicit_layer_name));
+        ASSERT_TRUE(string_eq(layers.at(1).layerName, component_explicit_layer_name2));
+        ASSERT_TRUE(string_eq(layers.at(2).layerName, component_explicit_layer_name1));
+        ASSERT_TRUE(string_eq(layers.at(3).layerName, meta_layer_name1));
+        ASSERT_TRUE(string_eq(layers.at(4).layerName, settings_implicit_layer_name));
+    }
+    {
+        EnvVarWrapper enable_meta_layer{"BakedBeans", "1"};
+        auto layer_props = env.GetLayerProperties(6);
+        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, settings_explicit_layer_name));
+        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, meta_layer_name1));
+        ASSERT_TRUE(string_eq(layer_props.at(2).layerName, meta_layer_name2));
+        ASSERT_TRUE(string_eq(layer_props.at(3).layerName, component_explicit_layer_name1));
+        ASSERT_TRUE(string_eq(layer_props.at(4).layerName, component_explicit_layer_name2));
+        ASSERT_TRUE(string_eq(layer_props.at(5).layerName, settings_implicit_layer_name));
+
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
+        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 5);
+        ASSERT_TRUE(string_eq(layers.at(0).layerName, settings_explicit_layer_name));
+        ASSERT_TRUE(string_eq(layers.at(1).layerName, component_explicit_layer_name2));
+        ASSERT_TRUE(string_eq(layers.at(2).layerName, component_explicit_layer_name1));
+        ASSERT_TRUE(string_eq(layers.at(3).layerName, meta_layer_name1));
+        ASSERT_TRUE(string_eq(layers.at(4).layerName, meta_layer_name2));
+    }
+}
+
+// Layers are correctly ordered by settings file.
+TEST(SettingsFile, LayerOrdering) {
+    FrameworkEnvironment env{};
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    env.get_test_icd().add_physical_device({});
+
+    const char* explicit_layer_name1 = "VK_LAYER_Regular_TestLayer1";
+    env.add_explicit_layer(
+        ManifestLayer{}.add_layer(
+            ManifestLayer::LayerDescription{}.set_name(explicit_layer_name1).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+        "explicit_test_layer1.json");
+
+    const char* explicit_layer_name2 = "VK_LAYER_Regular_TestLayer2";
+    env.add_explicit_layer(
+        ManifestLayer{}.add_layer(
+            ManifestLayer::LayerDescription{}.set_name(explicit_layer_name2).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+        "explicit_test_layer2.json");
+
+    const char* implicit_layer_name1 = "VK_LAYER_Implicit_TestLayer1";
+    env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
+                                                         .set_name(implicit_layer_name1)
+                                                         .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
+                                                         .set_disable_environment("Domierigato")),
+                           "implicit_layer1.json");
+
+    const char* implicit_layer_name2 = "VK_LAYER_Implicit_TestLayer2";
+    env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
+                                                         .set_name(implicit_layer_name2)
+                                                         .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
+                                                         .set_disable_environment("Mistehrobato")),
+                           "implicit_layer2.json");
+
+    env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all"));
+
+    std::vector<LoaderSettingsLayerConfiguration> layer_configs{4};
+    layer_configs.at(0)
+        .set_name(explicit_layer_name1)
+        .set_path(env.get_shimmed_layer_manifest_path(0).str())
+        .set_control("on")
+        .set_treat_as_implicit_manifest(false);
+    layer_configs.at(1)
+        .set_name(explicit_layer_name2)
+        .set_path(env.get_shimmed_layer_manifest_path(1).str())
+        .set_control("on")
+        .set_treat_as_implicit_manifest(false);
+    layer_configs.at(2)
+        .set_name(implicit_layer_name1)
+        .set_path(env.get_shimmed_layer_manifest_path(2).str())
+        .set_control("on")
+        .set_treat_as_implicit_manifest(true);
+    layer_configs.at(3)
+        .set_name(implicit_layer_name2)
+        .set_path(env.get_shimmed_layer_manifest_path(3).str())
+        .set_control("on")
+        .set_treat_as_implicit_manifest(true);
+
+    std::sort(layer_configs.begin(), layer_configs.end());
+    uint32_t permutation_count = 0;
+    do {
+        env.loader_settings.app_specific_settings.at(0).layer_configurations.clear();
+        env.loader_settings.app_specific_settings.at(0).add_layer_configurations(layer_configs);
+        env.update_loader_settings(env.loader_settings);
+
+        auto layer_props = env.GetLayerProperties(4);
+        for (uint32_t i = 0; i < 4; i++) {
+            ASSERT_TRUE(layer_configs.at(i).name == layer_props.at(i).layerName);
+        }
+
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
+        auto active_layers = inst.GetActiveLayers(inst.GetPhysDev(), 4);
+        for (uint32_t i = 0; i < 4; i++) {
+            ASSERT_TRUE(layer_configs.at(i).name == active_layers.at(i).layerName);
+        }
+        env.debug_log.clear();
+        permutation_count++;
+    } while (std::next_permutation(layer_configs.begin(), layer_configs.end()));
+    ASSERT_EQ(permutation_count, 24U);  // should be this many orderings
+}
+
+TEST(SettingsFile, EnvVarsWork_VK_LAYER_PATH) {
+    FrameworkEnvironment env{};
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    env.get_test_icd().add_physical_device({});
+
+    const char* explicit_layer_name1 = "VK_LAYER_Regular_TestLayer1";
+    env.add_explicit_layer(TestLayerDetails{
+        ManifestLayer{}.add_layer(
+            ManifestLayer::LayerDescription{}.set_name(explicit_layer_name1).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+        "explicit_test_layer1.json"}
+                               .set_discovery_type(ManifestDiscoveryType::env_var));
+
+    const char* implicit_layer_name1 = "VK_LAYER_Implicit_TestLayer1";
+    env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
+                                                         .set_name(implicit_layer_name1)
+                                                         .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
+                                                         .set_disable_environment("Domierigato")),
+                           "implicit_layer1.json");
+    const char* non_env_var_layer_name2 = "VK_LAYER_Regular_TestLayer2";
+    env.add_explicit_layer(TestLayerDetails{
+        ManifestLayer{}.add_layer(
+            ManifestLayer::LayerDescription{}.set_name(non_env_var_layer_name2).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+        "explicit_test_layer2.json"});
+
+    {
+        auto layer_props = env.GetLayerProperties(2);
+        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, implicit_layer_name1));
+        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, explicit_layer_name1));
+
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+        ASSERT_FALSE(env.debug_log.find(get_settings_location_log_message(env)));
+        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
+        ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_name1));
+    }
+    {
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.create_info.add_layer(explicit_layer_name1);
+        inst.CheckCreate();
+        ASSERT_FALSE(env.debug_log.find(get_settings_location_log_message(env)));
+        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2);
+        ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_name1));
+        ASSERT_TRUE(string_eq(layers.at(1).layerName, explicit_layer_name1));
+    }
+    env.update_loader_settings(env.loader_settings.add_app_specific_setting(
+        AppSpecificSettings{}
+            .add_stderr_log_filter("all")
+            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
+                                         .set_name(non_env_var_layer_name2)
+                                         .set_control("on")
+                                         .set_path(env.get_shimmed_layer_manifest_path(2).str()))
+            .add_layer_configuration(LoaderSettingsLayerConfiguration{}.set_control("unordered_layer_location"))));
+    {
+        auto layer_props = env.GetLayerProperties(3);
+        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, non_env_var_layer_name2));
+        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, implicit_layer_name1));
+        ASSERT_TRUE(string_eq(layer_props.at(2).layerName, explicit_layer_name1));
+
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
+        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2);
+        ASSERT_TRUE(string_eq(layers.at(0).layerName, non_env_var_layer_name2));
+        ASSERT_TRUE(string_eq(layers.at(1).layerName, implicit_layer_name1));
+    }
+    {
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.create_info.add_layer(explicit_layer_name1);
+        inst.CheckCreate();
+        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
+        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 3);
+        ASSERT_TRUE(string_eq(layers.at(0).layerName, non_env_var_layer_name2));
+        ASSERT_TRUE(string_eq(layers.at(1).layerName, implicit_layer_name1));
+        ASSERT_TRUE(string_eq(layers.at(2).layerName, explicit_layer_name1));
+    }
+}
+
+TEST(SettingsFile, EnvVarsWork_VK_ADD_LAYER_PATH) {
+    FrameworkEnvironment env{};
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    env.get_test_icd().add_physical_device({});
+
+    const char* implicit_layer_name1 = "VK_LAYER_Implicit_TestLayer1";
+    env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
+                                                         .set_name(implicit_layer_name1)
+                                                         .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
+                                                         .set_disable_environment("Domierigato")),
+                           "implicit_layer1.json");
+    const char* explicit_layer_name1 = "VK_LAYER_Regular_TestLayer1";
+    env.add_explicit_layer(TestLayerDetails{
+        ManifestLayer{}.add_layer(
+            ManifestLayer::LayerDescription{}.set_name(explicit_layer_name1).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+        "explicit_test_layer1.json"}
+                               .set_discovery_type(ManifestDiscoveryType::add_env_var));
+    const char* non_env_var_layer_name2 = "VK_LAYER_Regular_TestLayer2";
+    env.add_explicit_layer(TestLayerDetails{
+        ManifestLayer{}.add_layer(
+            ManifestLayer::LayerDescription{}.set_name(non_env_var_layer_name2).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+        "explicit_test_layer2.json"});
+
+    {
+        auto layer_props = env.GetLayerProperties(3);
+        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, implicit_layer_name1));
+        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, explicit_layer_name1));
+        ASSERT_TRUE(string_eq(layer_props.at(2).layerName, non_env_var_layer_name2));
+
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+        ASSERT_FALSE(env.debug_log.find(get_settings_location_log_message(env)));
+        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
+        ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_name1));
+    }
+    {
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.create_info.add_layer(explicit_layer_name1);
+        inst.CheckCreate();
+        ASSERT_FALSE(env.debug_log.find(get_settings_location_log_message(env)));
+        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 2);
+        ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_name1));
+        ASSERT_TRUE(string_eq(layers.at(1).layerName, explicit_layer_name1));
+    }
+
+    env.update_loader_settings(env.loader_settings.add_app_specific_setting(
+        AppSpecificSettings{}
+            .add_stderr_log_filter("all")
+            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
+                                         .set_name(explicit_layer_name1)
+                                         .set_control("on")
+                                         .set_path(env.get_shimmed_layer_manifest_path(1).str()))
+            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
+                                         .set_name(non_env_var_layer_name2)
+                                         .set_control("on")
+                                         .set_path(env.get_shimmed_layer_manifest_path(2).str()))
+            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
+                                         .set_name(implicit_layer_name1)
+                                         .set_control("on")
+                                         .set_path(env.get_shimmed_layer_manifest_path(0).str())
+                                         .set_treat_as_implicit_manifest(true))));
+    {
+        auto layer_props = env.GetLayerProperties(3);
+        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name1));
+        ASSERT_TRUE(string_eq(layer_props.at(1).layerName, non_env_var_layer_name2));
+        ASSERT_TRUE(string_eq(layer_props.at(2).layerName, implicit_layer_name1));
+
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
+        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 3);
+        ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name1));
+        ASSERT_TRUE(string_eq(layers.at(1).layerName, non_env_var_layer_name2));
+        ASSERT_TRUE(string_eq(layers.at(2).layerName, implicit_layer_name1));
+    }
+    {
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.create_info.add_layer(explicit_layer_name1);
+        inst.CheckCreate();
+        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
+        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 3);
+        ASSERT_TRUE(string_eq(layers.at(0).layerName, explicit_layer_name1));
+        ASSERT_TRUE(string_eq(layers.at(1).layerName, non_env_var_layer_name2));
+        ASSERT_TRUE(string_eq(layers.at(2).layerName, implicit_layer_name1));
+    }
+}
+
+TEST(SettingsFile, EnvVarsWork_VK_INSTANCE_LAYERS) {
+    FrameworkEnvironment env{};
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    env.get_test_icd().add_physical_device({});
+
+    const char* explicit_layer_name = "VK_LAYER_Regular_TestLayer1";
+    env.add_explicit_layer(TestLayerDetails{
+        ManifestLayer{}.add_layer(
+            ManifestLayer::LayerDescription{}.set_name(explicit_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+        "explicit_test_layer1.json"});
+
+    {
+        EnvVarWrapper vk_instance_layers{"VK_INSTANCE_LAYERS", explicit_layer_name};
+        auto layer_props = env.GetLayerProperties(1);
+        ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name));
+
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+        ASSERT_FALSE(env.debug_log.find(get_settings_location_log_message(env)));
+        auto layer = inst.GetActiveLayers(inst.GetPhysDev(), 1);
+        ASSERT_TRUE(string_eq(layer.at(0).layerName, explicit_layer_name));
+    }
+    env.update_loader_settings(
+        env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(
+            LoaderSettingsLayerConfiguration{}
+                .set_name(explicit_layer_name)
+                .set_control("off")
+                .set_path(env.get_shimmed_layer_manifest_path(0).str()))));
+    {
+        ASSERT_NO_FATAL_FAILURE(env.GetLayerProperties(0));
+
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
+        ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0));
+    }
+    {
+        EnvVarWrapper vk_instance_layers{"VK_INSTANCE_LAYERS", explicit_layer_name};
+        ASSERT_NO_FATAL_FAILURE(env.GetLayerProperties(0));
+
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
+        ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0));
+    }
+}
+// Make sure that layers disabled by settings file aren't enabled by VK_LOADER_LAYERS_ENABLE
+TEST(SettingsFile, EnvVarsWork_VK_LOADER_LAYERS_ENABLE) {
+    FrameworkEnvironment env{};
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    env.get_test_icd().add_physical_device({});
+
+    const char* explicit_layer_name = "VK_LAYER_Regular_TestLayer1";
+    env.add_explicit_layer(TestLayerDetails{
+        ManifestLayer{}.add_layer(
+            ManifestLayer::LayerDescription{}.set_name(explicit_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+        "explicit_test_layer1.json"});
+
+    env.update_loader_settings(
+        env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(
+            LoaderSettingsLayerConfiguration{}
+                .set_name(explicit_layer_name)
+                .set_control("off")
+                .set_path(env.get_shimmed_layer_manifest_path(0).str()))));
+
+    EnvVarWrapper vk_instance_layers{"VK_LOADER_LAYERS_ENABLE", explicit_layer_name};
+    ASSERT_NO_FATAL_FAILURE(env.GetLayerProperties(0));
+
+    InstWrapper inst{env.vulkan_functions};
+    FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+    inst.CheckCreate();
+    ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
+    ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0));
+}
+// Make sure that layers enabled by settings file aren't disabled by VK_LOADER_LAYERS_ENABLE
+TEST(SettingsFile, EnvVarsWork_VK_LOADER_LAYERS_DISABLE) {
+    FrameworkEnvironment env{};
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    env.get_test_icd().add_physical_device({});
+
+    const char* explicit_layer_name = "VK_LAYER_Regular_TestLayer1";
+    env.add_explicit_layer(TestLayerDetails{
+        ManifestLayer{}.add_layer(
+            ManifestLayer::LayerDescription{}.set_name(explicit_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+        "explicit_test_layer1.json"});
+
+    env.update_loader_settings(
+        env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(
+            LoaderSettingsLayerConfiguration{}
+                .set_name(explicit_layer_name)
+                .set_control("on")
+                .set_path(env.get_shimmed_layer_manifest_path(0).str()))));
+
+    EnvVarWrapper vk_instance_layers{"VK_LOADER_LAYERS_DISABLE", explicit_layer_name};
+    auto layer_props = env.GetLayerProperties(1);
+    ASSERT_TRUE(string_eq(layer_props.at(0).layerName, explicit_layer_name));
+
+    InstWrapper inst{env.vulkan_functions};
+    FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+    inst.CheckCreate();
+    ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
+    ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0));
+}
+
+#if defined(WIN32)
+TEST(SettingsFile, MultipleKeysInRegistryInUnsecureLocation) {
+    FrameworkEnvironment env{};
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    env.get_test_icd().add_physical_device({});
+    env.platform_shim->add_unsecured_manifest(ManifestCategory::settings, "jank_path");
+    env.platform_shim->add_unsecured_manifest(ManifestCategory::settings, "jank_path2");
+
+    const char* regular_layer_name = "VK_LAYER_TestLayer_0";
+    env.add_explicit_layer(TestLayerDetails{
+        ManifestLayer{}.add_layer(
+            ManifestLayer::LayerDescription{}.set_name(regular_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+        "regular_test_layer.json"}
+                               .set_discovery_type(ManifestDiscoveryType::override_folder));
+    env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
+        AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(
+            LoaderSettingsLayerConfiguration{}
+                .set_name(regular_layer_name)
+                .set_path(env.get_layer_manifest_path().str())
+                .set_control("on"))));
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+
+    auto layer_props = env.GetLayerProperties(1);
+    EXPECT_TRUE(string_eq(layer_props.at(0).layerName, regular_layer_name));
+
+    InstWrapper inst{env.vulkan_functions};
+    FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+    inst.CheckCreate();
+
+    ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
+    env.debug_log.clear();
+    auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
+    ASSERT_TRUE(string_eq(layers.at(0).layerName, regular_layer_name));
+}
+
+TEST(SettingsFile, MultipleKeysInRegistryInSecureLocation) {
+    FrameworkEnvironment env{FrameworkSettings{}.set_secure_loader_settings(true)};
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    env.get_test_icd().add_physical_device({});
+    env.platform_shim->add_manifest(ManifestCategory::settings, "jank_path");
+    env.platform_shim->add_manifest(ManifestCategory::settings, "jank_path2");
+
+    const char* regular_layer_name = "VK_LAYER_TestLayer_0";
+    env.add_explicit_layer(TestLayerDetails{
+        ManifestLayer{}.add_layer(
+            ManifestLayer::LayerDescription{}.set_name(regular_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+        "regular_test_layer.json"}
+                               .set_discovery_type(ManifestDiscoveryType::override_folder));
+    env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
+        AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(
+            LoaderSettingsLayerConfiguration{}
+                .set_name(regular_layer_name)
+                .set_path(env.get_layer_manifest_path().str())
+                .set_control("on"))));
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+
+    // Make sure it works if the settings file is in the HKEY_LOCAL_MACHINE
+    env.platform_shim->set_elevated_privilege(true);
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    {
+        auto layer_props = env.GetLayerProperties(1);
+        EXPECT_TRUE(string_eq(layer_props.at(0).layerName, regular_layer_name));
+
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+
+        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
+        env.debug_log.clear();
+        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
+        ASSERT_TRUE(string_eq(layers.at(0).layerName, regular_layer_name));
+    }
+}
+#endif
+
+// Preinstance functions respect the settings file
+TEST(SettingsFile, PreInstanceFunctions) {
+    FrameworkEnvironment env;
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA));
+    env.get_test_icd().add_physical_device({});
+
+    const char* implicit_layer_name = "VK_LAYER_ImplicitTestLayer";
+
+    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_ME")
+                           .add_pre_instance_function(ManifestLayer::LayerDescription::FunctionOverride{}
+                                                          .set_vk_func("vkEnumerateInstanceLayerProperties")
+                                                          .set_override_name("test_preinst_vkEnumerateInstanceLayerProperties"))
+                           .add_pre_instance_function(ManifestLayer::LayerDescription::FunctionOverride{}
+                                                          .set_vk_func("vkEnumerateInstanceExtensionProperties")
+                                                          .set_override_name("test_preinst_vkEnumerateInstanceExtensionProperties"))
+                           .add_pre_instance_function(ManifestLayer::LayerDescription::FunctionOverride{}
+                                                          .set_vk_func("vkEnumerateInstanceVersion")
+                                                          .set_override_name("test_preinst_vkEnumerateInstanceVersion"))),
+        "implicit_test_layer.json");
+
+    env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(
+        LoaderSettingsLayerConfiguration{}
+            .set_name(implicit_layer_name)
+            .set_control("on")
+            .set_path(env.get_shimmed_layer_manifest_path(0).str())
+            .set_treat_as_implicit_manifest(true)));
+    env.update_loader_settings(env.loader_settings);
+    {
+        auto& layer = env.get_test_layer(0);
+        // Check layer props
+        uint32_t layer_props = 43;
+        layer.set_reported_layer_props(layer_props);
+
+        uint32_t count = 0;
+        ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr));
+        ASSERT_EQ(count, layer_props);
+
+        // check extension props
+        uint32_t ext_props = 52;
+        layer.set_reported_extension_props(ext_props);
+        count = 0;
+        ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr));
+        ASSERT_EQ(count, ext_props);
+
+        // check version
+        uint32_t layer_version = VK_MAKE_API_VERSION(1, 2, 3, 4);
+        layer.set_reported_instance_version(layer_version);
+
+        uint32_t version = 0;
+        ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceVersion(&version));
+        ASSERT_EQ(version, layer_version);
+    }
+    // control is set to off
+    env.loader_settings.app_specific_settings.at(0).layer_configurations.at(0).set_control("off");
+    env.update_loader_settings(env.loader_settings);
+
+    {
+        auto& layer = env.get_test_layer(0);
+        // Check layer props
+        uint32_t layer_props = 43;
+        layer.set_reported_layer_props(layer_props);
+
+        uint32_t count = 0;
+        ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr));
+        ASSERT_EQ(count, 0U);  // dont use the intercepted count
+
+        // check extension props
+        uint32_t ext_props = 52;
+        layer.set_reported_extension_props(ext_props);
+        count = 0;
+        ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr));
+        ASSERT_EQ(count, 4U);  // dont use the intercepted count - use default count
+
+        // check version
+        uint32_t layer_version = VK_MAKE_API_VERSION(1, 2, 3, 4);
+        layer.set_reported_instance_version(layer_version);
+
+        uint32_t version = 0;
+        ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceVersion(&version));
+        ASSERT_EQ(version, VK_HEADER_VERSION_COMPLETE);
+    }
+
+    // control is set to auto
+    env.loader_settings.app_specific_settings.at(0).layer_configurations.at(0).set_control("auto");
+    env.update_loader_settings(env.loader_settings);
+
+    {
+        auto& layer = env.get_test_layer(0);
+        // Check layer props
+        uint32_t layer_props = 43;
+        layer.set_reported_layer_props(layer_props);
+
+        uint32_t count = 0;
+        ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceLayerProperties(&count, nullptr));
+        ASSERT_EQ(count, layer_props);
+
+        // check extension props
+        uint32_t ext_props = 52;
+        layer.set_reported_extension_props(ext_props);
+        count = 0;
+        ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr));
+        ASSERT_EQ(count, ext_props);
+
+        // check version
+        uint32_t layer_version = VK_MAKE_API_VERSION(1, 2, 3, 4);
+        layer.set_reported_instance_version(layer_version);
+
+        uint32_t version = 0;
+        ASSERT_EQ(VK_SUCCESS, env.vulkan_functions.vkEnumerateInstanceVersion(&version));
+        ASSERT_EQ(version, layer_version);
+    }
+}
+
+// If an implicit layer's disable environment variable is set, but the settings file says to turn the layer on, the layer should be
+// activated.
+TEST(SettingsFile, ImplicitLayerDisableEnvironmentVariableOverriden) {
+    auto check_log_for_insert_instance_layer_string = [](FrameworkEnvironment& env, const char* implicit_layer_name,
+                                                         bool check_for_enable) {
+        {
+            InstWrapper inst{env.vulkan_functions};
+            FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+            inst.CheckCreate(VK_SUCCESS);
+            if (check_for_enable) {
+                ASSERT_TRUE(env.debug_log.find(std::string("Insert instance layer \"") + implicit_layer_name));
+                auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1);
+                ASSERT_TRUE(string_eq(layers.at(0).layerName, implicit_layer_name));
+            } else {
+                ASSERT_FALSE(env.debug_log.find(std::string("Insert instance layer \"") + implicit_layer_name));
+                ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0));
+            }
+        }
+        env.debug_log.clear();
+    };
+
+    FrameworkEnvironment env;
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA));
+    env.get_test_icd().add_physical_device({});
+    const char* implicit_layer_name = "VK_LAYER_ImplicitTestLayer";
+
+    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_ME")
+                                                         .set_enable_environment("ENABLE_ME")),
+                           "implicit_test_layer.json");
+    env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(
+        LoaderSettingsLayerConfiguration{}
+            .set_name(implicit_layer_name)
+            .set_path(env.get_shimmed_layer_manifest_path(0).str())
+            .set_treat_as_implicit_manifest(true)));
+
+    // control is set to on
+    env.loader_settings.app_specific_settings.at(0).layer_configurations.at(0).set_control("on");
+    env.update_loader_settings(env.loader_settings);
+    {
+        EnvVarWrapper enable_env_var{"ENABLE_ME"};
+        EnvVarWrapper disable_env_var{"DISABLE_ME"};
+
+        auto layers = env.GetLayerProperties(1);
+        ASSERT_TRUE(string_eq(layers[0].layerName, implicit_layer_name));
+
+        check_log_for_insert_instance_layer_string(env, implicit_layer_name, true);
+
+        enable_env_var.set_new_value("0");
+        check_log_for_insert_instance_layer_string(env, implicit_layer_name, true);
+
+        enable_env_var.set_new_value("1");
+        check_log_for_insert_instance_layer_string(env, implicit_layer_name, true);
+
+        enable_env_var.remove_value();
+
+        disable_env_var.set_new_value("0");
+        check_log_for_insert_instance_layer_string(env, implicit_layer_name, true);
+
+        disable_env_var.set_new_value("1");
+        check_log_for_insert_instance_layer_string(env, implicit_layer_name, true);
+
+        enable_env_var.set_new_value("1");
+        disable_env_var.set_new_value("1");
+        check_log_for_insert_instance_layer_string(env, implicit_layer_name, true);
+    }
+
+    // control is set to off
+    env.loader_settings.app_specific_settings.at(0).layer_configurations.at(0).set_control("off");
+    env.update_loader_settings(env.loader_settings);
+    {
+        EnvVarWrapper enable_env_var{"ENABLE_ME"};
+        EnvVarWrapper disable_env_var{"DISABLE_ME"};
+
+        ASSERT_NO_FATAL_FAILURE(env.GetLayerProperties(0));
+
+        check_log_for_insert_instance_layer_string(env, implicit_layer_name, false);
+
+        enable_env_var.set_new_value("0");
+        check_log_for_insert_instance_layer_string(env, implicit_layer_name, false);
+
+        enable_env_var.set_new_value("1");
+        check_log_for_insert_instance_layer_string(env, implicit_layer_name, false);
+
+        enable_env_var.remove_value();
+
+        disable_env_var.set_new_value("0");
+        check_log_for_insert_instance_layer_string(env, implicit_layer_name, false);
+
+        disable_env_var.set_new_value("1");
+        check_log_for_insert_instance_layer_string(env, implicit_layer_name, false);
+
+        enable_env_var.set_new_value("1");
+        disable_env_var.set_new_value("1");
+        check_log_for_insert_instance_layer_string(env, implicit_layer_name, false);
+    }
+
+    // control is set to auto
+    env.loader_settings.app_specific_settings.at(0).layer_configurations.at(0).set_control("auto");
+    env.update_loader_settings(env.loader_settings);
+    {
+        EnvVarWrapper enable_env_var{"ENABLE_ME"};
+        EnvVarWrapper disable_env_var{"DISABLE_ME"};
+
+        auto layers = env.GetLayerProperties(1);
+        ASSERT_TRUE(string_eq(layers[0].layerName, implicit_layer_name));
+
+        check_log_for_insert_instance_layer_string(env, implicit_layer_name, false);
+
+        enable_env_var.set_new_value("0");
+        check_log_for_insert_instance_layer_string(env, implicit_layer_name, false);
+
+        enable_env_var.set_new_value("1");
+        check_log_for_insert_instance_layer_string(env, implicit_layer_name, true);
+
+        enable_env_var.remove_value();
+
+        disable_env_var.set_new_value("0");
+        check_log_for_insert_instance_layer_string(env, implicit_layer_name, false);
+
+        disable_env_var.set_new_value("1");
+        check_log_for_insert_instance_layer_string(env, implicit_layer_name, false);
+
+        enable_env_var.set_new_value("1");
+        disable_env_var.set_new_value("1");
+        check_log_for_insert_instance_layer_string(env, implicit_layer_name, false);
+    }
+}
+
+// Settings can say which filters to use - make sure those are propagated & treated correctly
+TEST(SettingsFile, StderrLogFilters) {
+    FrameworkEnvironment env{};
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    env.get_test_icd().add_physical_device({});
+    const char* explicit_layer_name = "Regular_TestLayer1";
+    env.add_explicit_layer(TestLayerDetails{
+        ManifestLayer{}.add_layer(
+            ManifestLayer::LayerDescription{}.set_name(explicit_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+        "explicit_test_layer1.json"});
+    env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
+        AppSpecificSettings{}
+            .add_layer_configuration(LoaderSettingsLayerConfiguration{}
+                                         .set_name(explicit_layer_name)
+                                         .set_path(env.get_shimmed_layer_manifest_path().str())
+                                         .set_control("on"))
+            .add_layer_configuration(
+                LoaderSettingsLayerConfiguration{}.set_name("VK_LAYER_missing").set_path("/road/to/nowhere").set_control("on"))));
+
+    std::string expected_output_verbose;
+    expected_output_verbose += "Layer Configurations count = 2\n";
+    expected_output_verbose += "---- Layer Configuration [0] ----\n";
+    expected_output_verbose += std::string("Name: ") + explicit_layer_name + "\n";
+    expected_output_verbose += "Path: " + env.get_shimmed_layer_manifest_path().str() + "\n";
+    expected_output_verbose += "Control: on\n";
+    expected_output_verbose += "---- Layer Configuration [1] ----\n";
+    expected_output_verbose += "Name: VK_LAYER_missing\n";
+    expected_output_verbose += "Path: /road/to/nowhere\n";
+    expected_output_verbose += "Control: on\n";
+    expected_output_verbose += "---------------------------------\n";
+
+    std::string expected_output_info = get_settings_location_log_message(env) + "\n";
+
+    std::string expected_output_warning =
+        "Layer name Regular_TestLayer1 does not conform to naming standard (Policy #LLP_LAYER_3)\n";
+
+    std::string expected_output_error = "loader_get_json: Failed to open JSON file /road/to/nowhere\n";
+
+    env.loader_settings.app_specific_settings.at(0).stderr_log = {"all"};
+    env.update_loader_settings(env.loader_settings);
+    {
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+
+        ASSERT_TRUE(env.debug_log.find(expected_output_verbose));
+        ASSERT_TRUE(env.debug_log.find(expected_output_info));
+        ASSERT_TRUE(env.debug_log.find(expected_output_warning));
+        ASSERT_TRUE(env.debug_log.find(expected_output_error));
+        auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1);
+        EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name));
+    }
+    env.debug_log.clear();
+    env.debug_log.create_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT;
+    env.loader_settings.app_specific_settings.at(0).stderr_log = {"info"};
+    env.update_loader_settings(env.loader_settings);
+    {
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+
+        ASSERT_TRUE(env.debug_log.find(expected_output_verbose));
+        ASSERT_FALSE(env.debug_log.find(expected_output_info));
+        ASSERT_FALSE(env.debug_log.find(expected_output_warning));
+        ASSERT_FALSE(env.debug_log.find(expected_output_error));
+        auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1);
+        EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name));
+    }
+    env.debug_log.clear();
+    env.debug_log.create_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT;
+    env.loader_settings.app_specific_settings.at(0).stderr_log = {"debug"};
+    env.update_loader_settings(env.loader_settings);
+    {
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+
+        ASSERT_FALSE(env.debug_log.find(expected_output_verbose));
+        ASSERT_TRUE(env.debug_log.find(expected_output_info));
+        ASSERT_FALSE(env.debug_log.find(expected_output_warning));
+        ASSERT_FALSE(env.debug_log.find(expected_output_error));
+        auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1);
+        EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name));
+    }
+    env.debug_log.clear();
+    env.debug_log.create_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
+    env.loader_settings.app_specific_settings.at(0).stderr_log = {"warn"};
+    env.update_loader_settings(env.loader_settings);
+    {
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+
+        ASSERT_FALSE(env.debug_log.find(expected_output_verbose));
+        ASSERT_FALSE(env.debug_log.find(expected_output_info));
+        ASSERT_TRUE(env.debug_log.find(expected_output_warning));
+        ASSERT_FALSE(env.debug_log.find(expected_output_error));
+        auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1);
+        EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name));
+    }
+    env.debug_log.clear();
+    env.debug_log.create_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
+    env.loader_settings.app_specific_settings.at(0).stderr_log = {"error"};
+    env.update_loader_settings(env.loader_settings);
+    {
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+
+        ASSERT_FALSE(env.debug_log.find(expected_output_verbose));
+        ASSERT_FALSE(env.debug_log.find(expected_output_info));
+        ASSERT_FALSE(env.debug_log.find(expected_output_warning));
+        ASSERT_TRUE(env.debug_log.find(expected_output_error));
+        auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1);
+        EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, explicit_layer_name));
+    }
+}
+
+// Enough layers exist that arrays need to be resized - make sure that works
+TEST(SettingsFile, TooManyLayers) {
+    FrameworkEnvironment env{};
+    env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2));
+    env.get_test_icd().add_physical_device({});
+    env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(
+        AppSpecificSettings{}.add_stderr_log_filter("all"));
+    std::string layer_name = "VK_LAYER_regular_layer_name_";
+    uint32_t layer_count = 40;
+    for (uint32_t i = 0; i < layer_count; i++) {
+        env.add_explicit_layer(TestLayerDetails{ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
+                                                                              .set_name(layer_name + std::to_string(i))
+                                                                              .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)),
+                                                layer_name + std::to_string(i) + ".json"}
+                                   .set_discovery_type(ManifestDiscoveryType::override_folder));
+        env.loader_settings.app_specific_settings.at(0).add_layer_configuration(LoaderSettingsLayerConfiguration{}
+                                                                                    .set_name(layer_name + std::to_string(i))
+                                                                                    .set_path(env.get_layer_manifest_path(i).str())
+                                                                                    .set_control("on"));
+    }
+    env.update_loader_settings(env.loader_settings);
+
+    {
+        auto layer_props = env.GetLayerProperties(40);
+        for (uint32_t i = 0; i < layer_count; i++) {
+            std::string expected_layer_name = layer_name + std::to_string(i);
+            EXPECT_TRUE(string_eq(layer_props.at(i).layerName, expected_layer_name.c_str()));
+        }
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+
+        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
+        env.debug_log.clear();
+        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 40);
+        for (uint32_t i = 0; i < layer_count; i++) {
+            std::string expected_layer_name = layer_name + std::to_string(i);
+            EXPECT_TRUE(string_eq(layers.at(i).layerName, expected_layer_name.c_str()));
+        }
+    }
+    env.loader_settings.app_specific_settings.at(0).layer_configurations.clear();
+
+    // Now reverse the order to make sure adding the 'last' layer first works
+    for (uint32_t i = 0; i < layer_count; i++) {
+        env.loader_settings.app_specific_settings.at(0).add_layer_configuration(
+            LoaderSettingsLayerConfiguration{}
+                .set_name(layer_name + std::to_string(layer_count - i - 1))
+                .set_path(env.get_layer_manifest_path(layer_count - i - 1).str())
+                .set_control("on"));
+    }
+    env.update_loader_settings(env.loader_settings);
+
+    {
+        auto layer_props = env.GetLayerProperties(40);
+        for (uint32_t i = 0; i < layer_count; i++) {
+            std::string expected_layer_name = layer_name + std::to_string(layer_count - i - 1);
+            EXPECT_TRUE(string_eq(layer_props.at(i).layerName, expected_layer_name.c_str()));
+        }
+        InstWrapper inst{env.vulkan_functions};
+        FillDebugUtilsCreateDetails(inst.create_info, env.debug_log);
+        inst.CheckCreate();
+
+        ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env)));
+        env.debug_log.clear();
+        auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 40);
+        for (uint32_t i = 0; i < layer_count; i++) {
+            std::string expected_layer_name = layer_name + std::to_string(layer_count - i - 1);
+            EXPECT_TRUE(string_eq(layers.at(i).layerName, expected_layer_name.c_str()));
+        }
+    }
+}