diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 68ab0db2a..4fba834b2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,6 +37,7 @@ jobs: linux: needs: codegen runs-on: ${{matrix.os}} + timeout-minutes: 30 strategy: matrix: compiler: [ {cc: gcc, cxx: g++}, {cc: clang, cxx: clang++} ] @@ -77,6 +78,7 @@ jobs: codegen: runs-on: ubuntu-latest + timeout-minutes: 30 steps: - uses: actions/checkout@v4 - run: scripts/update_deps.py --dir ext --no-build @@ -85,6 +87,7 @@ jobs: linux-no-asm: needs: codegen runs-on: ubuntu-24.04 + timeout-minutes: 30 steps: - uses: actions/checkout@v4 - run: sudo apt update @@ -105,6 +108,7 @@ jobs: linux-32: needs: codegen runs-on: ubuntu-24.04 + timeout-minutes: 30 strategy: matrix: config: [ Debug, Release ] @@ -143,6 +147,7 @@ jobs: linux-32-no-asm: needs: codegen runs-on: ubuntu-24.04 + timeout-minutes: 30 steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 @@ -178,6 +183,7 @@ jobs: # windows is 2x expensive to run on GitHub machines, so only run if we know something else simple passed as well needs: linux-no-asm runs-on: windows-latest + timeout-minutes: 30 strategy: matrix: arch: [ Win32, x64 ] @@ -199,6 +205,7 @@ jobs: # windows is 2x expensive to run on GitHub machines, so only run if we know something else simple passed as well needs: linux-no-asm runs-on: windows-latest + timeout-minutes: 30 strategy: matrix: arch: [ Win32, x64 ] @@ -218,13 +225,14 @@ jobs: # Test both clang and clang-cl (Chromium project uses clang-cl) windows_clang: # windows is 2x expensive to run on GitHub machines, so only run if we know something else simple passed as well - needs: linux-no-asm - runs-on: windows-2022 - strategy: - matrix: - compiler: [ clang, clang-cl ] - config: [ Debug, Release ] - steps: + needs: linux-no-asm + runs-on: windows-2022 + timeout-minutes: 30 + strategy: + matrix: + compiler: [ clang, clang-cl ] + config: [ Debug, Release ] + steps: - uses: actions/checkout@v4 - uses: ilammy/msvc-dev-cmd@v1 - run: | @@ -244,6 +252,7 @@ jobs: # Mac is 10x expensive to run on GitHub machines, so only run if we know something else passed as well needs: windows_clang runs-on: macos-13 + timeout-minutes: 30 strategy: matrix: config: [ Debug, Release ] @@ -270,14 +279,15 @@ jobs: - run: ctest --parallel --output-on-failure --test-dir build/ apple-cross-compile: - # Mac is 10x expensive to run on GitHub machines, so only run if we know something else passed as well - needs: windows_clang - name: ${{ matrix.CMAKE_SYSTEM_NAME }} - runs-on: macos-13 - strategy: - matrix: - CMAKE_SYSTEM_NAME: [ iOS, tvOS ] - steps: + # Mac is 10x expensive to run on GitHub machines, so only run if we know something else passed as well + needs: windows_clang + name: ${{ matrix.CMAKE_SYSTEM_NAME }} + runs-on: macos-13 + timeout-minutes: 30 + strategy: + matrix: + CMAKE_SYSTEM_NAME: [ iOS, tvOS ] + steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: @@ -306,6 +316,7 @@ jobs: needs: windows_clang name: "Universal Binary Testing (STATIC ${{ matrix.static }}) w/ ${{ matrix.generator }}" runs-on: macos-latest + timeout-minutes: 30 strategy: matrix: static: [ 'ON', 'OFF' ] @@ -336,99 +347,104 @@ jobs: vtool -show-build /tmp/lib/libvulkan.dylib | grep 'architecture arm64' chromium: - needs: codegen - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - run: scripts/gn/gn.py + needs: codegen + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + - run: scripts/gn/gn.py mingw: - # windows is 2x expensive to run on GitHub machines, so only run if we know something else simple passed as well - if: false # Disabled due to issues with msys2 making CMake unable to find a working compiler - needs: linux-no-asm - runs-on: windows-2022 - defaults: - run: - shell: bash - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - name: Setup uasm - run: | - C:/msys64/usr/bin/pacman -Sy --noconfirm --needed mingw-w64-x86_64-uasm - printf '%s\n' 'C:/msys64/mingw64/bin' >> $GITHUB_PATH - - name: UASM Check - run: uasm -? - - run: | - cmake -S. -B build \ - -D UPDATE_DEPS=ON \ - -D CMAKE_BUILD_TYPE=Release \ - -D BUILD_WERROR=ON \ - -G Ninja - - run: cmake --build build - - run: cmake --install build --prefix /tmp + # windows is 2x expensive to run on GitHub machines, so only run if we know something else simple passed as well + needs: linux-no-asm + if: false # Disabled due to issues with msys2 making CMake unable to find a working compiler + runs-on: windows-2022 + timeout-minutes: 30 + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + - name: Setup uasm + run: | + C:/msys64/usr/bin/pacman -Sy --noconfirm --needed mingw-w64-x86_64-uasm + printf '%s\n' 'C:/msys64/mingw64/bin' >> $GITHUB_PATH + - name: UASM Check + run: uasm -? + - run: | + cmake -S. -B build \ + -D UPDATE_DEPS=ON \ + -D CMAKE_BUILD_TYPE=Release \ + -D BUILD_WERROR=ON \ + -G Ninja + - run: cmake --build build + - run: cmake --install build --prefix /tmp mingw-use-gas: - # windows is 2x expensive to run on GitHub machines, so only run if we know something else simple passed as well - needs: linux-no-asm - runs-on: windows-2022 - defaults: - run: - shell: bash - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - run: | - cmake -S. -B build \ - -D UPDATE_DEPS=ON \ - -D CMAKE_BUILD_TYPE=Release \ - -D BUILD_WERROR=ON \ - -D USE_GAS=ON \ - -G Ninja - - run: cmake --build build - - run: cmake --install build --prefix /tmp + # windows is 2x expensive to run on GitHub machines, so only run if we know something else simple passed as well + needs: linux-no-asm + runs-on: windows-2022 + timeout-minutes: 30 + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + - run: | + cmake -S. -B build \ + -D UPDATE_DEPS=ON \ + -D CMAKE_BUILD_TYPE=Release \ + -D BUILD_WERROR=ON \ + -D USE_GAS=ON \ + -G Ninja + - run: cmake --build build + - run: cmake --install build --prefix /tmp mingw-no-asm: - # windows is 2x expensive to run on GitHub machines, so only run if we know something else simple passed as well - needs: linux-no-asm - runs-on: windows-2022 - defaults: - run: - shell: bash - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: '3.11' - # Make sure this doesn't fail even without explicitly setting '-D USE_MASM=OFF' and without uasm - - run: | - cmake -S. -B build \ - -D UPDATE_DEPS=ON \ - -D CMAKE_BUILD_TYPE=Release \ - -D BUILD_WERROR=ON \ - -G Ninja - - run: cmake --build build - - run: cmake --install build --prefix /tmp + # windows is 2x expensive to run on GitHub machines, so only run if we know something else simple passed as well + needs: linux-no-asm + runs-on: windows-2022 + timeout-minutes: 30 + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + # Make sure this doesn't fail even without explicitly setting '-D USE_MASM=OFF' and without uasm + - run: | + cmake -S. -B build \ + -D UPDATE_DEPS=ON \ + -D CMAKE_BUILD_TYPE=Release \ + -D BUILD_WERROR=ON \ + -G Ninja + - run: cmake --build build + - run: cmake --install build --prefix /tmp mingw-no-asm-explicit: - # windows is 2x expensive to run on GitHub machines, so only run if we know something else simple passed as well - needs: linux-no-asm - runs-on: windows-2022 - defaults: - run: - shell: bash - steps: - - uses: actions/checkout@v4 - - run: | - cmake -S. -B build \ - -D UPDATE_DEPS=ON \ - -D CMAKE_BUILD_TYPE=Release \ - -D BUILD_WERROR=ON \ - -D USE_MASM=OFF \ - -G Ninja - - run: cmake --build build - - run: cmake --install build --prefix /tmp + # windows is 2x expensive to run on GitHub machines, so only run if we know something else simple passed as well + needs: linux-no-asm + runs-on: windows-2022 + timeout-minutes: 30 + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + - run: | + cmake -S. -B build \ + -D UPDATE_DEPS=ON \ + -D CMAKE_BUILD_TYPE=Release \ + -D BUILD_WERROR=ON \ + -D USE_MASM=OFF \ + -G Ninja + - run: cmake --build build + - run: cmake --install build --prefix /tmp diff --git a/docs/LoaderSettingsFile.md b/docs/LoaderSettingsFile.md new file mode 100644 index 000000000..d73f41076 --- /dev/null +++ b/docs/LoaderSettingsFile.md @@ -0,0 +1,114 @@ + +[![Khronos Vulkan][1]][2] + +[1]: https://vulkan.lunarg.com/img/Vulkan_100px_Dec16.png "https://www.khronos.org/vulkan/" +[2]: https://www.khronos.org/vulkan/ + +# Loader Settings File + +[![Creative Commons][3]][4] + + + +[3]: https://i.creativecommons.org/l/by-nd/4.0/88x31.png "Creative Commons License" +[4]: https://creativecommons.org/licenses/by-nd/4.0/ + + +## Table of Contents + +- [Purpose of the Settings File](#purpose-of-the-settings-file) +- [Settings File Discovery](#settings-file-discovery) + - [Windows](#windows) + - [Linux/MacOS/BSD/QNX/Fuchsia/GNU](#linuxmacosbsdqnxfuchsiagnu) + - [Other Platforms](#other-platforms) + - [Exception for Elevated Privileges](#exception-for-elevated-privileges) +- [Per-Application Settings File](#per-application-settings-file) +- [File Format](#file-format) +- [Example Settings File](#example-settings-file) + - [Fields](#fields) +- [Behavior](#behavior) + + +## Purpose of the Settings File + +The purpose of the Loader Settings File is to give developers superb control over the +behavior of the Vulkan-Loader. +It enables enhanced controls over which layers to load, the order layers in the call chain, +logging, and which drivers are available. + +The Loader Settings File is intended to be used by "Developer Control Panels" for the Vulkan API, such as the Vulkan Configurator, as a replacement for setting debug envrionment variables. + +## Settings File Discovery + +The Loader Settings File is located by searching in specific file system paths or through +platform specific mechanisms such as the Windows Registry. + +### Windows + +The Vulkan Loader first searches the Registry Key HKEY_CURRENT_USER\SOFTWARE\Khronos\Vulkan\LoaderSettings for a DWORD value whose name is +a valid path to a file named 'vk_loader_settings.json'. +If there are no matching values or the file doesn't exist, the Vulkan Loader performs the +same behavior as described above for the Registry Key HKEY_LOCAL_MACHINE\SOFTWARE\Khronos\Vulkan\LoaderSettings. + +### Linux/MacOS/BSD/QNX/Fuchsia/GNU + +The Loader Settings File is located by searching for a file named vk_loader_settings.json in the following locations: + +`$HOME/.local/share/vulkan/loader_settings.d/` +`$XDG_DATA_HOME/vulkan/loader_settings.d/` +`/etc/vulkan/loader_settings.d/` + +Where $HOME and %XDG_DATA_HOME refer to the values contained in the environment variables of the same name. +If a given environment variables is not present, that path is ignored. + +### Other Platforms + +Platforms not listed above currently do not support the Loader Settings File due to not having an appropriate search mechanism. + +### Exception for Elevated Privileges + +Because the Loader Settings File contains paths to Layer and ICD manifests, which contain +the paths to various executable binaries, it is necessary to restrict the use of the Loader +Settings File when the application is running with elevated privileges. + +This is accomplished by not using any Loader Settings Files that are found in non-privileged locations. + +On Windows, running with Elevated Privileges will ignore HKEY_CURRENT_USER\SOFTWARE\Khronos\Vulkan\LoaderSettings. + +On Linux/MacOS/BSD/QNX/Fuchsia/GNU, running with Elevated Privileges will use a secure method of querying $HOME and $XDG_DATA_HOME to prevent +malicious injection of unsecure search directories. + +## Per-Application Settings File + +## File Format + +The Loader Settings File is a JSON file with a + + +## Example Settings File + + +```json +{ + "file_format_version" : "1.0.1", + "settings": { + + } +} +``` + +### Fields + + + + + + + + + + + + + +## Behavior diff --git a/loader/loader.c b/loader/loader.c index 8967c1676..3fb179454 100644 --- a/loader/loader.c +++ b/loader/loader.c @@ -305,14 +305,13 @@ VkResult create_string_list(const struct loader_instance *inst, uint32_t allocat 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); +VkResult incrase_str_capacity_by_at_least_one(const struct loader_instance *inst, struct loader_string_list *string_list) { + assert(string_list); 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) { - loader_instance_heap_free(inst, str); // Must clean up in case of failure return VK_ERROR_OUT_OF_HOST_MEMORY; } } else if (string_list->count + 1 > string_list->allocated_count) { @@ -320,15 +319,38 @@ VkResult append_str_to_string_list(const struct loader_instance *inst, struct lo 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) { - loader_instance_heap_free(inst, str); // Must clean up in case of failure return VK_ERROR_OUT_OF_HOST_MEMORY; } string_list->allocated_count *= 2; } + 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); + VkResult res = incrase_str_capacity_by_at_least_one(inst, string_list); + if (res == VK_ERROR_OUT_OF_HOST_MEMORY) { + loader_instance_heap_free(inst, str); // Must clean up in case of failure + return res; + } string_list->list[string_list->count++] = str; return VK_SUCCESS; } +VkResult prepend_str_to_string_list(const struct loader_instance *inst, struct loader_string_list *string_list, char *str) { + assert(string_list && str); + VkResult res = incrase_str_capacity_by_at_least_one(inst, string_list); + if (res == VK_ERROR_OUT_OF_HOST_MEMORY) { + loader_instance_heap_free(inst, str); // Must clean up in case of failure + return res; + } + // Shift everything down one + void *ptr_to_list = memmove(string_list->list + 1, string_list->list, sizeof(char *) * string_list->count); + if (ptr_to_list) string_list->list[0] = str; // Write new string to start of list + string_list->count++; + 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); @@ -341,6 +363,18 @@ VkResult copy_str_to_string_list(const struct loader_instance *inst, struct load return append_str_to_string_list(inst, string_list, new_str); } +VkResult copy_str_to_start_of_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'; + return prepend_str_to_string_list(inst, string_list, new_str); +} + void free_string_list(const struct loader_instance *inst, struct loader_string_list *string_list) { assert(string_list); if (string_list->list) { @@ -3002,10 +3036,8 @@ void copy_data_file_info(const char *cur_path, const char *relative_path, size_t } } -// If the file found is a manifest file name, add it to the out_files manifest list. +// If the file found is a manifest file name, add it to the end of out_files manifest list. VkResult add_if_manifest_file(const struct loader_instance *inst, const char *file_name, struct loader_string_list *out_files) { - VkResult vk_result = VK_SUCCESS; - assert(NULL != file_name && "add_if_manifest_file: Received NULL pointer for file_name"); assert(NULL != out_files && "add_if_manifest_file: Received NULL pointer for out_files"); @@ -3014,15 +3046,26 @@ VkResult add_if_manifest_file(const struct loader_instance *inst, const char *fi const char *name_suffix = file_name + name_len - 5; if (!is_json(name_suffix, name_len)) { // Use incomplete to indicate invalid name, but to keep going. - vk_result = VK_INCOMPLETE; - goto out; + return VK_INCOMPLETE; } - vk_result = copy_str_to_string_list(inst, out_files, file_name, name_len); + return copy_str_to_string_list(inst, out_files, file_name, name_len); +} -out: +// If the file found is a manifest file name, add it to the start of the out_files manifest list. +VkResult prepend_if_manifest_file(const struct loader_instance *inst, const char *file_name, struct loader_string_list *out_files) { + assert(NULL != file_name && "prepend_if_manifest_file: Received NULL pointer for file_name"); + assert(NULL != out_files && "prepend_if_manifest_file: Received NULL pointer for out_files"); - return vk_result; + // Look for files ending with ".json" suffix + size_t name_len = strlen(file_name); + const char *name_suffix = file_name + name_len - 5; + if (!is_json(name_suffix, name_len)) { + // Use incomplete to indicate invalid name, but to keep going. + return VK_INCOMPLETE; + } + + return copy_str_to_start_of_string_list(inst, out_files, file_name, name_len); } // Add any files found in the search_path. If any path in the search path points to a specific JSON, attempt to @@ -3214,12 +3257,14 @@ VkResult read_data_files_in_search_paths(const struct loader_instance *inst, enu switch (manifest_type) { case LOADER_DATA_FILE_MANIFEST_DRIVER: - override_env = loader_secure_getenv(VK_DRIVER_FILES_ENV_VAR, inst); - if (NULL == override_env) { - // Not there, so fall back to the old name - override_env = loader_secure_getenv(VK_ICD_FILENAMES_ENV_VAR, inst); + if (loader_settings_should_use_driver_environment_variables(inst)) { + override_env = loader_secure_getenv(VK_DRIVER_FILES_ENV_VAR, inst); + if (NULL == override_env) { + // Not there, so fall back to the old name + override_env = loader_secure_getenv(VK_ICD_FILENAMES_ENV_VAR, inst); + } + additional_env = loader_secure_getenv(VK_ADDITIONAL_DRIVER_FILES_ENV_VAR, inst); } - additional_env = loader_secure_getenv(VK_ADDITIONAL_DRIVER_FILES_ENV_VAR, inst); #if COMMON_UNIX_PLATFORMS relative_location = VK_DRIVERS_INFO_RELATIVE_DIR; #endif @@ -3759,18 +3804,26 @@ VkResult loader_icd_scan(const struct loader_instance *inst, struct loader_icd_t goto out; } - // Parse the filter environment variables to determine if we have any special behavior - res = parse_generic_filter_environment_var(inst, VK_DRIVERS_SELECT_ENV_VAR, &select_filter); - if (VK_SUCCESS != res) { - goto out; + if (loader_settings_should_use_driver_environment_variables(inst)) { + // Parse the filter environment variables to determine if we have any special behavior + res = parse_generic_filter_environment_var(inst, VK_DRIVERS_SELECT_ENV_VAR, &select_filter); + if (VK_SUCCESS != res) { + goto out; + } + res = parse_generic_filter_environment_var(inst, VK_DRIVERS_DISABLE_ENV_VAR, &disable_filter); + if (VK_SUCCESS != res) { + goto out; + } } - res = parse_generic_filter_environment_var(inst, VK_DRIVERS_DISABLE_ENV_VAR, &disable_filter); + + // Get a list of manifest files for ICDs + res = loader_get_data_files(inst, LOADER_DATA_FILE_MANIFEST_DRIVER, NULL, &manifest_files); if (VK_SUCCESS != res) { goto out; } - // Get a list of manifest files for ICDs - res = loader_get_data_files(inst, LOADER_DATA_FILE_MANIFEST_DRIVER, NULL, &manifest_files); + // Add any drivers provided by the loader settings file + res = loader_settings_get_additional_driver_files(inst, &manifest_files); if (VK_SUCCESS != res) { goto out; } @@ -5417,10 +5470,10 @@ VkResult loader_validate_device_extensions(struct loader_instance *this_instance // All named terminator_ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateInstance(const VkInstanceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkInstance *pInstance) { - struct loader_icd_term *icd_term; - VkExtensionProperties *prop; + struct loader_icd_term *icd_term = NULL; + VkExtensionProperties *prop = NULL; char **filtered_extension_names = NULL; - VkInstanceCreateInfo icd_create_info; + VkInstanceCreateInfo icd_create_info = {0}; VkResult res = VK_SUCCESS; bool one_icd_successful = false; @@ -5608,7 +5661,7 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateInstance(const VkInstanceCreateI } // Create an instance, substituting the version to 1.0 if necessary - VkApplicationInfo icd_app_info; + VkApplicationInfo icd_app_info = {0}; const uint32_t api_variant = 0; const uint32_t api_version_1_0 = VK_API_VERSION_1_0; uint32_t icd_version_nopatch = @@ -5625,6 +5678,25 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateInstance(const VkInstanceCreateI icd_app_info.apiVersion = icd_version; icd_create_info.pApplicationInfo = &icd_app_info; } + + // If the settings file has device_configurations, we need to raise the ApiVersion drivers use to 1.1 if the driver + // supports 1.1 or higher. This allows 1.0 apps to use the device_configurations without the app having to set its own + // ApiVersion to 1.1 on its own. + if (ptr_instance->settings.settings_active && ptr_instance->settings.device_configuration_count > 0 && + icd_version >= VK_API_VERSION_1_1 && requested_version < VK_API_VERSION_1_1) { + if (NULL != pCreateInfo->pApplicationInfo) { + memcpy(&icd_app_info, pCreateInfo->pApplicationInfo, sizeof(VkApplicationInfo)); + } + icd_app_info.apiVersion = VK_API_VERSION_1_1; + icd_create_info.pApplicationInfo = &icd_app_info; + + loader_log( + ptr_instance, VULKAN_LOADER_INFO_BIT, 0, + "terminator_CreateInstance: Raising the VkApplicationInfo::apiVersion from 1.0 to 1.1 on driver \"%s\" so that " + "the loader settings file is able to use this driver in the device_configuration selection logic.", + icd_term->scanned_icd->lib_name); + } + icd_result = ptr_instance->icd_tramp_list.scanned_list[i].CreateInstance(&icd_create_info, pAllocator, &(icd_term->instance)); if (VK_ERROR_OUT_OF_HOST_MEMORY == icd_result) { @@ -6738,28 +6810,133 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_EnumeratePhysicalDevices(VkInstance in goto out; } - uint32_t copy_count = inst->phys_dev_count_term; - if (NULL != pPhysicalDevices) { - if (copy_count > *pPhysicalDeviceCount) { - copy_count = *pPhysicalDeviceCount; - loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, - "terminator_EnumeratePhysicalDevices : Trimming device count from %d to %d.", inst->phys_dev_count_term, - copy_count); - res = VK_INCOMPLETE; + if (inst->settings.settings_active && inst->settings.device_configuration_count > 0) { + // Use settings file device_configurations if present + if (NULL == pPhysicalDevices) { + // take the minimum of the settings configurations count and number of terminators + *pPhysicalDeviceCount = (inst->settings.device_configuration_count < inst->phys_dev_count_term) + ? inst->settings.device_configuration_count + : inst->phys_dev_count_term; + } else { + res = loader_apply_settings_device_configurations(inst, pPhysicalDeviceCount, pPhysicalDevices); } + } else { + // Otherwise just copy the physical devices up normally and pass it up the chain + uint32_t copy_count = inst->phys_dev_count_term; + if (NULL != pPhysicalDevices) { + if (copy_count > *pPhysicalDeviceCount) { + copy_count = *pPhysicalDeviceCount; + loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, + "terminator_EnumeratePhysicalDevices : Trimming device count from %d to %d.", inst->phys_dev_count_term, + copy_count); + res = VK_INCOMPLETE; + } - for (uint32_t i = 0; i < copy_count; i++) { - pPhysicalDevices[i] = (VkPhysicalDevice)inst->phys_devs_term[i]; + for (uint32_t i = 0; i < copy_count; i++) { + pPhysicalDevices[i] = (VkPhysicalDevice)inst->phys_devs_term[i]; + } } - } - *pPhysicalDeviceCount = copy_count; + *pPhysicalDeviceCount = copy_count; + } out: return res; } +// Apply the device_configurations in the settings file to the output VkPhysicalDeviceList. +// That means looking up each VkPhysicalDevice's deviceUUID, filtering using that, and putting them in the order of +// device_configurations in the settings file. +VkResult loader_apply_settings_device_configurations(struct loader_instance *inst, uint32_t *pPhysicalDeviceCount, + VkPhysicalDevice *pPhysicalDevices) { + bool *pd_supports_11 = loader_stack_alloc(inst->phys_dev_count_term * sizeof(bool)); + if (NULL == pd_supports_11) { + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + memset(pd_supports_11, 0, inst->phys_dev_count_term * sizeof(bool)); + + VkPhysicalDeviceProperties *pd_props = loader_stack_alloc(inst->phys_dev_count_term * sizeof(VkPhysicalDeviceProperties)); + if (NULL == pd_props) { + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + memset(pd_props, 0, inst->phys_dev_count_term * sizeof(VkPhysicalDeviceProperties)); + + VkPhysicalDeviceVulkan11Properties *pd_vulkan_11_props = + loader_stack_alloc(inst->phys_dev_count_term * sizeof(VkPhysicalDeviceVulkan11Properties)); + if (NULL == pd_vulkan_11_props) { + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + memset(pd_vulkan_11_props, 0, inst->phys_dev_count_term * sizeof(VkPhysicalDeviceVulkan11Properties)); + + for (uint32_t i = 0; i < inst->phys_dev_count_term; i++) { + pd_vulkan_11_props[i].sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES; + + inst->phys_devs_term[i]->this_icd_term->dispatch.GetPhysicalDeviceProperties(inst->phys_devs_term[i]->phys_dev, + &pd_props[i]); + if (pd_props[i].apiVersion >= VK_API_VERSION_1_1) { + pd_supports_11[i] = true; + VkPhysicalDeviceProperties2 props2 = {0}; + props2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + props2.pNext = (void *)&pd_vulkan_11_props[i]; + if (inst->phys_devs_term[i]->this_icd_term->dispatch.GetPhysicalDeviceProperties2) { + inst->phys_devs_term[i]->this_icd_term->dispatch.GetPhysicalDeviceProperties2(inst->phys_devs_term[i]->phys_dev, + &props2); + } + } + } + + // Loop over the setting's device configurations, find each VkPhysicalDevice which matches the deviceUUID given, add to the + // pPhysicalDevices output list. + uint32_t written_output_index = 0; + + for (uint32_t i = 0; i < inst->settings.device_configuration_count; i++) { + uint8_t *current_deviceUUID = inst->settings.device_configurations[i].deviceUUID; + bool configuration_found = false; + for (uint32_t j = 0; j < inst->phys_dev_count_term; j++) { + // Don't compare deviceUUID's if they have nothing, since we require deviceUUID's to effectively sort them. + if (!pd_supports_11[j]) { + continue; + } + if (memcmp(current_deviceUUID, pd_vulkan_11_props[j].deviceUUID, sizeof(uint8_t) * VK_UUID_SIZE) == 0) { + configuration_found = true; + // Catch when there are more device_configurations than space available in the output + if (written_output_index >= *pPhysicalDeviceCount) { + *pPhysicalDeviceCount = written_output_index; // write out how many were written + return VK_INCOMPLETE; + } + pPhysicalDevices[written_output_index++] = (VkPhysicalDevice)inst->phys_devs_term[j]; + loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, "Insert VkPhysicalDevice \"%s\" to the pPhysicalDevices list", + pd_props[j].deviceName); + break; + } + } + if (!configuration_found) { + uint8_t *id = current_deviceUUID; + // Log that this configuration was missing. + if (inst->settings.device_configurations[i].deviceName[0] != '\0') { + loader_log( + inst, VULKAN_LOADER_WARN_BIT, 0, + "loader_apply_settings_device_configurations: settings file contained device_configuration which does not " + "appear in the enumerated VkPhysicalDevices. Missing VkPhysicalDevice with deviceName: \"%s\" and deviceUUID: " + "%x%x%x%x-%x%x-%x%x-%x%x-%x%x%x%x%x%x", + inst->settings.device_configurations[i].deviceName, id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7], + id[8], id[9], id[10], id[11], id[12], id[13], id[14], id[15]); + } else { + loader_log( + inst, VULKAN_LOADER_WARN_BIT, 0, + "loader_apply_settings_device_configurations: settings file contained device_configuration which does not " + "appear in the enumerated VkPhysicalDevices. Missing VkPhysicalDevice with deviceUUID: " + "%x%x%x%x-%x%x-%x%x-%x%x-%x%x%x%x%x%x", + id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7], id[8], id[9], id[10], id[11], id[12], id[13], id[14], + id[15]); + } + } + } + *pPhysicalDeviceCount = written_output_index; // update with how many were written + return VK_SUCCESS; +} + VKAPI_ATTR VkResult VKAPI_CALL terminator_EnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, const char *pLayerName, uint32_t *pPropertyCount, VkExtensionProperties *pProperties) { diff --git a/loader/loader.h b/loader/loader.h index a5527b964..2ed309c0e 100644 --- a/loader/loader.h +++ b/loader/loader.h @@ -110,14 +110,23 @@ VkResult loader_copy_to_new_str(const struct loader_instance *inst, const char * // Allocate a loader_string_list with enough space for allocated_count strings inside of it VkResult create_string_list(const struct loader_instance *inst, uint32_t allocated_count, struct loader_string_list *string_list); // Resize if there isn't enough space, then add the string str to the end of the loader_string_list -// This function takes ownership of the str passed in - but only when it succeeds +// This function takes ownership of the str passed in VkResult append_str_to_string_list(const struct loader_instance *inst, struct loader_string_list *string_list, char *str); -// Resize if there isn't enough space, then copy the string str to a new string the end of the loader_string_list +// Resize if there isn't enough space, then add the string str to the start of the loader_string_list +// This function takes ownership of the str passed in +VkResult prepend_str_to_string_list(const struct loader_instance *inst, struct loader_string_list *string_list, char *str); +// Copy the string str to a new string and append it to string_list, resizing string_list if there isn't enough space. // This function does not take ownership of the string, it merely copies it. -// This function appends a null terminator to the string automatically +// This function automatically appends a null terminator to the string being copied // The str_len parameter does not include the null terminator VkResult copy_str_to_string_list(const struct loader_instance *inst, struct loader_string_list *string_list, const char *str, size_t str_len); +// Copy the string str to a new string and prepend it to string_list, resizing string_list if there isn't enough space. +// This function does not take ownership of the string, it merely copies it. +// This function automatically appends a null terminator to the string being copied +// The str_len parameter does not include the null terminator +VkResult copy_str_to_start_of_string_list(const struct loader_instance *inst, struct loader_string_list *string_list, + const char *str, size_t str_len); // Free any string inside of loader_string_list and then free the list itself void free_string_list(const struct loader_instance *inst, struct loader_string_list *string_list); @@ -212,8 +221,13 @@ VkResult setup_loader_tramp_phys_dev_groups(struct loader_instance *inst, uint32 VkPhysicalDeviceGroupProperties *groups); void unload_drivers_without_physical_devices(struct loader_instance *inst); +VkResult loader_apply_settings_device_configurations(struct loader_instance *inst, uint32_t *pPhysicalDeviceCount, + VkPhysicalDevice *pPhysicalDevices); + VkStringErrorFlags vk_string_validate(const int max_length, const char *char_array); char *loader_get_next_path(char *path); +VkResult add_if_manifest_file(const struct loader_instance *inst, const char *file_name, struct loader_string_list *out_files); +VkResult prepend_if_manifest_file(const struct loader_instance *inst, const char *file_name, struct loader_string_list *out_files); VkResult add_data_files(const struct loader_instance *inst, char *search_path, struct loader_string_list *out_files, bool use_first_found_manifest); diff --git a/loader/settings.c b/loader/settings.c index fce896e83..a28c34fae 100644 --- a/loader/settings.c +++ b/loader/settings.c @@ -44,13 +44,35 @@ void free_layer_configuration(const struct loader_instance* inst, loader_setting memset(layer_configuration, 0, sizeof(loader_settings_layer_configuration)); } +void free_driver_configuration(const struct loader_instance* inst, loader_settings_driver_configuration* driver_configuration) { + loader_instance_heap_free(inst, driver_configuration->path); + memset(driver_configuration, 0, sizeof(loader_settings_driver_configuration)); +} + +void free_device_configuration(const struct loader_instance* inst, loader_settings_device_configuration* device_configuration) { + (void)inst; + memset(device_configuration, 0, sizeof(loader_settings_device_configuration)); +} + void free_loader_settings(const struct loader_instance* inst, loader_settings* settings) { if (NULL != settings->layer_configurations) { for (uint32_t i = 0; i < settings->layer_configuration_count; i++) { free_layer_configuration(inst, &settings->layer_configurations[i]); } + loader_instance_heap_free(inst, settings->layer_configurations); + } + if (NULL != settings->additional_drivers) { + for (uint32_t i = 0; i < settings->additional_driver_count; i++) { + free_driver_configuration(inst, &settings->additional_drivers[i]); + } + loader_instance_heap_free(inst, settings->additional_drivers); + } + if (NULL != settings->device_configurations) { + for (uint32_t i = 0; i < settings->device_configuration_count; i++) { + free_device_configuration(inst, &settings->device_configurations[i]); + } + loader_instance_heap_free(inst, settings->device_configurations); } - loader_instance_heap_free(inst, settings->layer_configurations); loader_instance_heap_free(inst, settings->settings_file_path); memset(settings, 0, sizeof(loader_settings)); } @@ -207,6 +229,164 @@ VkResult parse_layer_configurations(const struct loader_instance* inst, cJSON* s return res; } +VkResult parse_additional_driver(const struct loader_instance* inst, cJSON* additional_driver_json, + loader_settings_driver_configuration* additional_driver) { + VkResult res = VK_SUCCESS; + res = loader_parse_json_string(additional_driver_json, "path", &(additional_driver->path)); + if (res != VK_SUCCESS) { + goto out; + } +out: + if (res != VK_SUCCESS) { + free_driver_configuration(inst, additional_driver); + } + return res; +} + +VkResult parse_additional_drivers(const struct loader_instance* inst, cJSON* settings_object, loader_settings* loader_settings) { + VkResult res = VK_SUCCESS; + + cJSON* additional_drivers_use_exclusively_json = + loader_cJSON_GetObjectItem(settings_object, "additional_drivers_use_exclusively"); + if (additional_drivers_use_exclusively_json && additional_drivers_use_exclusively_json->type == cJSON_True) { + loader_settings->additional_drivers_use_exclusively = true; + } + + cJSON* additional_drivers_json = loader_cJSON_GetObjectItem(settings_object, "additional_drivers"); + if (NULL == additional_drivers_json) { + return VK_SUCCESS; + } + + uint32_t additional_driver_count = loader_cJSON_GetArraySize(additional_drivers_json); + if (additional_driver_count == 0) { + return VK_SUCCESS; + } + + loader_settings->additional_driver_count = additional_driver_count; + + loader_settings->additional_drivers = loader_instance_heap_calloc( + inst, sizeof(loader_settings_layer_configuration) * additional_driver_count, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); + if (NULL == loader_settings->additional_drivers) { + res = VK_ERROR_OUT_OF_HOST_MEMORY; + goto out; + } + + cJSON* driver = NULL; + size_t i = 0; + cJSON_ArrayForEach(driver, additional_drivers_json) { + if (driver->type != cJSON_Object) { + res = VK_ERROR_INITIALIZATION_FAILED; + goto out; + } + res = parse_additional_driver(inst, driver, &(loader_settings->additional_drivers[i++])); + if (VK_SUCCESS != res) { + goto out; + } + } +out: + if (res != VK_SUCCESS) { + if (loader_settings->additional_drivers) { + for (size_t index = 0; index < loader_settings->additional_driver_count; index++) { + free_driver_configuration(inst, &(loader_settings->additional_drivers[index])); + } + loader_settings->additional_driver_count = 0; + loader_instance_heap_free(inst, loader_settings->additional_drivers); + loader_settings->additional_drivers = NULL; + } + } + return res; +} + +VkResult parse_device_configuration(const struct loader_instance* inst, cJSON* device_configuration_json, + loader_settings_device_configuration* device_configuration) { + (void)inst; + VkResult res = VK_SUCCESS; + cJSON* deviceUUID_array = loader_cJSON_GetObjectItem(device_configuration_json, "deviceUUID"); + if (NULL == deviceUUID_array) { + res = VK_ERROR_INITIALIZATION_FAILED; + goto out; + } + if (VK_UUID_SIZE != loader_cJSON_GetArraySize(deviceUUID_array)) { + res = VK_ERROR_INITIALIZATION_FAILED; + goto out; + } + + cJSON* uuid_field = NULL; + size_t i = 0; + cJSON_ArrayForEach(uuid_field, deviceUUID_array) { + if (uuid_field->type != cJSON_Number) { + res = VK_ERROR_INITIALIZATION_FAILED; + goto out; + } + if (uuid_field->valueint < 0 || uuid_field->valueint > 255) { + res = VK_ERROR_INITIALIZATION_FAILED; + goto out; + } + device_configuration->deviceUUID[i] = (uint8_t)uuid_field->valueint; + i++; + } + + VkResult deviceNameRes = loader_parse_json_string_to_existing_str( + device_configuration_json, "deviceName", VK_MAX_PHYSICAL_DEVICE_NAME_SIZE, device_configuration->deviceName); + if (VK_ERROR_OUT_OF_HOST_MEMORY == deviceNameRes) { + res = deviceNameRes; + goto out; + } +out: + if (res != VK_SUCCESS) { + memset(device_configuration, 0, sizeof(loader_settings_device_configuration)); + } + return res; +} + +VkResult parse_device_configurations(const struct loader_instance* inst, cJSON* settings_object, loader_settings* loader_settings) { + VkResult res = VK_SUCCESS; + + cJSON* device_configurations = loader_cJSON_GetObjectItem(settings_object, "device_configurations"); + if (NULL == device_configurations) { + return VK_SUCCESS; + } + + uint32_t device_configuration_count = loader_cJSON_GetArraySize(device_configurations); + if (device_configuration_count == 0) { + return VK_SUCCESS; + } + + loader_settings->device_configuration_count = device_configuration_count; + + loader_settings->device_configurations = loader_instance_heap_calloc( + inst, sizeof(loader_settings_device_configuration) * device_configuration_count, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); + if (NULL == loader_settings->device_configurations) { + res = VK_ERROR_OUT_OF_HOST_MEMORY; + goto out; + } + + cJSON* device = NULL; + size_t i = 0; + cJSON_ArrayForEach(device, device_configurations) { + if (device->type != cJSON_Object) { + res = VK_ERROR_INITIALIZATION_FAILED; + goto out; + } + res = parse_device_configuration(inst, device, &(loader_settings->device_configurations[i++])); + if (VK_SUCCESS != res) { + goto out; + } + } +out: + if (res != VK_SUCCESS) { + if (loader_settings->device_configurations) { + for (size_t index = 0; index < loader_settings->device_configuration_count; index++) { + free_device_configuration(inst, &(loader_settings->device_configurations[index])); + } + loader_settings->device_configuration_count = 0; + loader_instance_heap_free(inst, loader_settings->device_configurations); + loader_settings->device_configurations = NULL; + } + } + return res; +} + VkResult check_if_settings_path_exists(const struct loader_instance* inst, const char* base, const char* suffix, char** settings_file_path) { if (NULL == base || NULL == suffix) { @@ -248,6 +428,30 @@ VkResult get_unix_settings_path(const struct loader_instance* inst, char** setti settings_file_path); } +bool check_if_layer_configurations_are_equal(loader_settings_layer_configuration* a, loader_settings_layer_configuration* b) { + if (!a->name || !b->name || 0 != strcmp(a->name, b->name)) { + return false; + } + if (!a->path || !b->path || 0 != strcmp(a->path, b->path)) { + return false; + } + return a->control == b->control; +} + +bool check_if_driver_configurations_are_equal(loader_settings_driver_configuration* a, loader_settings_driver_configuration* b) { + if (!a->path || !b->path || 0 != strcmp(a->path, b->path)) { + return false; + } + return true; +} + +bool check_if_device_configurations_are_equal(loader_settings_device_configuration* a, loader_settings_device_configuration* b) { + for (uint32_t i = 0; i < VK_UUID_SIZE; i++) { + if (a->deviceUUID[i] != b->deviceUUID[i]) return false; + } + return true; +} + bool check_if_settings_are_equal(loader_settings* a, loader_settings* b) { // If either pointer is null, return true if (NULL == a || NULL == b) return false; @@ -256,19 +460,17 @@ bool check_if_settings_are_equal(loader_settings* a, loader_settings* b) { are_equal &= a->has_unordered_layer_location == b->has_unordered_layer_location; are_equal &= a->debug_level == b->debug_level; are_equal &= a->layer_configuration_count == b->layer_configuration_count; + are_equal &= a->additional_driver_count == b->additional_driver_count; + are_equal &= a->device_configuration_count == b->device_configuration_count; if (!are_equal) return false; for (uint32_t i = 0; i < a->layer_configuration_count && i < b->layer_configuration_count; i++) { - if (a->layer_configurations[i].name && b->layer_configurations[i].name) { - are_equal &= 0 == strcmp(a->layer_configurations[i].name, b->layer_configurations[i].name); - } else { - are_equal = false; - } - if (a->layer_configurations[i].path && b->layer_configurations[i].path) { - are_equal &= 0 == strcmp(a->layer_configurations[i].path, b->layer_configurations[i].path); - } else { - are_equal = false; - } - are_equal &= a->layer_configurations[i].control == b->layer_configurations[i].control; + are_equal &= check_if_layer_configurations_are_equal(&a->layer_configurations[i], &b->layer_configurations[i]); + } + for (uint32_t i = 0; i < a->additional_driver_count && i < b->additional_driver_count; i++) { + are_equal &= check_if_driver_configurations_are_equal(&a->additional_drivers[i], &b->additional_drivers[i]); + } + for (uint32_t i = 0; i < a->device_configuration_count && i < b->device_configuration_count; i++) { + are_equal &= check_if_device_configurations_are_equal(&a->device_configurations[i], &b->device_configurations[i]); } return are_equal; } @@ -302,6 +504,27 @@ void log_settings(const struct loader_instance* inst, loader_settings* settings) loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "Control: %s", loader_settings_layer_control_to_string(settings->layer_configurations[i].control)); } + if (settings->additional_driver_count > 0) { + loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "----"); + loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "Use Additional Drivers Exclusively = %s", + settings->additional_drivers_use_exclusively ? "true" : "false"); + loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "Additional Driver Configurations count = %d", + settings->additional_driver_count); + for (uint32_t i = 0; i < settings->additional_driver_count; i++) { + loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "---- Driver Configuration [%d] ----", i); + loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "Path: %s", settings->additional_drivers[i].path); + } + } + if (settings->device_configuration_count > 0) { + loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "----"); + loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "Device Configurations count = %d", settings->device_configuration_count); + for (uint32_t i = 0; i < settings->device_configuration_count; i++) { + loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "---- Device Configuration [%d] ----", i); + uint8_t* id = settings->device_configurations[i].deviceUUID; + loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "deviceUUID: %x%x%x%x-%x%x-%x%x-%x%x-%x%x%x%x%x%x", id[0], id[1], id[2], + id[3], id[4], id[5], id[6], id[7], id[8], id[9], id[10], id[11], id[12], id[13], id[14], id[15]); + } + } loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, "---------------------------------"); } @@ -457,8 +680,9 @@ VkResult get_loader_settings(const struct loader_instance* inst, loader_settings } } - res = parse_layer_configurations(inst, settings_to_use, loader_settings); - if (res != VK_SUCCESS) { + VkResult layer_configurations_res = parse_layer_configurations(inst, settings_to_use, loader_settings); + if (VK_ERROR_OUT_OF_HOST_MEMORY == layer_configurations_res) { + res = layer_configurations_res; goto out; } @@ -471,9 +695,29 @@ VkResult get_loader_settings(const struct loader_instance* inst, loader_settings } } - loader_settings->settings_file_path = settings_file_path; - settings_file_path = NULL; - loader_settings->settings_active = true; + VkResult additional_drivers_res = parse_additional_drivers(inst, settings_to_use, loader_settings); + if (VK_ERROR_OUT_OF_HOST_MEMORY == additional_drivers_res) { + res = additional_drivers_res; + goto out; + } + + VkResult device_configurations_res = parse_device_configurations(inst, settings_to_use, loader_settings); + if (VK_ERROR_OUT_OF_HOST_MEMORY == device_configurations_res) { + res = device_configurations_res; + goto out; + } + + // Only consider the settings active if there is at least one "setting" active. + // Those are either logging, layers, additional_drivers, or device_configurations. + if (loader_settings->debug_level != 0 || loader_settings->layer_configuration_count != 0 || + loader_settings->additional_driver_count != 0 || loader_settings->device_configuration_count != 0) { + loader_settings->settings_file_path = settings_file_path; + settings_file_path = NULL; + loader_settings->settings_active = true; + } else { + loader_log(inst, VULKAN_LOADER_INFO_BIT, 0, + "vk_loader_settings.json file found at \"%s\" but did not contain any valid settings.", settings_file_path); + } out: if (NULL != json) { loader_cJSON_Delete(json); @@ -866,3 +1110,39 @@ VkResult enable_correct_layers_from_settings(const struct loader_instance* inst, out: return res; } + +VkResult loader_settings_get_additional_driver_files(const struct loader_instance* inst, struct loader_string_list* out_files) { + VkResult res = VK_SUCCESS; + + const loader_settings* settings = get_current_settings_and_lock(inst); + + if (NULL == settings || !settings->settings_active) { + goto out; + } + + if (settings->additional_drivers_use_exclusively) { + free_string_list(inst, out_files); + } + + for (uint32_t i = 0; i < settings->additional_driver_count; i++) { + res = prepend_if_manifest_file(inst, settings->additional_drivers[i].path, out_files); + } + +out: + release_current_settings_lock(inst); + return res; +} + +bool loader_settings_should_use_driver_environment_variables(const struct loader_instance* inst) { + bool should_use = true; + const loader_settings* settings = get_current_settings_and_lock(inst); + if (NULL == settings || !settings->settings_active) { + goto out; + } + if (settings->device_configuration_count > 0) { + should_use = false; + } +out: + release_current_settings_lock(inst); + return should_use; +} diff --git a/loader/settings.h b/loader/settings.h index 4e776ff0a..9ed5c80bf 100644 --- a/loader/settings.h +++ b/loader/settings.h @@ -34,6 +34,7 @@ struct loader_instance; struct loader_layer_list; +struct loader_string_list; struct loader_pointer_layer_list; struct loader_envvar_all_filters; typedef struct log_configuration log_configuration; @@ -61,6 +62,15 @@ typedef struct loader_settings_layer_configuration { } loader_settings_layer_configuration; +typedef struct loader_settings_driver_configuration { + char* path; +} loader_settings_driver_configuration; + +typedef struct loader_settings_device_configuration { + char deviceName[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE]; + uint8_t deviceUUID[VK_UUID_SIZE]; +} loader_settings_device_configuration; + typedef struct loader_settings { bool settings_active; bool has_unordered_layer_location; @@ -69,6 +79,13 @@ typedef struct loader_settings { uint32_t layer_configuration_count; loader_settings_layer_configuration* layer_configurations; + bool additional_drivers_use_exclusively; + uint32_t additional_driver_count; + loader_settings_driver_configuration* additional_drivers; + + uint32_t device_configuration_count; + loader_settings_device_configuration* device_configurations; + char* settings_file_path; } loader_settings; @@ -112,3 +129,11 @@ VkResult enable_correct_layers_from_settings(const struct loader_instance* inst, const struct loader_layer_list* instance_layers, struct loader_pointer_layer_list* target_layer_list, struct loader_pointer_layer_list* activated_layer_list); + +// Add any drivers that the loader settings file contains to the out_files list. If the additional_drivers_use_exclusively field is +// true, clear the out_files list before adding any additional drivers +VkResult loader_settings_get_additional_driver_files(const struct loader_instance* inst, struct loader_string_list* out_files); + +// Check if there are any device_configurations. If so, we don't want to allow environment variables from selecting or ignoring +// drivers. This is because the VkPhysicalDevices corresponding to a driver_configurations might not be present otherwise. +bool loader_settings_should_use_driver_environment_variables(const struct loader_instance* inst); diff --git a/tests/framework/data/fuzzer_output.json b/tests/framework/data/fuzzer_output.json index 571378087..6032f8079 100644 --- a/tests/framework/data/fuzzer_output.json +++ b/tests/framework/data/fuzzer_output.json @@ -6,7 +6,7 @@ "/out/settings_fuzzer", "/work/settings_fuzzer" ], - "stderr_log": [ + "stderr_logg": [ "all", "info", "warn", @@ -15,7 +15,8 @@ "debug", "layer", "driver", - "validation" + "validation", + "not_real" ], "log_locations": [ { diff --git a/tests/framework/icd/test_icd.cpp b/tests/framework/icd/test_icd.cpp index 560247425..a7e4cc400 100644 --- a/tests/framework/icd/test_icd.cpp +++ b/tests/framework/icd/test_icd.cpp @@ -1168,6 +1168,10 @@ VKAPI_ATTR void VKAPI_CALL test_vkGetPhysicalDeviceProperties2(VkPhysicalDevice auto* layered_driver_props = reinterpret_cast(pNext); layered_driver_props->underlyingAPI = phys_dev.layered_driver_underlying_api; } + if (pNext->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES) { + auto* vulkan_11_props = reinterpret_cast(pNext); + memcpy(vulkan_11_props->deviceUUID, phys_dev.deviceUUID.data(), VK_UUID_SIZE); + } pNext = reinterpret_cast(const_cast(pNext->pNext)); } } diff --git a/tests/framework/icd/test_icd.h b/tests/framework/icd/test_icd.h index fbaecd4dc..efab539b9 100644 --- a/tests/framework/icd/test_icd.h +++ b/tests/framework/icd/test_icd.h @@ -80,6 +80,7 @@ struct PhysicalDevice { DispatchableHandle vk_physical_device; BUILDER_VALUE(std::string, deviceName) + BUILDER_VALUE(VulkanUUID, deviceUUID) BUILDER_VALUE(VkPhysicalDeviceProperties, properties) BUILDER_VALUE(VkPhysicalDeviceFeatures, features) BUILDER_VALUE(VkPhysicalDeviceMemoryProperties, memory_properties) diff --git a/tests/framework/json_writer.h b/tests/framework/json_writer.h index f22db2892..4a4cd02a0 100644 --- a/tests/framework/json_writer.h +++ b/tests/framework/json_writer.h @@ -110,6 +110,28 @@ struct JsonWriter { output += std::string(value ? "true" : "false"); } + void AddKeyedNumber(std::string const& key, double number) { + CommaAndNewLine(); + Indent(); + output += "\"" + key + "\": " + std::to_string(number); + } + void AddNumber(double number) { + CommaAndNewLine(); + Indent(); + output += std::to_string(number); + } + + void AddKeyedInteger(std::string const& key, int64_t number) { + CommaAndNewLine(); + Indent(); + output += "\"" + key + "\": " + std::to_string(number); + } + void AddInteger(int64_t number) { + CommaAndNewLine(); + Indent(); + output += std::to_string(number); + } + // Json doesn't allow `\` in strings, it must be escaped. Thus we have to convert '\\' to '\\\\' in strings static std::string escape(std::string const& in_path) { std::string out; diff --git a/tests/framework/test_environment.cpp b/tests/framework/test_environment.cpp index d3295d90f..0dea3f794 100644 --- a/tests/framework/test_environment.cpp +++ b/tests/framework/test_environment.cpp @@ -680,19 +680,21 @@ TestICD& FrameworkEnvironment::add_icd(TestICDDetails icd_details) noexcept { } platform_shim->add_known_path(folder.location()); break; - case (ManifestDiscoveryType::override_folder): case (ManifestDiscoveryType::macos_bundle): platform_shim->add_manifest(ManifestCategory::icd, icds.back().manifest_path); break; case (ManifestDiscoveryType::unsecured_generic): platform_shim->add_unsecured_manifest(ManifestCategory::icd, icds.back().manifest_path); break; - case (ManifestDiscoveryType::null_dir): - case (ManifestDiscoveryType::none): - break; case (ManifestDiscoveryType::windows_app_package): platform_shim->set_app_package_path(folder.location()); break; + + case (ManifestDiscoveryType::override_folder): // should be found through override layer/settings file, not 'normal' + // search paths + case (ManifestDiscoveryType::null_dir): + case (ManifestDiscoveryType::none): + break; } } return icds.back().get_test_icd(); @@ -907,6 +909,32 @@ std::string get_loader_settings_file_contents(const LoaderSettings& loader_setti } writer.EndArray(); } + if (!setting.driver_configurations.empty()) { + writer.AddKeyedBool("additional_drivers_use_exclusively", setting.additional_drivers_use_exclusively); + writer.StartKeyedArray("additional_drivers"); + for (const auto& driver : setting.driver_configurations) { + writer.StartObject(); + writer.AddKeyedString("path", driver.path); + writer.EndObject(); + } + writer.EndArray(); + } + if (!setting.device_configurations.empty()) { + writer.StartKeyedArray("device_configurations"); + for (const auto& device : setting.device_configurations) { + writer.StartObject(); + if (!device.deviceName.empty()) { + writer.AddKeyedString("deviceName", device.deviceName); + } + writer.StartKeyedArray("deviceUUID"); + for (const auto& u : device.deviceUUID) { + writer.AddInteger(u); + } + writer.EndArray(); + writer.EndObject(); + } + writer.EndArray(); + } writer.EndObject(); } if (!one_setting_file) { diff --git a/tests/framework/test_environment.h b/tests/framework/test_environment.h index 29703e228..a7b9f15b2 100644 --- a/tests/framework/test_environment.h +++ b/tests/framework/test_environment.h @@ -513,6 +513,7 @@ struct LoaderSettingsLayerConfiguration { BUILDER_VALUE(std::string, control) BUILDER_VALUE(bool, treat_as_implicit_manifest) }; +// Needed for next_permutation inline bool operator==(LoaderSettingsLayerConfiguration const& a, LoaderSettingsLayerConfiguration const& b) { return a.name == b.name && a.path == b.path && a.control == b.control && a.treat_as_implicit_manifest == b.treat_as_implicit_manifest; @@ -525,6 +526,15 @@ inline bool operator>(LoaderSettingsLayerConfiguration const& a, LoaderSettingsL inline bool operator<=(LoaderSettingsLayerConfiguration const& a, LoaderSettingsLayerConfiguration const& b) { return !(b < a); } inline bool operator>=(LoaderSettingsLayerConfiguration const& a, LoaderSettingsLayerConfiguration const& b) { return !(a < b); } +struct LoaderSettingsDriverConfiguration { + BUILDER_VALUE(std::filesystem::path, path) +}; + +struct LoaderSettingsDeviceConfiguration { + BUILDER_VALUE(VulkanUUID, deviceUUID) + BUILDER_VALUE(std::string, deviceName) +}; + // Log files and their associated filter struct LoaderLogConfiguration { BUILDER_VECTOR(std::string, destinations, destination) @@ -533,6 +543,9 @@ struct LoaderLogConfiguration { struct AppSpecificSettings { BUILDER_VECTOR(std::string, app_keys, app_key) BUILDER_VECTOR(LoaderSettingsLayerConfiguration, layer_configurations, layer_configuration) + BUILDER_VECTOR(LoaderSettingsDriverConfiguration, driver_configurations, driver_configuration) + BUILDER_VECTOR(LoaderSettingsDeviceConfiguration, device_configurations, device_configuration) + BUILDER_VALUE(bool, additional_drivers_use_exclusively) BUILDER_VECTOR(std::string, stderr_log, stderr_log_filter) BUILDER_VECTOR(LoaderLogConfiguration, log_configurations, log_configuration) }; diff --git a/tests/framework/test_util.h b/tests/framework/test_util.h index 51decc734..06d62d45d 100644 --- a/tests/framework/test_util.h +++ b/tests/framework/test_util.h @@ -839,6 +839,8 @@ struct VulkanFunction { PFN_vkVoidFunction function = nullptr; }; +using VulkanUUID = std::array; + template bool check_permutation(std::initializer_list expected, std::array const& returned) { if (expected.size() != returned.size()) return false; diff --git a/tests/loader_settings_tests.cpp b/tests/loader_settings_tests.cpp index b3313c7b0..adc2ae995 100644 --- a/tests/loader_settings_tests.cpp +++ b/tests/loader_settings_tests.cpp @@ -41,6 +41,20 @@ std::string get_settings_location_log_message([[maybe_unused]] FrameworkEnvironm return s + "/home/fake_home/.local/share/vulkan/loader_settings.d/vk_loader_settings.json"; #endif } + +std::string get_settings_not_in_use_log_message([[maybe_unused]] FrameworkEnvironment const& env, + [[maybe_unused]] bool use_secure = false) { + std::string s = "vk_loader_settings.json file found at \""; +#if defined(WIN32) + s += (env.get_folder(ManifestLocation::settings_location).location() / "vk_loader_settings.json").string(); +#elif COMMON_UNIX_PLATFORMS + if (use_secure) + s += "/etc/vulkan/loader_settings.d/vk_loader_settings.json"; + else + s += "/home/fake_home/.local/share/vulkan/loader_settings.d/vk_loader_settings.json"; +#endif + return s + "\" but did not contain any valid settings."; +} enum class LayerType { exp, imp, @@ -487,19 +501,21 @@ TEST(SettingsFile, LayerListIsEmpty) { writer.StartObject(); writer.AddKeyedString("file_format_version", "1.0.0"); writer.StartKeyedObject("settings"); - writer.StartKeyedObject("layers"); - writer.EndObject(); + writer.StartKeyedArray("layers"); + writer.EndArray(); writer.EndObject(); writer.EndObject(); env.write_settings_file(writer.output); - ASSERT_NO_FATAL_FAILURE(env.GetLayerProperties(0)); + auto layer_props = env.GetLayerProperties(1); + ASSERT_TRUE(string_eq(layer_props.at(0).layerName, implicit_layer_name)); InstWrapper inst{env.vulkan_functions}; FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); inst.CheckCreate(); - ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env))); - ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0)); + ASSERT_TRUE(env.debug_log.find(get_settings_not_in_use_log_message(env))); + auto actice_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1); + ASSERT_TRUE(string_eq(actice_layer_props.at(0).layerName, implicit_layer_name)); } // If a settings file exists but contains no valid settings - don't consider it @@ -3018,3 +3034,363 @@ TEST(SettingsFile, EnvVarsWorkTogether) { EXPECT_TRUE(env.platform_shim->find_in_log("Insert instance layer \"VK_LAYER_add_env_var_implicit_layer\"")); } } + +// additional drivers being provided by settings file +TEST(SettingsFile, AdditionalDrivers) { + FrameworkEnvironment env{FrameworkSettings{}.set_log_filter("")}; + const char* regular_driver_name = "regular"; + const char* settings_driver_name = "settings"; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)) + .add_physical_device(PhysicalDevice{}.set_deviceName(regular_driver_name).finish()); + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2).set_discovery_type(ManifestDiscoveryType::override_folder)) + .add_physical_device(PhysicalDevice{}.set_deviceName(settings_driver_name).finish()); + + env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting( + AppSpecificSettings{}.add_stderr_log_filter("all").add_driver_configuration( + LoaderSettingsDriverConfiguration{}.set_path(env.get_icd_manifest_path(1)))); + env.update_loader_settings(env.loader_settings); + + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + auto pds = inst.GetPhysDevs(); + ASSERT_EQ(pds.size(), 2U); + VkPhysicalDeviceProperties props1{}, props2{}; + inst.functions->vkGetPhysicalDeviceProperties(pds.at(0), &props1); + inst.functions->vkGetPhysicalDeviceProperties(pds.at(1), &props2); + ASSERT_TRUE(string_eq(props1.deviceName, regular_driver_name)); + ASSERT_TRUE(string_eq(props2.deviceName, settings_driver_name)); +} +// settings file provided drivers replacing system found drivers +TEST(SettingsFile, ExclusiveAdditionalDrivers) { + FrameworkEnvironment env{}; + const char* regular_driver_name = "regular"; + const char* settings_driver_name = "settings"; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)) + .add_physical_device(PhysicalDevice{}.set_deviceName(regular_driver_name).finish()); + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2).set_discovery_type(ManifestDiscoveryType::override_folder)) + .add_physical_device(PhysicalDevice{}.set_deviceName(settings_driver_name).finish()); + + env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting( + AppSpecificSettings{}.set_additional_drivers_use_exclusively(true).add_driver_configuration( + LoaderSettingsDriverConfiguration{}.set_path(env.get_icd_manifest_path(1)))); + env.update_loader_settings(env.loader_settings); + + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + auto pds = inst.GetPhysDevs(); + ASSERT_EQ(pds.size(), 1U); + VkPhysicalDeviceProperties props1{}; + inst.functions->vkGetPhysicalDeviceProperties(pds.at(0), &props1); + ASSERT_TRUE(string_eq(props1.deviceName, settings_driver_name)); +} +// settings file provided drivers + VK_LOADER_DRIVERS_SELECT +TEST(SettingsFile, AdditionalDriversReplacesVK_LOADER_DRIVERS_SELECT) { + FrameworkEnvironment env{}; + const char* regular_driver_name = "regular"; + const char* settings_driver_name = "settings"; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)) + .add_physical_device(PhysicalDevice{}.set_deviceName(regular_driver_name).finish()); + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2).set_discovery_type(ManifestDiscoveryType::override_folder)) + .add_physical_device(PhysicalDevice{}.set_deviceName(settings_driver_name).finish()); + + env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting( + AppSpecificSettings{}.add_driver_configuration(LoaderSettingsDriverConfiguration{}.set_path(env.get_icd_manifest_path(1)))); + env.update_loader_settings(env.loader_settings); + + { + EnvVarWrapper VK_LOADER_DRIVERS_SELECT{"VK_LOADER_DRIVERS_SELECT", + std::string("*") + env.get_icd_manifest_path(0).stem().string() + std::string("*")}; + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + auto pds = inst.GetPhysDevs(); + ASSERT_EQ(pds.size(), 1U); + VkPhysicalDeviceProperties props1{}; + inst.functions->vkGetPhysicalDeviceProperties(pds.at(0), &props1); + ASSERT_TRUE(string_eq(props1.deviceName, regular_driver_name)); + } + { + EnvVarWrapper VK_LOADER_DRIVERS_SELECT{"VK_LOADER_DRIVERS_SELECT", + std::string("*") + env.get_icd_manifest_path(1).stem().string() + std::string("*")}; + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + auto pds = inst.GetPhysDevs(); + ASSERT_EQ(pds.size(), 1U); + VkPhysicalDeviceProperties props1{}; + inst.functions->vkGetPhysicalDeviceProperties(pds.at(0), &props1); + ASSERT_TRUE(string_eq(props1.deviceName, settings_driver_name)); + } + env.loader_settings.app_specific_settings.at(0).set_additional_drivers_use_exclusively(true); + env.update_loader_settings(env.loader_settings); + { + EnvVarWrapper VK_LOADER_DRIVERS_SELECT{"VK_LOADER_DRIVERS_SELECT", + std::string("*") + env.get_icd_manifest_path(0).stem().string() + std::string("*")}; + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(VK_ERROR_INCOMPATIBLE_DRIVER); + } + { + EnvVarWrapper VK_LOADER_DRIVERS_SELECT{"VK_LOADER_DRIVERS_SELECT", + std::string("*") + env.get_icd_manifest_path(1).stem().string() + std::string("*")}; + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + auto pds = inst.GetPhysDevs(); + ASSERT_EQ(pds.size(), 1U); + VkPhysicalDeviceProperties props1{}; + inst.functions->vkGetPhysicalDeviceProperties(pds.at(0), &props1); + ASSERT_TRUE(string_eq(props1.deviceName, settings_driver_name)); + } +} + +// settings file provided drivers + VK_LOADER_DRIVERS_DISABLE +TEST(SettingsFile, AdditionalDriversReplacesVK_LOADER_DRIVERS_DISABLE) { + // TODO +} + +TEST(SettingsFile, InvalidAdditionalDriversField) { + FrameworkEnvironment env{}; + const char* layer_name = "VK_LAYER_layer"; + const char* driver_name = "driver"; + const char* settings_driver_name = "settings_driver"; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2).set_discovery_type(ManifestDiscoveryType::override_folder)) + .add_physical_device(PhysicalDevice{}.set_deviceName(settings_driver_name).finish()); + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)).add_physical_device(PhysicalDevice{}.set_deviceName(driver_name).finish()); + + env.add_explicit_layer(TestLayerDetails{ + ManifestLayer{}.add_layer( + ManifestLayer::LayerDescription{}.set_name("VK_LAYER_layer").set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "regular_test_layer.json"} + .set_discovery_type(ManifestDiscoveryType::override_folder)); + JsonWriter writer; + writer.StartObject(); + writer.AddKeyedString("file_format_version", "1.0.0"); + + writer.StartKeyedObject("settings"); + writer.StartKeyedArray("additional_drivers"); + writer.AddString(env.get_shimmed_icd_manifest_path(0)); + writer.EndArray(); + writer.StartKeyedArray("layers"); + writer.StartObject(); + writer.AddKeyedString("name", layer_name); + writer.AddKeyedString("path", env.get_shimmed_layer_manifest_path(0)); + writer.AddKeyedString("control", "on"); + writer.EndObject(); + writer.EndArray(); + writer.EndObject(); + writer.EndObject(); + env.write_settings_file(writer.output); + + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + auto pd = inst.GetPhysDev(); + VkPhysicalDeviceProperties props1{}; + inst.functions->vkGetPhysicalDeviceProperties(pd, &props1); + ASSERT_TRUE(string_eq(props1.deviceName, driver_name)); + + auto active_layer_props = inst.GetActiveLayers(pd, 1); + EXPECT_TRUE(string_eq(active_layer_props.at(0).layerName, layer_name)); +} + +TEST(SettingsFile, DriverConfigurationsInSpecifiedOrder) { + FrameworkEnvironment env{}; + std::vector uuids{10, VulkanUUID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}; + + // Mix up the uuid's so that they are all unique + int count = 1; + for (auto& uuid : uuids) { + std::rotate(uuid.begin(), uuid.begin() + count, uuid.end()); + count++; + } + + auto& icd = env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)).set_icd_api_version(VK_API_VERSION_1_1); + for (uint32_t i = 0; i < uuids.size(); i++) { + // add the physical devices in reverse order of UUID's + icd.add_physical_device(PhysicalDevice() + .set_deviceName("PhysicalDevice_" + std::to_string(uuids.size() - 1 - i)) + .set_api_version(VK_API_VERSION_1_1) + .set_deviceUUID(uuids[uuids.size() - 1 - i]) + .finish()); + } + + env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(AppSpecificSettings{}); + for (uint32_t i = 0; i < uuids.size(); i++) { + env.loader_settings.app_specific_settings.at(0).add_device_configuration( + LoaderSettingsDeviceConfiguration{}.set_deviceUUID(uuids[i])); + } + env.update_loader_settings(env.loader_settings); + + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + auto pds = inst.GetPhysDevs(); + ASSERT_EQ(pds.size(), uuids.size()); + for (uint32_t i = 0; i < uuids.size(); i++) { + VkPhysicalDeviceProperties props{}; + + inst->vkGetPhysicalDeviceProperties(pds.at(i), &props); + std::string s = "PhysicalDevice_" + std::to_string(i); + ASSERT_TRUE(string_eq(s.c_str(), props.deviceName)); + } +} + +TEST(SettingsFile, OnlyOneDriverConfiguration) { + FrameworkEnvironment env{}; + std::vector uuids{10, VulkanUUID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}; + + // Mix up the uuid's so that they are all unique + int count = 1; + for (auto& uuid : uuids) { + std::rotate(uuid.begin(), uuid.begin() + count, uuid.end()); + count++; + } + + auto& icd = env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)).set_icd_api_version(VK_API_VERSION_1_1); + for (uint32_t i = 0; i < uuids.size(); i++) { + // add the physical devices in reverse order of UUID's + icd.add_physical_device( + PhysicalDevice().set_api_version(VK_API_VERSION_1_1).set_deviceUUID(uuids[uuids.size() - 1 - i]).finish()); + } + + env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(AppSpecificSettings{}); + + for (uint32_t i = 0; i < uuids.size(); i++) { + env.loader_settings.app_specific_settings.at(0).device_configurations.clear(); + env.loader_settings.app_specific_settings.at(0).add_device_configuration( + LoaderSettingsDeviceConfiguration{}.set_deviceUUID(uuids[i])); + + env.update_loader_settings(env.loader_settings); + + InstWrapper inst{env.vulkan_functions}; + inst.create_info.set_api_version(VK_API_VERSION_1_1); + inst.CheckCreate(); + auto pd = inst.GetPhysDev(); + + VkPhysicalDeviceVulkan11Properties vulkan_11_props{}; + vulkan_11_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES; + VkPhysicalDeviceProperties2 props2{}; + props2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + props2.pNext = &vulkan_11_props; + inst->vkGetPhysicalDeviceProperties2(pd, &props2); + + ASSERT_TRUE(0 == memcmp(vulkan_11_props.deviceUUID, uuids[i].data(), VK_UUID_SIZE * sizeof(uint8_t))); + } +} + +TEST(SettingsFile, MissingDriverConfiguration) { + FrameworkEnvironment env{}; + std::vector uuids{2, VulkanUUID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}; + + // Mix up the uuid's so that they are all unique + int count = 1; + for (auto& uuid : uuids) { + std::rotate(uuid.begin(), uuid.begin() + count, uuid.end()); + count++; + } + + auto& icd = env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)).set_icd_api_version(VK_API_VERSION_1_1); + icd.add_physical_device(PhysicalDevice().set_api_version(VK_API_VERSION_1_1).set_deviceUUID(uuids[1]).finish()); + + env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(AppSpecificSettings{}); + + env.loader_settings.app_specific_settings.at(0).device_configurations.clear(); + env.loader_settings.app_specific_settings.at(0).add_device_configuration( + LoaderSettingsDeviceConfiguration{}.set_deviceUUID(uuids[0])); + + env.update_loader_settings(env.loader_settings); + + InstWrapper inst{env.vulkan_functions}; + inst.CheckCreate(); + auto pd = inst.GetPhysDev(); +} + +// Three drivers, second on has the matching UUID in the settings file. +TEST(SettingsFile, DriverConfigurationIgnoresDriverEnvVars) { + FrameworkEnvironment env{}; + std::vector uuids{3, VulkanUUID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}; + + // Mix up the uuid's so that they are all unique + int count = 1; + for (auto& uuid : uuids) { + std::rotate(uuid.begin(), uuid.begin() + count, uuid.end()); + count++; + } + + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)) + .add_physical_device(PhysicalDevice().set_deviceName("A").set_deviceUUID(uuids[0]).finish()); + + auto& icd = env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)).set_icd_api_version(VK_API_VERSION_1_1); + icd.add_physical_device( + PhysicalDevice().set_api_version(VK_API_VERSION_1_1).set_deviceName("B").set_deviceUUID(uuids[1]).finish()); + + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2).set_discovery_type(ManifestDiscoveryType::env_var)) + .add_physical_device(PhysicalDevice().set_deviceName("C").set_deviceUUID(uuids[2]).finish()); + + env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(AppSpecificSettings{}); + + env.loader_settings.app_specific_settings.at(0).device_configurations.clear(); + env.loader_settings.app_specific_settings.at(0).add_device_configuration( + LoaderSettingsDeviceConfiguration{}.set_deviceUUID(uuids[1])); + + env.update_loader_settings(env.loader_settings); + + InstWrapper inst{env.vulkan_functions}; + inst.create_info.set_api_version(VK_API_VERSION_1_1); + inst.CheckCreate(); + auto pd = inst.GetPhysDev(); + + VkPhysicalDeviceVulkan11Properties vulkan_11_props{}; + vulkan_11_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES; + VkPhysicalDeviceProperties2 props2{}; + props2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + props2.pNext = &vulkan_11_props; + inst->vkGetPhysicalDeviceProperties2(pd, &props2); + + ASSERT_TRUE(0 == memcmp(vulkan_11_props.deviceUUID, uuids[1].data(), VK_UUID_SIZE * sizeof(uint8_t))); +} + +TEST(SettingsFile, DriverConfigurationsAndAdditionalDrivers) { + FrameworkEnvironment env{}; + + std::vector uuids{3, VulkanUUID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}; + // Mix up the uuid's so that they are all unique + int count = 1; + for (auto& uuid : uuids) { + std::rotate(uuid.begin(), uuid.begin() + count, uuid.end()); + count++; + } + + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2).set_discovery_type(ManifestDiscoveryType::override_folder)) + .add_physical_device(PhysicalDevice{} + .set_api_version(VK_API_VERSION_1_1) + .set_deviceName("additional_device") + .set_deviceUUID(uuids[0]) + .finish()); + + auto& icd = env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)) + .set_icd_api_version(VK_API_VERSION_1_1) + .add_physical_device(PhysicalDevice() + .set_api_version(VK_API_VERSION_1_1) + .set_deviceName("device_configuration_device") + .set_deviceUUID(uuids[1]) + .finish()); + env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting(AppSpecificSettings{}); + env.loader_settings.app_specific_settings.at(0).add_driver_configuration( + LoaderSettingsDriverConfiguration().set_path(env.get_icd_manifest_path(0))); + env.loader_settings.app_specific_settings.at(0).add_device_configuration( + LoaderSettingsDeviceConfiguration{}.set_deviceUUID(uuids[1])); + env.update_loader_settings(env.loader_settings); + + env.update_loader_settings(env.loader_settings); + InstWrapper inst{env.vulkan_functions}; + inst.create_info.set_api_version(VK_API_VERSION_1_1); + inst.CheckCreate(); + auto pd = inst.GetPhysDev(); + VkPhysicalDeviceProperties props{}; + inst->vkGetPhysicalDeviceProperties(pd, &props); + ASSERT_TRUE(string_eq("device_configuration_device", props.deviceName)); + VkPhysicalDeviceVulkan11Properties vulkan_11_props{}; + vulkan_11_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES; + VkPhysicalDeviceProperties2 props2{}; + props2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + props2.pNext = &vulkan_11_props; + inst->vkGetPhysicalDeviceProperties2(pd, &props2); + + ASSERT_TRUE(0 == memcmp(vulkan_11_props.deviceUUID, uuids[1].data(), VK_UUID_SIZE * sizeof(uint8_t))); +}
JSON NodeDescription and NotesRestrictionsParentIntrospection Query