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
+
+
+
+ | JSON Node |
+ Description and Notes |
+ Restrictions |
+ Parent |
+ Introspection Query |
+
+
+
+
+
+## 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)));
+}