"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",
loader.h
log.c
log.h
+ settings.c
+ settings.h
terminator.c
trampoline.c
unknown_function_handling.c
} 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);
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;
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
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
// 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;
}
// 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,
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,
}
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;
// 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 "
}
} 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;
}
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
+ // 7 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");
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();
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);
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.
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);
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) {
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, ®ular_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, ®ular_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 = ®ular_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);
}
// 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, ®ular_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, ®ular_instance_layers, &override_layer_valid);
if (override_layer_valid) {
- loader_remove_layers_in_blacklist(inst, instance_layers);
+ loader_remove_layers_in_blacklist(inst, ®ular_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, ®ular_instance_layers.list[i])) {
+ loader_remove_layer_in_list(inst, ®ular_instance_layers, i);
i--;
}
}
+ res = combine_settings_layers_with_regular_layers(inst, &settings_layers, ®ular_instance_layers, instance_layers);
+
out:
+ loader_delete_layer_list_and_properties(inst, &settings_layers);
+ loader_delete_layer_list_and_properties(inst, ®ular_instance_layers);
loader_instance_heap_free(inst, override_paths);
return res;
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) {
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, ®ular_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, ®ular_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 = ®ular_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);
// 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, ®ular_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, ®ular_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, ®ular_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, ®ular_instance_layers.list[i])) {
+ loader_remove_layer_in_list(inst, ®ular_instance_layers, i);
i--;
}
}
+ res = combine_settings_layers_with_regular_layers(inst, &settings_layers, ®ular_instance_layers, instance_layers);
+
out:
+ loader_delete_layer_list_and_properties(inst, &settings_layers);
+ loader_delete_layer_list_and_properties(inst, ®ular_instance_layers);
loader_instance_heap_free(inst, override_paths);
return res;
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;
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);
&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;
}
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;
// 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
}
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
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;
}
"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;
}
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) {
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;
#pragma once
#include "loader_common.h"
+#include "cJSON.h"
// Declare the once_init variable
LOADER_PLATFORM_THREAD_ONCE_EXTERN_DEFINITION(once_init)
// 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,
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,
#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,
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;
// 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;
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
// 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
#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;
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, ...) {
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
OutputDebugString(msg);
OutputDebugString("\n");
#endif
-
}
void loader_log_asm_function_not_supported(const struct loader_instance *inst, VkFlags msg_type, int32_t msg_code,
// 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);
--- /dev/null
+/*
+ *
+ * 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, ®ular_layers->list[i]) ||
+ check_if_layer_is_in_list(settings_layers, ®ular_layers->list[i]);
+ // If it isn't found, add it
+ if (!regular_layer_is_ordered) {
+ res = loader_append_layer_property(inst, output_layers, ®ular_layers->list[i]);
+ if (VK_SUCCESS != res) {
+ goto out;
+ }
+ } else {
+ // layer is already ordered and can be safely freed
+ loader_free_layer_properties(inst, ®ular_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;
+}
--- /dev/null
+/*
+ *
+ * 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);
#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"
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 = {
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 = {
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 "
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);
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;
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
}
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);
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);
// 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"
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)
#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)
// 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)
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;
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;
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++));
}
#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";
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);
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());
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
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,
}
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) {
}
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"));
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) {
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();
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() {
}
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());
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;
}
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();
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 {
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;
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
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 {
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);
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);
};
}
}
+// 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) {
--- /dev/null
+/*
+ * 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()));
+ }
+ }
+}