| /* |
| * |
| * Copyright (c) 2014-2023 The Khronos Group Inc. |
| * Copyright (c) 2014-2023 Valve Corporation |
| * Copyright (c) 2014-2023 LunarG, Inc. |
| * Copyright (C) 2015 Google Inc. |
| * Copyright (c) 2021-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. |
| * Copyright (c) 2023-2023 RasterGrid Kft. |
| * |
| * 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: Jon Ashburn <jon@lunarg.com> |
| * Author: Courtney Goeltzenleuchter <courtney@LunarG.com> |
| * Author: Mark Young <marky@lunarg.com> |
| * Author: Lenny Komow <lenny@lunarg.com> |
| * Author: Charles Giessen <charles@lunarg.com> |
| * |
| */ |
| |
| #include "loader.h" |
| |
| #include <ctype.h> |
| #include <inttypes.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include <stdbool.h> |
| #include <string.h> |
| #include <stddef.h> |
| |
| #if defined(__APPLE__) |
| #include <CoreFoundation/CoreFoundation.h> |
| #include <sys/param.h> |
| #endif |
| |
| #include <sys/types.h> |
| #if defined(_WIN32) |
| #include "dirent_on_windows.h" |
| #elif COMMON_UNIX_PLATFORMS |
| #include <dirent.h> |
| #else |
| #warning dirent.h not available on this platform |
| #endif // _WIN32 |
| |
| #include "allocation.h" |
| #include "cJSON.h" |
| #include "debug_utils.h" |
| #include "loader_environment.h" |
| #include "gpa_helper.h" |
| #include "log.h" |
| #include "unknown_function_handling.h" |
| #include "vk_loader_platform.h" |
| #include "wsi.h" |
| |
| #if defined(WIN32) |
| #include "loader_windows.h" |
| #endif |
| #if defined(LOADER_ENABLE_LINUX_SORT) |
| // This header is currently only used when sorting Linux devices, so don't include it otherwise. |
| #include "loader_linux.h" |
| #endif // LOADER_ENABLE_LINUX_SORT |
| |
| // Generated file containing all the extension data |
| #include "vk_loader_extensions.c" |
| |
| struct loader_struct loader = {0}; |
| |
| struct activated_layer_info { |
| char *name; |
| char *manifest; |
| char *library; |
| bool is_implicit; |
| char *disable_env; |
| }; |
| |
| // thread safety lock for accessing global data structures such as "loader" |
| // all entrypoints on the instance chain need to be locked except GPA |
| // additionally CreateDevice and DestroyDevice needs to be locked |
| loader_platform_thread_mutex loader_lock; |
| loader_platform_thread_mutex loader_preload_icd_lock; |
| loader_platform_thread_mutex loader_global_instance_list_lock; |
| |
| // A list of ICDs that gets initialized when the loader does its global initialization. This list should never be used by anything |
| // other than EnumerateInstanceExtensionProperties(), vkDestroyInstance, and loader_release(). This list does not change |
| // functionality, but the fact that the libraries already been loaded causes any call that needs to load ICD libraries to speed up |
| // significantly. This can have a huge impact when making repeated calls to vkEnumerateInstanceExtensionProperties and |
| // vkCreateInstance. |
| struct loader_icd_tramp_list preloaded_icds; |
| |
| // controls whether loader_platform_close_library() closes the libraries or not - controlled by an environment |
| // variables - this is just the definition of the variable, usage is in vk_loader_platform.h |
| bool loader_disable_dynamic_library_unloading; |
| |
| LOADER_PLATFORM_THREAD_ONCE_DECLARATION(once_init); |
| |
| // Creates loader_api_version struct that contains the major and minor fields, setting patch to 0 |
| loader_api_version loader_make_version(uint32_t version) { |
| loader_api_version out_version; |
| out_version.major = VK_API_VERSION_MAJOR(version); |
| out_version.minor = VK_API_VERSION_MINOR(version); |
| out_version.patch = 0; |
| return out_version; |
| } |
| |
| // Creates loader_api_version struct containing the major, minor, and patch fields |
| loader_api_version loader_make_full_version(uint32_t version) { |
| loader_api_version out_version; |
| out_version.major = VK_API_VERSION_MAJOR(version); |
| out_version.minor = VK_API_VERSION_MINOR(version); |
| out_version.patch = VK_API_VERSION_PATCH(version); |
| return out_version; |
| } |
| |
| loader_api_version loader_combine_version(uint32_t major, uint32_t minor, uint32_t patch) { |
| loader_api_version out_version; |
| out_version.major = (uint16_t)major; |
| out_version.minor = (uint16_t)minor; |
| out_version.patch = (uint16_t)patch; |
| return out_version; |
| } |
| |
| // Helper macros for determining if a version is valid or not |
| bool loader_check_version_meets_required(loader_api_version required, loader_api_version version) { |
| // major version is satisfied |
| return (version.major > required.major) || |
| // major version is equal, minor version is patch version is greater to minimum minor |
| (version.major == required.major && version.minor > required.minor) || |
| // major and minor version are equal, patch version is greater or equal to minimum patch |
| (version.major == required.major && version.minor == required.minor && version.patch >= required.patch); |
| } |
| |
| // Wrapper around opendir so that the dirent_on_windows gets the instance it needs |
| // while linux opendir & readdir does not |
| DIR *loader_opendir(const struct loader_instance *instance, const char *name) { |
| #if defined(_WIN32) |
| return opendir(instance ? &instance->alloc_callbacks : NULL, name); |
| #elif COMMON_UNIX_PLATFORMS |
| (void)instance; |
| return opendir(name); |
| #else |
| #warning dirent.h - opendir not available on this platform |
| #endif // _WIN32 |
| } |
| int loader_closedir(const struct loader_instance *instance, DIR *dir) { |
| #if defined(_WIN32) |
| return closedir(instance ? &instance->alloc_callbacks : NULL, dir); |
| #elif COMMON_UNIX_PLATFORMS |
| (void)instance; |
| return closedir(dir); |
| #else |
| #warning dirent.h - closedir not available on this platform |
| #endif // _WIN32 |
| } |
| |
| bool is_json(const char *path, size_t len) { |
| if (len < 5) { |
| return false; |
| } |
| return !strncmp(path, ".json", 5); |
| } |
| |
| // Handle error from to library loading |
| void loader_handle_load_library_error(const struct loader_instance *inst, const char *filename, |
| enum loader_layer_library_status *lib_status) { |
| const char *error_message = loader_platform_open_library_error(filename); |
| // If the error is due to incompatible architecture (eg 32 bit vs 64 bit), report it with INFO level |
| // Discussed in Github issue 262 & 644 |
| // "wrong ELF class" is a linux error, " with error 193" is a windows error |
| VkFlags err_flag = VULKAN_LOADER_ERROR_BIT; |
| if (strstr(error_message, "wrong ELF class:") != NULL || strstr(error_message, " with error 193") != NULL) { |
| err_flag = VULKAN_LOADER_INFO_BIT; |
| if (NULL != lib_status) { |
| *lib_status = LOADER_LAYER_LIB_ERROR_WRONG_BIT_TYPE; |
| } |
| } |
| // Check if the error is due to lack of memory |
| // "with error 8" is the windows error code for OOM cases, aka ERROR_NOT_ENOUGH_MEMORY |
| // Linux doesn't have such a nice error message - only if there are reported issues should this be called |
| else if (strstr(error_message, " with error 8") != NULL) { |
| if (NULL != lib_status) { |
| *lib_status = LOADER_LAYER_LIB_ERROR_OUT_OF_MEMORY; |
| } |
| } else if (NULL != lib_status) { |
| *lib_status = LOADER_LAYER_LIB_ERROR_FAILED_TO_LOAD; |
| } |
| loader_log(inst, err_flag, 0, error_message); |
| } |
| |
| VKAPI_ATTR VkResult VKAPI_CALL vkSetInstanceDispatch(VkInstance instance, void *object) { |
| struct loader_instance *inst = loader_get_instance(instance); |
| if (!inst) { |
| loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "vkSetInstanceDispatch: Can not retrieve Instance dispatch table."); |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| loader_set_dispatch(object, inst->disp); |
| return VK_SUCCESS; |
| } |
| |
| VKAPI_ATTR VkResult VKAPI_CALL vkSetDeviceDispatch(VkDevice device, void *object) { |
| struct loader_device *dev; |
| struct loader_icd_term *icd_term = loader_get_icd_and_device(device, &dev); |
| |
| if (NULL == icd_term || NULL == dev) { |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| loader_set_dispatch(object, &dev->loader_dispatch); |
| return VK_SUCCESS; |
| } |
| |
| void loader_free_layer_properties(const struct loader_instance *inst, struct loader_layer_properties *layer_properties) { |
| loader_instance_heap_free(inst, layer_properties->manifest_file_name); |
| loader_instance_heap_free(inst, layer_properties->lib_name); |
| loader_instance_heap_free(inst, layer_properties->functions.str_gipa); |
| loader_instance_heap_free(inst, layer_properties->functions.str_gdpa); |
| loader_instance_heap_free(inst, layer_properties->functions.str_negotiate_interface); |
| loader_destroy_generic_list(inst, (struct loader_generic_list *)&layer_properties->instance_extension_list); |
| if (layer_properties->device_extension_list.capacity > 0 && NULL != layer_properties->device_extension_list.list) { |
| for (uint32_t i = 0; i < layer_properties->device_extension_list.count; i++) { |
| free_string_list(inst, &layer_properties->device_extension_list.list[i].entrypoints); |
| } |
| } |
| loader_destroy_generic_list(inst, (struct loader_generic_list *)&layer_properties->device_extension_list); |
| loader_instance_heap_free(inst, layer_properties->disable_env_var.name); |
| loader_instance_heap_free(inst, layer_properties->disable_env_var.value); |
| loader_instance_heap_free(inst, layer_properties->enable_env_var.name); |
| loader_instance_heap_free(inst, layer_properties->enable_env_var.value); |
| free_string_list(inst, &layer_properties->component_layer_names); |
| loader_instance_heap_free(inst, layer_properties->pre_instance_functions.enumerate_instance_extension_properties); |
| loader_instance_heap_free(inst, layer_properties->pre_instance_functions.enumerate_instance_layer_properties); |
| loader_instance_heap_free(inst, layer_properties->pre_instance_functions.enumerate_instance_version); |
| free_string_list(inst, &layer_properties->override_paths); |
| free_string_list(inst, &layer_properties->blacklist_layer_names); |
| free_string_list(inst, &layer_properties->app_key_paths); |
| |
| // Make sure to clear out the removed layer, in case new layers are added in the previous location |
| memset(layer_properties, 0, sizeof(struct loader_layer_properties)); |
| } |
| |
| VkResult loader_init_library_list(struct loader_layer_list *instance_layers, loader_platform_dl_handle **libs) { |
| if (instance_layers->count > 0) { |
| *libs = loader_calloc(NULL, sizeof(loader_platform_dl_handle) * instance_layers->count, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); |
| if (*libs == NULL) { |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| } |
| return VK_SUCCESS; |
| } |
| |
| VkResult loader_copy_to_new_str(const struct loader_instance *inst, const char *source_str, char **dest_str) { |
| assert(source_str && dest_str); |
| size_t str_len = strlen(source_str) + 1; |
| *dest_str = loader_instance_heap_calloc(inst, str_len, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); |
| if (NULL == *dest_str) return VK_ERROR_OUT_OF_HOST_MEMORY; |
| loader_strncpy(*dest_str, str_len, source_str, str_len); |
| (*dest_str)[str_len - 1] = 0; |
| return VK_SUCCESS; |
| } |
| |
| VkResult create_string_list(const struct loader_instance *inst, uint32_t allocated_count, struct loader_string_list *string_list) { |
| assert(string_list); |
| string_list->list = loader_instance_heap_calloc(inst, sizeof(char *) * allocated_count, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); |
| if (NULL == string_list->list) { |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| string_list->allocated_count = allocated_count; |
| string_list->count = 0; |
| return VK_SUCCESS; |
| } |
| |
| VkResult append_str_to_string_list(const struct loader_instance *inst, struct loader_string_list *string_list, char *str) { |
| assert(string_list && str); |
| if (string_list->allocated_count == 0) { |
| string_list->allocated_count = 32; |
| string_list->list = |
| loader_instance_heap_calloc(inst, sizeof(char *) * string_list->allocated_count, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); |
| if (NULL == string_list->list) { |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| } else if (string_list->count + 1 > string_list->allocated_count) { |
| uint32_t new_allocated_count = string_list->allocated_count * 2; |
| string_list->list = loader_instance_heap_realloc(inst, string_list->list, sizeof(char *) * string_list->allocated_count, |
| sizeof(char *) * new_allocated_count, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); |
| 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 VK_SUCCESS; |
| } |
| |
| VkResult copy_str_to_string_list(const struct loader_instance *inst, struct loader_string_list *string_list, const char *str, |
| size_t str_len) { |
| assert(string_list && str); |
| char *new_str = loader_instance_heap_calloc(inst, sizeof(char *) * str_len + 1, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); |
| if (NULL == new_str) { |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| loader_strncpy(new_str, sizeof(char *) * str_len + 1, str, str_len); |
| new_str[str_len] = '\0'; |
| VkResult res = append_str_to_string_list(inst, string_list, new_str); |
| if (res != VK_SUCCESS) { |
| // Cleanup new_str if the append failed - as append_str_to_string_list takes ownership but not if the function fails |
| loader_instance_heap_free(inst, new_str); |
| } |
| return res; |
| } |
| |
| void free_string_list(const struct loader_instance *inst, struct loader_string_list *string_list) { |
| assert(string_list); |
| if (string_list->list) { |
| for (uint32_t i = 0; i < string_list->count; i++) { |
| loader_instance_heap_free(inst, string_list->list[i]); |
| string_list->list[i] = NULL; |
| } |
| loader_instance_heap_free(inst, string_list->list); |
| } |
| memset(string_list, 0, sizeof(struct loader_string_list)); |
| } |
| |
| // Given string of three part form "maj.min.pat" convert to a vulkan version number. |
| // Also can understand four part form "variant.major.minor.patch" if provided. |
| uint32_t loader_parse_version_string(char *vers_str) { |
| uint32_t variant = 0, major = 0, minor = 0, patch = 0; |
| char *vers_tok; |
| char *context = NULL; |
| if (!vers_str) { |
| return 0; |
| } |
| |
| vers_tok = thread_safe_strtok(vers_str, ".\"\n\r", &context); |
| if (NULL != vers_tok) { |
| major = (uint16_t)atoi(vers_tok); |
| vers_tok = thread_safe_strtok(NULL, ".\"\n\r", &context); |
| if (NULL != vers_tok) { |
| minor = (uint16_t)atoi(vers_tok); |
| vers_tok = thread_safe_strtok(NULL, ".\"\n\r", &context); |
| if (NULL != vers_tok) { |
| patch = (uint16_t)atoi(vers_tok); |
| vers_tok = thread_safe_strtok(NULL, ".\"\n\r", &context); |
| // check that we are using a 4 part version string |
| if (NULL != vers_tok) { |
| // if we are, move the values over into the correct place |
| variant = major; |
| major = minor; |
| minor = patch; |
| patch = (uint16_t)atoi(vers_tok); |
| } |
| } |
| } |
| } |
| |
| return VK_MAKE_API_VERSION(variant, major, minor, patch); |
| } |
| |
| bool compare_vk_extension_properties(const VkExtensionProperties *op1, const VkExtensionProperties *op2) { |
| return strcmp(op1->extensionName, op2->extensionName) == 0 ? true : false; |
| } |
| |
| // Search the given ext_array for an extension matching the given vk_ext_prop |
| bool has_vk_extension_property_array(const VkExtensionProperties *vk_ext_prop, const uint32_t count, |
| const VkExtensionProperties *ext_array) { |
| for (uint32_t i = 0; i < count; i++) { |
| if (compare_vk_extension_properties(vk_ext_prop, &ext_array[i])) return true; |
| } |
| return false; |
| } |
| |
| // Search the given ext_list for an extension matching the given vk_ext_prop |
| bool has_vk_extension_property(const VkExtensionProperties *vk_ext_prop, const struct loader_extension_list *ext_list) { |
| for (uint32_t i = 0; i < ext_list->count; i++) { |
| if (compare_vk_extension_properties(&ext_list->list[i], vk_ext_prop)) return true; |
| } |
| return false; |
| } |
| |
| // Search the given ext_list for a device extension matching the given ext_prop |
| bool has_vk_dev_ext_property(const VkExtensionProperties *ext_prop, const struct loader_device_extension_list *ext_list) { |
| for (uint32_t i = 0; i < ext_list->count; i++) { |
| if (compare_vk_extension_properties(&ext_list->list[i].props, ext_prop)) return true; |
| } |
| return false; |
| } |
| |
| 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) { |
| res = loader_init_generic_list(inst, (struct loader_generic_list *)layer_list, sizeof(struct loader_layer_properties)); |
| if (VK_SUCCESS != res) { |
| goto out; |
| } |
| } |
| |
| // Ensure enough room to add an entry |
| if ((layer_list->count + 1) * sizeof(struct loader_layer_properties) > layer_list->capacity) { |
| 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_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++; |
| 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 |
| struct loader_layer_properties *loader_find_layer_property(const char *name, const struct loader_layer_list *layer_list) { |
| for (uint32_t i = 0; i < layer_list->count; i++) { |
| const VkLayerProperties *item = &layer_list->list[i].info; |
| if (strcmp(name, item->layerName) == 0) return &layer_list->list[i]; |
| } |
| return NULL; |
| } |
| |
| struct loader_layer_properties *loader_find_pointer_layer_property(const char *name, |
| const struct loader_pointer_layer_list *layer_list) { |
| for (uint32_t i = 0; i < layer_list->count; i++) { |
| const VkLayerProperties *item = &layer_list->list[i]->info; |
| if (strcmp(name, item->layerName) == 0) return layer_list->list[i]; |
| } |
| return NULL; |
| } |
| |
| // Search the given layer list for a layer matching the given layer name |
| bool loader_find_layer_name_in_list(const char *name, const struct loader_pointer_layer_list *layer_list) { |
| if (NULL == layer_list) { |
| return false; |
| } |
| if (NULL != loader_find_pointer_layer_property(name, layer_list)) { |
| return true; |
| } |
| return false; |
| } |
| |
| // Search the given meta-layer's component list for a layer matching the given layer name |
| bool loader_find_layer_name_in_meta_layer(const struct loader_instance *inst, const char *layer_name, |
| struct loader_layer_list *layer_list, struct loader_layer_properties *meta_layer_props) { |
| for (uint32_t comp_layer = 0; comp_layer < meta_layer_props->component_layer_names.count; comp_layer++) { |
| if (!strcmp(meta_layer_props->component_layer_names.list[comp_layer], layer_name)) { |
| return true; |
| } |
| struct loader_layer_properties *comp_layer_props = |
| loader_find_layer_property(meta_layer_props->component_layer_names.list[comp_layer], layer_list); |
| if (comp_layer_props->type_flags & VK_LAYER_TYPE_FLAG_META_LAYER) { |
| return loader_find_layer_name_in_meta_layer(inst, layer_name, layer_list, comp_layer_props); |
| } |
| } |
| return false; |
| } |
| |
| // Search the override layer's blacklist for a layer matching the given layer name |
| bool loader_find_layer_name_in_blacklist(const char *layer_name, struct loader_layer_properties *meta_layer_props) { |
| for (uint32_t black_layer = 0; black_layer < meta_layer_props->blacklist_layer_names.count; ++black_layer) { |
| if (!strcmp(meta_layer_props->blacklist_layer_names.list[black_layer], layer_name)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| // Remove all layer properties entries from the list |
| void loader_delete_layer_list_and_properties(const struct loader_instance *inst, struct loader_layer_list *layer_list) { |
| uint32_t i; |
| if (!layer_list) return; |
| |
| for (i = 0; i < layer_list->count; i++) { |
| if (layer_list->list[i].lib_handle) { |
| loader_platform_close_library(layer_list->list[i].lib_handle); |
| loader_log(inst, VULKAN_LOADER_DEBUG_BIT | VULKAN_LOADER_LAYER_BIT, 0, "Unloading layer library %s", |
| layer_list->list[i].lib_name); |
| layer_list->list[i].lib_handle = NULL; |
| } |
| loader_free_layer_properties(inst, &(layer_list->list[i])); |
| } |
| layer_list->count = 0; |
| |
| if (layer_list->capacity > 0) { |
| layer_list->capacity = 0; |
| loader_instance_heap_free(inst, layer_list->list); |
| } |
| memset(layer_list, 0, sizeof(struct loader_layer_list)); |
| } |
| |
| void loader_remove_layer_in_list(const struct loader_instance *inst, struct loader_layer_list *layer_list, |
| uint32_t layer_to_remove) { |
| if (layer_list == NULL || layer_to_remove >= layer_list->count) { |
| return; |
| } |
| loader_free_layer_properties(inst, &(layer_list->list[layer_to_remove])); |
| |
| // Remove the current invalid meta-layer from the layer list. Use memmove since we are |
| // overlapping the source and destination addresses. |
| memmove(&layer_list->list[layer_to_remove], &layer_list->list[layer_to_remove + 1], |
| sizeof(struct loader_layer_properties) * (layer_list->count - 1 - layer_to_remove)); |
| |
| // Decrement the count (because we now have one less) and decrement the loop index since we need to |
| // re-check this index. |
| layer_list->count--; |
| } |
| |
| // Remove all layers in the layer list that are blacklisted by the override layer. |
| // NOTE: This should only be called if an override layer is found and not expired. |
| void loader_remove_layers_in_blacklist(const struct loader_instance *inst, struct loader_layer_list *layer_list) { |
| struct loader_layer_properties *override_prop = loader_find_layer_property(VK_OVERRIDE_LAYER_NAME, layer_list); |
| if (NULL == override_prop) { |
| return; |
| } |
| |
| for (int32_t j = 0; j < (int32_t)(layer_list->count); j++) { |
| struct loader_layer_properties cur_layer_prop = layer_list->list[j]; |
| const char *cur_layer_name = &cur_layer_prop.info.layerName[0]; |
| |
| // Skip the override layer itself. |
| if (!strcmp(VK_OVERRIDE_LAYER_NAME, cur_layer_name)) { |
| continue; |
| } |
| |
| // If found in the override layer's blacklist, remove it |
| if (loader_find_layer_name_in_blacklist(cur_layer_name, override_prop)) { |
| loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, |
| "loader_remove_layers_in_blacklist: Override layer is active and layer %s is in the blacklist inside of it. " |
| "Removing that layer from current layer list.", |
| cur_layer_name); |
| loader_remove_layer_in_list(inst, layer_list, j); |
| j--; |
| |
| // Re-do the query for the override layer |
| override_prop = loader_find_layer_property(VK_OVERRIDE_LAYER_NAME, layer_list); |
| } |
| } |
| } |
| |
| // Remove all layers in the layer list that are not found inside any implicit meta-layers. |
| void loader_remove_layers_not_in_implicit_meta_layers(const struct loader_instance *inst, struct loader_layer_list *layer_list) { |
| int32_t i; |
| int32_t j; |
| int32_t layer_count = (int32_t)(layer_list->count); |
| |
| for (i = 0; i < layer_count; i++) { |
| layer_list->list[i].keep = false; |
| } |
| |
| for (i = 0; i < layer_count; i++) { |
| struct loader_layer_properties *cur_layer_prop = &layer_list->list[i]; |
| |
| if (0 == (cur_layer_prop->type_flags & VK_LAYER_TYPE_FLAG_EXPLICIT_LAYER)) { |
| cur_layer_prop->keep = true; |
| continue; |
| } |
| for (j = 0; j < layer_count; j++) { |
| struct loader_layer_properties *layer_to_check = &layer_list->list[j]; |
| |
| if (i == j) { |
| continue; |
| } |
| |
| if (layer_to_check->type_flags & VK_LAYER_TYPE_FLAG_META_LAYER) { |
| // For all layers found in this meta layer, we want to keep them as well. |
| if (loader_find_layer_name_in_meta_layer(inst, cur_layer_prop->info.layerName, layer_list, layer_to_check)) { |
| cur_layer_prop->keep = true; |
| } |
| } |
| } |
| } |
| |
| // Remove any layers we don't want to keep (Don't use layer_count here as we need it to be |
| // dynamically updated if we delete a layer property in the list). |
| for (i = 0; i < (int32_t)(layer_list->count); i++) { |
| struct loader_layer_properties *cur_layer_prop = &layer_list->list[i]; |
| if (!cur_layer_prop->keep) { |
| loader_log( |
| inst, VULKAN_LOADER_DEBUG_BIT, 0, |
| "loader_remove_layers_not_in_implicit_meta_layers : Implicit meta-layers are active, and layer %s is not list " |
| "inside of any. So removing layer from current layer list.", |
| cur_layer_prop->info.layerName); |
| loader_remove_layer_in_list(inst, layer_list, i); |
| i--; |
| } |
| } |
| } |
| |
| VkResult loader_add_instance_extensions(const struct loader_instance *inst, |
| const PFN_vkEnumerateInstanceExtensionProperties fp_get_props, const char *lib_name, |
| struct loader_extension_list *ext_list) { |
| uint32_t i, count = 0; |
| VkExtensionProperties *ext_props; |
| VkResult res = VK_SUCCESS; |
| |
| if (!fp_get_props) { |
| // No EnumerateInstanceExtensionProperties defined |
| goto out; |
| } |
| |
| // Make sure we never call ourself by accident, this should never happen outside of error paths |
| if (fp_get_props == vkEnumerateInstanceExtensionProperties) { |
| loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, |
| "loader_add_instance_extensions: %s's vkEnumerateInstanceExtensionProperties points to the loader, this would " |
| "lead to infinite recursion.", |
| lib_name); |
| goto out; |
| } |
| |
| res = fp_get_props(NULL, &count, NULL); |
| if (res != VK_SUCCESS) { |
| loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, |
| "loader_add_instance_extensions: Error getting Instance extension count from %s", lib_name); |
| goto out; |
| } |
| |
| if (count == 0) { |
| // No ExtensionProperties to report |
| goto out; |
| } |
| |
| ext_props = loader_stack_alloc(count * sizeof(VkExtensionProperties)); |
| if (NULL == ext_props) { |
| res = VK_ERROR_OUT_OF_HOST_MEMORY; |
| goto out; |
| } |
| |
| res = fp_get_props(NULL, &count, ext_props); |
| if (res != VK_SUCCESS) { |
| loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_add_instance_extensions: Error getting Instance extensions from %s", |
| lib_name); |
| goto out; |
| } |
| |
| for (i = 0; i < count; i++) { |
| bool ext_unsupported = wsi_unsupported_instance_extension(&ext_props[i]); |
| if (!ext_unsupported) { |
| res = loader_add_to_ext_list(inst, ext_list, 1, &ext_props[i]); |
| if (res != VK_SUCCESS) { |
| goto out; |
| } |
| } |
| } |
| |
| out: |
| return res; |
| } |
| |
| VkResult loader_add_device_extensions(const struct loader_instance *inst, |
| PFN_vkEnumerateDeviceExtensionProperties fpEnumerateDeviceExtensionProperties, |
| VkPhysicalDevice physical_device, const char *lib_name, |
| struct loader_extension_list *ext_list) { |
| uint32_t i = 0, count = 0; |
| VkResult res = VK_SUCCESS; |
| VkExtensionProperties *ext_props = NULL; |
| |
| res = fpEnumerateDeviceExtensionProperties(physical_device, NULL, &count, NULL); |
| if (res != VK_SUCCESS) { |
| loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, |
| "loader_add_device_extensions: Error getting physical device extension info count from library %s", lib_name); |
| return res; |
| } |
| if (count > 0) { |
| ext_props = loader_stack_alloc(count * sizeof(VkExtensionProperties)); |
| if (!ext_props) { |
| loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, |
| "loader_add_device_extensions: Failed to allocate space for device extension properties from library %s.", |
| lib_name); |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| res = fpEnumerateDeviceExtensionProperties(physical_device, NULL, &count, ext_props); |
| if (res != VK_SUCCESS) { |
| return res; |
| } |
| for (i = 0; i < count; i++) { |
| res = loader_add_to_ext_list(inst, ext_list, 1, &ext_props[i]); |
| if (res != VK_SUCCESS) { |
| return res; |
| } |
| } |
| } |
| |
| return VK_SUCCESS; |
| } |
| |
| VkResult loader_init_generic_list(const struct loader_instance *inst, struct loader_generic_list *list_info, size_t element_size) { |
| size_t capacity = 32 * element_size; |
| list_info->count = 0; |
| list_info->capacity = 0; |
| list_info->list = loader_instance_heap_calloc(inst, capacity, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); |
| if (list_info->list == NULL) { |
| loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_init_generic_list: Failed to allocate space for generic list"); |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| list_info->capacity = capacity; |
| return VK_SUCCESS; |
| } |
| |
| VkResult loader_resize_generic_list(const struct loader_instance *inst, struct loader_generic_list *list_info) { |
| list_info->list = loader_instance_heap_realloc(inst, list_info->list, list_info->capacity, list_info->capacity * 2, |
| VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); |
| if (list_info->list == NULL) { |
| loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_resize_generic_list: Failed to allocate space for generic list"); |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| list_info->capacity = list_info->capacity * 2; |
| return VK_SUCCESS; |
| } |
| |
| void loader_destroy_generic_list(const struct loader_instance *inst, struct loader_generic_list *list) { |
| loader_instance_heap_free(inst, list->list); |
| memset(list, 0, sizeof(struct loader_generic_list)); |
| } |
| |
| VkResult loader_get_next_available_entry(const struct loader_instance *inst, struct loader_used_object_list *list_info, |
| uint32_t *free_index, const VkAllocationCallbacks *pAllocator) { |
| if (NULL == list_info->list) { |
| VkResult res = |
| loader_init_generic_list(inst, (struct loader_generic_list *)list_info, sizeof(struct loader_used_object_status)); |
| if (VK_SUCCESS != res) { |
| return res; |
| } |
| } |
| for (uint32_t i = 0; i < list_info->capacity / sizeof(struct loader_used_object_status); i++) { |
| if (list_info->list[i].status == VK_FALSE) { |
| list_info->list[i].status = VK_TRUE; |
| if (pAllocator) { |
| list_info->list[i].allocation_callbacks = *pAllocator; |
| } else { |
| memset(&list_info->list[i].allocation_callbacks, 0, sizeof(VkAllocationCallbacks)); |
| } |
| *free_index = i; |
| return VK_SUCCESS; |
| } |
| } |
| // No free space, must resize |
| |
| size_t old_capacity = list_info->capacity; |
| VkResult res = loader_resize_generic_list(inst, (struct loader_generic_list *)list_info); |
| if (VK_SUCCESS != res) { |
| return res; |
| } |
| uint32_t new_index = (uint32_t)(old_capacity / sizeof(struct loader_used_object_status)); |
| // Zero out the newly allocated back half of list. |
| memset(&list_info->list[new_index], 0, old_capacity); |
| list_info->list[new_index].status = VK_TRUE; |
| if (pAllocator) { |
| list_info->list[new_index].allocation_callbacks = *pAllocator; |
| } else { |
| memset(&list_info->list[new_index].allocation_callbacks, 0, sizeof(VkAllocationCallbacks)); |
| } |
| *free_index = new_index; |
| return VK_SUCCESS; |
| } |
| |
| void loader_release_object_from_list(struct loader_used_object_list *list_info, uint32_t index_to_free) { |
| if (list_info->list && list_info->capacity > index_to_free * sizeof(struct loader_used_object_status)) { |
| list_info->list[index_to_free].status = VK_FALSE; |
| memset(&list_info->list[index_to_free].allocation_callbacks, 0, sizeof(VkAllocationCallbacks)); |
| } |
| } |
| |
| // Append non-duplicate extension properties defined in props to the given ext_list. |
| // Return - Vk_SUCCESS on success |
| VkResult loader_add_to_ext_list(const struct loader_instance *inst, struct loader_extension_list *ext_list, |
| uint32_t prop_list_count, const VkExtensionProperties *props) { |
| if (ext_list->list == NULL || ext_list->capacity == 0) { |
| VkResult res = loader_init_generic_list(inst, (struct loader_generic_list *)ext_list, sizeof(VkExtensionProperties)); |
| if (VK_SUCCESS != res) { |
| return res; |
| } |
| } |
| |
| for (uint32_t i = 0; i < prop_list_count; i++) { |
| const VkExtensionProperties *cur_ext = &props[i]; |
| |
| // look for duplicates |
| if (has_vk_extension_property(cur_ext, ext_list)) { |
| continue; |
| } |
| |
| // add to list at end |
| // check for enough capacity |
| if (ext_list->count * sizeof(VkExtensionProperties) >= ext_list->capacity) { |
| void *new_ptr = loader_instance_heap_realloc(inst, ext_list->list, ext_list->capacity, ext_list->capacity * 2, |
| VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); |
| if (new_ptr == NULL) { |
| loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, |
| "loader_add_to_ext_list: Failed to reallocate space for extension list"); |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| ext_list->list = new_ptr; |
| |
| // double capacity |
| ext_list->capacity *= 2; |
| } |
| |
| memcpy(&ext_list->list[ext_list->count], cur_ext, sizeof(VkExtensionProperties)); |
| ext_list->count++; |
| } |
| return VK_SUCCESS; |
| } |
| |
| // 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 |
| 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; |
| bool should_free_entrys = true; |
| if (ext_list->list == NULL || ext_list->capacity == 0) { |
| res = loader_init_generic_list(inst, (struct loader_generic_list *)ext_list, sizeof(struct loader_dev_ext_props)); |
| if (VK_SUCCESS != res) { |
| goto out; |
| } |
| } |
| |
| // look for duplicates |
| if (has_vk_dev_ext_property(props, ext_list)) { |
| goto out; |
| } |
| |
| uint32_t idx = ext_list->count; |
| // add to list at end |
| // check for enough capacity |
| if (idx * sizeof(struct loader_dev_ext_props) >= ext_list->capacity) { |
| void *new_ptr = loader_instance_heap_realloc(inst, ext_list->list, ext_list->capacity, ext_list->capacity * 2, |
| VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); |
| |
| if (NULL == new_ptr) { |
| loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, |
| "loader_add_to_dev_ext_list: Failed to reallocate space for device extension list"); |
| res = VK_ERROR_OUT_OF_HOST_MEMORY; |
| goto out; |
| } |
| ext_list->list = new_ptr; |
| |
| // double capacity |
| ext_list->capacity *= 2; |
| } |
| |
| memcpy(&ext_list->list[idx].props, props, sizeof(*props)); |
| if (entrys) { |
| ext_list->list[idx].entrypoints = *entrys; |
| should_free_entrys = false; |
| } |
| ext_list->count++; |
| out: |
| if (NULL != entrys && should_free_entrys) { |
| free_string_list(inst, entrys); |
| } |
| return res; |
| } |
| |
| // Create storage for pointers to loader_layer_properties |
| bool loader_init_pointer_layer_list(const struct loader_instance *inst, struct loader_pointer_layer_list *list) { |
| list->capacity = 32 * sizeof(void *); |
| list->list = loader_instance_heap_calloc(inst, list->capacity, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); |
| if (list->list == NULL) { |
| return false; |
| } |
| list->count = 0; |
| return true; |
| } |
| |
| // Search the given array of layer names for an entry matching the given VkLayerProperties |
| bool loader_names_array_has_layer_property(const VkLayerProperties *vk_layer_prop, uint32_t layer_info_count, |
| struct activated_layer_info *layer_info) { |
| for (uint32_t i = 0; i < layer_info_count; i++) { |
| if (strcmp(vk_layer_prop->layerName, layer_info[i].name) == 0) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void loader_destroy_pointer_layer_list(const struct loader_instance *inst, struct loader_pointer_layer_list *layer_list) { |
| loader_instance_heap_free(inst, layer_list->list); |
| memset(layer_list, 0, sizeof(struct loader_pointer_layer_list)); |
| } |
| |
| // Append layer properties defined in prop_list to the given layer_info list |
| VkResult loader_add_layer_properties_to_list(const struct loader_instance *inst, struct loader_pointer_layer_list *list, |
| struct loader_layer_properties *props) { |
| if (list->list == NULL || list->capacity == 0) { |
| if (!loader_init_pointer_layer_list(inst, list)) { |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| } |
| |
| // Check for enough capacity |
| if (((list->count + 1) * sizeof(struct loader_layer_properties)) >= list->capacity) { |
| size_t new_capacity = list->capacity * 2; |
| void *new_ptr = |
| loader_instance_heap_realloc(inst, list->list, list->capacity, new_capacity, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); |
| if (NULL == new_ptr) { |
| loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, |
| "loader_add_layer_properties_to_list: Realloc failed for when attempting to add new layer"); |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| list->list = new_ptr; |
| list->capacity = new_capacity; |
| } |
| list->list[list->count++] = props; |
| |
| return VK_SUCCESS; |
| } |
| |
| // Determine if the provided explicit layer should be available by querying the appropriate environmental variables. |
| bool loader_layer_is_available(const struct loader_instance *inst, const struct loader_envvar_all_filters *filters, |
| const struct loader_layer_properties *prop) { |
| bool available = true; |
| bool is_implicit = (0 == (prop->type_flags & VK_LAYER_TYPE_FLAG_EXPLICIT_LAYER)); |
| bool disabled_by_type = |
| (is_implicit) ? (filters->disable_filter.disable_all_implicit) : (filters->disable_filter.disable_all_explicit); |
| if ((filters->disable_filter.disable_all || disabled_by_type || |
| check_name_matches_filter_environment_var(prop->info.layerName, &filters->disable_filter.additional_filters)) && |
| !check_name_matches_filter_environment_var(prop->info.layerName, &filters->allow_filter)) { |
| available = false; |
| } |
| if (check_name_matches_filter_environment_var(prop->info.layerName, &filters->enable_filter)) { |
| available = true; |
| } else if (!available) { |
| loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, |
| "Layer \"%s\" forced disabled because name matches filter of env var \'%s\'.", prop->info.layerName, |
| VK_LAYERS_DISABLE_ENV_VAR); |
| } |
| |
| return available; |
| } |
| |
| // Search the given search_list for any layers in the props list. Add these to the |
| // output layer_list. |
| VkResult loader_add_layer_names_to_list(const struct loader_instance *inst, const struct loader_envvar_all_filters *filters, |
| struct loader_pointer_layer_list *output_list, |
| struct loader_pointer_layer_list *expanded_output_list, uint32_t name_count, |
| const char *const *names, const struct loader_layer_list *source_list) { |
| VkResult err = VK_SUCCESS; |
| |
| for (uint32_t i = 0; i < name_count; i++) { |
| const char *source_name = names[i]; |
| |
| struct loader_layer_properties *layer_prop = loader_find_layer_property(source_name, source_list); |
| if (NULL == layer_prop) { |
| loader_log(inst, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_LAYER_BIT, 0, |
| "loader_add_layer_names_to_list: Unable to find layer \"%s\"", source_name); |
| err = VK_ERROR_LAYER_NOT_PRESENT; |
| continue; |
| } |
| |
| // Make sure the layer isn't already in the output_list, skip adding it if it is. |
| if (loader_find_layer_name_in_list(source_name, output_list)) { |
| continue; |
| } |
| |
| if (!loader_layer_is_available(inst, filters, layer_prop)) { |
| continue; |
| } |
| |
| // If not a meta-layer, simply add it. |
| if (0 == (layer_prop->type_flags & VK_LAYER_TYPE_FLAG_META_LAYER)) { |
| err = loader_add_layer_properties_to_list(inst, output_list, layer_prop); |
| if (err == VK_ERROR_OUT_OF_HOST_MEMORY) return err; |
| err = loader_add_layer_properties_to_list(inst, expanded_output_list, layer_prop); |
| if (err == VK_ERROR_OUT_OF_HOST_MEMORY) return err; |
| } else { |
| err = loader_add_meta_layer(inst, filters, layer_prop, output_list, expanded_output_list, source_list, NULL); |
| if (err == VK_ERROR_OUT_OF_HOST_MEMORY) return err; |
| } |
| } |
| |
| return err; |
| } |
| |
| // Determine if the provided implicit layer should be enabled by querying the appropriate environmental variables. |
| // For an implicit layer, at least a disable environment variable is required. |
| bool loader_implicit_layer_is_enabled(const struct loader_instance *inst, const struct loader_envvar_all_filters *filters, |
| const struct loader_layer_properties *prop) { |
| bool enable = false; |
| bool forced_disabled = false; |
| bool forced_enabled = false; |
| |
| if ((filters->disable_filter.disable_all || filters->disable_filter.disable_all_implicit || |
| check_name_matches_filter_environment_var(prop->info.layerName, &filters->disable_filter.additional_filters)) && |
| !check_name_matches_filter_environment_var(prop->info.layerName, &filters->allow_filter)) { |
| forced_disabled = true; |
| } |
| if (check_name_matches_filter_environment_var(prop->info.layerName, &filters->enable_filter)) { |
| forced_enabled = true; |
| } |
| |
| // If no enable_environment variable is specified, this implicit layer is always be enabled by default. |
| if (NULL == prop->enable_env_var.name) { |
| enable = true; |
| } else { |
| char *env_value = loader_getenv(prop->enable_env_var.name, inst); |
| if (env_value && !strcmp(prop->enable_env_var.value, env_value)) { |
| enable = true; |
| } |
| |
| // Otherwise, only enable this layer if the enable environment variable is defined |
| loader_free_getenv(env_value, inst); |
| } |
| |
| if (forced_enabled) { |
| // Only report a message that we've forced on a layer if it wouldn't have been enabled |
| // normally. |
| if (!enable) { |
| enable = true; |
| loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, |
| "Implicit layer \"%s\" forced enabled due to env var \'%s\'.", prop->info.layerName, |
| VK_LAYERS_ENABLE_ENV_VAR); |
| } |
| } else if (enable && forced_disabled) { |
| enable = false; |
| // Report a message that we've forced off a layer if it would have been enabled normally. |
| loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, |
| "Implicit layer \"%s\" forced disabled because name matches filter of env var \'%s\'.", prop->info.layerName, |
| VK_LAYERS_DISABLE_ENV_VAR); |
| return enable; |
| } |
| |
| // The disable_environment has priority over everything else. If it is defined, the layer is always |
| // disabled. |
| if (NULL != prop->disable_env_var.name) { |
| char *env_value = loader_getenv(prop->disable_env_var.name, inst); |
| if (NULL != env_value) { |
| enable = false; |
| } |
| loader_free_getenv(env_value, inst); |
| } else if ((prop->type_flags & VK_LAYER_TYPE_FLAG_EXPLICIT_LAYER) == 0) { |
| loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, |
| "Implicit layer \"%s\" missing disabled environment variable!", prop->info.layerName, VK_LAYERS_DISABLE_ENV_VAR); |
| } |
| |
| // Enable this layer if it is included in the override layer |
| if (inst != NULL && inst->override_layer_present) { |
| struct loader_layer_properties *override = NULL; |
| for (uint32_t i = 0; i < inst->instance_layer_list.count; ++i) { |
| if (strcmp(inst->instance_layer_list.list[i].info.layerName, VK_OVERRIDE_LAYER_NAME) == 0) { |
| override = &inst->instance_layer_list.list[i]; |
| break; |
| } |
| } |
| if (override != NULL) { |
| for (uint32_t i = 0; i < override->component_layer_names.count; ++i) { |
| if (strcmp(override->component_layer_names.list[i], prop->info.layerName) == 0) { |
| enable = true; |
| break; |
| } |
| } |
| } |
| } |
| |
| return enable; |
| } |
| |
| // 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. |
| VkResult loader_add_implicit_layer(const struct loader_instance *inst, struct loader_layer_properties *prop, |
| const struct loader_envvar_all_filters *filters, struct loader_pointer_layer_list *target_list, |
| struct loader_pointer_layer_list *expanded_target_list, |
| const struct loader_layer_list *source_list) { |
| VkResult result = VK_SUCCESS; |
| if (loader_implicit_layer_is_enabled(inst, filters, prop)) { |
| if (0 == (prop->type_flags & VK_LAYER_TYPE_FLAG_META_LAYER)) { |
| // Make sure the layer isn't already in the output_list, skip adding it if it is. |
| if (loader_find_layer_name_in_list(&prop->info.layerName[0], target_list)) { |
| return result; |
| } |
| |
| result = loader_add_layer_properties_to_list(inst, target_list, prop); |
| if (result == VK_ERROR_OUT_OF_HOST_MEMORY) return result; |
| if (NULL != expanded_target_list) { |
| result = loader_add_layer_properties_to_list(inst, expanded_target_list, prop); |
| } |
| } else { |
| result = loader_add_meta_layer(inst, filters, prop, target_list, expanded_target_list, source_list, NULL); |
| } |
| } |
| return result; |
| } |
| |
| // Add the component layers of a meta-layer to the active list of layers |
| VkResult loader_add_meta_layer(const struct loader_instance *inst, const struct loader_envvar_all_filters *filters, |
| struct loader_layer_properties *prop, struct loader_pointer_layer_list *target_list, |
| struct loader_pointer_layer_list *expanded_target_list, const struct loader_layer_list *source_list, |
| bool *out_found_all_component_layers) { |
| VkResult result = VK_SUCCESS; |
| bool found_all_component_layers = true; |
| |
| // We need to add all the individual component layers |
| loader_api_version meta_layer_api_version = loader_make_version(prop->info.specVersion); |
| for (uint32_t comp_layer = 0; comp_layer < prop->component_layer_names.count; comp_layer++) { |
| struct loader_layer_properties *search_prop = |
| loader_find_layer_property(prop->component_layer_names.list[comp_layer], source_list); |
| if (search_prop != NULL) { |
| loader_api_version search_prop_version = loader_make_version(prop->info.specVersion); |
| if (!loader_check_version_meets_required(meta_layer_api_version, search_prop_version)) { |
| loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, |
| "Meta-layer \"%s\" API version %u.%u, component layer \"%s\" version %u.%u, may have " |
| "incompatibilities (Policy #LLP_LAYER_8)!", |
| prop->info.layerName, meta_layer_api_version.major, meta_layer_api_version.minor, |
| search_prop->info.layerName, search_prop_version.major, search_prop_version.minor); |
| } |
| |
| if (!loader_layer_is_available(inst, filters, search_prop)) { |
| loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, |
| "Meta Layer \"%s\" component layer \"%s\" disabled.", prop->info.layerName, search_prop->info.layerName); |
| continue; |
| } |
| |
| // If the component layer is itself an implicit layer, we need to do the implicit layer enable |
| // checks |
| if (0 == (search_prop->type_flags & VK_LAYER_TYPE_FLAG_EXPLICIT_LAYER)) { |
| result = loader_add_implicit_layer(inst, search_prop, filters, target_list, expanded_target_list, source_list); |
| if (result == VK_ERROR_OUT_OF_HOST_MEMORY) return result; |
| } else { |
| if (0 != (search_prop->type_flags & VK_LAYER_TYPE_FLAG_META_LAYER)) { |
| bool found_layers_in_component_meta_layer = true; |
| result = loader_add_meta_layer(inst, filters, search_prop, target_list, expanded_target_list, source_list, |
| &found_layers_in_component_meta_layer); |
| if (result == VK_ERROR_OUT_OF_HOST_MEMORY) return result; |
| if (!found_layers_in_component_meta_layer) found_all_component_layers = false; |
| } else if (!loader_find_layer_name_in_list(&search_prop->info.layerName[0], target_list)) { |
| // Make sure the layer isn't already in the output_list, skip adding it if it is. |
| result = loader_add_layer_properties_to_list(inst, target_list, search_prop); |
| if (result == VK_ERROR_OUT_OF_HOST_MEMORY) return result; |
| if (NULL != expanded_target_list) { |
| result = loader_add_layer_properties_to_list(inst, expanded_target_list, search_prop); |
| if (result == VK_ERROR_OUT_OF_HOST_MEMORY) return result; |
| } |
| } |
| } |
| } else { |
| loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, |
| "Failed to find layer name \"%s\" component layer \"%s\" to activate (Policy #LLP_LAYER_7)", |
| prop->component_layer_names.list[comp_layer], prop->component_layer_names.list[comp_layer]); |
| found_all_component_layers = false; |
| } |
| } |
| |
| // Add this layer to the overall target list (not the expanded one) |
| if (found_all_component_layers) { |
| result = loader_add_layer_properties_to_list(inst, target_list, prop); |
| if (result == VK_ERROR_OUT_OF_HOST_MEMORY) return result; |
| // Write the result to out_found_all_component_layers in case this function is being recursed |
| if (out_found_all_component_layers) *out_found_all_component_layers = found_all_component_layers; |
| } |
| |
| return result; |
| } |
| |
| VkExtensionProperties *get_extension_property(const char *name, const struct loader_extension_list *list) { |
| for (uint32_t i = 0; i < list->count; i++) { |
| if (strcmp(name, list->list[i].extensionName) == 0) return &list->list[i]; |
| } |
| return NULL; |
| } |
| |
| VkExtensionProperties *get_dev_extension_property(const char *name, const struct loader_device_extension_list *list) { |
| for (uint32_t i = 0; i < list->count; i++) { |
| if (strcmp(name, list->list[i].props.extensionName) == 0) return &list->list[i].props; |
| } |
| return NULL; |
| } |
| |
| // For Instance extensions implemented within the loader (i.e. DEBUG_REPORT |
| // the extension must provide two entry points for the loader to use: |
| // - "trampoline" entry point - this is the address returned by GetProcAddr |
| // and will always do what's necessary to support a |
| // global call. |
| // - "terminator" function - this function will be put at the end of the |
| // instance chain and will contain the necessary logic |
| // to call / process the extension for the appropriate |
| // ICDs that are available. |
| // There is no generic mechanism for including these functions, the references |
| // must be placed into the appropriate loader entry points. |
| // GetInstanceProcAddr: call extension GetInstanceProcAddr to check for GetProcAddr |
| // requests |
| // loader_coalesce_extensions(void) - add extension records to the list of global |
| // extension available to the app. |
| // instance_disp - add function pointer for terminator function |
| // to this array. |
| // The extension itself should be in a separate file that will be linked directly |
| // with the loader. |
| VkResult loader_get_icd_loader_instance_extensions(const struct loader_instance *inst, struct loader_icd_tramp_list *icd_tramp_list, |
| struct loader_extension_list *inst_exts) { |
| struct loader_extension_list icd_exts; |
| VkResult res = VK_SUCCESS; |
| char *env_value; |
| bool filter_extensions = true; |
| |
| // Check if a user wants to disable the instance extension filtering behavior |
| env_value = loader_getenv("VK_LOADER_DISABLE_INST_EXT_FILTER", inst); |
| if (NULL != env_value && atoi(env_value) != 0) { |
| filter_extensions = false; |
| } |
| loader_free_getenv(env_value, inst); |
| |
| // traverse scanned icd list adding non-duplicate extensions to the list |
| for (uint32_t i = 0; i < icd_tramp_list->count; i++) { |
| res = loader_init_generic_list(inst, (struct loader_generic_list *)&icd_exts, sizeof(VkExtensionProperties)); |
| if (VK_SUCCESS != res) { |
| goto out; |
| } |
| res = loader_add_instance_extensions(inst, icd_tramp_list->scanned_list[i].EnumerateInstanceExtensionProperties, |
| icd_tramp_list->scanned_list[i].lib_name, &icd_exts); |
| if (VK_SUCCESS == res) { |
| if (filter_extensions) { |
| // Remove any extensions not recognized by the loader |
| for (int32_t j = 0; j < (int32_t)icd_exts.count; j++) { |
| // See if the extension is in the list of supported extensions |
| bool found = false; |
| for (uint32_t k = 0; LOADER_INSTANCE_EXTENSIONS[k] != NULL; k++) { |
| if (strcmp(icd_exts.list[j].extensionName, LOADER_INSTANCE_EXTENSIONS[k]) == 0) { |
| found = true; |
| break; |
| } |
| } |
| |
| // If it isn't in the list, remove it |
| if (!found) { |
| for (uint32_t k = j + 1; k < icd_exts.count; k++) { |
| icd_exts.list[k - 1] = icd_exts.list[k]; |
| } |
| --icd_exts.count; |
| --j; |
| } |
| } |
| } |
| |
| res = loader_add_to_ext_list(inst, inst_exts, icd_exts.count, icd_exts.list); |
| } |
| loader_destroy_generic_list(inst, (struct loader_generic_list *)&icd_exts); |
| if (VK_SUCCESS != res) { |
| goto out; |
| } |
| }; |
| |
| // Traverse loader's extensions, adding non-duplicate extensions to the list |
| res = add_debug_extensions_to_ext_list(inst, inst_exts); |
| if (res == VK_ERROR_OUT_OF_HOST_MEMORY) { |
| goto out; |
| } |
| const VkExtensionProperties portability_enumeration_extension_info[] = { |
| {VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME, VK_KHR_PORTABILITY_ENUMERATION_SPEC_VERSION}}; |
| |
| // Add VK_KHR_portability_subset |
| res = loader_add_to_ext_list(inst, inst_exts, sizeof(portability_enumeration_extension_info) / sizeof(VkExtensionProperties), |
| portability_enumeration_extension_info); |
| if (res == VK_ERROR_OUT_OF_HOST_MEMORY) { |
| goto out; |
| } |
| |
| const VkExtensionProperties direct_driver_loading_extension_info[] = { |
| {VK_LUNARG_DIRECT_DRIVER_LOADING_EXTENSION_NAME, VK_LUNARG_DIRECT_DRIVER_LOADING_SPEC_VERSION}}; |
| |
| // Add VK_LUNARG_direct_driver_loading |
| res = loader_add_to_ext_list(inst, inst_exts, sizeof(direct_driver_loading_extension_info) / sizeof(VkExtensionProperties), |
| direct_driver_loading_extension_info); |
| if (res == VK_ERROR_OUT_OF_HOST_MEMORY) { |
| goto out; |
| } |
| |
| out: |
| return res; |
| } |
| |
| struct loader_icd_term *loader_get_icd_and_device(const void *device, struct loader_device **found_dev) { |
| VkLayerDispatchTable *dispatch_table_device = loader_get_dispatch(device); |
| if (NULL == dispatch_table_device) { |
| *found_dev = NULL; |
| return NULL; |
| } |
| loader_platform_thread_lock_mutex(&loader_global_instance_list_lock); |
| *found_dev = NULL; |
| |
| for (struct loader_instance *inst = loader.instances; inst; inst = inst->next) { |
| for (struct loader_icd_term *icd_term = inst->icd_terms; icd_term; icd_term = icd_term->next) { |
| for (struct loader_device *dev = icd_term->logical_device_list; dev; dev = dev->next) { |
| // Value comparison of device prevents object wrapping by layers |
| if (loader_get_dispatch(dev->icd_device) == dispatch_table_device || |
| (dev->chain_device != VK_NULL_HANDLE && loader_get_dispatch(dev->chain_device) == dispatch_table_device)) { |
| *found_dev = dev; |
| loader_platform_thread_unlock_mutex(&loader_global_instance_list_lock); |
| return icd_term; |
| } |
| } |
| } |
| } |
| loader_platform_thread_unlock_mutex(&loader_global_instance_list_lock); |
| return NULL; |
| } |
| |
| void loader_destroy_logical_device(struct loader_device *dev, const VkAllocationCallbacks *pAllocator) { |
| if (pAllocator) { |
| dev->alloc_callbacks = *pAllocator; |
| } |
| loader_device_heap_free(dev, dev); |
| } |
| |
| struct loader_device *loader_create_logical_device(const struct loader_instance *inst, const VkAllocationCallbacks *pAllocator) { |
| struct loader_device *new_dev; |
| new_dev = loader_calloc(pAllocator, sizeof(struct loader_device), VK_SYSTEM_ALLOCATION_SCOPE_DEVICE); |
| |
| if (!new_dev) { |
| loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_create_logical_device: Failed to alloc struct loader_device"); |
| return NULL; |
| } |
| |
| new_dev->loader_dispatch.core_dispatch.magic = DEVICE_DISP_TABLE_MAGIC_NUMBER; |
| |
| if (pAllocator) { |
| new_dev->alloc_callbacks = *pAllocator; |
| } |
| |
| return new_dev; |
| } |
| |
| void loader_add_logical_device(struct loader_icd_term *icd_term, struct loader_device *dev) { |
| dev->next = icd_term->logical_device_list; |
| icd_term->logical_device_list = dev; |
| } |
| |
| void loader_remove_logical_device(struct loader_icd_term *icd_term, struct loader_device *found_dev, |
| const VkAllocationCallbacks *pAllocator) { |
| struct loader_device *dev, *prev_dev; |
| |
| if (!icd_term || !found_dev) return; |
| |
| prev_dev = NULL; |
| dev = icd_term->logical_device_list; |
| while (dev && dev != found_dev) { |
| prev_dev = dev; |
| dev = dev->next; |
| } |
| |
| if (prev_dev) |
| prev_dev->next = found_dev->next; |
| else |
| icd_term->logical_device_list = found_dev->next; |
| loader_destroy_logical_device(found_dev, pAllocator); |
| } |
| |
| const VkAllocationCallbacks *ignore_null_callback(const VkAllocationCallbacks *callbacks) { |
| return NULL != callbacks->pfnAllocation && NULL != callbacks->pfnFree && NULL != callbacks->pfnReallocation && |
| NULL != callbacks->pfnInternalAllocation && NULL != callbacks->pfnInternalFree |
| ? callbacks |
| : NULL; |
| } |
| |
| // Try to close any open objects on the loader_icd_term - this must be done before destroying the instance |
| void loader_icd_close_objects(struct loader_instance *ptr_inst, struct loader_icd_term *icd_term) { |
| for (uint32_t i = 0; i < icd_term->surface_list.capacity / sizeof(VkSurfaceKHR); i++) { |
| if (ptr_inst->surfaces_list.capacity > i * sizeof(struct loader_used_object_status) && |
| ptr_inst->surfaces_list.list[i].status == VK_TRUE && NULL != icd_term->surface_list.list && |
| icd_term->surface_list.list[i] && NULL != icd_term->dispatch.DestroySurfaceKHR) { |
| icd_term->dispatch.DestroySurfaceKHR(icd_term->instance, icd_term->surface_list.list[i], |
| ignore_null_callback(&(ptr_inst->surfaces_list.list[i].allocation_callbacks))); |
| icd_term->surface_list.list[i] = (VkSurfaceKHR)(uintptr_t)NULL; |
| } |
| } |
| for (uint32_t i = 0; i < icd_term->debug_utils_messenger_list.capacity / sizeof(VkDebugUtilsMessengerEXT); i++) { |
| if (ptr_inst->debug_utils_messengers_list.capacity > i * sizeof(struct loader_used_object_status) && |
| ptr_inst->debug_utils_messengers_list.list[i].status == VK_TRUE && NULL != icd_term->debug_utils_messenger_list.list && |
| icd_term->debug_utils_messenger_list.list[i] && NULL != icd_term->dispatch.DestroyDebugUtilsMessengerEXT) { |
| icd_term->dispatch.DestroyDebugUtilsMessengerEXT( |
| icd_term->instance, icd_term->debug_utils_messenger_list.list[i], |
| ignore_null_callback(&(ptr_inst->debug_utils_messengers_list.list[i].allocation_callbacks))); |
| icd_term->debug_utils_messenger_list.list[i] = (VkDebugUtilsMessengerEXT)(uintptr_t)NULL; |
| } |
| } |
| for (uint32_t i = 0; i < icd_term->debug_report_callback_list.capacity / sizeof(VkDebugReportCallbackEXT); i++) { |
| if (ptr_inst->debug_report_callbacks_list.capacity > i * sizeof(struct loader_used_object_status) && |
| ptr_inst->debug_report_callbacks_list.list[i].status == VK_TRUE && NULL != icd_term->debug_report_callback_list.list && |
| icd_term->debug_report_callback_list.list[i] && NULL != icd_term->dispatch.DestroyDebugReportCallbackEXT) { |
| icd_term->dispatch.DestroyDebugReportCallbackEXT( |
| icd_term->instance, icd_term->debug_report_callback_list.list[i], |
| ignore_null_callback(&(ptr_inst->debug_report_callbacks_list.list[i].allocation_callbacks))); |
| icd_term->debug_report_callback_list.list[i] = (VkDebugReportCallbackEXT)(uintptr_t)NULL; |
| } |
| } |
| } |
| // Free resources allocated inside the loader_icd_term |
| void loader_icd_destroy(struct loader_instance *ptr_inst, struct loader_icd_term *icd_term, |
| const VkAllocationCallbacks *pAllocator) { |
| ptr_inst->icd_terms_count--; |
| for (struct loader_device *dev = icd_term->logical_device_list; dev;) { |
| struct loader_device *next_dev = dev->next; |
| loader_destroy_logical_device(dev, pAllocator); |
| dev = next_dev; |
| } |
| |
| loader_destroy_generic_list(ptr_inst, (struct loader_generic_list *)&icd_term->surface_list); |
| loader_destroy_generic_list(ptr_inst, (struct loader_generic_list *)&icd_term->debug_utils_messenger_list); |
| loader_destroy_generic_list(ptr_inst, (struct loader_generic_list *)&icd_term->debug_report_callback_list); |
| |
| loader_instance_heap_free(ptr_inst, icd_term); |
| } |
| |
| struct loader_icd_term *loader_icd_add(struct loader_instance *ptr_inst, const struct loader_scanned_icd *scanned_icd) { |
| struct loader_icd_term *icd_term; |
| |
| icd_term = loader_instance_heap_calloc(ptr_inst, sizeof(struct loader_icd_term), VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); |
| if (!icd_term) { |
| return NULL; |
| } |
| |
| icd_term->scanned_icd = scanned_icd; |
| icd_term->this_instance = ptr_inst; |
| |
| // Prepend to the list |
| icd_term->next = ptr_inst->icd_terms; |
| ptr_inst->icd_terms = icd_term; |
| ptr_inst->icd_terms_count++; |
| |
| return icd_term; |
| } |
| // Closes the library handle in the scanned ICD, free the lib_name string, and zeros out all data |
| void loader_unload_scanned_icd(struct loader_instance *inst, struct loader_scanned_icd *scanned_icd) { |
| if (NULL == scanned_icd) { |
| return; |
| } |
| if (scanned_icd->handle) { |
| loader_platform_close_library(scanned_icd->handle); |
| scanned_icd->handle = NULL; |
| } |
| loader_instance_heap_free(inst, scanned_icd->lib_name); |
| memset(scanned_icd, 0, sizeof(struct loader_scanned_icd)); |
| } |
| |
| // Determine the ICD interface version to use. |
| // @param icd |
| // @param pVersion Output parameter indicating which version to use or 0 if |
| // the negotiation API is not supported by the ICD |
| // @return bool indicating true if the selected interface version is supported |
| // by the loader, false indicates the version is not supported |
| bool loader_get_icd_interface_version(PFN_vkNegotiateLoaderICDInterfaceVersion fp_negotiate_icd_version, uint32_t *pVersion) { |
| if (fp_negotiate_icd_version == NULL) { |
| // ICD does not support the negotiation API, it supports version 0 or 1 |
| // calling code must determine if it is version 0 or 1 |
| *pVersion = 0; |
| } else { |
| // ICD supports the negotiation API, so call it with the loader's |
| // latest version supported |
| *pVersion = CURRENT_LOADER_ICD_INTERFACE_VERSION; |
| VkResult result = fp_negotiate_icd_version(pVersion); |
| |
| if (result == VK_ERROR_INCOMPATIBLE_DRIVER) { |
| // ICD no longer supports the loader's latest interface version so |
| // fail loading the ICD |
| return false; |
| } |
| } |
| |
| #if MIN_SUPPORTED_LOADER_ICD_INTERFACE_VERSION > 0 |
| if (*pVersion < MIN_SUPPORTED_LOADER_ICD_INTERFACE_VERSION) { |
| // Loader no longer supports the ICD's latest interface version so fail |
| // loading the ICD |
| return false; |
| } |
| #endif |
| return true; |
| } |
| |
| void loader_clear_scanned_icd_list(const struct loader_instance *inst, struct loader_icd_tramp_list *icd_tramp_list) { |
| if (0 != icd_tramp_list->capacity && icd_tramp_list->scanned_list) { |
| for (uint32_t i = 0; i < icd_tramp_list->count; i++) { |
| if (icd_tramp_list->scanned_list[i].handle) { |
| loader_platform_close_library(icd_tramp_list->scanned_list[i].handle); |
| icd_tramp_list->scanned_list[i].handle = NULL; |
| } |
| loader_instance_heap_free(inst, icd_tramp_list->scanned_list[i].lib_name); |
| } |
| loader_instance_heap_free(inst, icd_tramp_list->scanned_list); |
| } |
| memset(icd_tramp_list, 0, sizeof(struct loader_icd_tramp_list)); |
| } |
| |
| VkResult loader_init_scanned_icd_list(const struct loader_instance *inst, struct loader_icd_tramp_list *icd_tramp_list) { |
| VkResult res = VK_SUCCESS; |
| loader_clear_scanned_icd_list(inst, icd_tramp_list); |
| icd_tramp_list->capacity = 8 * sizeof(struct loader_scanned_icd); |
| icd_tramp_list->scanned_list = loader_instance_heap_alloc(inst, icd_tramp_list->capacity, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); |
| if (NULL == icd_tramp_list->scanned_list) { |
| loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, |
| "loader_init_scanned_icd_list: Realloc failed for layer list when attempting to add new layer"); |
| res = VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| return res; |
| } |
| |
| VkResult loader_add_direct_driver(const struct loader_instance *inst, uint32_t index, |
| const VkDirectDriverLoadingInfoLUNARG *pDriver, struct loader_icd_tramp_list *icd_tramp_list) { |
| // Assume pDriver is valid, since there is no real way to check it. Calling code should make sure the pointer to the array |
| // of VkDirectDriverLoadingInfoLUNARG structures is non-null. |
| if (NULL == pDriver->pfnGetInstanceProcAddr) { |
| loader_log( |
| inst, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_DRIVER_BIT, 0, |
| "loader_add_direct_driver: VkDirectDriverLoadingInfoLUNARG structure at index %d contains a NULL pointer for the " |
| "pfnGetInstanceProcAddr member, skipping.", |
| index); |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| |
| PFN_vkGetInstanceProcAddr fp_get_proc_addr = pDriver->pfnGetInstanceProcAddr; |
| PFN_vkCreateInstance fp_create_inst = NULL; |
| PFN_vkEnumerateInstanceExtensionProperties fp_get_inst_ext_props = NULL; |
| PFN_GetPhysicalDeviceProcAddr fp_get_phys_dev_proc_addr = NULL; |
| PFN_vkNegotiateLoaderICDInterfaceVersion fp_negotiate_icd_version = NULL; |
| #if defined(VK_USE_PLATFORM_WIN32_KHR) |
| PFN_vk_icdEnumerateAdapterPhysicalDevices fp_enum_dxgi_adapter_phys_devs = NULL; |
| #endif |
| struct loader_scanned_icd *new_scanned_icd; |
| uint32_t interface_version = 0; |
| |
| // Try to get the negotiate ICD interface version function |
| fp_negotiate_icd_version = (PFN_vk_icdNegotiateLoaderICDInterfaceVersion)pDriver->pfnGetInstanceProcAddr( |
| NULL, "vk_icdNegotiateLoaderICDInterfaceVersion"); |
| |
| if (NULL == fp_negotiate_icd_version) { |
| loader_log(inst, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_DRIVER_BIT, 0, |
| "loader_add_direct_driver: Could not get 'vk_icdNegotiateLoaderICDInterfaceVersion' from " |
| "VkDirectDriverLoadingInfoLUNARG structure at " |
| "index %d, skipping.", |
| index); |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| |
| if (!loader_get_icd_interface_version(fp_negotiate_icd_version, &interface_version)) { |
| loader_log( |
| inst, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_DRIVER_BIT, 0, |
| "loader_add_direct_driver: VkDirectDriverLoadingInfoLUNARG structure at index %d supports interface version %d, " |
| "which is incompatible with the Loader Driver Interface version that supports the VK_LUNARG_direct_driver_loading " |
| "extension, skipping.", |
| index, interface_version); |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| |
| if (interface_version < 7) { |
| loader_log( |
| inst, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_DRIVER_BIT, 0, |
| "loader_add_direct_driver: VkDirectDriverLoadingInfoLUNARG structure at index %d supports interface version %d, " |
| "which is incompatible with the Loader Driver Interface version that supports the VK_LUNARG_direct_driver_loading " |
| "extension, skipping.", |
| index, interface_version); |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| |
| fp_create_inst = (PFN_vkCreateInstance)pDriver->pfnGetInstanceProcAddr(NULL, "vkCreateInstance"); |
| if (NULL == fp_create_inst) { |
| loader_log(inst, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_DRIVER_BIT, 0, |
| "loader_add_direct_driver: Could not get 'vkCreateInstance' from VkDirectDriverLoadingInfoLUNARG structure at " |
| "index %d, skipping.", |
| index); |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| fp_get_inst_ext_props = |
| (PFN_vkEnumerateInstanceExtensionProperties)pDriver->pfnGetInstanceProcAddr(NULL, "vkEnumerateInstanceExtensionProperties"); |
| if (NULL == fp_get_inst_ext_props) { |
| loader_log(inst, VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_DRIVER_BIT, 0, |
| "loader_add_direct_driver: Could not get 'vkEnumerateInstanceExtensionProperties' from " |
| "VkDirectDriverLoadingInfoLUNARG structure at index %d, skipping.", |
| index); |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| |
| fp_get_phys_dev_proc_addr = |
| (PFN_vk_icdGetPhysicalDeviceProcAddr)pDriver->pfnGetInstanceProcAddr(NULL, "vk_icdGetPhysicalDeviceProcAddr"); |
| #if defined(VK_USE_PLATFORM_WIN32_KHR) |
| // Query "vk_icdEnumerateAdapterPhysicalDevices" with vk_icdGetInstanceProcAddr if the library reports interface version |
| // 7 or greater, otherwise fallback to loading it from the platform dynamic linker |
| fp_enum_dxgi_adapter_phys_devs = |
| (PFN_vk_icdEnumerateAdapterPhysicalDevices)pDriver->pfnGetInstanceProcAddr(NULL, "vk_icdEnumerateAdapterPhysicalDevices"); |
| #endif |
| |
| // check for enough capacity |
| if ((icd_tramp_list->count * sizeof(struct loader_scanned_icd)) >= icd_tramp_list->capacity) { |
| void *new_ptr = loader_instance_heap_realloc(inst, icd_tramp_list->scanned_list, icd_tramp_list->capacity, |
| icd_tramp_list->capacity * 2, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); |
| if (NULL == new_ptr) { |
| loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, |
| "loader_add_direct_driver: Realloc failed on icd library list for ICD index %u", index); |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| icd_tramp_list->scanned_list = new_ptr; |
| |
| // double capacity |
| icd_tramp_list->capacity *= 2; |
| } |
| |
| // Driver must be 1.1 to support version 7 |
| uint32_t api_version = VK_API_VERSION_1_1; |
| PFN_vkEnumerateInstanceVersion icd_enumerate_instance_version = |
| (PFN_vkEnumerateInstanceVersion)pDriver->pfnGetInstanceProcAddr(NULL, "vkEnumerateInstanceVersion"); |
| |
| if (icd_enumerate_instance_version) { |
| VkResult res = icd_enumerate_instance_version(&api_version); |
| if (res != VK_SUCCESS) { |
| return res; |
| } |
| } |
| |
| new_scanned_icd = &(icd_tramp_list->scanned_list[icd_tramp_list->count]); |
| new_scanned_icd->handle = NULL; |
| new_scanned_icd->api_version = api_version; |
| new_scanned_icd->GetInstanceProcAddr = fp_get_proc_addr; |
| new_scanned_icd->GetPhysicalDeviceProcAddr = fp_get_phys_dev_proc_addr; |
| new_scanned_icd->EnumerateInstanceExtensionProperties = fp_get_inst_ext_props; |
| new_scanned_icd->CreateInstance = fp_create_inst; |
| #if defined(VK_USE_PLATFORM_WIN32_KHR) |
| new_scanned_icd->EnumerateAdapterPhysicalDevices = fp_enum_dxgi_adapter_phys_devs; |
| #endif |
| new_scanned_icd->interface_version = interface_version; |
| |
| new_scanned_icd->lib_name = NULL; |
| icd_tramp_list->count++; |
| |
| loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0, |
| "loader_add_direct_driver: Adding driver found in index %d of " |
| "VkDirectDriverLoadingListLUNARG::pDrivers structure. pfnGetInstanceProcAddr was set to %p", |
| index, pDriver->pfnGetInstanceProcAddr); |
| |
| return VK_SUCCESS; |
| } |
| |
| // Search through VkInstanceCreateInfo's pNext chain for any drivers from the direct driver loading extension and load them. |
| VkResult loader_scan_for_direct_drivers(const struct loader_instance *inst, const VkInstanceCreateInfo *pCreateInfo, |
| struct loader_icd_tramp_list *icd_tramp_list, bool *direct_driver_loading_exclusive_mode) { |
| if (NULL == pCreateInfo) { |
| // Don't do this logic unless we are being called from vkCreateInstance, when pCreateInfo will be non-null |
| return VK_SUCCESS; |
| } |
| bool direct_driver_loading_enabled = false; |
| // Try to if VK_LUNARG_direct_driver_loading is enabled and if we are using it exclusively |
| // Skip this step if inst is NULL, aka when this function is being called before instance creation |
| if (inst != NULL && pCreateInfo->ppEnabledExtensionNames && pCreateInfo->enabledExtensionCount > 0) { |
| // Look through the enabled extension list, make sure VK_LUNARG_direct_driver_loading is present |
| for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) { |
| if (strcmp(pCreateInfo->ppEnabledExtensionNames[i], VK_LUNARG_DIRECT_DRIVER_LOADING_EXTENSION_NAME) == 0) { |
| direct_driver_loading_enabled = true; |
| break; |
| } |
| } |
| } |
| const VkDirectDriverLoadingListLUNARG *ddl_list = NULL; |
| // Find the VkDirectDriverLoadingListLUNARG struct in the pNext chain of vkInstanceCreateInfo |
| const VkBaseOutStructure *chain = pCreateInfo->pNext; |
| while (chain) { |
| if (chain->sType == VK_STRUCTURE_TYPE_DIRECT_DRIVER_LOADING_LIST_LUNARG) { |
| ddl_list = (VkDirectDriverLoadingListLUNARG *)chain; |
| break; |
| } |
| chain = (const VkBaseOutStructure *)chain->pNext; |
| } |
| if (NULL == ddl_list) { |
| if (direct_driver_loading_enabled) { |
| loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0, |
| "loader_scan_for_direct_drivers: The VK_LUNARG_direct_driver_loading extension was enabled but the " |
| "pNext chain of " |
| "VkInstanceCreateInfo did not contain the " |
| "VkDirectDriverLoadingListLUNARG structure."); |
| } |
| // Always want to exit early if there was no VkDirectDriverLoadingListLUNARG in the pNext chain |
| return VK_SUCCESS; |
| } |
| |
| if (!direct_driver_loading_enabled) { |
| loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0, |
| "loader_scan_for_direct_drivers: The pNext chain of VkInstanceCreateInfo contained the " |
| "VkDirectDriverLoadingListLUNARG structure, but the VK_LUNARG_direct_driver_loading extension was " |
| "not enabled."); |
| return VK_SUCCESS; |
| } |
| // If we are using exclusive mode, skip looking for any more drivers from system or environment variables |
| if (ddl_list->mode == VK_DIRECT_DRIVER_LOADING_MODE_EXCLUSIVE_LUNARG) { |
| *direct_driver_loading_exclusive_mode = true; |
| loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DRIVER_BIT, 0, |
| "loader_scan_for_direct_drivers: The VK_LUNARG_direct_driver_loading extension is active and specified " |
| "VK_DIRECT_DRIVER_LOADING_MODE_EXCLUSIVE_LUNARG, skipping system and environment " |
| "variable driver search mechanisms."); |
| } |
| if (NULL == ddl_list->pDrivers) { |
| loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0, |
| "loader_scan_for_direct_drivers: The VkDirectDriverLoadingListLUNARG structure in the pNext chain of " |
| "VkInstanceCreateInfo has a NULL pDrivers member."); |
| return VK_SUCCESS; |
| } |
| if (ddl_list->driverCount == 0) { |
| loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_DRIVER_BIT, 0, |
| "loader_scan_for_direct_drivers: The VkDirectDriverLoadingListLUNARG structure in the pNext chain of " |
| "VkInstanceCreateInfo has a non-null pDrivers member but a driverCount member with a value " |
| "of zero."); |
| return VK_SUCCESS; |
| } |
| // Go through all VkDirectDriverLoadingInfoLUNARG entries and add each driver |
| // Because icd_tramp's are prepended, this will result in the drivers appearing at the end |
| for (uint32_t i = 0; i < ddl_list->driverCount; i++) { |
| VkResult res = loader_add_direct_driver(inst, i, &ddl_list->pDrivers[i], icd_tramp_list); |
| if (res == VK_ERROR_OUT_OF_HOST_MEMORY) { |
| return res; |
| } |
| } |
| |
| return VK_SUCCESS; |
| } |
| |
| VkResult loader_scanned_icd_add(const struct loader_instance *inst, struct loader_icd_tramp_list *icd_tramp_list, |
| const char *filename, uint32_t api_version, enum loader_layer_library_status *lib_status) { |
| loader_platform_dl_handle handle = NULL; |
| PFN_vkCreateInstance fp_create_inst = NULL; |
| PFN_vkEnumerateInstanceExtensionProperties fp_get_inst_ext_props = NULL; |
| PFN_vkGetInstanceProcAddr fp_get_proc_addr = NULL; |
| PFN_GetPhysicalDeviceProcAddr fp_get_phys_dev_proc_addr = NULL; |
| PFN_vkNegotiateLoaderICDInterfaceVersion fp_negotiate_icd_version = NULL; |
| #if defined(VK_USE_PLATFORM_WIN32_KHR) |
| PFN_vk_icdEnumerateAdapterPhysicalDevices fp_enum_dxgi_adapter_phys_devs = NULL; |
| #endif |
| struct loader_scanned_icd *new_scanned_icd = NULL; |
| uint32_t interface_vers; |
| VkResult res = VK_SUCCESS; |
| |
| // This shouldn't happen, but the check is necessary because dlopen returns a handle to the main program when |
| // filename is NULL |
| if (filename == NULL) { |
| loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_scanned_icd_add: A NULL filename was used, skipping this ICD", |
| filename); |
| res = VK_ERROR_INCOMPATIBLE_DRIVER; |
| goto out; |
| } |
| |
| // TODO implement smarter opening/closing of libraries. For now this |
| // function leaves libraries open and the scanned_icd_clear closes them |
| #if defined(__Fuchsia__) |
| handle = loader_platform_open_driver(filename); |
| #else |
| handle = loader_platform_open_library(filename); |
| #endif |
| if (NULL == handle) { |
| loader_handle_load_library_error(inst, filename, lib_status); |
| if (lib_status && *lib_status == LOADER_LAYER_LIB_ERROR_OUT_OF_MEMORY) { |
| res = VK_ERROR_OUT_OF_HOST_MEMORY; |
| } else { |
| res = VK_ERROR_INCOMPATIBLE_DRIVER; |
| } |
| goto out; |
| } |
| |
| // Try to load the driver's exported vk_icdNegotiateLoaderICDInterfaceVersion |
| fp_negotiate_icd_version = loader_platform_get_proc_address(handle, "vk_icdNegotiateLoaderICDInterfaceVersion"); |
| |
| // If it isn't exported, we are dealing with either a v0, v1, or a v7 and up driver |
| if (NULL == fp_negotiate_icd_version) { |
| // Try to load the driver's exported vk_icdGetInstanceProcAddr - if this is a v7 or up driver, we can use it to get |
| // the driver's vk_icdNegotiateLoaderICDInterfaceVersion function |
| fp_get_proc_addr = loader_platform_get_proc_address(handle, "vk_icdGetInstanceProcAddr"); |
| |
| // If we successfully loaded vk_icdGetInstanceProcAddr, try to get vk_icdNegotiateLoaderICDInterfaceVersion |
| if (fp_get_proc_addr) { |
| fp_negotiate_icd_version = |
| (PFN_vk_icdNegotiateLoaderICDInterfaceVersion)fp_get_proc_addr(NULL, "vk_icdNegotiateLoaderICDInterfaceVersion"); |
| } |
| } |
| |
| // Try to negotiate the Loader and Driver Interface Versions |
| // loader_get_icd_interface_version will check if fp_negotiate_icd_version is NULL, so we don't have to. |
| // If it *is* NULL, that means this driver uses interface version 0 or 1 |
| if (!loader_get_icd_interface_version(fp_negotiate_icd_version, &interface_vers)) { |
| loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, |
| "loader_scanned_icd_add: ICD %s doesn't support interface version compatible with loader, skip this ICD.", |
| filename); |
| goto out; |
| } |
| |
| // If we didn't already query vk_icdGetInstanceProcAddr, try now |
| if (NULL == fp_get_proc_addr) { |
| fp_get_proc_addr = loader_platform_get_proc_address(handle, "vk_icdGetInstanceProcAddr"); |
| } |
| |
| // 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 |
| 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 " |
| "vk_icdGetInstanceProcAddr, skip this ICD.", |
| filename, interface_vers); |
| goto out; |
| } |
| // Use deprecated interface from version 0 |
| fp_get_proc_addr = loader_platform_get_proc_address(handle, "vkGetInstanceProcAddr"); |
| if (NULL == fp_get_proc_addr) { |
| loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, |
| "loader_scanned_icd_add: Attempt to retrieve either \'vkGetInstanceProcAddr\' or " |
| "\'vk_icdGetInstanceProcAddr\' from ICD %s failed.", |
| filename); |
| goto out; |
| } else { |
| loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, |
| "loader_scanned_icd_add: Using deprecated ICD interface of \'vkGetInstanceProcAddr\' instead of " |
| "\'vk_icdGetInstanceProcAddr\' for ICD %s", |
| filename); |
| } |
| fp_create_inst = loader_platform_get_proc_address(handle, "vkCreateInstance"); |
| if (NULL == fp_create_inst) { |
| loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, |
| "loader_scanned_icd_add: Failed querying \'vkCreateInstance\' via dlsym/LoadLibrary for ICD %s", filename); |
| goto out; |
| } |
| fp_get_inst_ext_props = loader_platform_get_proc_address(handle, "vkEnumerateInstanceExtensionProperties"); |
| if (NULL == fp_get_inst_ext_props) { |
| loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, |
| "loader_scanned_icd_add: Could not get \'vkEnumerateInstanceExtensionProperties\' via dlsym/LoadLibrary " |
| "for ICD %s", |
| filename); |
| goto out; |
| } |
| } 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 (interface_vers == 0) { |
| interface_vers = 1; |
| } |
| |
| fp_create_inst = (PFN_vkCreateInstance)fp_get_proc_addr(NULL, "vkCreateInstance"); |
| if (NULL == fp_create_inst) { |
| loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, |
| "loader_scanned_icd_add: Could not get \'vkCreateInstance\' via \'vk_icdGetInstanceProcAddr\' for ICD %s", |
| filename); |
| goto out; |
| } |
| fp_get_inst_ext_props = |
| (PFN_vkEnumerateInstanceExtensionProperties)fp_get_proc_addr(NULL, "vkEnumerateInstanceExtensionProperties"); |
| if (NULL == fp_get_inst_ext_props) { |
| loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, |
| "loader_scanned_icd_add: Could not get \'vkEnumerateInstanceExtensionProperties\' via " |
| "\'vk_icdGetInstanceProcAddr\' for ICD %s", |
| filename); |
| goto out; |
| } |
| // Query "vk_icdGetPhysicalDeviceProcAddr" 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_get_phys_dev_proc_addr = |
| (PFN_vk_icdGetPhysicalDeviceProcAddr)fp_get_proc_addr(NULL, "vk_icdGetPhysicalDeviceProcAddr"); |
| } |
| if (NULL == fp_get_phys_dev_proc_addr && interface_vers >= 3) { |
| 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 |
| if (interface_vers >= 7) { |
| fp_enum_dxgi_adapter_phys_devs = |
| (PFN_vk_icdEnumerateAdapterPhysicalDevices)fp_get_proc_addr(NULL, "vk_icdEnumerateAdapterPhysicalDevices"); |
| } |
| if (NULL == fp_enum_dxgi_adapter_phys_devs && interface_vers >= 6) { |
| fp_enum_dxgi_adapter_phys_devs = loader_platform_get_proc_address(handle, "vk_icdEnumerateAdapterPhysicalDevices"); |
| } |
| #endif |
| } |
| |
| // check for enough capacity |
| if ((icd_tramp_list->count * sizeof(struct loader_scanned_icd)) >= icd_tramp_list->capacity) { |
| void *new_ptr = loader_instance_heap_realloc(inst, icd_tramp_list->scanned_list, icd_tramp_list->capacity, |
| icd_tramp_list->capacity * 2, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); |
| if (NULL == new_ptr) { |
| res = VK_ERROR_OUT_OF_HOST_MEMORY; |
| loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_scanned_icd_add: Realloc failed on icd library list for ICD %s", |
| filename); |
| goto out; |
| } |
| icd_tramp_list->scanned_list = new_ptr; |
| |
| // double capacity |
| icd_tramp_list->capacity *= 2; |
| } |
| |
| loader_api_version api_version_struct = loader_make_version(api_version); |
| if (interface_vers <= 4 && loader_check_version_meets_required(LOADER_VERSION_1_1_0, api_version_struct)) { |
| loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, |
| "loader_scanned_icd_add: Driver %s supports Vulkan %u.%u, but only supports loader interface version %u." |
| " Interface version 5 or newer required to support this version of Vulkan (Policy #LDP_DRIVER_7)", |
| filename, api_version_struct.major, api_version_struct.minor, interface_vers); |
| } |
| |
| new_scanned_icd = &(icd_tramp_list->scanned_list[icd_tramp_list->count]); |
| new_scanned_icd->handle = handle; |
| new_scanned_icd->api_version = api_version; |
| new_scanned_icd->GetInstanceProcAddr = fp_get_proc_addr; |
| new_scanned_icd->GetPhysicalDeviceProcAddr = fp_get_phys_dev_proc_addr; |
| new_scanned_icd->EnumerateInstanceExtensionProperties = fp_get_inst_ext_props; |
| new_scanned_icd->CreateInstance = fp_create_inst; |
| #if defined(VK_USE_PLATFORM_WIN32_KHR) |
| new_scanned_icd->EnumerateAdapterPhysicalDevices = fp_enum_dxgi_adapter_phys_devs; |
| #endif |
| new_scanned_icd->interface_version = interface_vers; |
| |
| res = loader_copy_to_new_str(inst, filename, &new_scanned_icd->lib_name); |
| if (VK_ERROR_OUT_OF_HOST_MEMORY == res) { |
| loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_scanned_icd_add: Out of memory can't add ICD %s", filename); |
| goto out; |
| } |
| icd_tramp_list->count++; |
| |
| out: |
| |
| return res; |
| } |
| |
| void loader_initialize(void) { |
| // initialize mutexes |
| 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(); |
| #if defined(_WIN32) |
| windows_initialization(); |
| #endif |
| |
| loader_api_version version = loader_make_full_version(VK_HEADER_VERSION_COMPLETE); |
| loader_log(NULL, VULKAN_LOADER_INFO_BIT, 0, "Vulkan Loader Version %d.%d.%d", version.major, version.minor, version.patch); |
| |
| #if defined(GIT_BRANCH_NAME) && defined(GIT_TAG_INFO) |
| loader_log(NULL, VULKAN_LOADER_INFO_BIT, 0, "[Vulkan Loader Git - Tag: " GIT_BRANCH_NAME ", Branch/Commit: " GIT_TAG_INFO "]"); |
| #endif |
| |
| char *loader_disable_dynamic_library_unloading_env_var = loader_getenv("VK_LOADER_DISABLE_DYNAMIC_LIBRARY_UNLOADING", NULL); |
| if (loader_disable_dynamic_library_unloading_env_var && |
| 0 == strncmp(loader_disable_dynamic_library_unloading_env_var, "1", 2)) { |
| loader_disable_dynamic_library_unloading = true; |
| loader_log(NULL, VULKAN_LOADER_WARN_BIT, 0, "Vulkan Loader: library unloading is disabled"); |
| } else { |
| loader_disable_dynamic_library_unloading = false; |
| } |
| loader_free_getenv(loader_disable_dynamic_library_unloading_env_var, NULL); |
| #if defined(LOADER_USE_UNSAFE_FILE_SEARCH) |
| loader_log(NULL, VULKAN_LOADER_WARN_BIT, 0, "Vulkan Loader: unsafe searching is enabled"); |
| #endif |
| } |
| |
| void loader_release() { |
| // Guarantee release of the preloaded ICD libraries. This may have already been called in vkDestroyInstance. |
| 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); |
| } |
| |
| // Preload the ICD libraries that are likely to be needed so we don't repeatedly load/unload them later |
| void loader_preload_icds(void) { |
| loader_platform_thread_lock_mutex(&loader_preload_icd_lock); |
| |
| // Already preloaded, skip loading again. |
| if (preloaded_icds.scanned_list != NULL) { |
| loader_platform_thread_unlock_mutex(&loader_preload_icd_lock); |
| return; |
| } |
| |
| VkResult result = loader_icd_scan(NULL, &preloaded_icds, NULL, NULL); |
| if (result != VK_SUCCESS) { |
| loader_clear_scanned_icd_list(NULL, &preloaded_icds); |
| } |
| loader_platform_thread_unlock_mutex(&loader_preload_icd_lock); |
| } |
| |
| // Release the ICD libraries that were preloaded |
| void loader_unload_preloaded_icds(void) { |
| loader_platform_thread_lock_mutex(&loader_preload_icd_lock); |
| loader_clear_scanned_icd_list(NULL, &preloaded_icds); |
| loader_platform_thread_unlock_mutex(&loader_preload_icd_lock); |
| } |
| |
| #if !defined(_WIN32) |
| __attribute__((constructor)) void loader_init_library(void) { loader_initialize(); } |
| |
| __attribute__((destructor)) void loader_free_library(void) { loader_release(); } |
| #endif |
| |
| // Get next file or dirname given a string list or registry key path |
| // |
| // \returns |
| // A pointer to first char in the next path. |
| // The next path (or NULL) in the list is returned in next_path. |
| // Note: input string is modified in some cases. PASS IN A COPY! |
| char *loader_get_next_path(char *path) { |
| uint32_t len; |
| char *next; |
| |
| if (path == NULL) return NULL; |
| next = strchr(path, PATH_SEPARATOR); |
| if (next == NULL) { |
| len = (uint32_t)strlen(path); |
| next = path + len; |
| } else { |
| *next = '\0'; |
| next++; |
| } |
| |
| return next; |
| } |
| |
| /* Processes a json manifest's library_path and the location of the json manifest to create the path of the library |
| * The output is stored in out_fullpath by allocating a string - so its the caller's responsibility to free it |
| * The output is the combination of the base path of manifest_file_path concatenated with library path |
| * If library_path is an absolute path, we do not prepend the base path of manifest_file_path |
| * |
| * This function takes ownership of library_path - caller does not need to worry about freeing it. |
| */ |
| VkResult combine_manifest_directory_and_library_path(const struct loader_instance *inst, char *library_path, |
| const char *manifest_file_path, char **out_fullpath) { |
| assert(library_path && manifest_file_path && out_fullpath); |
| if (loader_platform_is_path_absolute(library_path)) { |
| *out_fullpath = library_path; |
| return VK_SUCCESS; |
| } |
| VkResult res = VK_SUCCESS; |
| |
| size_t library_path_len = strlen(library_path); |
| size_t manifest_file_path_str_len = strlen(manifest_file_path); |
| bool library_path_contains_directory_symbol = false; |
| for (size_t i = 0; i < library_path_len; i++) { |
| if (library_path[i] == DIRECTORY_SYMBOL) { |
| library_path_contains_directory_symbol = true; |
| break; |
| } |
| } |
| // Means that the library_path is neither absolute nor relative - thus we should not modify it at all |
| if (!library_path_contains_directory_symbol) { |
| *out_fullpath = library_path; |
| return VK_SUCCESS; |
| } |
| // must include both a directory symbol and the null terminator |
| size_t new_str_len = library_path_len + manifest_file_path_str_len + 1 + 1; |
| |
| *out_fullpath = loader_instance_heap_calloc(inst, new_str_len, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); |
| if (NULL == *out_fullpath) { |
| res = VK_ERROR_OUT_OF_HOST_MEMORY; |
| goto out; |
| } |
| size_t cur_loc_in_out_fullpath = 0; |
| // look for the last occurrence of DIRECTORY_SYMBOL in manifest_file_path |
| size_t last_directory_symbol = 0; |
| bool found_directory_symbol = false; |
| for (size_t i = 0; i < manifest_file_path_str_len; i++) { |
| if (manifest_file_path[i] == DIRECTORY_SYMBOL) { |
| last_directory_symbol = i + 1; // we want to include the symbol |
| found_directory_symbol = true; |
| // dont break because we want to find the last occurrence |
| } |
| } |
| // Add manifest_file_path up to the last directory symbol |
| if (found_directory_symbol) { |
| loader_strncpy(*out_fullpath, new_str_len, manifest_file_path, last_directory_symbol); |
| cur_loc_in_out_fullpath += last_directory_symbol; |
| } |
| loader_strncpy(&(*out_fullpath)[cur_loc_in_out_fullpath], new_str_len - cur_loc_in_out_fullpath, library_path, |
| library_path_len); |
| cur_loc_in_out_fullpath += library_path_len + 1; |
| (*out_fullpath)[cur_loc_in_out_fullpath] = '\0'; |
| |
| out: |
| loader_instance_heap_free(inst, library_path); |
| |
| return res; |
| } |
| |
| // Given a filename (file) and a list of paths (in_dirs), try to find an existing |
| // file in the paths. If filename already is a path then no searching in the given paths. |
| // |
| // @return - A string in out_fullpath of either the full path or file. |
| void loader_get_fullpath(const char *file, const char *in_dirs, size_t out_size, char *out_fullpath) { |
| if (!loader_platform_is_path(file) && *in_dirs) { |
| size_t dirs_copy_len = strlen(in_dirs) + 1; |
| char *dirs_copy = loader_stack_alloc(dirs_copy_len); |
| loader_strncpy(dirs_copy, dirs_copy_len, in_dirs, dirs_copy_len); |
| |
| // find if file exists after prepending paths in given list |
| // for (dir = dirs_copy; *dir && (next_dir = loader_get_next_path(dir)); dir = next_dir) { |
| char *dir = dirs_copy; |
| char *next_dir = loader_get_next_path(dir); |
| while (*dir && next_dir) { |
| int path_concat_ret = snprintf(out_fullpath, out_size, "%s%c%s", dir, DIRECTORY_SYMBOL, file); |
| if (path_concat_ret < 0) { |
| continue; |
| } |
| if (loader_platform_file_exists(out_fullpath)) { |
| return; |
| } |
| dir = next_dir; |
| next_dir = loader_get_next_path(dir); |
| } |
| } |
| |
| (void)snprintf(out_fullpath, out_size, "%s", file); |
| } |
| |
| // Verify that all component layers in a meta-layer are valid. |
| bool verify_meta_layer_component_layers(const struct loader_instance *inst, struct loader_layer_properties *prop, |
| struct loader_layer_list *instance_layers) { |
| loader_api_version meta_layer_version = loader_make_version(prop->info.specVersion); |
| |
| for (uint32_t comp_layer = 0; comp_layer < prop->component_layer_names.count; comp_layer++) { |
| struct loader_layer_properties *comp_prop = |
| loader_find_layer_property(prop->component_layer_names.list[comp_layer], instance_layers); |
| if (comp_prop == NULL) { |
| loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, |
| "verify_meta_layer_component_layers: Meta-layer %s can't find component layer %s at index %d." |
| " Skipping this layer.", |
| prop->info.layerName, prop->component_layer_names.list[comp_layer], comp_layer); |
| |
| return false; |
| } |
| |
| // Check the version of each layer, they need to be at least MAJOR and MINOR |
| loader_api_version comp_prop_version = loader_make_version(comp_prop->info.specVersion); |
| if (!loader_check_version_meets_required(meta_layer_version, comp_prop_version)) { |
| loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, |
| "verify_meta_layer_component_layers: Meta-layer uses API version %d.%d, but component " |
| "layer %d has API version %d.%d that is lower. Skipping this layer.", |
| meta_layer_version.major, meta_layer_version.minor, comp_layer, comp_prop_version.major, |
| comp_prop_version.minor); |
| |
| return false; |
| } |
| |
| // Make sure the layer isn't using it's own name |
| if (!strcmp(prop->info.layerName, prop->component_layer_names.list[comp_layer])) { |
| loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, |
| "verify_meta_layer_component_layers: Meta-layer %s lists itself in its component layer " |
| "list at index %d. Skipping this layer.", |
| prop->info.layerName, comp_layer); |
| |
| return false; |
| } |
| if (comp_prop->type_flags & VK_LAYER_TYPE_FLAG_META_LAYER) { |
| loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, |
| "verify_meta_layer_component_layers: Adding meta-layer %s which also contains meta-layer %s", |
| prop->info.layerName, comp_prop->info.layerName); |
| |
| // Make sure if the layer is using a meta-layer in its component list that we also verify that. |
| if (!verify_meta_layer_component_layers(inst, comp_prop, instance_layers)) { |
| loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, |
| "Meta-layer %s component layer %s can not find all component layers." |
| " Skipping this layer.", |
| prop->info.layerName, prop->component_layer_names.list[comp_layer]); |
| return false; |
| } |
| } |
| } |
| // Didn't exit early so that means it passed all checks |
| loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_LAYER_BIT, 0, |
| "Meta-layer \"%s\" all %d component layers appear to be valid.", prop->info.layerName, |
| prop->component_layer_names.count); |
| |
| // If layer logging is on, list the internals included in the meta-layer |
| for (uint32_t comp_layer = 0; comp_layer < prop->component_layer_names.count; comp_layer++) { |
| loader_log(inst, VULKAN_LOADER_LAYER_BIT, 0, " [%d] %s", comp_layer, prop->component_layer_names.list[comp_layer]); |
| } |
| return true; |
| } |
| |
| // Add any instance and device extensions from component layers to this layer |
| // list, so that anyone querying extensions will only need to look at the meta-layer |
| bool update_meta_layer_extensions_from_component_layers(const struct loader_instance *inst, struct loader_layer_properties *prop, |
| struct loader_layer_list *instance_layers) { |
| VkResult res = VK_SUCCESS; |
| for (uint32_t comp_layer = 0; comp_layer < prop->component_layer_names.count; comp_layer++) { |
| struct loader_layer_properties *comp_prop = |
| loader_find_layer_property(prop->component_layer_names.list[comp_layer], instance_layers); |
| |
| if (NULL != comp_prop->instance_extension_list.list) { |
| for (uint32_t ext = 0; ext < comp_prop->instance_extension_list.count; ext++) { |
| loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "Meta-layer %s component layer %s adding instance extension %s", |
| prop->info.layerName, prop->component_layer_names.list[comp_layer], |
| comp_prop->instance_extension_list.list[ext].extensionName); |
| |
| if (!has_vk_extension_property(&comp_prop->instance_extension_list.list[ext], &prop->instance_extension_list)) { |
| res = loader_add_to_ext_list(inst, &prop->instance_extension_list, 1, |
| &comp_prop->instance_extension_list.list[ext]); |
| if (VK_ERROR_OUT_OF_HOST_MEMORY == res) { |
| return res; |
| } |
| } |
| } |
| } |
| if (NULL != comp_prop->device_extension_list.list) { |
| for (uint32_t ext = 0; ext < comp_prop->device_extension_list.count; ext++) { |
| loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "Meta-layer %s component layer %s adding device extension %s", |
| prop->info.layerName, prop->component_layer_names.list[comp_layer], |
| comp_prop->device_extension_list.list[ext].props.extensionName); |
| |
| if (!has_vk_dev_ext_property(&comp_prop->device_extension_list.list[ext].props, &prop->device_extension_list)) { |
| loader_add_to_dev_ext_list(inst, &prop->device_extension_list, |
| &comp_prop->device_extension_list.list[ext].props, NULL); |
| if (VK_ERROR_OUT_OF_HOST_MEMORY == res) { |
| return res; |
| } |
| } |
| } |
| } |
| } |
| return res; |
| } |
| |
| // Verify that all meta-layers in a layer list are valid. |
| VkResult verify_all_meta_layers(struct loader_instance *inst, const struct loader_envvar_all_filters *filters, |
| struct loader_layer_list *instance_layers, bool *override_layer_present) { |
| VkResult res = VK_SUCCESS; |
| *override_layer_present = false; |
| for (int32_t i = 0; i < (int32_t)instance_layers->count; i++) { |
| struct loader_layer_properties *prop = &instance_layers->list[i]; |
| |
| // If this is a meta-layer, make sure it is valid |
| if (prop->type_flags & VK_LAYER_TYPE_FLAG_META_LAYER) { |
| if (verify_meta_layer_component_layers(inst, prop, instance_layers)) { |
| // If any meta layer is valid, update its extension list to include the extensions from its component layers. |
| res = update_meta_layer_extensions_from_component_layers(inst, prop, instance_layers); |
| if (VK_ERROR_OUT_OF_HOST_MEMORY == res) { |
| return res; |
| } |
| if (prop->is_override && loader_implicit_layer_is_enabled(inst, filters, prop)) { |
| *override_layer_present = true; |
| } |
| } else { |
| loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, |
| "Removing meta-layer %s from instance layer list since it appears invalid.", prop->info.layerName); |
| |
| loader_remove_layer_in_list(inst, instance_layers, i); |
| i--; |
| } |
| } |
| } |
| return res; |
| } |
| |
| // If the current working directory matches any app_key_path of the layers, remove all other override layers. |
| // Otherwise if no matching app_key was found, remove all but the global override layer, which has no app_key_path. |
| void remove_all_non_valid_override_layers(struct loader_instance *inst, struct loader_layer_list *instance_layers) { |
| if (instance_layers == NULL) { |
| return; |
| } |
| |
| 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. |
| bool found_active_override_layer = false; |
| int global_layer_index = -1; |
| for (uint32_t i = 0; i < instance_layers->count; i++) { |
| struct loader_layer_properties *props = &instance_layers->list[i]; |
| if (strcmp(props->info.layerName, VK_OVERRIDE_LAYER_NAME) == 0) { |
| if (props->app_key_paths.count > 0) { // not the global layer |
| for (uint32_t j = 0; j < props->app_key_paths.count; j++) { |
| if (strcmp(props->app_key_paths.list[j], cur_path) == 0) { |
| if (!found_active_override_layer) { |
| found_active_override_layer = true; |
| } else { |
| loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, |
| "remove_all_non_valid_override_layers: Multiple override layers where the same path in " |
| "app_keys " |
| "was found. Using the first layer found"); |
| |
| // Remove duplicate active override layers that have the same app_key_path |
| loader_remove_layer_in_list(inst, instance_layers, i); |
| i--; |
| } |
| } |
| } |
| if (!found_active_override_layer) { |
| loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_LAYER_BIT, 0, |
| "--Override layer found but not used because app \'%s\' is not in \'app_keys\' list!", cur_path); |
| |
| // Remove non-global override layers that don't have an app_key that matches cur_path |
| loader_remove_layer_in_list(inst, instance_layers, i); |
| i--; |
| } |
| } else { |
| if (global_layer_index == -1) { |
| global_layer_index = i; |
| } else { |
| loader_log( |
| inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, |
| "remove_all_non_valid_override_layers: Multiple global override layers found. Using the first global " |
| "layer found"); |
| loader_remove_layer_in_list(inst, instance_layers, i); |
| i--; |
| } |
| } |
| } |
| } |
| // Remove global layer if layer with same the app_key_path as the path to the current executable is found |
| if (found_active_override_layer && global_layer_index >= 0) { |
| loader_remove_layer_in_list(inst, instance_layers, global_layer_index); |
| } |
| // Should be at most 1 override layer in the list now. |
| if (found_active_override_layer) { |
| loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_LAYER_BIT, 0, "Using the override layer for app key %s", cur_path); |
| } else if (global_layer_index >= 0) { |
| loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_LAYER_BIT, 0, "Using the global override layer"); |
| } |
| } |
| |
| /* The following are required in the "layer" object: |
| * "name" |
| * "type" |
| * (for non-meta layers) "library_path" |
| * (for meta layers) "component_layers" |
| * "api_version" |
| * "implementation_version" |
| * "description" |
| * (for implicit layers) "disable_environment" |
| */ |
| |
| VkResult loader_read_layer_json(const struct loader_instance *inst, struct loader_layer_list *layer_instance_list, |
| cJSON *layer_node, loader_api_version version, bool is_implicit, char *filename) { |
| assert(layer_instance_list); |
| char *type = NULL; |
| char *api_version = NULL; |
| char *implementation_version = NULL; |
| VkResult result = VK_SUCCESS; |
| struct loader_layer_properties props = {0}; |
| |
| // Parse name |
| |
| result = loader_parse_json_string_to_existing_str(inst, layer_node, "name", VK_MAX_EXTENSION_NAME_SIZE, props.info.layerName); |
| if (VK_ERROR_OUT_OF_HOST_MEMORY == result) goto out; |
| if (VK_ERROR_INITIALIZATION_FAILED == result) { |
| loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, |
| "Layer located at %s didn't find required layer value \"name\" in manifest JSON file, skipping this layer", |
| filename); |
| goto out; |
| } |
| |
| // Check if this layer's name matches the override layer name, set is_override to true if so. |
| if (!strcmp(props.info.layerName, VK_OVERRIDE_LAYER_NAME)) { |
| props.is_override = true; |
| } |
| |
| if (0 != strncmp(props.info.layerName, "VK_LAYER_", 9)) { |
| loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, "Layer name %s does not conform to naming standard (Policy #LLP_LAYER_3)", |
| props.info.layerName); |
| } |
| |
| // Parse type |
| |
| result = loader_parse_json_string(layer_node, "type", &type); |
| if (VK_ERROR_OUT_OF_HOST_MEMORY == result) goto out; |
| if (VK_ERROR_INITIALIZATION_FAILED == result) { |
| loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, |
| "Layer located at %s didn't find required layer value \"type\" in manifest JSON file, skipping this layer", |
| filename); |
| goto out; |
| } |
| |
| // Add list entry |
| if (!strcmp(type, "DEVICE")) { |
| loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, "Device layers are deprecated. Skipping this layer"); |
| result = VK_ERROR_INITIALIZATION_FAILED; |
| goto out; |
| } |
| |
| // Allow either GLOBAL or INSTANCE type interchangeably to handle layers that must work with older loaders |
| if (!strcmp(type, "INSTANCE") || !strcmp(type, "GLOBAL")) { |
| props.type_flags = VK_LAYER_TYPE_FLAG_INSTANCE_LAYER; |
| if (!is_implicit) { |
| props.type_flags |= VK_LAYER_TYPE_FLAG_EXPLICIT_LAYER; |
| } |
| } else { |
| result = VK_ERROR_INITIALIZATION_FAILED; |
| goto out; |
| } |
| |
| // Parse api_version |
| |
| result = loader_parse_json_string(layer_node, "api_version", &api_version); |
| if (VK_ERROR_OUT_OF_HOST_MEMORY == result) goto out; |
| if (VK_ERROR_INITIALIZATION_FAILED == result) { |
| loader_log( |
| inst, VULKAN_LOADER_WARN_BIT, 0, |
| "Layer located at %s didn't find required layer value \"api_version\" in manifest JSON file, skipping this layer", |
| filename); |
| goto out; |
| } |
| |
| props.info.specVersion = loader_parse_version_string(api_version); |
| |
| // Make sure the layer's manifest doesn't contain a non zero variant value |
| if (VK_API_VERSION_VARIANT(props.info.specVersion) != 0) { |
| loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_LAYER_BIT, 0, |
| "Layer \"%s\" has an \'api_version\' field which contains a non-zero variant value of %d. " |
| " Skipping Layer.", |
| props.info.layerName, VK_API_VERSION_VARIANT(props.info.specVersion)); |
| result = VK_ERROR_INITIALIZATION_FAILED; |
| goto out; |
| } |
| |
| // Parse implementation_version |
| |
| result = loader_parse_json_string(layer_node, "implementation_version", &implementation_version); |
| if (VK_ERROR_OUT_OF_HOST_MEMORY == result) goto out; |
| if (VK_ERROR_INITIALIZATION_FAILED == result) { |
| loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, |
| "Layer located at %s didn't find required layer value \"implementation_version\" in manifest JSON file, " |
| "skipping this layer", |
| filename); |
| goto out; |
| } |
| props.info.implementationVersion = atoi(implementation_version); |
| |
| // Parse description |
| |
| result = loader_parse_json_string_to_existing_str(inst, layer_node, "description", VK_MAX_EXTENSION_NAME_SIZE, |
| props.info.description); |
| if (VK_ERROR_OUT_OF_HOST_MEMORY == result) goto out; |
| if (VK_ERROR_INITIALIZATION_FAILED == result) { |
| loader_log( |
| inst, VULKAN_LOADER_WARN_BIT, 0, |
| "Layer located at %s didn't find required layer value \"description\" in manifest JSON file, skipping this layer", |
| filename); |
| goto out; |
| } |
| |
| // Parse library_path |
| |
| // Library path no longer required unless component_layers is also not defined |
| cJSON *library_path = loader_cJSON_GetObjectItem(layer_node, "library_path"); |
| |
| if (NULL != library_path) { |
| if (NULL != loader_cJSON_GetObjectItem(layer_node, "component_layers")) { |
| loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, |
| "Indicating meta-layer-specific component_layers, but also defining layer library path. Both are not " |
| "compatible, so skipping this layer"); |
| result = VK_ERROR_INITIALIZATION_FAILED; |
| goto out; |
| } |
| |
| result = loader_copy_to_new_str(inst, filename, &props.manifest_file_name); |
| if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; |
| |
| char *library_path_str = loader_cJSON_Print(library_path); |
| if (NULL == library_path_str) { |
| loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, |
| "Skipping layer due to problem accessing the library_path value in manifest JSON file %s", filename); |
| result = VK_ERROR_OUT_OF_HOST_MEMORY; |
| goto out; |
| } |
| |
| // This function takes ownership of library_path_str - so we don't need to clean it up |
| result = combine_manifest_directory_and_library_path(inst, library_path_str, filename, &props.lib_name); |
| if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; |
| } |
| |
| // Parse component_layers |
| |
| if (NULL == library_path) { |
| if (!loader_check_version_meets_required(LOADER_VERSION_1_1_0, version)) { |
| loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, |
| "Indicating meta-layer-specific component_layers, but using older JSON file version."); |
| } |
| |
| result = loader_parse_json_array_of_strings(inst, layer_node, "component_layers", &(props.component_layer_names)); |
| if (VK_ERROR_OUT_OF_HOST_MEMORY == result) { |
| goto out; |
| } |
| if (VK_ERROR_INITIALIZATION_FAILED == result) { |
| loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, |
| "Layer missing both library_path and component_layers fields. One or the other MUST be defined. Skipping " |
| "this layer"); |
| goto out; |
| } |
| // This is now, officially, a meta-layer |
| props.type_flags |= VK_LAYER_TYPE_FLAG_META_LAYER; |
| loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_LAYER_BIT, 0, "Encountered meta-layer \"%s\"", |
| props.info.layerName); |
| } |
| |
| // Parse blacklisted_layers |
| |
| if (props.is_override) { |
| result = loader_parse_json_array_of_strings(inst, layer_node, "blacklisted_layers", &(props.blacklist_layer_names)); |
| if (VK_ERROR_OUT_OF_HOST_MEMORY == result) { |
| goto out; |
| } |
| } |
| |
| // Parse override_paths |
| |
| result = loader_parse_json_array_of_strings(inst, layer_node, "override_paths", &(props.override_paths)); |
| if (VK_ERROR_OUT_OF_HOST_MEMORY == result) { |
| goto out; |
| } |
| if (NULL != props.override_paths.list && !loader_check_version_meets_required(loader_combine_version(1, 1, 0), version)) { |
| loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, |
| "Indicating meta-layer-specific override paths, but using older JSON file version."); |
| } |
| |
| // Parse disable_environment |
| |
| if (is_implicit) { |
| cJSON *disable_environment = loader_cJSON_GetObjectItem(layer_node, "disable_environment"); |
| if (disable_environment == NULL) { |
| loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, |
| "Didn't find required layer object disable_environment in manifest JSON file, skipping this layer"); |
| result = VK_ERROR_INITIALIZATION_FAILED; |
| goto out; |
| } |
| |
| if (!disable_environment->child || disable_environment->child->type != cJSON_String) { |
| loader_log(inst, VULKAN_LOADER_WARN_BIT, 0, |
| "Didn't find required layer child value disable_environment in manifest JSON file, skipping this layer " |
| "(Policy #LLP_LAYER_9)"); |
| result = VK_ERROR_INITIALIZATION_FAILED; |
| goto out; |
| } |
| result = loader_copy_to_new_str(inst, disable_environment->child->string, &(props.disable_env_var.name)); |
| if (VK_SUCCESS != result) goto out; |
| result = loader_copy_to_new_str(inst, disable_environment->child->valuestring, &(props.disable_env_var.value)); |
| if (VK_SUCCESS != result) goto out; |
| } |
| |
| // Now get all optional items and objects and put in list: |
| // functions |
| // instance_extensions |
| // device_extensions |
| // enable_environment (implicit layers only) |
| // library_arch |
| |
| // Layer interface functions |
| // vkGetInstanceProcAddr |
| // vkGetDeviceProcAddr |
| // vkNegotiateLoaderLayerInterfaceVersion (starting with JSON file 1.1.0) |
| cJSON *functions = loader_cJSON_GetObjectItem(layer_node, "functions"); |
| if (functions != NULL) { |
| if (loader_check_version_meets_required(loader_combine_version(1, 1, 0), version)) { |
| result = loader_parse_json_string(functions, "vkNegotiateLoaderLayerInterfaceVersion", |
| &props.functions.str_negotiate_interface); |
| if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; |
| } |
| result = loader_parse_json_string(functions, "vkGetInstanceProcAddr", &props.functions.str_gipa); |
| if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; |
| |
| if (NULL == props.functions.str_negotiate_interface && props.functions.str_gipa && |
| loader_check_version_meets_required(loader_combine_version(1, 1, 0), version)) { |
| loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, |
| "Layer \"%s\" using deprecated \'vkGetInstanceProcAddr\' tag which was deprecated starting with JSON " |
| "file version 1.1.0. The new vkNegotiateLoaderLayerInterfaceVersion function is preferred, though for " |
| "compatibility reasons it may be desirable to continue using the deprecated tag.", |
| props.info.layerName); |
| } |
| |
| result = loader_parse_json_string(functions, "vkGetDeviceProcAddr", &props.functions.str_gdpa); |
| if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; |
| |
| if (NULL == props.functions.str_negotiate_interface && props.functions.str_gdpa && |
| loader_check_version_meets_required(loader_combine_version(1, 1, 0), version)) { |
| loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, |
| "Layer \"%s\" using deprecated \'vkGetDeviceProcAddr\' tag which was deprecated starting with JSON " |
| "file version 1.1.0. The new vkNegotiateLoaderLayerInterfaceVersion function is preferred, though for " |
| "compatibility reasons it may be desirable to continue using the deprecated tag.", |
| props.info.layerName); |
| } |
| } |
| |
| // instance_extensions |
| // array of { |
| // name |
| // spec_version |
| // } |
| |
| cJSON *instance_extensions = loader_cJSON_GetObjectItem(layer_node, "instance_extensions"); |
| if (instance_extensions != NULL) { |
| int count = loader_cJSON_GetArraySize(instance_extensions); |
| for (int i = 0; i < count; i++) { |
| VkExtensionProperties ext_prop = {0}; |
| cJSON *ext_item = loader_cJSON_GetArrayItem(instance_extensions, i); |
| result = loader_parse_json_string_to_existing_str(inst, ext_item, "name", VK_MAX_EXTENSION_NAME_SIZE, |
| ext_prop.extensionName); |
| if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; |
| if (result == VK_ERROR_INITIALIZATION_FAILED) continue; |
| char *spec_version = NULL; |
| result = loader_parse_json_string(ext_item, "spec_version", &spec_version); |
| if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; |
| if (NULL != spec_version) { |
| ext_prop.specVersion = atoi(spec_version); |
| } |
| loader_instance_heap_free(inst, spec_version); |
| bool ext_unsupported = wsi_unsupported_instance_extension(&ext_prop); |
| if (!ext_unsupported) { |
| loader_add_to_ext_list(inst, &props.instance_extension_list, 1, &ext_prop); |
| } |
| } |
| } |
| |
| // device_extensions |
| // array of { |
| // name |
| // spec_version |
| // entrypoints |
| // } |
| cJSON *device_extensions = loader_cJSON_GetObjectItem(layer_node, "device_extensions"); |
| if (device_extensions != NULL) { |
| int count = loader_cJSON_GetArraySize(device_extensions); |
| for (int i = 0; i < count; i++) { |
| VkExtensionProperties ext_prop = {0}; |
| |
| cJSON *ext_item = loader_cJSON_GetArrayItem(device_extensions, i); |
| |
| result = loader_parse_json_string_to_existing_str(inst, ext_item, "name", VK_MAX_EXTENSION_NAME_SIZE, |
| ext_prop.extensionName); |
| if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; |
| |
| char *spec_version = NULL; |
| result = loader_parse_json_string(ext_item, "spec_version", &spec_version); |
| if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; |
| if (NULL != spec_version) { |
| ext_prop.specVersion = atoi(spec_version); |
| } |
| loader_instance_heap_free(inst, spec_version); |
| |
| cJSON *entrypoints = loader_cJSON_GetObjectItem(ext_item, "entrypoints"); |
| if (entrypoints == NULL) { |
| result = loader_add_to_dev_ext_list(inst, &props.device_extension_list, &ext_prop, NULL); |
| if (result == VK_ERROR_OUT_OF_HOST_MEMORY) goto out; |
| continue; |
| } |
| |
| |