diff --git a/CMakeLists.txt b/CMakeLists.txt index 659049ef2..b9d434af6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -258,6 +258,19 @@ if (NOT (WIN32 OR APPLE)) endif() endif() +if (NOT (WIN32)) + # Check for the existance of the realpath() command + include(CheckFunctionExists) + + check_function_exists(realpath HAVE_REALPATH) + + if (HAVE_REALPATH) + target_compile_definitions(loader_common_options INTERFACE HAVE_REALPATH) + else() + message(INFO "Platform support for realpath() is missing. Using fallback path normalization implementation.") + endif() +endif() + option(LOADER_CODEGEN "Enable vulkan loader code generation") if(LOADER_CODEGEN) find_package(Python3 REQUIRED) diff --git a/loader/loader.c b/loader/loader.c index 9832bb7b0..6102a525d 100644 --- a/loader/loader.c +++ b/loader/loader.c @@ -387,6 +387,269 @@ void free_string_list(const struct loader_instance *inst, struct loader_string_l memset(string_list, 0, sizeof(struct loader_string_list)); } +// In place modify the passed in path to do the following: +// If HAVE_REALPATH is defined, then this simply calls realpath() so its behavior is defined by realpath() +// Else: +// * Windows-only: Replace forward slashes with backwards slashes (platform correct directory separator) +// * Replace contiguous directory separators with a single directory separator +// * Replace "/./" separator with "/" (where / is the platform correct directory separator) +// * Replace "//../" with just "/" (where / is the platform correct directory separator) +VkResult normalize_path(const struct loader_instance *inst, char **passed_in_path) { + // passed_in_path doesn't point to anything, can't modify inplace so just return + if (passed_in_path == NULL) { + return VK_SUCCESS; + } + +// POSIX systems has the realpath() function to do this for us, fallback to basic normalization on other platforms +#if defined(HAVE_REALPATH) + char *path = loader_instance_heap_calloc(inst, PATH_MAX, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); + if (NULL == path) { + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + char *ret = realpath(*passed_in_path, path); + if (NULL == ret) { + // error path + int error_code = errno; + loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, + "normalize_path: Call to realpath() failed with error code %d when given the path %s", error_code, + *passed_in_path); + loader_instance_heap_free(inst, path); + } else { + // Replace string pointed to by passed_in_path with the one given to us by realpath() + loader_instance_heap_free(inst, *passed_in_path); + *passed_in_path = path; + } + return VK_SUCCESS; + +// Windows has GetFullPathName which does essentially the same thing. Note that we call GetFullPathNameA because the path has +// already been converted from the wide char format when it was initially gotten +#elif defined(WIN32) + VkResult res = VK_SUCCESS; + DWORD path_len = (DWORD)strlen(*passed_in_path) + 1; + char *path = loader_instance_heap_calloc(inst, (size_t)path_len, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); + if (NULL == path) { + res = VK_ERROR_OUT_OF_HOST_MEMORY; + goto out; + } + DWORD actual_len = GetFullPathNameA(*passed_in_path, path_len, path, NULL); + if (actual_len == 0) { + size_t last_error = (size_t)GetLastError(); + loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, + "normalize_path: Call to GetFullPathNameA() failed with error code %llu when given the path %s", last_error, + *passed_in_path); + res = VK_ERROR_INITIALIZATION_FAILED; + goto out; + } + + // If path_len wasn't big enough, need to realloc and call again + // actual_len doesn't include null terminator + if (actual_len + 1 > path_len) { + path = loader_instance_heap_realloc(inst, path, path_len, actual_len + 1, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); + if (NULL == path) { + res = VK_ERROR_OUT_OF_HOST_MEMORY; + goto out; + } + // store the updated allocation size (sans null terminator) + path_len = actual_len + 1; + + actual_len = GetFullPathNameA(*passed_in_path, path_len, path, NULL); + if (actual_len == 0) { + size_t last_error = (size_t)GetLastError(); + loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, + "normalize_path: Call to GetFullPathNameA() failed with error code %llu when given the path %s", last_error, + *passed_in_path); + res = VK_ERROR_INITIALIZATION_FAILED; + goto out; + // actual_len doesn't include null terminator + } else if (actual_len + 1 != path_len) { + loader_log(inst, VULKAN_LOADER_DEBUG_BIT, 0, + "normalize_path: Call to GetFullPathNameA() with too small of a buffer when given the path %s after the " + "initial call to GetFullPathNameA() failed for the same reason. Buffer size is %llu, actual size is %llu", + *passed_in_path, (size_t)path_len, (size_t)actual_len); + res = VK_ERROR_INITIALIZATION_FAILED; + goto out; + } + } + // Replace string pointed to by passed_in_path with the one given to us by realpath() + loader_instance_heap_free(inst, *passed_in_path); + *passed_in_path = path; +out: + if (VK_SUCCESS != res) { + if (NULL != path) { + loader_instance_heap_free(inst, path); + } + } + return res; + +#else + (void)inst; + char *path = *passed_in_path; + size_t path_len = strlen(path) + 1; + + size_t output_index = 0; + // Iterate through the string up to the last character, excluding the null terminator + for (size_t i = 0; i < path_len - 1; i++) { + if (i + 1 < path_len && path[i] == DIRECTORY_SYMBOL && path[i + 1] == DIRECTORY_SYMBOL) { + continue; + } else if (i + 2 < path_len && path[i] == DIRECTORY_SYMBOL && path[i + 1] == '.' && path[i + 2] == DIRECTORY_SYMBOL) { + i += 1; + } else { + path[output_index++] = path[i]; + } + } + // Add null terminator and set the new length + path[output_index++] = '\0'; + path_len = output_index; + + // Loop while there are still ..'s in the path. Easiest implementation resolves them one by one, which requires quadratic + // iteration through the string + char *directory_stack = loader_stack_alloc(path_len); + if (directory_stack == NULL) { + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + + size_t top_of_stack = 0; + + // Iterate through the path, push characters as we see them, if we find a "..", pop off the top of the directory stack until the + // current directory is gone. + for (size_t i = 0; i < path_len - 1; i++) { + // if the next part of path is "/../" we need to pop from the directory stack until we hit the previous directory symbol. + if (i + 3 < path_len && path[i] == DIRECTORY_SYMBOL && path[i + 1] == '.' && path[i + 2] == '.' && path_len && + path[i + 3] == DIRECTORY_SYMBOL) { + // Pop until we hit the next directory symbol in the stack + while (top_of_stack > 0 && directory_stack[top_of_stack - 1] != DIRECTORY_SYMBOL) { + top_of_stack--; + directory_stack[top_of_stack] = '\0'; + } + // Amend the directory stack so that the top isn't a directory separator + if (top_of_stack > 0 && directory_stack[top_of_stack - 1] == DIRECTORY_SYMBOL) { + top_of_stack--; + directory_stack[top_of_stack] = '\0'; + } + i += 2; // need to skip the second dot & directory separator + } else { + // push characters as we come across them + directory_stack[top_of_stack++] = path[i]; + } + } + + // Can't forget the null terminator + directory_stack[top_of_stack] = '\0'; + + // We now have the path without any ..'s, so just copy it out + loader_strncpy(path, path_len, directory_stack, path_len); + path[top_of_stack] = '\0'; + path_len = top_of_stack + 1; + + return VK_SUCCESS; +#endif +} + +// Queries the path to the library that lib_handle & gipa are assoicated with, allocating a string to hold it and returning it in +// out_path +VkResult get_library_path_of_dl_handle(const struct loader_instance *inst, loader_platform_dl_handle lib_handle, + PFN_vkGetInstanceProcAddr gipa, char **out_path) { +#if COMMON_UNIX_PLATFORMS + (void)lib_handle; + Dl_info dl_info = {0}; + if (dladdr(gipa, &dl_info) != 0 && NULL != dl_info.dli_fname) { + return loader_copy_to_new_str(inst, dl_info.dli_fname, out_path); + } + return VK_SUCCESS; + +#elif defined(WIN32) + (void)gipa; + size_t module_file_name_len = MAX_PATH; // start with reasonably large buffer + wchar_t *buffer_utf16 = (wchar_t *)loader_stack_alloc(module_file_name_len * sizeof(wchar_t)); + DWORD ret = GetModuleFileNameW(lib_handle, buffer_utf16, (DWORD)module_file_name_len); + if (ret == 0) { + return VK_SUCCESS; + } + while (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + module_file_name_len *= 2; + buffer_utf16 = (wchar_t *)loader_stack_alloc(module_file_name_len * sizeof(wchar_t)); + ret = GetModuleFileNameW(lib_handle, buffer_utf16, (DWORD)module_file_name_len); + if (ret == 0) { + return VK_SUCCESS; + } + } + + // Need to convert from utf16 to utf8 + int buffer_utf8_size = WideCharToMultiByte(CP_UTF8, 0, buffer_utf16, -1, NULL, 0, NULL, NULL); + if (buffer_utf8_size <= 0) { + return VK_SUCCESS; + } + + char *buffer_utf8 = loader_instance_heap_calloc(inst, buffer_utf8_size, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); + if (NULL == buffer_utf8) { + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + if (WideCharToMultiByte(CP_UTF8, 0, buffer_utf16, -1, buffer_utf8, buffer_utf8_size, NULL, NULL) != buffer_utf8_size) { + return VK_SUCCESS; + } + + // Successfully got the 'real' path to the library. + *out_path = buffer_utf8; + return VK_SUCCESS; + +#else + // Do nothing, platform doesn't handle getting the path to a library +#endif +} + +// Find and replace the path that was loaded using the lib_name path with the real path of the library. This is done to provide +// accurate logging info for users. +// This function prints a warning if there is a mismatch between the lib_name path and the real path. +VkResult fixup_library_binary_path(const struct loader_instance *inst, char **lib_name, loader_platform_dl_handle lib_handle, + PFN_vkGetInstanceProcAddr gipa) { + if (lib_name == NULL) { + // do nothing as we got an invalid lib_path pointer + return VK_SUCCESS; + } + + bool system_path = true; + size_t lib_name_len = strlen(*lib_name) + 1; + for (size_t i = 0; i < lib_name_len; i++) { + if ((*lib_name)[i] == DIRECTORY_SYMBOL) { + system_path = false; + break; + } + } + + if (!system_path) { + // The OS path we get for a binary is normalized, so we need to normalize the path passed into LoadLibrary/dlopen so that + // mismatches are minimized. EG, do not warn when we give dlopen/LoadLibrary "/foo/./bar" but get "/foo/bar" as the loaded + // binary path from the OS. + VkResult res = normalize_path(inst, lib_name); + if (res == VK_ERROR_OUT_OF_HOST_MEMORY) { + return res; + } + } + char *os_determined_lib_name = NULL; + VkResult res = get_library_path_of_dl_handle(inst, lib_handle, gipa, &os_determined_lib_name); + if (res == VK_ERROR_OUT_OF_HOST_MEMORY) { + return res; + } + + if (NULL != os_determined_lib_name) { + if (0 != strcmp(os_determined_lib_name, *lib_name)) { + // Paths do not match, so we need to replace lib_name with the real path + if (!system_path) { + // Only warn when the library_path is relative or absolute, not system. EG lib_name had no directory separators + loader_log(inst, VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_LAYER_BIT, 0, + "Path to given binary %s was found to differ from OS loaded path %s", *lib_name, os_determined_lib_name); + } + loader_instance_heap_free(inst, *lib_name); + *lib_name = os_determined_lib_name; + } else { + // Paths match, so just need to free temporary allocation + loader_instance_heap_free(inst, os_determined_lib_name); + } + } + + return res; +} + // Given string of three part form "maj.min.pat" convert to a vulkan version number. // Also can understand four part form "variant.major.minor.patch" if provided. uint32_t loader_parse_version_string(char *vers_str) { @@ -1996,6 +2259,11 @@ VkResult loader_scanned_icd_add(const struct loader_instance *inst, struct loade } icd_tramp_list->count++; + // Uses OS calls to find the 'true' path to the binary, for more accurate logging later on. + res = fixup_library_binary_path(inst, &(new_scanned_icd->lib_name), new_scanned_icd->handle, fp_get_proc_addr); + if (res == VK_ERROR_OUT_OF_HOST_MEMORY) { + goto out; + } out: if (res != VK_SUCCESS) { if (NULL != handle) { @@ -3002,7 +3270,7 @@ void copy_data_file_info(const char *cur_path, const char *relative_path, size_t char *cur_write = *output_path; while (cur_path[start] != '\0') { - while (cur_path[start] == PATH_SEPARATOR) { + while (cur_path[start] == PATH_SEPARATOR && cur_path[start] != '\0') { start++; } stop = start; @@ -4807,6 +5075,11 @@ VkResult loader_create_instance_chain(const VkInstanceCreateInfo *pCreateInfo, c chain_info.u.pLayerInfo = &layer_instance_link_info[num_activated_layers]; + res = fixup_library_binary_path(inst, &(layer_prop->lib_name), layer_prop->lib_handle, cur_gipa); + if (res == VK_ERROR_OUT_OF_HOST_MEMORY) { + return res; + } + activated_layers[num_activated_layers].name = layer_prop->info.layerName; activated_layers[num_activated_layers].manifest = layer_prop->manifest_file_name; activated_layers[num_activated_layers].library = layer_prop->lib_name; diff --git a/loader/settings.c b/loader/settings.c index a28c34fae..cc6a60c24 100644 --- a/loader/settings.c +++ b/loader/settings.c @@ -387,46 +387,131 @@ VkResult parse_device_configurations(const struct loader_instance* inst, cJSON* return res; } +#if COMMON_UNIX_PLATFORMS +// Given a base and suffix path, determine if a file at that location exists, and if it is return success. +// Since base may contain multiple paths seperated by PATH_SEPARATOR, we must extract each segment and check segment + suffix +// individually 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) { return VK_ERROR_INITIALIZATION_FAILED; } + size_t base_len = strlen(base); size_t suffix_len = strlen(suffix); - size_t path_len = base_len + suffix_len + 1; - *settings_file_path = loader_instance_heap_calloc(inst, path_len, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); - if (NULL == *settings_file_path) { - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - loader_strncpy(*settings_file_path, path_len, base, base_len); - loader_strncat(*settings_file_path, path_len, suffix, suffix_len); - if (!loader_platform_file_exists(*settings_file_path)) { + uint32_t start = 0; + uint32_t stop = 0; + while (base[start] != '\0' && start < base_len && stop < base_len) { + start = stop; + stop = start + 1; + while (base[stop] != PATH_SEPARATOR && base[stop] != '\0' && stop < base_len) { + stop++; + } + + size_t segment_len = (stop - start); + if (segment_len <= 1) { + // segment is *just* a PATH_SEPARATOR, skip it + continue; + } + size_t path_len = segment_len + suffix_len + 1; + *settings_file_path = loader_instance_heap_calloc(inst, path_len, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); + if (NULL == *settings_file_path) { + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + loader_strncpy(*settings_file_path, path_len, base + start, segment_len); + loader_strncat(*settings_file_path, path_len, suffix, suffix_len); + + if (loader_platform_file_exists(*settings_file_path)) { + return VK_SUCCESS; + } loader_instance_heap_free(inst, *settings_file_path); *settings_file_path = NULL; - return VK_ERROR_INITIALIZATION_FAILED; } - return VK_SUCCESS; + + return VK_ERROR_INITIALIZATION_FAILED; } + +// Follow the logic of read_data_files_in_search_paths but only look for "/vulkan/loader_settings.d/" VK_LOADER_SETTINGS_FILENAME VkResult get_unix_settings_path(const struct loader_instance* inst, char** settings_file_path) { - VkResult res = - check_if_settings_path_exists(inst, loader_secure_getenv("HOME", inst), - "/.local/share/vulkan/loader_settings.d/" VK_LOADER_SETTINGS_FILENAME, settings_file_path); + // First, get XDG env-vars we use. Don't need to worry about free'ing because on linux getenv is non-allocating + char* xdg_config_home = loader_secure_getenv("XDG_CONFIG_HOME", inst); + char* xdg_config_dirs = loader_secure_getenv("XDG_CONFIG_DIRS", inst); + char* xdg_data_home = loader_secure_getenv("XDG_DATA_HOME", inst); + char* xdg_data_dirs = loader_secure_getenv("XDG_DATA_DIRS", inst); + + // Use fallback directories for xdg_config_dirs and xdg_data_dirs if they are NULL. +#if !defined(__Fuchsia__) && !defined(__QNX__) + if (NULL == xdg_config_dirs || '\0' == xdg_config_dirs[0]) { + xdg_config_dirs = FALLBACK_CONFIG_DIRS; + } +#endif + +#if !defined(__Fuchsia__) && !defined(__QNX__) + if (NULL == xdg_data_dirs || '\0' == xdg_data_dirs[0]) { + xdg_data_dirs = FALLBACK_DATA_DIRS; + } +#endif + + VkResult res = check_if_settings_path_exists(inst, xdg_config_home, "/vulkan/loader_settings.d/" VK_LOADER_SETTINGS_FILENAME, + settings_file_path); + if (res == VK_SUCCESS) { + return res; + } + + res = check_if_settings_path_exists(inst, xdg_data_home, "/vulkan/loader_settings.d/" VK_LOADER_SETTINGS_FILENAME, + settings_file_path); + if (res == VK_SUCCESS) { + return res; + } + + // Check home if either xdg_config_home or xdg_data_home wasn't set + char* home = loader_secure_getenv("HOME", inst); + if (home != NULL) { + if (NULL == xdg_config_home || '\0' == xdg_config_home[0]) { + res = check_if_settings_path_exists(inst, home, "/.config/vulkan/loader_settings.d/" VK_LOADER_SETTINGS_FILENAME, + settings_file_path); + if (res == VK_SUCCESS) { + return res; + } + } + if (NULL == xdg_data_home || '\0' == xdg_data_home[0]) { + res = check_if_settings_path_exists(inst, home, "/.local/share/vulkan/loader_settings.d/" VK_LOADER_SETTINGS_FILENAME, + settings_file_path); + if (res == VK_SUCCESS) { + return res; + } + } + } + + res = check_if_settings_path_exists(inst, xdg_config_dirs, "/vulkan/loader_settings.d/" VK_LOADER_SETTINGS_FILENAME, + settings_file_path); if (res == VK_SUCCESS) { return res; } - // If HOME isn't set, fallback to XDG_DATA_HOME - res = check_if_settings_path_exists(inst, loader_secure_getenv("XDG_DATA_HOME", inst), - "/vulkan/loader_settings.d/" VK_LOADER_SETTINGS_FILENAME, settings_file_path); + + res = check_if_settings_path_exists(inst, SYSCONFDIR, "/vulkan/loader_settings.d/" VK_LOADER_SETTINGS_FILENAME, + settings_file_path); + if (res == VK_SUCCESS) { + return res; + } +#if defined(EXTRASYSCONFDIR) + + res = check_if_settings_path_exists(inst, EXTRASYSCONFDIR, "/vulkan/loader_settings.d/" VK_LOADER_SETTINGS_FILENAME, + settings_file_path); if (res == VK_SUCCESS) { return res; } - // if XDG_DATA_HOME isn't set, fallback to /etc. - // note that the settings_fil_path_suffix stays the same since its the same layout as for XDG_DATA_HOME - return check_if_settings_path_exists(inst, "/etc", "/vulkan/loader_settings.d/" VK_LOADER_SETTINGS_FILENAME, - settings_file_path); +#endif + res = check_if_settings_path_exists(inst, xdg_data_dirs, "/vulkan/loader_settings.d/" VK_LOADER_SETTINGS_FILENAME, + settings_file_path); + if (res == VK_SUCCESS) { + return res; + } + + return VK_ERROR_INITIALIZATION_FAILED; } +#endif 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)) { diff --git a/scripts/generate_source.py b/scripts/generate_source.py index e12f7313d..65661152b 100755 --- a/scripts/generate_source.py +++ b/scripts/generate_source.py @@ -66,6 +66,7 @@ def RunGenerators(api: str, registry: str, directory: str, styleFile: str, targe from generators.dispatch_table_helper_generator import DispatchTableHelperGenerator from generators.helper_file_generator import HelperFileGenerator from generators.loader_extension_generator import LoaderExtensionGenerator + from generators.vk_result_to_string_generator import VkResultToStringGenerator # These set fields that are needed by both OutputGenerator and BaseGenerator, # but are uniform and don't need to be set at a per-generated file level @@ -75,6 +76,7 @@ def RunGenerators(api: str, registry: str, directory: str, styleFile: str, targe # Generated directory and dispatch table helper file name may be API specific (e.g. Vulkan SC) generated_directory = 'loader/generated' dispatch_table_helper_filename = 'vk_dispatch_table_helper.h' + result_to_string_filename = 'vk_result_to_string_helper.h' generators.update({ 'vk_layer_dispatch_table.h': { @@ -101,6 +103,11 @@ def RunGenerators(api: str, registry: str, directory: str, styleFile: str, targe 'generator' : DispatchTableHelperGenerator, 'genCombined': False, 'directory' : 'tests/framework/layer/generated', + }, + f'{result_to_string_filename}': { + 'generator' : VkResultToStringGenerator, + 'genCombined': False, + 'directory' : 'tests/framework/generated', } }) diff --git a/scripts/generators/dispatch_table_helper_generator.py b/scripts/generators/dispatch_table_helper_generator.py index 410938a92..cf0060990 100644 --- a/scripts/generators/dispatch_table_helper_generator.py +++ b/scripts/generators/dispatch_table_helper_generator.py @@ -60,7 +60,7 @@ def generate(self): #include #include #include -#include "vk_layer_dispatch_table.h" +#include "loader/generated/vk_layer_dispatch_table.h" ''') diff --git a/scripts/generators/vk_result_to_string_generator.py b/scripts/generators/vk_result_to_string_generator.py new file mode 100644 index 000000000..9efb4aa24 --- /dev/null +++ b/scripts/generators/vk_result_to_string_generator.py @@ -0,0 +1,79 @@ +#!/usr/bin/python3 -i +# +# Copyright (c) 2015-2021 The Khronos Group Inc. +# Copyright (c) 2015-2021 Valve Corporation +# Copyright (c) 2015-2021 LunarG, Inc. +# Copyright (c) 2015-2021 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: Mark Lobodzinski +# Author: Charles Giessen + +import os +from base_generator import BaseGenerator + +class VkResultToStringGenerator(BaseGenerator): + def __init__(self): + BaseGenerator.__init__(self) + + def generate(self): + out = [] + + out.append(f'''#pragma once +// *** THIS FILE IS GENERATED - DO NOT EDIT *** +// See {os.path.basename(__file__)} for modifications + +/* + * Copyright (c) 2025 The Khronos Group Inc. + * Copyright (c) 2025 Valve Corporation + * Copyright (c) 2025 LunarG, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + + */ + +#include +#include + +#include + +''') + + self.OutputVkResultToStringHelper(out) + out.append('\n') + + self.write(''.join(out)) + + # Create a dispatch table from the corresponding table_type and append it to out + def OutputVkResultToStringHelper(self, out: list): + out.append('inline std::ostream& operator<<(std::ostream& os, const VkResult& result) {\n') + out.append(' switch (result) {\n') + for field in self.vk.enums['VkResult'].fields: + out.append(f' case({field.name}):\n') + out.append(f' return os << "{field.name}";\n') + out.append(' default:\n') + out.append(' return os << static_cast(result);\n') + out.append(' }\n') + out.append(' }\n') diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8145f638d..e719dfce4 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -68,6 +68,8 @@ endif() option(ENABLE_LIVE_VERIFICATION_TESTS "Enable tests which expect to run on live drivers. Meant for manual verification only" OFF) +set(TEST_EXECUTION_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/executing_tests") + include(GoogleTest) add_subdirectory(framework) @@ -94,7 +96,11 @@ add_executable( loader_testing_main.cpp loader_fuzz_tests.cpp ) -target_link_libraries(test_fuzzing PUBLIC testing_dependencies vulkan) +target_link_libraries(test_fuzzing PUBLIC testing_dependencies) +# testing_dependencies already links to vulkan when linking statically +if (NOT APPLE_STATIC_LOADER) + target_link_libraries(test_fuzzing PUBLIC vulkan) +endif() target_include_directories(test_fuzzing PUBLIC ${CMAKE_SOURCE_DIR}/loader ${CMAKE_SOURCE_DIR}/loader/generated) # Threading tests live in separate executabe just for threading tests as it'll need support diff --git a/tests/framework/CMakeLists.txt b/tests/framework/CMakeLists.txt index 0dcf46eb8..0e6bff12a 100644 --- a/tests/framework/CMakeLists.txt +++ b/tests/framework/CMakeLists.txt @@ -15,38 +15,15 @@ # limitations under the License. # ~~~ -add_library(testing_framework_util STATIC test_util.cpp) -target_link_libraries(testing_framework_util PUBLIC loader_common_options Vulkan::Headers) - -if(UNIX OR APPLE) - target_link_libraries(testing_framework_util PUBLIC ${CMAKE_DL_LIBS}) -endif() - -if(UNIX) - target_compile_options(testing_framework_util PUBLIC -fPIC) -endif() -# Gives access to all headers in the framework folder, in the framework binary, and in the whole project (mainly for loader/generated) -target_include_directories(testing_framework_util PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}" "${PROJECT_SOURCE_DIR}/loader/generated") +#configure the framework_config.h.in file - used to locate all the binaries generated so that it can be used in the tests +#setup framework_config_temp.h.in in the current binary directory +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/framework_config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/framework_config_temp.h.in") -if (UNIX) - if (LOADER_ENABLE_ADDRESS_SANITIZER) - target_compile_options(testing_framework_util PUBLIC -fsanitize=address,undefined) - target_link_options(testing_framework_util PUBLIC -fsanitize=address,undefined) - endif() - if (LOADER_ENABLE_THREAD_SANITIZER) - target_compile_options(testing_framework_util PUBLIC -fsanitize=thread) - target_link_options(testing_framework_util PUBLIC -fsanitize=thread) - target_compile_options(gtest PUBLIC -fsanitize=thread) - target_link_options(gtest PUBLIC -fsanitize=thread) - endif() -endif() +# setup framework_config_$ using framework_config_temp.h.in as a source +file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/framework_config_$.h" INPUT "${CMAKE_CURRENT_BINARY_DIR}/framework_config_temp.h.in") -if (MSVC) - # silence hidden class member warnings in test framework - target_compile_options(testing_framework_util PUBLIC /wd4458) - # Make sure exception handling is enabled for the test framework - target_compile_options(testing_framework_util PUBLIC /EHsc) -endif() +# Create a variable to hold the path to the correct header file, used as a compiler definition in the utils target +set(FRAMEWORK_CONFIG_HEADER_PATH "${CMAKE_CURRENT_BINARY_DIR}/framework_config_$.h") function(AddSharedLibrary LIBRARY_NAME) set(singleValueArgs DEF_FILE) @@ -63,26 +40,14 @@ function(AddSharedLibrary LIBRARY_NAME) endif() endfunction() +add_subdirectory(util) add_subdirectory(data) add_subdirectory(shim) add_subdirectory(icd) add_subdirectory(layer) -#configure the framework_config.h.in file - used to locate all the binaries generated so that it can be used in the tests -#setup framework_config_temp.h.in in the current binary directory -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/framework_config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/framework_config_temp.h.in") - -# setup framework_config_$ using framework_config_temp.h.in as a source -file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/framework_config_$.h" INPUT "${CMAKE_CURRENT_BINARY_DIR}/framework_config_temp.h.in") - -# Add a compiler definition for the path to framework_config.h with the correct config -target_compile_definitions(testing_framework_util PUBLIC FRAMEWORK_CONFIG_HEADER="framework_config_$.h") - add_library(testing_dependencies STATIC test_environment.cpp test_environment.h) target_link_libraries(testing_dependencies PUBLIC gtest Vulkan::Headers testing_framework_util shim-library) target_include_directories(testing_dependencies PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) -if (APPLE_STATIC_LOADER) - target_compile_definitions(testing_dependencies PUBLIC "APPLE_STATIC_LOADER=1") - target_link_libraries(testing_dependencies PUBLIC vulkan) -endif() + diff --git a/tests/framework/README.md b/tests/framework/README.md index 1dc5b73a4..20e1209ae 100644 --- a/tests/framework/README.md +++ b/tests/framework/README.md @@ -136,8 +136,7 @@ There are many utilities that the test framework and tests have access to. These * Environment Variable Wrapper: `EnvVarWrapper` for creating, setting, getting, and removing environment variables in a RAII manner * Windows API error handling helpers * filesystem abstractions: - * `create_folder`/`delete_folder` - * `FolderManager` + * `Folder` * Creates a new folder with the given name at construction time. * Allows writing manifests and files (eg, icd or layer binaries) * Automatically destroys the folder and all contained files at destruction diff --git a/tests/framework/framework_config.h.in b/tests/framework/framework_config.h.in index b70e42b11..8c0c1f78e 100644 --- a/tests/framework/framework_config.h.in +++ b/tests/framework/framework_config.h.in @@ -26,7 +26,7 @@ */ #pragma once -#define FRAMEWORK_BUILD_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" +#define TEST_EXECUTION_DIRECTORY "${TEST_EXECUTION_DIRECTORY}" #define FRAMEWORK_VULKAN_LIBRARY_PATH "$" diff --git a/tests/framework/generated/vk_result_to_string_helper.h b/tests/framework/generated/vk_result_to_string_helper.h new file mode 100644 index 000000000..88dfa6a46 --- /dev/null +++ b/tests/framework/generated/vk_result_to_string_helper.h @@ -0,0 +1,133 @@ +#pragma once +// *** THIS FILE IS GENERATED - DO NOT EDIT *** +// See vk_result_to_string_generator.py for modifications + +/* + * Copyright (c) 2025 The Khronos Group Inc. + * Copyright (c) 2025 Valve Corporation + * Copyright (c) 2025 LunarG, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + + */ + +#include +#include + +#include + +inline std::ostream& operator<<(std::ostream& os, const VkResult& result) { + switch (result) { + case (VK_SUCCESS): + return os << "VK_SUCCESS"; + case (VK_NOT_READY): + return os << "VK_NOT_READY"; + case (VK_TIMEOUT): + return os << "VK_TIMEOUT"; + case (VK_EVENT_SET): + return os << "VK_EVENT_SET"; + case (VK_EVENT_RESET): + return os << "VK_EVENT_RESET"; + case (VK_INCOMPLETE): + return os << "VK_INCOMPLETE"; + case (VK_ERROR_OUT_OF_HOST_MEMORY): + return os << "VK_ERROR_OUT_OF_HOST_MEMORY"; + case (VK_ERROR_OUT_OF_DEVICE_MEMORY): + return os << "VK_ERROR_OUT_OF_DEVICE_MEMORY"; + case (VK_ERROR_INITIALIZATION_FAILED): + return os << "VK_ERROR_INITIALIZATION_FAILED"; + case (VK_ERROR_DEVICE_LOST): + return os << "VK_ERROR_DEVICE_LOST"; + case (VK_ERROR_MEMORY_MAP_FAILED): + return os << "VK_ERROR_MEMORY_MAP_FAILED"; + case (VK_ERROR_LAYER_NOT_PRESENT): + return os << "VK_ERROR_LAYER_NOT_PRESENT"; + case (VK_ERROR_EXTENSION_NOT_PRESENT): + return os << "VK_ERROR_EXTENSION_NOT_PRESENT"; + case (VK_ERROR_FEATURE_NOT_PRESENT): + return os << "VK_ERROR_FEATURE_NOT_PRESENT"; + case (VK_ERROR_INCOMPATIBLE_DRIVER): + return os << "VK_ERROR_INCOMPATIBLE_DRIVER"; + case (VK_ERROR_TOO_MANY_OBJECTS): + return os << "VK_ERROR_TOO_MANY_OBJECTS"; + case (VK_ERROR_FORMAT_NOT_SUPPORTED): + return os << "VK_ERROR_FORMAT_NOT_SUPPORTED"; + case (VK_ERROR_FRAGMENTED_POOL): + return os << "VK_ERROR_FRAGMENTED_POOL"; + case (VK_ERROR_UNKNOWN): + return os << "VK_ERROR_UNKNOWN"; + case (VK_ERROR_VALIDATION_FAILED): + return os << "VK_ERROR_VALIDATION_FAILED"; + case (VK_ERROR_OUT_OF_POOL_MEMORY): + return os << "VK_ERROR_OUT_OF_POOL_MEMORY"; + case (VK_ERROR_INVALID_EXTERNAL_HANDLE): + return os << "VK_ERROR_INVALID_EXTERNAL_HANDLE"; + case (VK_ERROR_FRAGMENTATION): + return os << "VK_ERROR_FRAGMENTATION"; + case (VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS): + return os << "VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS"; + case (VK_PIPELINE_COMPILE_REQUIRED): + return os << "VK_PIPELINE_COMPILE_REQUIRED"; + case (VK_ERROR_NOT_PERMITTED): + return os << "VK_ERROR_NOT_PERMITTED"; + case (VK_ERROR_SURFACE_LOST_KHR): + return os << "VK_ERROR_SURFACE_LOST_KHR"; + case (VK_ERROR_NATIVE_WINDOW_IN_USE_KHR): + return os << "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR"; + case (VK_SUBOPTIMAL_KHR): + return os << "VK_SUBOPTIMAL_KHR"; + case (VK_ERROR_OUT_OF_DATE_KHR): + return os << "VK_ERROR_OUT_OF_DATE_KHR"; + case (VK_ERROR_INCOMPATIBLE_DISPLAY_KHR): + return os << "VK_ERROR_INCOMPATIBLE_DISPLAY_KHR"; + case (VK_ERROR_INVALID_SHADER_NV): + return os << "VK_ERROR_INVALID_SHADER_NV"; + case (VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR): + return os << "VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR"; + case (VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR): + return os << "VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR"; + case (VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR): + return os << "VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR"; + case (VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR): + return os << "VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR"; + case (VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR): + return os << "VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR"; + case (VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR): + return os << "VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR"; + case (VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT): + return os << "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT"; + case (VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT): + return os << "VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT"; + case (VK_THREAD_IDLE_KHR): + return os << "VK_THREAD_IDLE_KHR"; + case (VK_THREAD_DONE_KHR): + return os << "VK_THREAD_DONE_KHR"; + case (VK_OPERATION_DEFERRED_KHR): + return os << "VK_OPERATION_DEFERRED_KHR"; + case (VK_OPERATION_NOT_DEFERRED_KHR): + return os << "VK_OPERATION_NOT_DEFERRED_KHR"; + case (VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR): + return os << "VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR"; + case (VK_ERROR_COMPRESSION_EXHAUSTED_EXT): + return os << "VK_ERROR_COMPRESSION_EXHAUSTED_EXT"; + case (VK_INCOMPATIBLE_SHADER_BINARY_EXT): + return os << "VK_INCOMPATIBLE_SHADER_BINARY_EXT"; + case (VK_PIPELINE_BINARY_MISSING_KHR): + return os << "VK_PIPELINE_BINARY_MISSING_KHR"; + case (VK_ERROR_NOT_ENOUGH_SPACE_KHR): + return os << "VK_ERROR_NOT_ENOUGH_SPACE_KHR"; + default: + return os << static_cast(result); + } +} diff --git a/tests/framework/icd/test_icd.cpp b/tests/framework/icd/test_icd.cpp index 7e09c6d61..b657672b7 100644 --- a/tests/framework/icd/test_icd.cpp +++ b/tests/framework/icd/test_icd.cpp @@ -29,6 +29,13 @@ #include "test_icd.h" +#include +#include + +#include + +#include "equality_helpers.h" + // export vk_icdGetInstanceProcAddr #if !defined(TEST_ICD_EXPORT_ICD_GIPA) #define TEST_ICD_EXPORT_ICD_GIPA 0 diff --git a/tests/framework/icd/test_icd.h b/tests/framework/icd/test_icd.h index 33b1dc714..57ee27d4b 100644 --- a/tests/framework/icd/test_icd.h +++ b/tests/framework/icd/test_icd.h @@ -27,7 +27,13 @@ #pragma once -#include "test_util.h" +#include +#include +#include + +#include "util/dispatchable_handle.h" +#include "util/platform_wsi.h" +#include "util/functions.h" #include "layer/layer_util.h" @@ -70,6 +76,9 @@ inline std::ostream& operator<<(std::ostream& os, const InterfaceVersionCheck& r } return os << static_cast(result); } + +using VulkanUUID = std::array; + // clang-format on // Move only type because it holds a DispatchableHandle diff --git a/tests/framework/json_writer.h b/tests/framework/json_writer.h deleted file mode 100644 index 4a4cd02a0..000000000 --- a/tests/framework/json_writer.h +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (c) 2023 The Khronos Group Inc. - * Copyright (c) 2023 Valve Corporation - * Copyright (c) 2023 LunarG, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and/or associated documentation files (the "Materials"), to - * deal in the Materials without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Materials, and to permit persons to whom the Materials are - * furnished to do so, subject to the following conditions: - * - * The above copyright notice(s) and this permission notice shall be included in - * all copies or substantial portions of the Materials. - * - * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE - * USE OR OTHER DEALINGS IN THE MATERIALS. - * - * Author: Charles Giessen - */ - -#pragma once - -#include -#include -#include - -// Utility class to simplify the writing of JSON manifest files - -struct JsonWriter { - std::string output; - - // the bool represents whether an object has been written & a comma is needed - std::stack stack; - - void StartObject() { - CommaAndNewLine(); - Indent(); - output += "{"; - stack.push(false); - } - void StartKeyedObject(std::string const& key) { - CommaAndNewLine(); - Indent(); - output += "\"" + key + "\": {"; - stack.push(false); - } - void EndObject() { - stack.pop(); - output += "\n"; - Indent(); - output += "}"; - } - void StartKeyedArray(std::string const& key) { - CommaAndNewLine(); - Indent(); - output += "\"" + key + "\": ["; - stack.push(false); - } - void StartArray() { - CommaAndNewLine(); - Indent(); - output += "["; - stack.push(false); - } - void EndArray() { - stack.pop(); - output += "\n"; - Indent(); - output += "]"; - } - - void AddKeyedString(std::string const& key, std::string const& value) { - CommaAndNewLine(); - Indent(); - output += "\"" + key + "\": \"" + escape(value) + "\""; - } - void AddString(std::string const& value) { - CommaAndNewLine(); - Indent(); - output += "\"" + escape(value) + "\""; - } -#if defined(WIN32) - void AddKeyedString(std::string const& key, std::wstring const& value) { - CommaAndNewLine(); - Indent(); - output += "\"" + key + "\": \"" + escape(narrow(value)) + "\""; - } - void AddString(std::wstring const& value) { - CommaAndNewLine(); - Indent(); - output += "\"" + escape(narrow(value)) + "\""; - } -#endif - - void AddKeyedBool(std::string const& key, bool value) { - CommaAndNewLine(); - Indent(); - output += "\"" + key + "\": " + (value ? "true" : "false"); - } - void AddBool(bool value) { - CommaAndNewLine(); - Indent(); - 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; - for (auto& c : in_path) { - if (c == '\\') - out += "\\\\"; - else - out += c; - } - return out; - } - static std::string escape(std::filesystem::path const& in_path) { return escape(narrow(in_path.native())); } - - private: - void CommaAndNewLine() { - if (stack.size() > 0) { - if (stack.top() == false) { - stack.top() = true; - } else { - output += ","; - } - output += "\n"; - } - } - void Indent() { - for (uint32_t i = 0; i < stack.size(); i++) { - output += '\t'; - } - } -}; diff --git a/tests/framework/layer/generated/vk_dispatch_table_helper.h b/tests/framework/layer/generated/vk_dispatch_table_helper.h index 498352781..4b54ba2f7 100644 --- a/tests/framework/layer/generated/vk_dispatch_table_helper.h +++ b/tests/framework/layer/generated/vk_dispatch_table_helper.h @@ -28,7 +28,7 @@ #include #include #include -#include "vk_layer_dispatch_table.h" +#include "loader/generated/vk_layer_dispatch_table.h" // clang-format off static inline void layer_init_device_dispatch_table(VkDevice device, VkLayerDispatchTable *table, PFN_vkGetDeviceProcAddr gpa) { diff --git a/tests/framework/layer/layer_util.h b/tests/framework/layer/layer_util.h index 3f4bc755a..7107b4642 100644 --- a/tests/framework/layer/layer_util.h +++ b/tests/framework/layer/layer_util.h @@ -27,7 +27,9 @@ #pragma once -#include "test_util.h" +#include "builder_defines.h" + +#include "vulkan_object_wrappers.h" struct LayerDefinition { BUILDER_VALUE(std::string, layerName) @@ -38,10 +40,10 @@ struct LayerDefinition { VkLayerProperties get() const noexcept { VkLayerProperties props{}; - copy_string_to_char_array(layerName, &props.layerName[0], VK_MAX_EXTENSION_NAME_SIZE); + layerName.copy(props.layerName, VK_MAX_EXTENSION_NAME_SIZE); props.specVersion = specVersion; props.implementationVersion = implementationVersion; - copy_string_to_char_array(description, &props.description[0], VK_MAX_DESCRIPTION_SIZE); + description.copy(props.description, VK_MAX_DESCRIPTION_SIZE); return props; } }; diff --git a/tests/framework/layer/test_layer.cpp b/tests/framework/layer/test_layer.cpp index f891927ad..b98f543f4 100644 --- a/tests/framework/layer/test_layer.cpp +++ b/tests/framework/layer/test_layer.cpp @@ -27,6 +27,13 @@ #include "test_layer.h" +#include + +#include + +#include "util/test_defines.h" +#include "util/equality_helpers.h" + #include "generated/vk_dispatch_table_helper.h" // export the enumeration functions instance|device+layer|extension @@ -243,7 +250,7 @@ VKAPI_ATTR VkResult VKAPI_CALL test_vkCreateInstance(const VkInstanceCreateInfo* VkLayerInstanceCreateInfo* chain_info = get_chain_info(pCreateInfo, VK_LAYER_LINK_INFO); PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr; - PFN_vk_icdGetPhysicalDeviceProcAddr fpGetPhysicalDeviceProcAddr = chain_info->u.pLayerInfo->pfnNextGetPhysicalDeviceProcAddr; + PFN_GetPhysicalDeviceProcAddr fpGetPhysicalDeviceProcAddr = chain_info->u.pLayerInfo->pfnNextGetPhysicalDeviceProcAddr; PFN_vkCreateInstance fpCreateInstance = (PFN_vkCreateInstance)fpGetInstanceProcAddr(NULL, "vkCreateInstance"); if (fpCreateInstance == NULL) { return VK_ERROR_INITIALIZATION_FAILED; diff --git a/tests/framework/layer/test_layer.h b/tests/framework/layer/test_layer.h index 14e60bcc8..085be617d 100644 --- a/tests/framework/layer/test_layer.h +++ b/tests/framework/layer/test_layer.h @@ -27,14 +27,18 @@ #pragma once -#include "test_util.h" - #include +#include -#include "layer/layer_util.h" +#include +#include #include "loader/generated/vk_layer_dispatch_table.h" +#include "util/functions.h" + +#include "layer_util.h" + /* Interface Version 0 */ diff --git a/tests/framework/shim/shim.h b/tests/framework/shim/shim.h index bc47c3987..4dbd472c6 100644 --- a/tests/framework/shim/shim.h +++ b/tests/framework/shim/shim.h @@ -27,13 +27,19 @@ #pragma once -#include "test_util.h" +#include +#include +#include +#include #include #include -#include +#include -#if defined(WIN32) +#if defined(_WIN32) + +#include +#include #include #include #include @@ -46,10 +52,18 @@ #include #endif -enum class ManifestCategory { implicit_layer, explicit_layer, icd, settings }; +#include "util/test_defines.h" + +#if TESTING_COMMON_UNIX_PLATFORMS +#include +#endif + +#include "util/folder_manager.h" +#include "util/functions.h" + enum class GpuType { unspecified, integrated, discrete, external }; -#if defined(WIN32) +#if defined(_WIN32) #define VK_VARIANT_REG_STR "" #define VK_VARIANT_REG_STR_W L"" @@ -117,15 +131,15 @@ struct D3DKMT_Adapter { D3DKMT_Adapter& add_path(std::filesystem::path src, std::vector& dest); }; -#elif COMMON_UNIX_PLATFORMS +#elif TESTING_COMMON_UNIX_PLATFORMS struct DirEntry { DIR* directory = nullptr; - std::string folder_path; + fs::Folder* folder_represented; std::vector contents; // the current item being read by an app (incremented by readdir, reset to zero by opendir & closedir) size_t current_index = 0; - bool is_fake_path = false; // true when this entry is for folder redirection + // bool is_fake_path = false; // true when this entry is for folder redirection }; #endif @@ -136,12 +150,9 @@ struct FrameworkEnvironment; // forward declaration // defined in the .cpp wont be found by the rest of the application struct PlatformShim { PlatformShim() { fputs_stderr_log.reserve(65536); } - PlatformShim(GetFoldersFunc get_folders_by_name_function) : get_folders_by_name_function(get_folders_by_name_function) { - fputs_stderr_log.reserve(65536); - } // Used to get info about which drivers & layers have been added to folders - GetFoldersFunc get_folders_by_name_function; + fs::FileSystemManager* file_system_manager; // Captures the output to stderr from fputs & fputc - aka the output of loader_log() std::string fputs_stderr_log; @@ -149,28 +160,25 @@ struct PlatformShim { // Test Framework interface void reset(); - void redirect_all_paths(std::filesystem::path const& path); - void redirect_category(std::filesystem::path const& new_path, ManifestCategory category); - // fake paths are paths that the loader normally looks in but actually point to locations inside the test framework - void set_fake_path(ManifestCategory category, std::filesystem::path const& path); + // void set_fake_path(ManifestCategory category, std::filesystem::path const& path); // known paths are real paths but since the test framework guarantee's the order files are found in, files in these paths // need to be ordered correctly - void add_known_path(std::filesystem::path const& path); - - void add_manifest(ManifestCategory category, std::filesystem::path const& path); - void add_unsecured_manifest(ManifestCategory category, std::filesystem::path const& path); + // void add_known_path(std::filesystem::path const& path); void clear_logs() { fputs_stderr_log.clear(); } bool find_in_log(std::string const& search_text) const { return fputs_stderr_log.find(search_text) != std::string::npos; } // platform specific shim interface -#if defined(WIN32) +#if defined(_WIN32) // Control Platform Elevation Level void set_elevated_privilege(bool elev) { elevation_level = (elev) ? SECURITY_MANDATORY_HIGH_RID : SECURITY_MANDATORY_LOW_RID; } unsigned long elevation_level = SECURITY_MANDATORY_LOW_RID; + void add_manifest_to_registry(ManifestCategory category, std::filesystem::path const& path); + void add_unsecured_manifest_to_registry(ManifestCategory category, std::filesystem::path const& path); + void add_dxgi_adapter(GpuType gpu_preference, DXGI_ADAPTER_DESC1 desc1); void add_d3dkmt_adapter(D3DKMT_Adapter const& adapter); void set_app_package_path(std::filesystem::path const& path); @@ -202,27 +210,18 @@ struct PlatformShim { size_t created_key_count = 0; std::vector created_keys; -#elif COMMON_UNIX_PLATFORMS - bool is_fake_path(std::filesystem::path const& path); - std::filesystem::path const& get_real_path_from_fake_path(std::filesystem::path const& path); +#elif TESTING_COMMON_UNIX_PLATFORMS + // Add a filename that dlopen can "find" with no absolute or relative path given. + // EG, dlopen("libfoobar.so") gets turned into dlopen(actual_path) + void add_system_library(std::string const& filename, std::filesystem::path const& actual_path); - void redirect_path(std::filesystem::path const& path, std::filesystem::path const& new_path); - void remove_redirect(std::filesystem::path const& path); + // Returns the real path of the system library "filename", if it exists. Else returns empty path + std::filesystem::path get_system_library(std::string const& filename); - bool is_known_path(std::filesystem::path const& path); - void remove_known_path(std::filesystem::path const& path); - - void redirect_dlopen_name(std::filesystem::path const& filename, std::filesystem::path const& actual_path); - bool is_dlopen_redirect_name(std::filesystem::path const& filename); - - std::filesystem::path query_default_redirect_path(ManifestCategory category); + std::unordered_map system_library_redirection_map; void set_app_package_path(std::filesystem::path const& path); - std::unordered_map redirection_map; - std::unordered_map dlopen_redirection_map; - std::unordered_set known_path_set; - void set_elevated_privilege(bool elev) { use_fake_elevation = elev; } bool use_fake_elevation = false; @@ -232,6 +231,7 @@ struct PlatformShim { std::string bundle_contents; #endif #endif + bool is_finished_setup = false; bool is_during_destruction = false; }; @@ -240,12 +240,12 @@ std::string category_path_name(ManifestCategory category); extern "C" { // dynamically link on windows and macos -#if defined(WIN32) || defined(__APPLE__) -using PFN_get_platform_shim = PlatformShim* (*)(GetFoldersFunc get_folders_by_name_function); +#if defined(_WIN32) || defined(__APPLE__) +using PFN_get_platform_shim = PlatformShim* (*)(); #define GET_PLATFORM_SHIM_STR "get_platform_shim" #elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__GNU__) || defined(__QNX__) // statically link on linux -PlatformShim* get_platform_shim(GetFoldersFunc get_folders_by_name_function); +PlatformShim* get_platform_shim(); #endif } diff --git a/tests/framework/shim/shim_common.cpp b/tests/framework/shim/shim_common.cpp index a21902ceb..f3d021fb6 100644 --- a/tests/framework/shim/shim_common.cpp +++ b/tests/framework/shim/shim_common.cpp @@ -27,20 +27,16 @@ #include "shim.h" -void PlatformShim::redirect_all_paths(std::filesystem::path const& path) { - redirect_category(path, ManifestCategory::implicit_layer); - redirect_category(path, ManifestCategory::explicit_layer); - redirect_category(path, ManifestCategory::icd); -} +#include "env_var_wrapper.h" std::vector parse_env_var_list(std::string const& var) { std::vector items; size_t start = 0; size_t len = 0; for (size_t i = 0; i < var.size(); i++) { -#if defined(WIN32) +#if defined(_WIN32) if (var[i] == ';') { -#elif COMMON_UNIX_PLATFORMS +#elif TESTING_COMMON_UNIX_PLATFORMS if (var[i] == ':') { #endif if (len != 0) { @@ -58,7 +54,7 @@ std::vector parse_env_var_list(std::string const& var) { return items; } -#if defined(WIN32) +#if defined(_WIN32) D3DKMT_Adapter& D3DKMT_Adapter::add_driver_manifest_path(std::filesystem::path const& src) { return add_path(src, driver_paths); } D3DKMT_Adapter& D3DKMT_Adapter::add_implicit_layer_manifest_path(std::filesystem::path const& src) { @@ -91,10 +87,7 @@ void PlatformShim::reset() { hkey_current_user_settings.clear(); } -void PlatformShim::set_fake_path([[maybe_unused]] ManifestCategory category, [[maybe_unused]] std::filesystem::path const& path) {} -void PlatformShim::add_known_path([[maybe_unused]] std::filesystem::path const& path) {} - -void PlatformShim::add_manifest(ManifestCategory category, std::filesystem::path const& path) { +void PlatformShim::add_manifest_to_registry(ManifestCategory category, std::filesystem::path const& path) { if (category == ManifestCategory::settings) { hkey_local_machine_settings.emplace_back(path); } else if (category == ManifestCategory::implicit_layer) { @@ -106,7 +99,7 @@ void PlatformShim::add_manifest(ManifestCategory category, std::filesystem::path } } -void PlatformShim::add_unsecured_manifest(ManifestCategory category, std::filesystem::path const& path) { +void PlatformShim::add_unsecured_manifest_to_registry(ManifestCategory category, std::filesystem::path const& path) { if (category == ManifestCategory::settings) { hkey_current_user_settings.emplace_back(path); } else if (category == ManifestCategory::implicit_layer) { @@ -146,9 +139,7 @@ void PlatformShim::add_CM_Device_ID([[maybe_unused]] std::wstring const& id, [[m // // add_key_value_string(id_key, "VulkanLayerName", layer_path.c_str()); } -void PlatformShim::redirect_category(std::filesystem::path const&, ManifestCategory) {} - -#elif COMMON_UNIX_PLATFORMS +#elif TESTING_COMMON_UNIX_PLATFORMS #include #include @@ -162,93 +153,12 @@ std::string category_path_name(ManifestCategory category) { return "icd.d"; } -void PlatformShim::reset() { redirection_map.clear(); } - -bool PlatformShim::is_fake_path(std::filesystem::path const& path) { return redirection_map.count(path) > 0; } -std::filesystem::path const& PlatformShim::get_real_path_from_fake_path(std::filesystem::path const& path) { - return redirection_map.at(path); -} -void PlatformShim::redirect_path(std::filesystem::path const& path, std::filesystem::path const& new_path) { - redirection_map[path] = new_path; -} -void PlatformShim::remove_redirect(std::filesystem::path const& path) { redirection_map.erase(path); } - -bool PlatformShim::is_known_path(std::filesystem::path const& path) { return known_path_set.count(path) > 0; } -void PlatformShim::add_known_path(std::filesystem::path const& path) { known_path_set.insert(path); } -void PlatformShim::remove_known_path(std::filesystem::path const& path) { known_path_set.erase(path); } - -void PlatformShim::add_manifest([[maybe_unused]] ManifestCategory category, [[maybe_unused]] std::filesystem::path const& path) {} -void PlatformShim::add_unsecured_manifest([[maybe_unused]] ManifestCategory category, - [[maybe_unused]] std::filesystem::path const& path) {} - -void PlatformShim::set_app_package_path(std::filesystem::path const& path) {} - -void parse_and_add_env_var_override(std::vector& paths, std::string env_var_contents) { - auto parsed_paths = parse_env_var_list(env_var_contents); - paths.insert(paths.end(), parsed_paths.begin(), parsed_paths.end()); -} - -void PlatformShim::redirect_category(std::filesystem::path const& new_path, ManifestCategory category) { - std::vector paths; - auto home = std::filesystem::path(get_env_var("HOME")); - if (category == ManifestCategory::settings) { - redirect_path(home / ".local/share/vulkan" / category_path_name(category), new_path); - return; - } - - if (!home.empty()) { - paths.push_back((home / ".config")); - paths.push_back((home / ".local/share")); - } - // Don't report errors on apple - these env-vars are not suppose to be defined - bool report_errors = true; -#if defined(__APPLE__) - report_errors = false; -#endif - parse_and_add_env_var_override(paths, get_env_var("XDG_CONFIG_HOME", report_errors)); - if (category == ManifestCategory::explicit_layer) { - parse_and_add_env_var_override(paths, get_env_var("VK_LAYER_PATH", false)); // don't report failure - } - if (category == ManifestCategory::implicit_layer) { - parse_and_add_env_var_override(paths, get_env_var("VK_IMPLICIT_LAYER_PATH", false)); // don't report failure - } - parse_and_add_env_var_override(paths, FALLBACK_DATA_DIRS); - parse_and_add_env_var_override(paths, FALLBACK_CONFIG_DIRS); - - auto sys_conf_dir = std::string(SYSCONFDIR); - if (!sys_conf_dir.empty()) { - paths.push_back(sys_conf_dir); - } -#if defined(EXTRASYSCONFDIR) - // EXTRASYSCONFDIR default is /etc, if SYSCONFDIR wasn't defined, it will have /etc put - // as its default. Don't want to double add it - auto extra_sys_conf_dir = std::string(EXTRASYSCONFDIR); - if (!extra_sys_conf_dir.empty() && sys_conf_dir != extra_sys_conf_dir) { - paths.push_back(extra_sys_conf_dir); - } -#endif - - for (auto& path : paths) { - if (!path.empty()) { - redirect_path(std::filesystem::path(path) / "vulkan" / category_path_name(category), new_path); - } - } -} - -void PlatformShim::set_fake_path(ManifestCategory category, std::filesystem::path const& path) { - // use /etc as the 'redirection path' by default since its always searched - redirect_path(std::filesystem::path(SYSCONFDIR) / "vulkan" / category_path_name(category), path); -} - -void PlatformShim::redirect_dlopen_name(std::filesystem::path const& filename, std::filesystem::path const& actual_path) { - dlopen_redirection_map[filename] = actual_path; -} - -bool PlatformShim::is_dlopen_redirect_name(std::filesystem::path const& filename) { - return dlopen_redirection_map.count(filename) == 1; +void PlatformShim::add_system_library(std::string const& filename, std::filesystem::path const& actual_path) { + system_library_redirection_map[filename] = actual_path; } -std::filesystem::path PlatformShim::query_default_redirect_path(ManifestCategory category) { - return std::filesystem::path(SYSCONFDIR) / "vulkan" / category_path_name(category); +std::filesystem::path PlatformShim::get_system_library(std::string const& filename) { + return system_library_redirection_map.count(filename) == 1 ? system_library_redirection_map.at(filename) + : std::filesystem::path{}; } #endif diff --git a/tests/framework/shim/unix_shim.cpp b/tests/framework/shim/unix_shim.cpp index a3abba5a8..da3fd151a 100644 --- a/tests/framework/shim/unix_shim.cpp +++ b/tests/framework/shim/unix_shim.cpp @@ -28,21 +28,26 @@ #include "shim.h" #include +#include #if defined(__APPLE__) #include #endif +#include + +#include "util/folder_manager.h" + PlatformShim platform_shim; extern "C" { #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__GNU__) || defined(__QNX__) -PlatformShim* get_platform_shim(GetFoldersFunc get_folders_by_name_function) { - platform_shim = PlatformShim(get_folders_by_name_function); +PlatformShim* get_platform_shim() { + platform_shim = PlatformShim(); return &platform_shim; } #elif defined(__APPLE__) -FRAMEWORK_EXPORT PlatformShim* get_platform_shim(GetFoldersFunc get_folders_by_name_function) { - platform_shim = PlatformShim(get_folders_by_name_function); +FRAMEWORK_EXPORT PlatformShim* get_platform_shim() { + platform_shim = PlatformShim(); return &platform_shim; } #endif @@ -141,20 +146,16 @@ FRAMEWORK_EXPORT DIR* OPENDIR_FUNC_NAME(const char* path_name) { #if !defined(__APPLE__) if (!real_opendir) real_opendir = (PFN_OPENDIR)dlsym(RTLD_NEXT, "opendir"); #endif - if (platform_shim.is_during_destruction) { + if (platform_shim.is_during_destruction || !platform_shim.is_finished_setup) { return real_opendir(path_name); } - DIR* dir; - if (platform_shim.is_fake_path(path_name)) { - auto real_path_name = platform_shim.get_real_path_from_fake_path(std::filesystem::path(path_name)); - dir = real_opendir(real_path_name.c_str()); - platform_shim.dir_entries.push_back(DirEntry{dir, std::string(path_name), {}, 0, true}); - } else if (platform_shim.is_known_path(path_name)) { - dir = real_opendir(path_name); - platform_shim.dir_entries.push_back(DirEntry{dir, std::string(path_name), {}, 0, false}); - } else { - dir = real_opendir(path_name); + DIR* dir = nullptr; + + if (auto* folder = platform_shim.file_system_manager->get_folder_for_given_path(path_name); folder != nullptr) { + dir = real_opendir(folder->location().c_str()); + platform_shim.dir_entries.push_back(DirEntry{dir, folder, {}, 0}); } + // Dont pass through as this allows non-test paths to escape containment return dir; } @@ -171,18 +172,19 @@ FRAMEWORK_EXPORT struct dirent* READDIR_FUNC_NAME(DIR* dir_stream) { } #endif - if (platform_shim.is_during_destruction) { + if (platform_shim.is_during_destruction || !platform_shim.is_finished_setup) { return real_readdir(dir_stream); } auto it = std::find_if(platform_shim.dir_entries.begin(), platform_shim.dir_entries.end(), [dir_stream](DirEntry const& entry) { return entry.directory == dir_stream; }); + // Folder has not been opened with opendir previously, probably not a loader call so we should ignore it if (it == platform_shim.dir_entries.end()) { return real_readdir(dir_stream); } // Folder was found but this is the first file to be read from it if (it->current_index == 0) { - std::vector folder_contents; + std::vector readdir_contents; std::vector dirent_filenames; while (true) { errno = 0; @@ -198,21 +200,17 @@ FRAMEWORK_EXPORT struct dirent* READDIR_FUNC_NAME(DIR* dir_stream) { (dir_entry->d_name[0] == '.' && dir_entry->d_name[1] == '\0')) { continue; } - folder_contents.push_back(dir_entry); + readdir_contents.push_back(dir_entry); dirent_filenames.push_back(&dir_entry->d_name[0]); } - auto real_path = it->folder_path; - if (it->is_fake_path) { - real_path = platform_shim.redirection_map.at(it->folder_path); - } - auto filenames = platform_shim.get_folders_by_name_function(real_path.c_str()); - // Add the dirent structures in the order they appear in the FolderManager - // Ignore anything which wasn't in the FolderManager - for (auto const& file : filenames) { + // Add the dirent structures in the order they appear in the list of files provided by the Folder + // Ignore anything which wasn't in the Folder + auto folder_contents = it->folder_represented->get_files(); + for (auto const& file : folder_contents) { for (size_t i = 0; i < dirent_filenames.size(); i++) { if (file == dirent_filenames.at(i)) { - it->contents.push_back(folder_contents.at(i)); + it->contents.push_back(readdir_contents.at(i)); break; } } @@ -226,7 +224,7 @@ FRAMEWORK_EXPORT int CLOSEDIR_FUNC_NAME(DIR* dir_stream) { #if !defined(__APPLE__) if (!real_closedir) real_closedir = (PFN_CLOSEDIR)dlsym(RTLD_NEXT, "closedir"); #endif - if (platform_shim.is_during_destruction) { + if (platform_shim.is_during_destruction || !platform_shim.is_finished_setup) { return real_closedir(dir_stream); } auto it = std::find_if(platform_shim.dir_entries.begin(), platform_shim.dir_entries.end(), @@ -243,48 +241,62 @@ FRAMEWORK_EXPORT int ACCESS_FUNC_NAME(const char* in_pathname, int mode) { #if !defined(__APPLE__) if (!real_access) real_access = (PFN_ACCESS)dlsym(RTLD_NEXT, "access"); #endif - std::filesystem::path path{in_pathname}; - if (!path.has_parent_path()) { + if (platform_shim.is_during_destruction || !platform_shim.is_finished_setup) { return real_access(in_pathname, mode); } - if (platform_shim.is_fake_path(path.parent_path())) { - std::filesystem::path real_path = platform_shim.get_real_path_from_fake_path(path.parent_path()); + std::filesystem::path path{in_pathname}; + if (!path.has_parent_path()) { + return real_access(in_pathname, mode); + } else if (auto real_path = platform_shim.file_system_manager->get_real_path_of_redirected_path(path.parent_path()); + !real_path.empty()) { real_path /= path.filename(); return real_access(real_path.c_str(), mode); + } else if (platform_shim.file_system_manager->is_folder_path(path.parent_path())) { + return real_access(in_pathname, mode); + } else { + return -1; } - return real_access(in_pathname, mode); } FRAMEWORK_EXPORT FILE* FOPEN_FUNC_NAME(const char* in_filename, const char* mode) { #if !defined(__APPLE__) if (!real_fopen) real_fopen = (PFN_FOPEN)dlsym(RTLD_NEXT, "fopen"); #endif - std::filesystem::path path{in_filename}; - if (!path.has_parent_path()) { + if (platform_shim.is_during_destruction || !platform_shim.is_finished_setup) { return real_fopen(in_filename, mode); } - FILE* f_ptr; - if (platform_shim.is_fake_path(path.parent_path())) { - auto real_path = platform_shim.get_real_path_from_fake_path(path.parent_path()) / path.filename(); - f_ptr = real_fopen(real_path.c_str(), mode); + std::filesystem::path path{in_filename}; + if (!path.has_parent_path()) { + return real_fopen(in_filename, mode); + } else if (auto real_path = platform_shim.file_system_manager->get_real_path_of_redirected_path(path.parent_path()); + !real_path.empty()) { + real_path /= path.filename(); + return real_fopen(real_path.c_str(), mode); } else { - f_ptr = real_fopen(in_filename, mode); + return real_fopen(in_filename, mode); } - - return f_ptr; } FRAMEWORK_EXPORT void* DLOPEN_FUNC_NAME(const char* in_filename, int flags) { #if !defined(__APPLE__) if (!real_dlopen) real_dlopen = (PFN_DLOPEN)dlsym(RTLD_NEXT, "dlopen"); #endif + if (platform_shim.is_during_destruction || !platform_shim.is_finished_setup) { + return real_dlopen(in_filename, flags); + } - if (platform_shim.is_dlopen_redirect_name(in_filename)) { - return real_dlopen(platform_shim.dlopen_redirection_map[in_filename].c_str(), flags); + std::filesystem::path path{in_filename}; + if (auto real_path = platform_shim.get_system_library(path); !real_path.empty()) { + return real_dlopen(real_path.c_str(), flags); + } else if (auto real_path = platform_shim.file_system_manager->get_real_path_of_redirected_path(path.parent_path()); + !real_path.empty()) { + real_path /= path.filename(); + return real_dlopen(real_path.c_str(), flags); + } else { + return real_dlopen(in_filename, flags); } - return real_dlopen(in_filename, flags); } FRAMEWORK_EXPORT uid_t GETEUID_FUNC_NAME(void) { diff --git a/tests/framework/shim/windows_shim.cpp b/tests/framework/shim/windows_shim.cpp index 1ca1a68e2..1c6bc0b9f 100644 --- a/tests/framework/shim/windows_shim.cpp +++ b/tests/framework/shim/windows_shim.cpp @@ -31,6 +31,10 @@ #include #endif +#include + +#include + #include #include @@ -38,6 +42,9 @@ #include "detours.h" +#include "util/dynamic_library_wrapper.h" +#include "util/wide_char_handling.h" + static PlatformShim platform_shim; extern "C" { @@ -512,8 +519,5 @@ BOOL WINAPI DllMain([[maybe_unused]] HINSTANCE hinst, DWORD dwReason, [[maybe_un } return TRUE; } -FRAMEWORK_EXPORT PlatformShim *get_platform_shim(GetFoldersFunc get_folders_by_name_function) { - platform_shim = PlatformShim(get_folders_by_name_function); - return &platform_shim; -} +FRAMEWORK_EXPORT PlatformShim *get_platform_shim() { return &platform_shim; } } diff --git a/tests/framework/test_environment.cpp b/tests/framework/test_environment.cpp index 0dea3f794..261d21149 100644 --- a/tests/framework/test_environment.cpp +++ b/tests/framework/test_environment.cpp @@ -27,151 +27,11 @@ #include "test_environment.h" +#include #include +#include -std::filesystem::path get_loader_path() { - auto loader_path = std::filesystem::path(FRAMEWORK_VULKAN_LIBRARY_PATH); - auto env_var_res = get_env_var("VK_LOADER_TEST_LOADER_PATH", false); - if (!env_var_res.empty()) { - loader_path = std::filesystem::path(env_var_res); - } - return loader_path; -} - -void init_vulkan_functions(VulkanFunctions& funcs) { -#if defined(APPLE_STATIC_LOADER) -#define GPA(name) name -#else -#define GPA(name) funcs.loader.get_symbol(#name) -#endif - - // clang-format off - funcs.vkGetInstanceProcAddr = GPA(vkGetInstanceProcAddr); - funcs.vkEnumerateInstanceExtensionProperties = GPA(vkEnumerateInstanceExtensionProperties); - funcs.vkEnumerateInstanceLayerProperties = GPA(vkEnumerateInstanceLayerProperties); - funcs.vkEnumerateInstanceVersion = GPA(vkEnumerateInstanceVersion); - funcs.vkCreateInstance = GPA(vkCreateInstance); - funcs.vkDestroyInstance = GPA(vkDestroyInstance); - funcs.vkEnumeratePhysicalDevices = GPA(vkEnumeratePhysicalDevices); - funcs.vkEnumeratePhysicalDeviceGroups = GPA(vkEnumeratePhysicalDeviceGroups); - funcs.vkGetPhysicalDeviceFeatures = GPA(vkGetPhysicalDeviceFeatures); - funcs.vkGetPhysicalDeviceFeatures2 = GPA(vkGetPhysicalDeviceFeatures2); - funcs.vkGetPhysicalDeviceFormatProperties = GPA(vkGetPhysicalDeviceFormatProperties); - funcs.vkGetPhysicalDeviceFormatProperties2 = GPA(vkGetPhysicalDeviceFormatProperties2); - funcs.vkGetPhysicalDeviceImageFormatProperties = GPA(vkGetPhysicalDeviceImageFormatProperties); - funcs.vkGetPhysicalDeviceImageFormatProperties2 = GPA(vkGetPhysicalDeviceImageFormatProperties2); - funcs.vkGetPhysicalDeviceSparseImageFormatProperties = GPA(vkGetPhysicalDeviceSparseImageFormatProperties); - funcs.vkGetPhysicalDeviceSparseImageFormatProperties2 = GPA(vkGetPhysicalDeviceSparseImageFormatProperties2); - funcs.vkGetPhysicalDeviceProperties = GPA(vkGetPhysicalDeviceProperties); - funcs.vkGetPhysicalDeviceProperties2 = GPA(vkGetPhysicalDeviceProperties2); - funcs.vkGetPhysicalDeviceQueueFamilyProperties = GPA(vkGetPhysicalDeviceQueueFamilyProperties); - funcs.vkGetPhysicalDeviceQueueFamilyProperties2 = GPA(vkGetPhysicalDeviceQueueFamilyProperties2); - funcs.vkGetPhysicalDeviceMemoryProperties = GPA(vkGetPhysicalDeviceMemoryProperties); - funcs.vkGetPhysicalDeviceMemoryProperties2 = GPA(vkGetPhysicalDeviceMemoryProperties2); - funcs.vkGetPhysicalDeviceSurfaceSupportKHR = GPA(vkGetPhysicalDeviceSurfaceSupportKHR); - funcs.vkGetPhysicalDeviceSurfaceFormatsKHR = GPA(vkGetPhysicalDeviceSurfaceFormatsKHR); - funcs.vkGetPhysicalDeviceSurfacePresentModesKHR = GPA(vkGetPhysicalDeviceSurfacePresentModesKHR); - funcs.vkGetPhysicalDeviceSurfaceCapabilitiesKHR = GPA(vkGetPhysicalDeviceSurfaceCapabilitiesKHR); - funcs.vkEnumerateDeviceExtensionProperties = GPA(vkEnumerateDeviceExtensionProperties); - funcs.vkEnumerateDeviceLayerProperties = GPA(vkEnumerateDeviceLayerProperties); - funcs.vkGetPhysicalDeviceExternalBufferProperties = GPA(vkGetPhysicalDeviceExternalBufferProperties); - funcs.vkGetPhysicalDeviceExternalFenceProperties = GPA(vkGetPhysicalDeviceExternalFenceProperties); - funcs.vkGetPhysicalDeviceExternalSemaphoreProperties = GPA(vkGetPhysicalDeviceExternalSemaphoreProperties); - - funcs.vkDestroySurfaceKHR = GPA(vkDestroySurfaceKHR); - funcs.vkGetDeviceProcAddr = GPA(vkGetDeviceProcAddr); - funcs.vkCreateDevice = GPA(vkCreateDevice); - - funcs.vkCreateHeadlessSurfaceEXT = GPA(vkCreateHeadlessSurfaceEXT); - funcs.vkCreateDisplayPlaneSurfaceKHR = GPA(vkCreateDisplayPlaneSurfaceKHR); - funcs.vkGetPhysicalDeviceDisplayPropertiesKHR = GPA(vkGetPhysicalDeviceDisplayPropertiesKHR); - funcs.vkGetPhysicalDeviceDisplayPlanePropertiesKHR = GPA(vkGetPhysicalDeviceDisplayPlanePropertiesKHR); - funcs.vkGetDisplayPlaneSupportedDisplaysKHR = GPA(vkGetDisplayPlaneSupportedDisplaysKHR); - funcs.vkGetDisplayModePropertiesKHR = GPA(vkGetDisplayModePropertiesKHR); - funcs.vkCreateDisplayModeKHR = GPA(vkCreateDisplayModeKHR); - funcs.vkGetDisplayPlaneCapabilitiesKHR = GPA(vkGetDisplayPlaneCapabilitiesKHR); - funcs.vkGetPhysicalDevicePresentRectanglesKHR = GPA(vkGetPhysicalDevicePresentRectanglesKHR); - funcs.vkGetPhysicalDeviceDisplayProperties2KHR = GPA(vkGetPhysicalDeviceDisplayProperties2KHR); - funcs.vkGetPhysicalDeviceDisplayPlaneProperties2KHR = GPA(vkGetPhysicalDeviceDisplayPlaneProperties2KHR); - funcs.vkGetDisplayModeProperties2KHR = GPA(vkGetDisplayModeProperties2KHR); - funcs.vkGetDisplayPlaneCapabilities2KHR = GPA(vkGetDisplayPlaneCapabilities2KHR); - funcs.vkGetPhysicalDeviceSurfaceCapabilities2KHR = GPA(vkGetPhysicalDeviceSurfaceCapabilities2KHR); - funcs.vkGetPhysicalDeviceSurfaceFormats2KHR = GPA(vkGetPhysicalDeviceSurfaceFormats2KHR); - -#if defined(VK_USE_PLATFORM_ANDROID_KHR) - funcs.vkCreateAndroidSurfaceKHR = GPA(vkCreateAndroidSurfaceKHR); -#endif // VK_USE_PLATFORM_ANDROID_KHR -#if defined(VK_USE_PLATFORM_DIRECTFB_EXT) - funcs.vkCreateDirectFBSurfaceEXT = GPA(vkCreateDirectFBSurfaceEXT); - funcs.vkGetPhysicalDeviceDirectFBPresentationSupportEXT = GPA(vkGetPhysicalDeviceDirectFBPresentationSupportEXT); -#endif // VK_USE_PLATFORM_DIRECTFB_EXT -#if defined(VK_USE_PLATFORM_FUCHSIA) - funcs.vkCreateImagePipeSurfaceFUCHSIA = GPA(vkCreateImagePipeSurfaceFUCHSIA); -#endif // VK_USE_PLATFORM_FUCHSIA -#if defined(VK_USE_PLATFORM_GGP) - funcs.vkCreateStreamDescriptorSurfaceGGP = GPA(vkCreateStreamDescriptorSurfaceGGP); -#endif // VK_USE_PLATFORM_GGP -#if defined(VK_USE_PLATFORM_IOS_MVK) - funcs.vkCreateIOSSurfaceMVK = GPA(vkCreateIOSSurfaceMVK); -#endif // VK_USE_PLATFORM_IOS_MVK -#if defined(VK_USE_PLATFORM_MACOS_MVK) - funcs.vkCreateMacOSSurfaceMVK = GPA(vkCreateMacOSSurfaceMVK); -#endif // VK_USE_PLATFORM_MACOS_MVK -#if defined(VK_USE_PLATFORM_METAL_EXT) - funcs.vkCreateMetalSurfaceEXT = GPA(vkCreateMetalSurfaceEXT); -#endif // VK_USE_PLATFORM_METAL_EXT -#if defined(VK_USE_PLATFORM_SCREEN_QNX) - funcs.vkCreateScreenSurfaceQNX = GPA(vkCreateScreenSurfaceQNX); - funcs.vkGetPhysicalDeviceScreenPresentationSupportQNX = GPA(vkGetPhysicalDeviceScreenPresentationSupportQNX); -#endif // VK_USE_PLATFORM_SCREEN_QNX -#if defined(VK_USE_PLATFORM_WAYLAND_KHR) - funcs.vkCreateWaylandSurfaceKHR = GPA(vkCreateWaylandSurfaceKHR); - funcs.vkGetPhysicalDeviceWaylandPresentationSupportKHR = GPA(vkGetPhysicalDeviceWaylandPresentationSupportKHR); -#endif // VK_USE_PLATFORM_WAYLAND_KHR -#if defined(VK_USE_PLATFORM_XCB_KHR) - funcs.vkCreateXcbSurfaceKHR = GPA(vkCreateXcbSurfaceKHR); - funcs.vkGetPhysicalDeviceXcbPresentationSupportKHR = GPA(vkGetPhysicalDeviceXcbPresentationSupportKHR); -#endif // VK_USE_PLATFORM_XCB_KHR -#if defined(VK_USE_PLATFORM_XLIB_KHR) - funcs.vkCreateXlibSurfaceKHR = GPA(vkCreateXlibSurfaceKHR); - funcs.vkGetPhysicalDeviceXlibPresentationSupportKHR = GPA(vkGetPhysicalDeviceXlibPresentationSupportKHR); -#endif // VK_USE_PLATFORM_XLIB_KHR -#if defined(VK_USE_PLATFORM_WIN32_KHR) - funcs.vkCreateWin32SurfaceKHR = GPA(vkCreateWin32SurfaceKHR); - funcs.vkGetPhysicalDeviceWin32PresentationSupportKHR = GPA(vkGetPhysicalDeviceWin32PresentationSupportKHR); -#endif // VK_USE_PLATFORM_WIN32_KHR - funcs.vkDestroyDevice = GPA(vkDestroyDevice); - funcs.vkGetDeviceQueue = GPA(vkGetDeviceQueue); -#undef GPA - // clang-format on -} - -#if defined(APPLE_STATIC_LOADER) -VulkanFunctions::VulkanFunctions() { -#else -VulkanFunctions::VulkanFunctions() : loader(get_loader_path()) { -#endif - init_vulkan_functions(*this); -} - -void VulkanFunctions::load_instance_functions(VkInstance instance) { - vkCreateDebugReportCallbackEXT = FromVoidStarFunc(vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT")); - vkDestroyDebugReportCallbackEXT = FromVoidStarFunc(vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT")); - vkCreateDebugUtilsMessengerEXT = FromVoidStarFunc(vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT")); - vkDestroyDebugUtilsMessengerEXT = FromVoidStarFunc(vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT")); -} - -DeviceFunctions::DeviceFunctions(const VulkanFunctions& vulkan_functions, VkDevice device) { - vkGetDeviceProcAddr = vulkan_functions.vkGetDeviceProcAddr; - vkDestroyDevice = load(device, "vkDestroyDevice"); - vkGetDeviceQueue = load(device, "vkGetDeviceQueue"); - vkCreateCommandPool = load(device, "vkCreateCommandPool"); - vkAllocateCommandBuffers = load(device, "vkAllocateCommandBuffers"); - vkDestroyCommandPool = load(device, "vkDestroyCommandPool"); - vkCreateSwapchainKHR = load(device, "vkCreateSwapchainKHR"); - vkGetSwapchainImagesKHR = load(device, "vkGetSwapchainImagesKHR"); - vkDestroySwapchainKHR = load(device, "vkDestroySwapchainKHR"); -} +#include "json_writer.h" InstWrapper::InstWrapper(VulkanFunctions& functions, VkAllocationCallbacks* callbacks) noexcept : functions(&functions), callbacks(callbacks) {} @@ -341,139 +201,23 @@ bool FindPrefixPostfixStringOnLine(DebugUtilsLogger const& env_log, const char* return env_log.find_prefix_then_postfix(prefix, postfix); } -namespace fs { -FolderManager::FolderManager(std::filesystem::path root_path, std::string name) noexcept : folder(root_path / name) { - clear(); - // Don't actually create the folder yet, as we will do it on demand -} -FolderManager::~FolderManager() noexcept { clear(); } -FolderManager::FolderManager(FolderManager&& other) noexcept : actually_created(other.actually_created), folder(other.folder) { - other.folder.clear(); -} -FolderManager& FolderManager::operator=(FolderManager&& other) noexcept { - folder = other.folder; - actually_created = other.actually_created; - other.folder.clear(); - return *this; -} - -void FolderManager::check_if_first_use() { - if (!actually_created) { - if (!::testing::internal::InDeathTestChild()) { - std::error_code err; - if (!std::filesystem::create_directories(folder, err)) { - std::cerr << "Failed to create folder " << folder << " because " << err.message() << "\n"; - } - } - actually_created = true; - } -} - -std::filesystem::path FolderManager::write_manifest(std::filesystem::path const& name, std::string const& contents) { - check_if_first_use(); - std::filesystem::path out_path = folder / name; - if (!::testing::internal::InDeathTestChild()) { - auto file = std::ofstream(out_path, std::ios_base::trunc | std::ios_base::out); - if (!file) { - std::cerr << "Failed to create manifest " << name << " at " << out_path << "\n"; - return out_path; - } - file << contents << std::endl; - } - insert_file_to_tracking(name); - return out_path; -} - -// close file handle, delete file, remove `name` from managed file list. -void FolderManager::remove(std::filesystem::path const& name) { - check_if_first_use(); - std::filesystem::path out_path = folder / name; - if (!::testing::internal::InDeathTestChild()) { - std::error_code err; - if (!std::filesystem::remove(out_path, err)) { - std::cerr << "Failed to remove file " << name << " at " << out_path << " because " << err.message() << "\n"; - } - } - - auto found = std::find(added_files.begin(), added_files.end(), name); - if (found != added_files.end()) { - added_files.erase(found); - } else { - std::cout << "File " << name << " not in tracked files of folder " << folder << ".\n"; - } -} - -// copy file into this folder -std::filesystem::path FolderManager::copy_file(std::filesystem::path const& file, std::filesystem::path const& new_name) { - check_if_first_use(); - insert_file_to_tracking(new_name); - - auto new_filepath = folder / new_name; - if (!::testing::internal::InDeathTestChild()) { - std::error_code err; - if (!std::filesystem::copy_file(file, new_filepath, err)) { - std::cerr << "Failed to copy file " << file << " to " << new_filepath << " because " << err.message() << "\n"; - } - } - return new_filepath; -} - -std::vector FolderManager::get_files() const { return added_files; } - -std::filesystem::path FolderManager::add_symlink(std::filesystem::path const& target, std::filesystem::path const& link_name) { - check_if_first_use(); - - if (!::testing::internal::InDeathTestChild()) { - std::error_code err; - std::filesystem::create_symlink(target, folder / link_name, err); - if (err.value() != 0) { - std::cerr << "Failed to create symlink with target" << target << " with name " << folder / link_name << " because " - << err.message() << "\n"; - } - } - insert_file_to_tracking(link_name); - return folder / link_name; -} -void FolderManager::insert_file_to_tracking(std::filesystem::path const& name) { - auto found = std::find(added_files.begin(), added_files.end(), name); - if (found != added_files.end()) { - std::cout << "Overwriting manifest " << name << ". Was this intended?\n"; - } else { - added_files.emplace_back(name); - } -} - -void FolderManager::clear() const noexcept { - if (!::testing::internal::InDeathTestChild()) { - std::error_code err; - std::filesystem::remove_all(folder, err); - if (err.value() != 0) { - std::cerr << "Failed to remove folder " << folder << " because " << err.message() << "\n"; - } - } -} - -} // namespace fs - -PlatformShimWrapper::PlatformShimWrapper(GetFoldersFunc get_folders_by_name_function, const char* log_filter) noexcept +PlatformShimWrapper::PlatformShimWrapper(fs::FileSystemManager& file_system_manager, const char* log_filter) noexcept : loader_logging{"VK_LOADER_DEBUG"} { #if defined(WIN32) || defined(__APPLE__) shim_library = LibraryWrapper(SHIM_LIBRARY_NAME); - PFN_get_platform_shim get_platform_shim_func = shim_library.get_symbol(GET_PLATFORM_SHIM_STR); - assert(get_platform_shim_func != NULL && "Must be able to get \"platform_shim\""); - platform_shim = get_platform_shim_func(get_folders_by_name_function); + PFN_get_platform_shim get_platform_shim = shim_library.get_symbol(GET_PLATFORM_SHIM_STR); + assert(get_platform_shim != NULL && "Must be able to get \"platform_shim\""); + platform_shim = get_platform_shim(); #elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__GNU__) - platform_shim = get_platform_shim(get_folders_by_name_function); + platform_shim = get_platform_shim(); #endif - platform_shim->reset(); + platform_shim->file_system_manager = &file_system_manager; if (log_filter) { loader_logging.set_new_value(log_filter); } } -PlatformShimWrapper::~PlatformShimWrapper() noexcept { platform_shim->reset(); } - TestICDHandle::TestICDHandle() noexcept {} TestICDHandle::TestICDHandle(std::filesystem::path const& icd_path) noexcept : icd_library(icd_path) { proc_addr_get_test_icd = icd_library.get_symbol(GET_TEST_ICD_FUNC_STR); @@ -487,7 +231,7 @@ TestICD& TestICDHandle::reset_icd() noexcept { assert(proc_addr_reset_icd != NULL && "symbol must be loaded before use"); return *proc_addr_reset_icd(); } -std::filesystem::path TestICDHandle::get_icd_full_path() noexcept { return icd_library.lib_path; } +std::filesystem::path TestICDHandle::get_icd_full_path() noexcept { return icd_library.get_path(); } std::filesystem::path TestICDHandle::get_icd_manifest_path() noexcept { return manifest_path; } std::filesystem::path TestICDHandle::get_shimmed_manifest_path() noexcept { return shimmed_manifest_path; } @@ -504,78 +248,101 @@ TestLayer& TestLayerHandle::reset_layer() noexcept { assert(proc_addr_reset_layer != NULL && "symbol must be loaded before use"); return *proc_addr_reset_layer(); } -std::filesystem::path TestLayerHandle::get_layer_full_path() noexcept { return layer_library.lib_path; } +std::filesystem::path TestLayerHandle::get_layer_full_path() noexcept { return layer_library.get_path(); } std::filesystem::path TestLayerHandle::get_layer_manifest_path() noexcept { return manifest_path; } std::filesystem::path TestLayerHandle::get_shimmed_manifest_path() noexcept { return shimmed_manifest_path; } FrameworkEnvironment::FrameworkEnvironment() noexcept : FrameworkEnvironment(FrameworkSettings{}) {} FrameworkEnvironment::FrameworkEnvironment(FrameworkSettings const& settings) noexcept - : settings(settings), - test_folder(std::filesystem::path(FRAMEWORK_BUILD_DIRECTORY), - std::string(::testing::UnitTest::GetInstance()->GetInstance()->current_test_suite()->name()) + "_" + - ::testing::UnitTest::GetInstance()->current_test_info()->name()), - platform_shim( - [this](const char* folder_name) -> std::vector { - for (auto& folder : folders) { - if (folder.location() == folder_name) { - return folder.get_files(); - } - } - return {}; - }, - settings.log_filter) { - // Clean out test folder in case a previous run's files are still around - test_folder.clear(); - - // This order is important, it matches the enum ManifestLocation, used to index the folders vector - folders.emplace_back(test_folder.location(), std::string("null_dir")); - folders.emplace_back(test_folder.location(), std::string("icd_manifests")); - folders.emplace_back(test_folder.location(), std::string("icd_env_vars_manifests")); - folders.emplace_back(test_folder.location(), std::string("explicit_layer_manifests")); - folders.emplace_back(test_folder.location(), std::string("explicit_env_var_layer_folder")); - folders.emplace_back(test_folder.location(), std::string("explicit_add_env_var_layer_folder")); - folders.emplace_back(test_folder.location(), std::string("implicit_layer_manifests")); - folders.emplace_back(test_folder.location(), std::string("implicit_env_var_layer_manifests")); - folders.emplace_back(test_folder.location(), std::string("implicit_add_env_var_layer_manifests")); - folders.emplace_back(test_folder.location(), std::string("override_layer_manifests")); - folders.emplace_back(test_folder.location(), std::string("app_package_manifests")); - folders.emplace_back(test_folder.location(), std::string("macos_bundle")); - folders.emplace_back(test_folder.location(), std::string("unsecured_location")); - folders.emplace_back(test_folder.location(), std::string("settings_location")); - - platform_shim->redirect_all_paths(get_folder(ManifestLocation::null).location()); - if (settings.enable_default_search_paths) { - platform_shim->set_fake_path(ManifestCategory::icd, get_folder(ManifestLocation::driver).location()); - platform_shim->set_fake_path(ManifestCategory::explicit_layer, get_folder(ManifestLocation::explicit_layer).location()); - platform_shim->set_fake_path(ManifestCategory::implicit_layer, get_folder(ManifestLocation::implicit_layer).location()); -#if COMMON_UNIX_PLATFORMS - auto home = get_env_var("HOME"); - auto unsecured_location = get_folder(ManifestLocation::unsecured_location).location(); - platform_shim->redirect_path(home + "/.local/share/vulkan/icd.d", unsecured_location); - platform_shim->redirect_path(home + "/.local/share/vulkan/implicit_layer.d", unsecured_location); - platform_shim->redirect_path(home + "/.local/share/vulkan/explicit_layer.d", unsecured_location); + : settings(settings), platform_shim(file_system_manager, settings.log_filter) { + // Setup default path redirections + + platform_shim->set_elevated_privilege(settings.run_as_if_with_elevated_privleges); + +#if TESTING_COMMON_UNIX_PLATFORMS + if (!settings.home_env_var.empty()) env_var_home.set_new_value(settings.home_env_var); +#if !defined(__APPLE__) + if (!settings.xdg_config_home_env_var.empty()) env_var_xdg_config_home.set_new_value(settings.xdg_config_home_env_var); + if (!settings.xdg_config_dirs_env_var.empty()) env_var_xdg_config_dirs.set_new_value(settings.xdg_config_dirs_env_var); + if (!settings.xdg_data_home_env_var.empty()) env_var_xdg_data_home.set_new_value(settings.xdg_data_home_env_var); + if (!settings.xdg_data_dirs_env_var.empty()) env_var_xdg_data_dirs.set_new_value(settings.xdg_data_dirs_env_var); #endif +#endif + + const std::array, 4> secured_redirection_map = { + std::pair{"icd.d", ManifestLocation::driver}, + std::pair{"implicit_layer.d", ManifestLocation::implicit_layer}, + std::pair{"explicit_layer.d", ManifestLocation::explicit_layer}, + std::pair{"loader_settings.d", ManifestLocation::settings_location}, + }; + + const std::array, 4> unsecured_redirection_map = { + std::pair{"icd.d", ManifestLocation::unsecured_driver}, + std::pair{"implicit_layer.d", ManifestLocation::unsecured_implicit_layer}, + std::pair{"explicit_layer.d", ManifestLocation::unsecured_explicit_layer}, + std::pair{"loader_settings.d", ManifestLocation::unsecured_settings}, + }; + +#if TESTING_COMMON_UNIX_PLATFORMS && !defined(__APPLE__) + // Always are searching SYSCONFDIR on unix (but not apple, which is handled separately) + secure_manifest_base_location = SYSCONFDIR; + + for (auto const& [redirect, location] : secured_redirection_map) { + file_system_manager.add_path_redirect(secure_manifest_base_location + "/vulkan/" + redirect, location); } -#if COMMON_UNIX_PLATFORMS - if (settings.secure_loader_settings) { - platform_shim->redirect_path("/etc/vulkan/loader_settings.d", get_folder(ManifestLocation::settings_location).location()); - } else { - platform_shim->redirect_path(get_env_var("HOME") + "/.local/share/vulkan/loader_settings.d", - get_folder(ManifestLocation::settings_location).location()); + + // Determines which unsecure path should be used + if (!settings.run_as_if_with_elevated_privleges) { + if (!settings.xdg_config_home_env_var.empty()) { + auto env_var_list = split_env_var_as_list(settings.xdg_config_home_env_var); + unsecure_manifest_base_location = env_var_list.at(0); + if (!env_var_list.empty()) { + for (auto const& [redirect, location] : unsecured_redirection_map) { + file_system_manager.add_path_redirect(env_var_list.at(0) + "/vulkan/" + redirect, location); + } + } + } else if (!settings.xdg_data_home_env_var.empty()) { + auto env_var_list = split_env_var_as_list(settings.xdg_data_home_env_var); + if (!env_var_list.empty()) { + unsecure_manifest_base_location = env_var_list.at(0); + for (auto const& [redirect, location] : unsecured_redirection_map) { + file_system_manager.add_path_redirect(env_var_list.at(0) + "/vulkan/" + redirect, location); + } + } + } else { + std::string home = settings.home_env_var; + for (auto const& [redirect, location] : unsecured_redirection_map) { + unsecure_manifest_base_location = home + "/.config"; + file_system_manager.add_path_redirect(home + "/.config/vulkan/" + redirect, location); + } + } } #endif #if defined(__APPLE__) + // Since XDG env-var shouldn't ever be defined on apple, FALLBACK_DATA_DIRS takes over as the 'global' location to search, eg + // /usr/local/share + auto env_var_list = split_env_var_as_list(FALLBACK_DATA_DIRS); + assert(env_var_list.size() > 0 && "FALLBACK_DATA_DIRS was set to an empty path"); + secure_manifest_base_location = env_var_list.at(0); + for (auto const& [redirect, location] : secured_redirection_map) { + file_system_manager.add_path_redirect(secure_manifest_base_location + "/vulkan/" + redirect, location); + } + + std::string home = settings.home_env_var; + unsecure_manifest_base_location = home + "/.config"; + for (auto const& [redirect, location] : unsecured_redirection_map) { + file_system_manager.add_path_redirect(home + "/.config/vulkan/" + redirect, location); + } + // Necessary since bundles look in sub folders for manifests, not the test framework folder itself auto bundle_location = get_folder(ManifestLocation::macos_bundle).location(); - platform_shim->redirect_path(bundle_location / "vulkan/icd.d", bundle_location); - platform_shim->redirect_path(bundle_location / "vulkan/explicit_layer.d", bundle_location); - platform_shim->redirect_path(bundle_location / "vulkan/implicit_layer.d", bundle_location); + file_system_manager.add_path_redirect(bundle_location / "vulkan/icd.d", ManifestLocation::macos_bundle); + file_system_manager.add_path_redirect(bundle_location / "vulkan/explicit_layer.d", ManifestLocation::macos_bundle); + file_system_manager.add_path_redirect(bundle_location / "vulkan/implicit_layer.d", ManifestLocation::macos_bundle); #endif - // only set the settings file if there are elements in the app_specific_settings vector - if (!settings.loader_settings.app_specific_settings.empty()) { - update_loader_settings(settings.loader_settings); - } + + platform_shim->is_finished_setup = true; } FrameworkEnvironment::~FrameworkEnvironment() { @@ -588,23 +355,27 @@ FrameworkEnvironment::~FrameworkEnvironment() { TestICD& FrameworkEnvironment::add_icd(TestICDDetails icd_details) noexcept { size_t cur_icd_index = icds.size(); - fs::FolderManager* fs_ptr = &get_folder(ManifestLocation::driver); + fs::Folder* fs_ptr = &get_folder(ManifestLocation::driver); switch (icd_details.discovery_type) { case (ManifestDiscoveryType::env_var): case (ManifestDiscoveryType::add_env_var): fs_ptr = &get_folder(ManifestLocation::driver_env_var); break; +#if defined(WIN32) case (ManifestDiscoveryType::windows_app_package): fs_ptr = &get_folder(ManifestLocation::windows_app_package); break; +#endif case (ManifestDiscoveryType::override_folder): fs_ptr = &get_folder(ManifestLocation::override_layer); break; +#if defined(__APPLE__) case (ManifestDiscoveryType::macos_bundle): fs_ptr = &get_folder(ManifestLocation::macos_bundle); break; +#endif case (ManifestDiscoveryType::unsecured_generic): - fs_ptr = &get_folder(ManifestLocation::unsecured_location); + fs_ptr = &get_folder(ManifestLocation::unsecured_driver); break; case (ManifestDiscoveryType::null_dir): case (ManifestDiscoveryType::none): @@ -623,12 +394,9 @@ TestICD& FrameworkEnvironment::add_icd(TestICDDetails icd_details) noexcept { new_lib_name += icd_details.icd_manifest.lib_path.extension(); auto new_driver_location = folder.copy_file(icd_details.icd_manifest.lib_path, new_lib_name); -#if COMMON_UNIX_PLATFORMS +#if TESTING_COMMON_UNIX_PLATFORMS if (icd_details.library_path_type == LibraryPathType::default_search_paths) { - platform_shim->redirect_dlopen_name(new_lib_name, new_driver_location); - } else if (icd_details.library_path_type == LibraryPathType::relative) { - platform_shim->redirect_dlopen_name(std::filesystem::path(SYSCONFDIR) / "vulkan" / "icd.d" / "." / new_lib_name, - new_driver_location); + platform_shim->add_system_library(new_lib_name, new_driver_location); } #endif #if defined(WIN32) @@ -658,10 +426,20 @@ TestICD& FrameworkEnvironment::add_icd(TestICDDetails icd_details) noexcept { icds.back().shimmed_manifest_path = icds.back().manifest_path; switch (icd_details.discovery_type) { case (ManifestDiscoveryType::generic): - platform_shim->add_manifest(ManifestCategory::icd, icds.back().manifest_path); -#if COMMON_UNIX_PLATFORMS +#if defined(WIN32) + platform_shim->add_manifest_to_registry(ManifestCategory::icd, icds.back().manifest_path); +#elif TESTING_COMMON_UNIX_PLATFORMS icds.back().shimmed_manifest_path = - platform_shim->query_default_redirect_path(ManifestCategory::icd) / new_manifest_path; + file_system_manager.get_path_redirect_by_manifest_location(ManifestLocation::driver) / new_manifest_path; +#endif + break; + case (ManifestDiscoveryType::unsecured_generic): +#if defined(WIN32) + platform_shim->add_unsecured_manifest_to_registry(ManifestCategory::icd, icds.back().manifest_path); +#elif TESTING_COMMON_UNIX_PLATFORMS + icds.back().shimmed_manifest_path = + file_system_manager.get_path_redirect_by_manifest_location(ManifestLocation::unsecured_driver) / + new_manifest_path; #endif break; case (ManifestDiscoveryType::env_var): @@ -670,7 +448,6 @@ TestICD& FrameworkEnvironment::add_icd(TestICDDetails icd_details) noexcept { } else { env_var_vk_icd_filenames.add_to_list(folder.location() / new_manifest_path); } - platform_shim->add_known_path(folder.location()); break; case (ManifestDiscoveryType::add_env_var): if (icd_details.is_dir) { @@ -678,18 +455,17 @@ TestICD& FrameworkEnvironment::add_icd(TestICDDetails icd_details) noexcept { } else { add_env_var_vk_icd_filenames.add_to_list(folder.location() / new_manifest_path); } - platform_shim->add_known_path(folder.location()); - break; - 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; + +#if defined(WIN32) case (ManifestDiscoveryType::windows_app_package): platform_shim->set_app_package_path(folder.location()); break; - +#endif +#if defined(__APPLE__) + case (ManifestDiscoveryType::macos_bundle): // macos_bundle contents are always discoverable by the loader +#endif case (ManifestDiscoveryType::override_folder): // should be found through override layer/settings file, not 'normal' // search paths case (ManifestDiscoveryType::null_dir): @@ -720,7 +496,8 @@ void FrameworkEnvironment::add_explicit_layer(TestLayerDetails layer_details) no } void FrameworkEnvironment::add_layer_impl(TestLayerDetails layer_details, ManifestCategory category) { - fs::FolderManager* fs_ptr = &get_folder(ManifestLocation::explicit_layer); + fs::Folder* fs_ptr = &get_folder(ManifestLocation::explicit_layer); + EnvVarWrapper* env_var_to_use = nullptr; switch (layer_details.discovery_type) { case (ManifestDiscoveryType::generic): if (category == ManifestCategory::implicit_layer) fs_ptr = &get_folder(ManifestLocation::implicit_layer); @@ -728,53 +505,48 @@ void FrameworkEnvironment::add_layer_impl(TestLayerDetails layer_details, Manife case (ManifestDiscoveryType::env_var): if (category == ManifestCategory::explicit_layer) { fs_ptr = &get_folder(ManifestLocation::explicit_layer_env_var); - if (layer_details.is_dir) { - env_var_vk_layer_paths.add_to_list(fs_ptr->location()); - } else { - env_var_vk_layer_paths.add_to_list(fs_ptr->location() / layer_details.json_name); - } - } - if (category == ManifestCategory::implicit_layer) { + env_var_to_use = &env_var_vk_layer_paths; + } else if (category == ManifestCategory::implicit_layer) { fs_ptr = &get_folder(ManifestLocation::implicit_layer_env_var); - if (layer_details.is_dir) { - env_var_vk_implicit_layer_paths.add_to_list(fs_ptr->location()); - } else { - env_var_vk_implicit_layer_paths.add_to_list(fs_ptr->location() / layer_details.json_name); - } + env_var_to_use = &env_var_vk_implicit_layer_paths; + } + if (layer_details.is_dir) { + env_var_to_use->add_to_list(fs_ptr->location()); + } else { + env_var_to_use->add_to_list(fs_ptr->location() / layer_details.json_name); } - platform_shim->add_known_path(fs_ptr->location()); break; case (ManifestDiscoveryType::add_env_var): if (category == ManifestCategory::explicit_layer) { fs_ptr = &get_folder(ManifestLocation::explicit_layer_add_env_var); - if (layer_details.is_dir) { - add_env_var_vk_layer_paths.add_to_list(fs_ptr->location()); - } else { - add_env_var_vk_layer_paths.add_to_list(fs_ptr->location() / layer_details.json_name); - } - } - if (category == ManifestCategory::implicit_layer) { + env_var_to_use = &add_env_var_vk_layer_paths; + } else if (category == ManifestCategory::implicit_layer) { fs_ptr = &get_folder(ManifestLocation::implicit_layer_add_env_var); - if (layer_details.is_dir) { - add_env_var_vk_implicit_layer_paths.add_to_list(fs_ptr->location()); - } else { - add_env_var_vk_implicit_layer_paths.add_to_list(fs_ptr->location() / layer_details.json_name); - } + env_var_to_use = &add_env_var_vk_implicit_layer_paths; + } + if (layer_details.is_dir) { + env_var_to_use->add_to_list(fs_ptr->location()); + } else { + env_var_to_use->add_to_list(fs_ptr->location() / layer_details.json_name); } - platform_shim->add_known_path(fs_ptr->location()); break; case (ManifestDiscoveryType::override_folder): fs_ptr = &get_folder(ManifestLocation::override_layer); break; +#if defined(__APPLE__) case (ManifestDiscoveryType::macos_bundle): fs_ptr = &(get_folder(ManifestLocation::macos_bundle)); break; +#endif case (ManifestDiscoveryType::unsecured_generic): - fs_ptr = &(get_folder(ManifestLocation::unsecured_location)); + fs_ptr = &(get_folder(category == ManifestCategory::implicit_layer ? ManifestLocation::unsecured_implicit_layer + : ManifestLocation::unsecured_explicit_layer)); break; +#if defined(WIN32) case (ManifestDiscoveryType::windows_app_package): fs_ptr = &(get_folder(ManifestLocation::windows_app_package)); break; +#endif case (ManifestDiscoveryType::none): case (ManifestDiscoveryType::null_dir): fs_ptr = &(get_folder(ManifestLocation::null)); @@ -791,14 +563,9 @@ void FrameworkEnvironment::add_layer_impl(TestLayerDetails layer_details, Manife auto new_layer_location = folder.copy_file(layer.lib_path, new_lib_path); -#if COMMON_UNIX_PLATFORMS +#if TESTING_COMMON_UNIX_PLATFORMS if (layer_details.library_path_type == LibraryPathType::default_search_paths) { - platform_shim->redirect_dlopen_name(new_lib_path, new_layer_location); - } - if (layer_details.library_path_type == LibraryPathType::relative) { - platform_shim->redirect_dlopen_name( - std::filesystem::path(SYSCONFDIR) / "vulkan" / category_path_name(category) / "." / new_lib_path, - new_layer_location); + platform_shim->add_system_library(new_lib_path, new_layer_location); } #endif #if defined(WIN32) @@ -828,22 +595,28 @@ void FrameworkEnvironment::add_layer_impl(TestLayerDetails layer_details, Manife if (layer_details.discovery_type != ManifestDiscoveryType::none) { // Write a manifest file to a folder as long as the discovery type isn't none auto layer_manifest_loc = folder.write_manifest(layer_details.json_name, layer_details.layer_manifest.get_manifest_str()); +#if defined(WIN32) // only add the manifest to the registry if its a generic location (as if it was installed) - both system and user local if (layer_details.discovery_type == ManifestDiscoveryType::generic) { - platform_shim->add_manifest(category, layer_manifest_loc); + platform_shim->add_manifest_to_registry(category, layer_manifest_loc); } if (layer_details.discovery_type == ManifestDiscoveryType::unsecured_generic) { - platform_shim->add_unsecured_manifest(category, layer_manifest_loc); + platform_shim->add_unsecured_manifest_to_registry(category, layer_manifest_loc); } if (layer_details.discovery_type == ManifestDiscoveryType::windows_app_package) { platform_shim->set_app_package_path(folder.location()); } +#endif for (size_t i = new_layers_start; i < layers.size(); i++) { layers.at(i).manifest_path = layer_manifest_loc; layers.at(i).shimmed_manifest_path = layer_manifest_loc; -#if COMMON_UNIX_PLATFORMS +#if TESTING_COMMON_UNIX_PLATFORMS if (layer_details.discovery_type == ManifestDiscoveryType::generic) { - layers.at(i).shimmed_manifest_path = platform_shim->query_default_redirect_path(category) / layer_details.json_name; + layers.at(i).shimmed_manifest_path = + ((category == ManifestCategory::implicit_layer) + ? file_system_manager.get_path_redirect_by_manifest_location(ManifestLocation::implicit_layer) + : file_system_manager.get_path_redirect_by_manifest_location(ManifestLocation::explicit_layer)) / + layer_details.json_name; } #endif } @@ -944,36 +717,41 @@ std::string get_loader_settings_file_contents(const LoaderSettings& loader_setti writer.EndObject(); return writer.output; } -void FrameworkEnvironment::write_settings_file(std::string const& file_contents) { - auto out_path = get_folder(ManifestLocation::settings_location).write_manifest("vk_loader_settings.json", file_contents); +void FrameworkEnvironment::write_settings_file(std::string const& file_contents, bool write_to_secure_location) { + auto location = write_to_secure_location ? ManifestLocation::settings_location : ManifestLocation::unsecured_settings; + auto out_path = get_folder(location).write_manifest("vk_loader_settings.json", file_contents); #if defined(WIN32) - platform_shim->hkey_current_user_settings.clear(); - platform_shim->hkey_local_machine_settings.clear(); + if (write_to_secure_location) { + platform_shim->hkey_local_machine_settings.clear(); + platform_shim->add_manifest_to_registry(ManifestCategory::settings, out_path); + } else { + platform_shim->hkey_current_user_settings.clear(); + platform_shim->add_unsecured_manifest_to_registry(ManifestCategory::settings, out_path); + } #endif - if (settings.secure_loader_settings) - platform_shim->add_manifest(ManifestCategory::settings, out_path); - else - platform_shim->add_unsecured_manifest(ManifestCategory::settings, out_path); } -void FrameworkEnvironment::update_loader_settings(const LoaderSettings& settings) noexcept { - write_settings_file(get_loader_settings_file_contents(settings)); +void FrameworkEnvironment::update_loader_settings(const LoaderSettings& settings, bool write_to_secure_location) noexcept { + write_settings_file(get_loader_settings_file_contents(settings), write_to_secure_location); } void FrameworkEnvironment::remove_loader_settings() { get_folder(ManifestLocation::settings_location).remove("vk_loader_settings.json"); } +void FrameworkEnvironment::write_file_from_string(std::string const& source_string, ManifestCategory category, + ManifestLocation location, std::string const& file_name) { + auto out_path = get_folder(location).write_manifest(file_name, source_string); + +#if defined(WIN32) + // Only writes to the hkey_local_machine registries, doesn't support hkey_current_user registries + platform_shim->add_manifest_to_registry(category, out_path); +#endif +} void FrameworkEnvironment::write_file_from_source(const char* source_file, ManifestCategory category, ManifestLocation location, std::string const& file_name) { std::fstream file{source_file, std::ios_base::in}; ASSERT_TRUE(file.is_open()); std::stringstream file_stream; file_stream << file.rdbuf(); - - auto out_path = get_folder(location).write_manifest(file_name, file_stream.str()); - - if (settings.secure_loader_settings) - platform_shim->add_manifest(category, out_path); - else - platform_shim->add_unsecured_manifest(category, out_path); + write_file_from_string(file_stream.str(), category, location, file_name); } TestICD& FrameworkEnvironment::get_test_icd(size_t index) noexcept { return icds[index].get_test_icd(); } @@ -998,19 +776,24 @@ std::filesystem::path FrameworkEnvironment::get_shimmed_layer_manifest_path(size return layers[index].get_shimmed_manifest_path(); } -fs::FolderManager& FrameworkEnvironment::get_folder(ManifestLocation location) noexcept { - // index it directly using the enum location since they will always be in that order - return folders.at(static_cast(location)); +fs::Folder& FrameworkEnvironment::get_folder(ManifestLocation location) noexcept { + return file_system_manager.get_folder(location); } -fs::FolderManager const& FrameworkEnvironment::get_folder(ManifestLocation location) const noexcept { - return folders.at(static_cast(location)); +fs::Folder const& FrameworkEnvironment::get_folder(ManifestLocation location) const noexcept { + return file_system_manager.get_folder(location); } #if defined(__APPLE__) void FrameworkEnvironment::setup_macos_bundle() noexcept { - platform_shim->bundle_contents = get_folder(ManifestLocation::macos_bundle).location(); + platform_shim->bundle_contents = file_system_manager.get_folder(ManifestLocation::macos_bundle).location(); } #endif +void FrameworkEnvironment::add_symlink(ManifestLocation location, std::filesystem::path const& target, + std::filesystem::path const& link_name) { + auto symlinked_path = get_folder(location).add_symlink(target, link_name); + file_system_manager.add_path_redirect(symlinked_path, location); +} + std::vector FrameworkEnvironment::GetInstanceExtensions(uint32_t expected_count, const char* layer_name) { uint32_t count = 0; VkResult res = vulkan_functions.vkEnumerateInstanceExtensionProperties(layer_name, &count, nullptr); diff --git a/tests/framework/test_environment.h b/tests/framework/test_environment.h index a7b9f15b2..0a1f89e0d 100644 --- a/tests/framework/test_environment.h +++ b/tests/framework/test_environment.h @@ -46,10 +46,18 @@ #endif #endif -// Use the NDK's header on Android #include "gtest/gtest.h" -#include "test_util.h" +#include "util/builder_defines.h" +#include "util/dispatch_table.h" +#include "util/dynamic_library_wrapper.h" +#include "util/env_var_wrapper.h" +#include "util/equality_helpers.h" +#include "util/folder_manager.h" +#include "util/functions.h" +#include "util/manifest_builders.h" +#include "util/test_defines.h" +#include "util/vulkan_object_wrappers.h" #include "shim/shim.h" @@ -57,14 +65,7 @@ #include "layer/test_layer.h" -#include FRAMEWORK_CONFIG_HEADER - -// Useful defines -#if COMMON_UNIX_PLATFORMS -#define HOME_DIR "/home/fake_home" -#define USER_LOCAL_SHARE_DIR HOME_DIR "/.local/share" -#define ETC_DIR "/etc" -#endif +#include "generated/vk_result_to_string_helper.h" // handle checking template @@ -111,152 +112,26 @@ void handle_assert_equal(size_t count, T left[], T right[]) { } } -// VulkanFunctions - loads vulkan functions for tests to use - -struct VulkanFunctions { -#if !defined(APPLE_STATIC_LOADER) - LibraryWrapper loader; -#endif - // Pre-Instance - PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = nullptr; - PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties = nullptr; - PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties = nullptr; - PFN_vkEnumerateInstanceVersion vkEnumerateInstanceVersion = nullptr; - PFN_vkCreateInstance vkCreateInstance = nullptr; - - // Instance - PFN_vkDestroyInstance vkDestroyInstance = nullptr; - PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices = nullptr; - PFN_vkEnumeratePhysicalDeviceGroups vkEnumeratePhysicalDeviceGroups = nullptr; - PFN_vkGetPhysicalDeviceFeatures vkGetPhysicalDeviceFeatures = nullptr; - PFN_vkGetPhysicalDeviceFeatures2 vkGetPhysicalDeviceFeatures2 = nullptr; - PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties = nullptr; - PFN_vkGetPhysicalDeviceFormatProperties2 vkGetPhysicalDeviceFormatProperties2 = nullptr; - PFN_vkGetPhysicalDeviceImageFormatProperties vkGetPhysicalDeviceImageFormatProperties = nullptr; - PFN_vkGetPhysicalDeviceImageFormatProperties2 vkGetPhysicalDeviceImageFormatProperties2 = nullptr; - PFN_vkGetPhysicalDeviceSparseImageFormatProperties vkGetPhysicalDeviceSparseImageFormatProperties = nullptr; - PFN_vkGetPhysicalDeviceSparseImageFormatProperties2 vkGetPhysicalDeviceSparseImageFormatProperties2 = nullptr; - PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties = nullptr; - PFN_vkGetPhysicalDeviceProperties2 vkGetPhysicalDeviceProperties2 = nullptr; - PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties = nullptr; - PFN_vkGetPhysicalDeviceQueueFamilyProperties2 vkGetPhysicalDeviceQueueFamilyProperties2 = nullptr; - PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties = nullptr; - PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2 = nullptr; - PFN_vkGetPhysicalDeviceSurfaceSupportKHR vkGetPhysicalDeviceSurfaceSupportKHR = nullptr; - PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR = nullptr; - PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR = nullptr; - PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR = nullptr; - PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties = nullptr; - PFN_vkEnumerateDeviceLayerProperties vkEnumerateDeviceLayerProperties = nullptr; - PFN_vkGetPhysicalDeviceExternalBufferProperties vkGetPhysicalDeviceExternalBufferProperties = nullptr; - PFN_vkGetPhysicalDeviceExternalFenceProperties vkGetPhysicalDeviceExternalFenceProperties = nullptr; - PFN_vkGetPhysicalDeviceExternalSemaphoreProperties vkGetPhysicalDeviceExternalSemaphoreProperties = nullptr; - - PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr = nullptr; - PFN_vkCreateDevice vkCreateDevice = nullptr; - - // WSI - PFN_vkCreateHeadlessSurfaceEXT vkCreateHeadlessSurfaceEXT = nullptr; - PFN_vkCreateDisplayPlaneSurfaceKHR vkCreateDisplayPlaneSurfaceKHR = nullptr; - PFN_vkGetPhysicalDeviceDisplayPropertiesKHR vkGetPhysicalDeviceDisplayPropertiesKHR = nullptr; - PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR vkGetPhysicalDeviceDisplayPlanePropertiesKHR = nullptr; - PFN_vkGetDisplayPlaneSupportedDisplaysKHR vkGetDisplayPlaneSupportedDisplaysKHR = nullptr; - PFN_vkGetDisplayModePropertiesKHR vkGetDisplayModePropertiesKHR = nullptr; - PFN_vkCreateDisplayModeKHR vkCreateDisplayModeKHR = nullptr; - PFN_vkGetDisplayPlaneCapabilitiesKHR vkGetDisplayPlaneCapabilitiesKHR = nullptr; - PFN_vkGetPhysicalDevicePresentRectanglesKHR vkGetPhysicalDevicePresentRectanglesKHR = nullptr; - PFN_vkGetPhysicalDeviceDisplayProperties2KHR vkGetPhysicalDeviceDisplayProperties2KHR = nullptr; - PFN_vkGetPhysicalDeviceDisplayPlaneProperties2KHR vkGetPhysicalDeviceDisplayPlaneProperties2KHR = nullptr; - PFN_vkGetDisplayModeProperties2KHR vkGetDisplayModeProperties2KHR = nullptr; - PFN_vkGetDisplayPlaneCapabilities2KHR vkGetDisplayPlaneCapabilities2KHR = nullptr; - PFN_vkGetPhysicalDeviceSurfaceCapabilities2KHR vkGetPhysicalDeviceSurfaceCapabilities2KHR = nullptr; - PFN_vkGetPhysicalDeviceSurfaceFormats2KHR vkGetPhysicalDeviceSurfaceFormats2KHR = nullptr; - -#if defined(VK_USE_PLATFORM_ANDROID_KHR) - PFN_vkCreateAndroidSurfaceKHR vkCreateAndroidSurfaceKHR = nullptr; -#endif // VK_USE_PLATFORM_ANDROID_KHR -#if defined(VK_USE_PLATFORM_DIRECTFB_EXT) - PFN_vkCreateDirectFBSurfaceEXT vkCreateDirectFBSurfaceEXT = nullptr; - PFN_vkGetPhysicalDeviceDirectFBPresentationSupportEXT vkGetPhysicalDeviceDirectFBPresentationSupportEXT = nullptr; -#endif // VK_USE_PLATFORM_DIRECTFB_EXT -#if defined(VK_USE_PLATFORM_FUCHSIA) - PFN_vkCreateImagePipeSurfaceFUCHSIA vkCreateImagePipeSurfaceFUCHSIA = nullptr; -#endif // VK_USE_PLATFORM_FUCHSIA -#if defined(VK_USE_PLATFORM_GGP) - PFN_vkCreateStreamDescriptorSurfaceGGP vkCreateStreamDescriptorSurfaceGGP = nullptr; -#endif // VK_USE_PLATFORM_GGP -#if defined(VK_USE_PLATFORM_IOS_MVK) - PFN_vkCreateIOSSurfaceMVK vkCreateIOSSurfaceMVK = nullptr; -#endif // VK_USE_PLATFORM_IOS_MVK -#if defined(VK_USE_PLATFORM_MACOS_MVK) - PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK = nullptr; -#endif // VK_USE_PLATFORM_MACOS_MVK -#if defined(VK_USE_PLATFORM_METAL_EXT) - PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT = nullptr; -#endif // VK_USE_PLATFORM_METAL_EXT -#if defined(VK_USE_PLATFORM_SCREEN_QNX) - PFN_vkCreateScreenSurfaceQNX vkCreateScreenSurfaceQNX = nullptr; - PFN_vkGetPhysicalDeviceScreenPresentationSupportQNX vkGetPhysicalDeviceScreenPresentationSupportQNX = nullptr; -#endif // VK_USE_PLATFORM_SCREEN_QNX -#if defined(VK_USE_PLATFORM_WAYLAND_KHR) - PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR = nullptr; - PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR vkGetPhysicalDeviceWaylandPresentationSupportKHR = nullptr; -#endif // VK_USE_PLATFORM_WAYLAND_KHR -#if defined(VK_USE_PLATFORM_XCB_KHR) - PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR = nullptr; - PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR vkGetPhysicalDeviceXcbPresentationSupportKHR = nullptr; -#endif // VK_USE_PLATFORM_XCB_KHR -#if defined(VK_USE_PLATFORM_XLIB_KHR) - PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR = nullptr; - PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR vkGetPhysicalDeviceXlibPresentationSupportKHR = nullptr; -#endif // VK_USE_PLATFORM_XLIB_KHR -#if defined(VK_USE_PLATFORM_WIN32_KHR) - PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR = nullptr; - PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR vkGetPhysicalDeviceWin32PresentationSupportKHR = nullptr; -#endif // VK_USE_PLATFORM_WIN32_KHR - PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR = nullptr; - - // instance extensions functions (can only be loaded with a valid instance) - PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT = nullptr; // Null unless the extension is enabled - PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT = nullptr; // Null unless the extension is enabled - PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT = nullptr; // Null unless the extension is enabled - PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallbackEXT = nullptr; // Null unless the extension is enabled - - // device functions - PFN_vkDestroyDevice vkDestroyDevice = nullptr; - PFN_vkGetDeviceQueue vkGetDeviceQueue = nullptr; - - VulkanFunctions(); - - void load_instance_functions(VkInstance instance); - - FromVoidStarFunc load(VkInstance inst, const char* func_name) const { - return FromVoidStarFunc(vkGetInstanceProcAddr(inst, func_name)); +template +bool check_permutation(std::initializer_list expected, std::array const& returned) { + if (expected.size() != returned.size()) return false; + for (uint32_t i = 0; i < expected.size(); i++) { + auto found = std::find_if(std::begin(returned), std::end(returned), + [&](T elem) { return string_eq(*(expected.begin() + i), elem.layerName); }); + if (found == std::end(returned)) return false; } - - FromVoidStarFunc load(VkDevice device, const char* func_name) const { - return FromVoidStarFunc(vkGetDeviceProcAddr(device, func_name)); - } -}; - -struct DeviceFunctions { - PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr = nullptr; - PFN_vkDestroyDevice vkDestroyDevice = nullptr; - PFN_vkGetDeviceQueue vkGetDeviceQueue = nullptr; - PFN_vkCreateCommandPool vkCreateCommandPool = nullptr; - PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers = nullptr; - PFN_vkDestroyCommandPool vkDestroyCommandPool = nullptr; - PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR = nullptr; - PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR = nullptr; - PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR = nullptr; - - DeviceFunctions() = default; - DeviceFunctions(const VulkanFunctions& vulkan_functions, VkDevice device); - - FromVoidStarFunc load(VkDevice device, const char* func_name) const { - return FromVoidStarFunc(vkGetDeviceProcAddr(device, func_name)); + return true; +} +template +bool check_permutation(std::initializer_list expected, std::vector const& returned) { + if (expected.size() != returned.size()) return false; + for (uint32_t i = 0; i < expected.size(); i++) { + auto found = std::find_if(std::begin(returned), std::end(returned), + [&](T elem) { return string_eq(*(expected.begin() + i), elem.layerName); }); + if (found == std::end(returned)) return false; } -}; + return true; +} // InstWrapper & DeviceWrapper - used to make creating instances & devices easier when writing tests struct InstWrapper { @@ -463,50 +338,6 @@ VkResult CreateDebugUtilsMessenger(DebugUtilsWrapper& debug_utils); void FillDebugUtilsCreateDetails(InstanceCreateInfo& create_info, DebugUtilsLogger& logger); void FillDebugUtilsCreateDetails(InstanceCreateInfo& create_info, DebugUtilsWrapper& wrapper); -namespace fs { - -int create_folder(std::filesystem::path const& path); -int delete_folder(std::filesystem::path const& folder); - -class FolderManager { - public: - explicit FolderManager(std::filesystem::path root_path, std::string name) noexcept; - ~FolderManager() noexcept; - FolderManager(FolderManager const&) = delete; - FolderManager& operator=(FolderManager const&) = delete; - FolderManager(FolderManager&& other) noexcept; - FolderManager& operator=(FolderManager&& other) noexcept; - - // Add a manifest to the folder - std::filesystem::path write_manifest(std::filesystem::path const& name, std::string const& contents); - - // close file handle, delete file, remove `name` from managed file list. - void remove(std::filesystem::path const& name); - - // Remove all contents in the path - void clear() const noexcept; - - // copy file into this folder with name `new_name`. Returns the full path of the file that was copied - std::filesystem::path copy_file(std::filesystem::path const& file, std::filesystem::path const& new_name); - - // location of the managed folder - std::filesystem::path location() const { return folder; } - - std::vector get_files() const; - - // Create a symlink in this folder to target with the filename set to link_name - std::filesystem::path add_symlink(std::filesystem::path const& target, std::filesystem::path const& link_name); - - private: - bool actually_created = false; - std::filesystem::path folder; - std::vector added_files; - - void insert_file_to_tracking(std::filesystem::path const& name); - void check_if_first_use(); -}; -} // namespace fs - struct LoaderSettingsLayerConfiguration { BUILDER_VALUE(std::string, name) BUILDER_VALUE(std::filesystem::path, path) @@ -530,6 +361,8 @@ struct LoaderSettingsDriverConfiguration { BUILDER_VALUE(std::filesystem::path, path) }; +using VulkanUUID = std::array; + struct LoaderSettingsDeviceConfiguration { BUILDER_VALUE(VulkanUUID, deviceUUID) BUILDER_VALUE(std::string, deviceName) @@ -558,8 +391,7 @@ struct LoaderSettings { struct FrameworkEnvironment; // forward declaration struct PlatformShimWrapper { - PlatformShimWrapper(GetFoldersFunc get_folders_by_name_function, const char* log_filter) noexcept; - ~PlatformShimWrapper() noexcept; + PlatformShimWrapper(fs::FileSystemManager& file_system_manager, const char* log_filter) noexcept; PlatformShimWrapper(PlatformShimWrapper const&) = delete; PlatformShimWrapper& operator=(PlatformShimWrapper const&) = delete; @@ -608,25 +440,6 @@ struct TestLayerHandle { shimmed_manifest_path; // path to where the loader will find the manifest file (eg /usr/local/share/vulkan/<...>) }; -// Controls whether to create a manifest and where to put it -enum class ManifestDiscoveryType { - generic, // put the manifest in the regular locations - unsecured_generic, // put the manifest in a user folder rather than system - none, // Do not write the manifest anywhere (for Direct Driver Loading) - null_dir, // put the manifest in the 'null_dir' which the loader does not search in (D3DKMT for instance) - env_var, // use the corresponding env-var for it - add_env_var, // use the corresponding add-env-var for it - override_folder, // add to a special folder for the override layer to use - windows_app_package, // let the app package search find it - macos_bundle, // place it in a location only accessible to macos bundles -}; - -enum class LibraryPathType { - absolute, // default for testing - the exact path of the binary - relative, // Relative to the manifest file - default_search_paths, // Dont add any path information to the library_path - force the use of the default search paths -}; - struct TestICDDetails { TestICDDetails(ManifestICD icd_manifest) noexcept : icd_manifest(icd_manifest) {} TestICDDetails(std::filesystem::path icd_binary_path, uint32_t api_version = VK_API_VERSION_1_0) noexcept { @@ -655,30 +468,19 @@ struct TestLayerDetails { BUILDER_VALUE_WITH_DEFAULT(LibraryPathType, library_path_type, LibraryPathType::absolute); }; -// Locations manifests can go in the test framework -// If this enum is added to - the contructor of FrameworkEnvironment also needs to be updated with the new enum value -enum class ManifestLocation { - null = 0, - driver = 1, - driver_env_var = 2, - explicit_layer = 3, - explicit_layer_env_var = 4, - explicit_layer_add_env_var = 5, - implicit_layer = 6, - implicit_layer_env_var = 7, - implicit_layer_add_env_var = 8, - override_layer = 9, - windows_app_package = 10, - macos_bundle = 11, - unsecured_location = 12, - settings_location = 13, -}; - struct FrameworkSettings { BUILDER_VALUE_WITH_DEFAULT(const char*, log_filter, "all"); - BUILDER_VALUE_WITH_DEFAULT(bool, enable_default_search_paths, true); - BUILDER_VALUE(LoaderSettings, loader_settings); - BUILDER_VALUE(bool, secure_loader_settings); + BUILDER_VALUE_WITH_DEFAULT(bool, run_as_if_with_elevated_privleges, false); + +#if TESTING_COMMON_UNIX_PLATFORMS + BUILDER_VALUE_WITH_DEFAULT(std::string, home_env_var, "/home/fake_home"); +#if !defined(__APPLE__) + BUILDER_VALUE(std::string, xdg_config_home_env_var); + BUILDER_VALUE(std::string, xdg_config_dirs_env_var); + BUILDER_VALUE(std::string, xdg_data_home_env_var); + BUILDER_VALUE(std::string, xdg_data_dirs_env_var); +#endif +#endif }; struct FrameworkEnvironment { @@ -697,12 +499,20 @@ struct FrameworkEnvironment { void add_fake_implicit_layer(ManifestLayer layer_manifest, const std::string& json_name) noexcept; void add_fake_explicit_layer(ManifestLayer layer_manifest, const std::string& json_name) noexcept; - // resets the current settings with the values contained in loader_settings - void write_settings_file(std::string const& file_contents); - // apply any changes made to FrameworkEnvironment's loader_settings member - void update_loader_settings(const LoaderSettings& loader_settings) noexcept; + // Resets the current settings with the values contained in loader_settings. + // Write_to_secure_location determines whether to write to the secure or unsecure settings folder. + void write_settings_file(std::string const& file_contents, bool write_to_secure_location); + + // Apply any changes made to FrameworkEnvironment's loader_settings member. + // By default writes to the secure settings location + void update_loader_settings(const LoaderSettings& loader_settings, bool write_to_secure_location = true) noexcept; + void remove_loader_settings(); + // Creates a file called `file_name` for the given `category` in the given `location` with `source_string` as the contents + void write_file_from_string(std::string const& source_string, ManifestCategory category, ManifestLocation location, + std::string const& file_name); + // Creates a file called `file_name` for the given `category` in the given `location` with contents copied from `source_file` void write_file_from_source(const char* source_file, ManifestCategory category, ManifestLocation location, std::string const& file_name); @@ -718,16 +528,18 @@ struct FrameworkEnvironment { std::filesystem::path get_layer_manifest_path(size_t index = 0) noexcept; std::filesystem::path get_shimmed_layer_manifest_path(size_t index = 0) noexcept; - fs::FolderManager& get_folder(ManifestLocation location) noexcept; - fs::FolderManager const& get_folder(ManifestLocation location) const noexcept; + fs::Folder& get_folder(ManifestLocation location) noexcept; + fs::Folder const& get_folder(ManifestLocation location) const noexcept; #if defined(__APPLE__) // Set the path of the app bundle to the appropriate test framework bundle void setup_macos_bundle() noexcept; #endif + void add_symlink(ManifestLocation location, std::filesystem::path const& target, std::filesystem::path const& link_name); + FrameworkSettings settings; - fs::FolderManager test_folder; + fs::FileSystemManager file_system_manager; // Query the global extensions // Optional: use layer_name to query the extensions of a specific layer @@ -736,7 +548,6 @@ struct FrameworkEnvironment { std::vector GetLayerProperties(uint32_t count); PlatformShimWrapper platform_shim; - std::vector folders; std::vector icds; std::vector layers; @@ -751,6 +562,18 @@ struct FrameworkEnvironment { EnvVarWrapper env_var_vk_implicit_layer_paths{"VK_IMPLICIT_LAYER_PATH"}; EnvVarWrapper add_env_var_vk_implicit_layer_paths{"VK_ADD_IMPLICIT_LAYER_PATH"}; +#if TESTING_COMMON_UNIX_PLATFORMS + EnvVarWrapper env_var_home{"HOME", "/home/fake_home"}; +#if !defined(__APPLE__) + EnvVarWrapper env_var_xdg_config_home{"XDG_CONFIG_HOME"}; + EnvVarWrapper env_var_xdg_config_dirs{"XDG_CONFIG_DIRS"}; + EnvVarWrapper env_var_xdg_data_home{"XDG_DATA_HOME"}; + EnvVarWrapper env_var_xdg_data_dirs{"XDG_DATA_DIRS"}; +#endif + std::string secure_manifest_base_location; + std::string unsecure_manifest_base_location; +#endif + LoaderSettings loader_settings; // the current settings written to disk private: void add_layer_impl(TestLayerDetails layer_details, ManifestCategory category); diff --git a/tests/framework/test_util.cpp b/tests/framework/test_util.cpp deleted file mode 100644 index 9e45b4143..000000000 --- a/tests/framework/test_util.cpp +++ /dev/null @@ -1,375 +0,0 @@ -/* - * Copyright (c) 2021-2023 The Khronos Group Inc. - * Copyright (c) 2021-2023 Valve Corporation - * Copyright (c) 2021-2023 LunarG, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and/or associated documentation files (the "Materials"), to - * deal in the Materials without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Materials, and to permit persons to whom the Materials are - * furnished to do so, subject to the following conditions: - * - * The above copyright notice(s) and this permission notice shall be included in - * all copies or substantial portions of the Materials. - * - * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE - * USE OR OTHER DEALINGS IN THE MATERIALS. - * - * Author: Charles Giessen - */ - -#include "test_util.h" - -#include - -#if defined(WIN32) -#include -#include -const char* win_api_error_str(LSTATUS status) { - if (status == ERROR_INVALID_FUNCTION) return "ERROR_INVALID_FUNCTION"; - if (status == ERROR_FILE_NOT_FOUND) return "ERROR_FILE_NOT_FOUND"; - if (status == ERROR_PATH_NOT_FOUND) return "ERROR_PATH_NOT_FOUND"; - if (status == ERROR_TOO_MANY_OPEN_FILES) return "ERROR_TOO_MANY_OPEN_FILES"; - if (status == ERROR_ACCESS_DENIED) return "ERROR_ACCESS_DENIED"; - if (status == ERROR_INVALID_HANDLE) return "ERROR_INVALID_HANDLE"; - if (status == ERROR_ENVVAR_NOT_FOUND) return "ERROR_ENVVAR_NOT_FOUND"; - if (status == ERROR_SETENV_FAILED) return "ERROR_SETENV_FAILED"; - return "UNKNOWN ERROR"; -} - -void print_error_message(LSTATUS status, const char* function_name, std::string optional_message) { - LPVOID lpMsgBuf; - DWORD dw = GetLastError(); - - FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, dw, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast(&lpMsgBuf), 0, nullptr); - - std::cerr << function_name << " failed with " << win_api_error_str(status) << ": " - << std::string(reinterpret_cast(lpMsgBuf)); - if (optional_message != "") { - std::cerr << " | " << optional_message; - } - std::cerr << "\n"; - LocalFree(lpMsgBuf); -} - -void EnvVarWrapper::set_env_var() { - BOOL ret = SetEnvironmentVariableW(widen(name).c_str(), widen(cur_value).c_str()); - if (ret == 0) { - print_error_message(ERROR_SETENV_FAILED, "SetEnvironmentVariableW"); - } -} -void EnvVarWrapper::remove_env_var() const { SetEnvironmentVariableW(widen(name).c_str(), nullptr); } -std::string get_env_var(std::string const& name, bool report_failure) { - std::wstring name_utf16 = widen(name); - DWORD value_size = GetEnvironmentVariableW(name_utf16.c_str(), nullptr, 0); - if (0 == value_size) { - if (report_failure) print_error_message(ERROR_ENVVAR_NOT_FOUND, "GetEnvironmentVariableW"); - return {}; - } - std::wstring value(value_size, L'\0'); - if (GetEnvironmentVariableW(name_utf16.c_str(), &value[0], value_size) != value_size - 1) { - return {}; - } - return narrow(value); -} -#elif COMMON_UNIX_PLATFORMS - -void EnvVarWrapper::set_env_var() { setenv(name.c_str(), cur_value.c_str(), 1); } -void EnvVarWrapper::remove_env_var() const { unsetenv(name.c_str()); } -std::string get_env_var(std::string const& name, bool report_failure) { - char* ret = getenv(name.c_str()); - if (ret == nullptr) { - if (report_failure) std::cerr << "Failed to get environment variable:" << name << "\n"; - return std::string(); - } - return ret; -} -#endif - -template -void print_object_of_t(JsonWriter& writer, const char* object_name, std::vector const& vec) { - if (vec.size() == 0) return; - writer.StartKeyedObject(object_name); - for (auto& element : vec) { - element.get_manifest_str(writer); - } - writer.EndObject(); -} - -template -void print_array_of_t(JsonWriter& writer, const char* object_name, std::vector const& vec) { - if (vec.size() == 0) return; - writer.StartKeyedArray(object_name); - for (auto& element : vec) { - element.get_manifest_str(writer); - } - writer.EndArray(); -} -void print_vector_of_strings(JsonWriter& writer, const char* object_name, std::vector const& strings) { - if (strings.size() == 0) return; - writer.StartKeyedArray(object_name); - for (auto const& str : strings) { - writer.AddString(std::filesystem::path(str).native()); - } - writer.EndArray(); -} -void print_vector_of_strings(JsonWriter& writer, const char* object_name, std::vector const& paths) { - if (paths.size() == 0) return; - writer.StartKeyedArray(object_name); - for (auto const& path : paths) { - writer.AddString(path.native()); - } - writer.EndArray(); -} - -std::string to_text(bool b) { return b ? std::string("true") : std::string("false"); } - -std::string ManifestICD::get_manifest_str() const { - JsonWriter writer; - writer.StartObject(); - writer.AddKeyedString("file_format_version", file_format_version.get_version_str()); - writer.StartKeyedObject("ICD"); - writer.AddKeyedString("library_path", lib_path.native()); - writer.AddKeyedString("api_version", version_to_string(api_version)); - writer.AddKeyedBool("is_portability_driver", is_portability_driver); - if (!library_arch.empty()) writer.AddKeyedString("library_arch", library_arch); - writer.EndObject(); - writer.EndObject(); - return writer.output; -} - -void ManifestLayer::LayerDescription::Extension::get_manifest_str(JsonWriter& writer) const { - writer.StartObject(); - writer.AddKeyedString("name", name); - writer.AddKeyedString("spec_version", std::to_string(spec_version)); - writer.AddKeyedString("spec_version", std::to_string(spec_version)); - print_vector_of_strings(writer, "entrypoints", entrypoints); - writer.EndObject(); -} - -void ManifestLayer::LayerDescription::get_manifest_str(JsonWriter& writer) const { - writer.AddKeyedString("name", name); - writer.AddKeyedString("type", get_type_str(type)); - if (!lib_path.empty()) { - writer.AddKeyedString("library_path", lib_path.native()); - } - writer.AddKeyedString("api_version", version_to_string(api_version)); - writer.AddKeyedString("implementation_version", std::to_string(implementation_version)); - writer.AddKeyedString("description", description); - print_object_of_t(writer, "functions", functions); - print_array_of_t(writer, "instance_extensions", instance_extensions); - print_array_of_t(writer, "device_extensions", device_extensions); - if (!enable_environment.empty()) { - writer.StartKeyedObject("enable_environment"); - writer.AddKeyedString(enable_environment, "1"); - writer.EndObject(); - } - if (!disable_environment.empty()) { - writer.StartKeyedObject("disable_environment"); - writer.AddKeyedString(disable_environment, "1"); - writer.EndObject(); - } - print_vector_of_strings(writer, "component_layers", component_layers); - print_vector_of_strings(writer, "blacklisted_layers", blacklisted_layers); - print_vector_of_strings(writer, "override_paths", override_paths); - print_vector_of_strings(writer, "app_keys", app_keys); - print_object_of_t(writer, "pre_instance_functions", pre_instance_functions); - if (!library_arch.empty()) { - writer.AddKeyedString("library_arch", library_arch); - } -} - -VkLayerProperties ManifestLayer::LayerDescription::get_layer_properties() const { - VkLayerProperties properties{}; - copy_string_to_char_array(name, properties.layerName, VK_MAX_EXTENSION_NAME_SIZE); - copy_string_to_char_array(description, properties.description, VK_MAX_EXTENSION_NAME_SIZE); - properties.implementationVersion = implementation_version; - properties.specVersion = api_version; - return properties; -} - -std::string ManifestLayer::get_manifest_str() const { - JsonWriter writer; - writer.StartObject(); - writer.AddKeyedString("file_format_version", file_format_version.get_version_str()); - if (layers.size() == 1) { - writer.StartKeyedObject("layer"); - layers.at(0).get_manifest_str(writer); - writer.EndObject(); - } else { - writer.StartKeyedArray("layers"); - for (size_t i = 0; i < layers.size(); i++) { - writer.StartObject(); - layers.at(i).get_manifest_str(writer); - writer.EndObject(); - } - writer.EndArray(); - } - writer.EndObject(); - return writer.output; -} - -const char* get_platform_wsi_extension([[maybe_unused]] const char* api_selection) { -#if defined(VK_USE_PLATFORM_ANDROID_KHR) - return "VK_KHR_android_surface"; -#elif defined(VK_USE_PLATFORM_DIRECTFB_EXT) - return "VK_EXT_directfb_surface"; -#elif defined(VK_USE_PLATFORM_FUCHSIA) - return "VK_FUCHSIA_imagepipe_surface"; -#elif defined(VK_USE_PLATFORM_GGP) - return "VK_GGP_stream_descriptor_surface"; -#elif defined(VK_USE_PLATFORM_IOS_MVK) - return "VK_MVK_ios_surface"; -#elif defined(VK_USE_PLATFORM_MACOS_MVK) || defined(VK_USE_PLATFORM_METAL_EXT) -#if defined(VK_USE_PLATFORM_MACOS_MVK) - if (string_eq(api_selection, "VK_USE_PLATFORM_MACOS_MVK")) return "VK_MVK_macos_surface"; -#endif -#if defined(VK_USE_PLATFORM_METAL_EXT) - if (string_eq(api_selection, "VK_USE_PLATFORM_METAL_EXT")) return "VK_EXT_metal_surface"; - return "VK_EXT_metal_surface"; -#endif -#elif defined(VK_USE_PLATFORM_SCREEN_QNX) - return "VK_QNX_screen_surface"; -#elif defined(VK_USE_PLATFORM_VI_NN) - return "VK_NN_vi_surface"; -#elif defined(VK_USE_PLATFORM_XCB_KHR) || defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_WAYLAND_KHR) -#if defined(VK_USE_PLATFORM_XCB_KHR) - if (string_eq(api_selection, "VK_USE_PLATFORM_XCB_KHR")) return "VK_KHR_xcb_surface"; -#endif -#if defined(VK_USE_PLATFORM_XLIB_KHR) - if (string_eq(api_selection, "VK_USE_PLATFORM_XLIB_KHR")) return "VK_KHR_xlib_surface"; -#endif -#if defined(VK_USE_PLATFORM_WAYLAND_KHR) - if (string_eq(api_selection, "VK_USE_PLATFORM_WAYLAND_KHR")) return "VK_KHR_wayland_surface"; -#endif -#if defined(VK_USE_PLATFORM_XCB_KHR) - return "VK_KHR_xcb_surface"; -#endif -#elif defined(VK_USE_PLATFORM_WIN32_KHR) - return "VK_KHR_win32_surface"; -#else - return "VK_KHR_display"; -#endif -} - -bool string_eq(const char* a, const char* b) noexcept { return a && b && strcmp(a, b) == 0; } -bool string_eq(const char* a, const char* b, size_t len) noexcept { return a && b && strncmp(a, b, len) == 0; } - -InstanceCreateInfo::InstanceCreateInfo() { - instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - application_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; -} - -VkInstanceCreateInfo* InstanceCreateInfo::get() noexcept { - if (fill_in_application_info) { - application_info.pApplicationName = app_name.c_str(); - application_info.pEngineName = engine_name.c_str(); - application_info.applicationVersion = app_version; - application_info.engineVersion = engine_version; - application_info.apiVersion = api_version; - instance_info.pApplicationInfo = &application_info; - } - instance_info.flags = flags; - instance_info.enabledLayerCount = static_cast(enabled_layers.size()); - instance_info.ppEnabledLayerNames = enabled_layers.data(); - instance_info.enabledExtensionCount = static_cast(enabled_extensions.size()); - instance_info.ppEnabledExtensionNames = enabled_extensions.data(); - return &instance_info; -} -InstanceCreateInfo& InstanceCreateInfo::set_api_version(uint32_t major, uint32_t minor, uint32_t patch) { - this->api_version = VK_MAKE_API_VERSION(0, major, minor, patch); - return *this; -} -InstanceCreateInfo& InstanceCreateInfo::setup_WSI(const char* api_selection) { - add_extensions({"VK_KHR_surface", get_platform_wsi_extension(api_selection)}); - return *this; -} - -DeviceQueueCreateInfo::DeviceQueueCreateInfo() { queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; } -DeviceQueueCreateInfo::DeviceQueueCreateInfo(const VkDeviceQueueCreateInfo* create_info) { - queue_create_info = *create_info; - for (uint32_t i = 0; i < create_info->queueCount; i++) { - priorities.push_back(create_info->pQueuePriorities[i]); - } -} - -VkDeviceQueueCreateInfo DeviceQueueCreateInfo::get() noexcept { - queue_create_info.pQueuePriorities = priorities.data(); - queue_create_info.queueCount = 1; - queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - return queue_create_info; -} - -DeviceCreateInfo::DeviceCreateInfo(const VkDeviceCreateInfo* create_info) { - dev = *create_info; - for (uint32_t i = 0; i < create_info->enabledExtensionCount; i++) { - enabled_extensions.push_back(create_info->ppEnabledExtensionNames[i]); - } - for (uint32_t i = 0; i < create_info->enabledLayerCount; i++) { - enabled_layers.push_back(create_info->ppEnabledLayerNames[i]); - } - for (uint32_t i = 0; i < create_info->queueCreateInfoCount; i++) { - device_queue_infos.push_back(create_info->pQueueCreateInfos[i]); - } -} - -VkDeviceCreateInfo* DeviceCreateInfo::get() noexcept { - dev.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - dev.enabledLayerCount = static_cast(enabled_layers.size()); - dev.ppEnabledLayerNames = enabled_layers.data(); - dev.enabledExtensionCount = static_cast(enabled_extensions.size()); - dev.ppEnabledExtensionNames = enabled_extensions.data(); - uint32_t index = 0; - for (auto& queue : queue_info_details) { - queue.queue_create_info.queueFamilyIndex = index++; - queue.queue_create_info.queueCount = 1; - device_queue_infos.push_back(queue.get()); - } - - dev.queueCreateInfoCount = static_cast(device_queue_infos.size()); - dev.pQueueCreateInfos = device_queue_infos.data(); - return &dev; -} - -#if defined(WIN32) -std::string narrow(const std::wstring& utf16) { - if (utf16.empty()) { - return {}; - } - int size = WideCharToMultiByte(CP_UTF8, 0, utf16.data(), static_cast(utf16.size()), nullptr, 0, nullptr, nullptr); - if (size <= 0) { - return {}; - } - std::string utf8(size, '\0'); - if (WideCharToMultiByte(CP_UTF8, 0, utf16.data(), static_cast(utf16.size()), &utf8[0], size, nullptr, nullptr) != size) { - return {}; - } - return utf8; -} - -std::wstring widen(const std::string& utf8) { - if (utf8.empty()) { - return {}; - } - int size = MultiByteToWideChar(CP_UTF8, 0, utf8.data(), static_cast(utf8.size()), nullptr, 0); - if (size <= 0) { - return {}; - } - std::wstring utf16(size, L'\0'); - if (MultiByteToWideChar(CP_UTF8, 0, utf8.data(), static_cast(utf8.size()), &utf16[0], size) != size) { - return {}; - } - return utf16; -} -#else -std::string narrow(const std::string& utf16) { return utf16; } -std::string widen(const std::string& utf8) { return utf8; } -#endif diff --git a/tests/framework/test_util.h b/tests/framework/test_util.h deleted file mode 100644 index 06d62d45d..000000000 --- a/tests/framework/test_util.h +++ /dev/null @@ -1,968 +0,0 @@ -/* - * Copyright (c) 2021-2023 The Khronos Group Inc. - * Copyright (c) 2021-2023 Valve Corporation - * Copyright (c) 2021-2023 LunarG, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and/or associated documentation files (the "Materials"), to - * deal in the Materials without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Materials, and to permit persons to whom the Materials are - * furnished to do so, subject to the following conditions: - * - * The above copyright notice(s) and this permission notice shall be included in - * all copies or substantial portions of the Materials. - * - * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE - * USE OR OTHER DEALINGS IN THE MATERIALS. - * - * Author: Charles Giessen - */ - -/* - * Contains all the utilities needed to make the framework and tests work. - * Contains: - * All the standard library includes and main platform specific includes - * Dll export macro - * Manifest ICD & Layer structs - * per-platform library loading - mirrors the vk_loader_platform - * LibraryWrapper - RAII wrapper for a library - * DispatchableHandle - RAII wrapper for vulkan dispatchable handle objects - * ostream overload for VkResult - prettifies googletest output - * Instance & Device create info helpers - * operator == overloads for many vulkan structs - more concise tests - */ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -// Set of platforms with a common set of functionality which is queried throughout the program -#if defined(__linux__) || defined(__APPLE__) || defined(__Fuchsia__) || defined(__QNX__) || defined(__FreeBSD__) || \ - defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__GNU__) -#define COMMON_UNIX_PLATFORMS 1 -#else -#define COMMON_UNIX_PLATFORMS 0 -#endif - -#if defined(WIN32) -#include -#include -#include -#elif COMMON_UNIX_PLATFORMS -#include -#include -#include -#include -#include - -// Prevent macro collisions from -#undef major -#undef minor - -#endif - -#include -#include -#include - -#include FRAMEWORK_CONFIG_HEADER - -#if defined(__GNUC__) && __GNUC__ >= 4 -#define FRAMEWORK_EXPORT __attribute__((visibility("default"))) -#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590) -#define FRAMEWORK_EXPORT __attribute__((visibility("default"))) -#elif defined(WIN32) -#define FRAMEWORK_EXPORT __declspec(dllexport) -#else -#define FRAMEWORK_EXPORT -#endif - -// Define it here so that json_writer.h has access to these functions -#if defined(WIN32) -// Convert an UTF-16 wstring to an UTF-8 string -std::string narrow(const std::wstring& utf16); -// Convert an UTF-8 string to an UTF-16 wstring -std::wstring widen(const std::string& utf8); -#else -// Do nothing passthrough for the sake of Windows & UTF-16 -std::string narrow(const std::string& utf16); -// Do nothing passthrough for the sake of Windows & UTF-16 -std::string widen(const std::string& utf8); -#endif - -#include "json_writer.h" - -using GetFoldersFunc = std::function(const char*)>; - -// get_env_var() - returns a std::string of `name`. if report_failure is true, then it will log to stderr that it didn't find the -// env-var -// NOTE: This is only intended for test framework code, all test code MUST use EnvVarWrapper -std::string get_env_var(std::string const& name, bool report_failure = true); - -/* - * Wrapper around Environment Variables with common operations - * Since Environment Variables leak between tests, there needs to be RAII code to remove them during test cleanup - - */ - -// Wrapper to set & remove env-vars automatically -struct EnvVarWrapper { - // Constructor which unsets the env-var - EnvVarWrapper(std::string const& name) noexcept : name(name) { - initial_value = get_env_var(name, false); - remove_env_var(); - } - // Constructor which set the env-var to the specified value - EnvVarWrapper(std::string const& name, std::string const& value) noexcept : name(name), cur_value(value) { - initial_value = get_env_var(name, false); - set_env_var(); - } - ~EnvVarWrapper() noexcept { - remove_env_var(); - if (!initial_value.empty()) { - set_new_value(initial_value); - } - } - - // delete copy operators - EnvVarWrapper(const EnvVarWrapper&) = delete; - EnvVarWrapper& operator=(const EnvVarWrapper&) = delete; - - void set_new_value(std::string const& value) { - cur_value = value; - set_env_var(); - } - void add_to_list(std::string const& list_item) { - if (!cur_value.empty()) { - cur_value += OS_ENV_VAR_LIST_SEPARATOR; - } - cur_value += list_item; - set_env_var(); - } -#if defined(WIN32) - void add_to_list(std::wstring const& list_item) { - if (!cur_value.empty()) { - cur_value += OS_ENV_VAR_LIST_SEPARATOR; - } - cur_value += narrow(list_item); - set_env_var(); - } -#endif - void remove_value() const { remove_env_var(); } - const char* get() const { return name.c_str(); } - const char* value() const { return cur_value.c_str(); } - - private: - std::string name; - std::string cur_value; - std::string initial_value; - - void set_env_var(); - void remove_env_var() const; -#if defined(WIN32) - // Environment variable list separator - not for filesystem paths - const char OS_ENV_VAR_LIST_SEPARATOR = ';'; -#elif COMMON_UNIX_PLATFORMS - // Environment variable list separator - not for filesystem paths - const char OS_ENV_VAR_LIST_SEPARATOR = ':'; -#endif -}; - -// Windows specific error handling logic -#if defined(WIN32) -const long ERROR_SETENV_FAILED = 10543; // chosen at random, attempts to not conflict -const long ERROR_REMOVEDIRECTORY_FAILED = 10544; // chosen at random, attempts to not conflict -const char* win_api_error_str(LSTATUS status); -void print_error_message(LSTATUS status, const char* function_name, std::string optional_message = ""); -#endif - -// copy the contents of a std::string into a char array and add a null terminator at the end -// src - std::string to read from -// dst - char array to write to -// size_dst - number of characters in the dst array -inline void copy_string_to_char_array(std::string const& src, char* dst, size_t size_dst) { dst[src.copy(dst, size_dst - 1)] = 0; } - -#if defined(WIN32) -typedef HMODULE test_platform_dl_handle; -inline test_platform_dl_handle test_platform_open_library(const wchar_t* lib_path) { - // Try loading the library the original way first. - test_platform_dl_handle lib_handle = LoadLibraryW(lib_path); - if (lib_handle == nullptr && GetLastError() == ERROR_MOD_NOT_FOUND) { - // If that failed, then try loading it with broader search folders. - lib_handle = LoadLibraryExW(lib_path, nullptr, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR); - } - return lib_handle; -} -inline void test_platform_open_library_print_error(std::filesystem::path const& libPath) { - std::wcerr << L"Unable to open library: " << libPath << L" due to: " << std::to_wstring(GetLastError()) << L"\n"; -} -inline void test_platform_close_library(test_platform_dl_handle library) { FreeLibrary(library); } -inline void* test_platform_get_proc_address(test_platform_dl_handle library, const char* name) { - assert(library); - assert(name); - return reinterpret_cast(GetProcAddress(library, name)); -} -inline char* test_platform_get_proc_address_error(const char* name) { - static char errorMsg[120]; - snprintf(errorMsg, 119, "Failed to find function \"%s\" in dynamic library", name); - return errorMsg; -} - -#elif COMMON_UNIX_PLATFORMS - -typedef void* test_platform_dl_handle; -inline test_platform_dl_handle test_platform_open_library(const char* libPath) { return dlopen(libPath, RTLD_LAZY | RTLD_LOCAL); } -inline void test_platform_open_library_print_error(std::filesystem::path const& libPath) { - std::wcerr << "Unable to open library: " << libPath << " due to: " << dlerror() << "\n"; -} -inline void test_platform_close_library(test_platform_dl_handle library) { - char* loader_disable_dynamic_library_unloading_env_var = getenv("VK_LOADER_DISABLE_DYNAMIC_LIBRARY_UNLOADING"); - if (NULL == loader_disable_dynamic_library_unloading_env_var || - 0 != strncmp(loader_disable_dynamic_library_unloading_env_var, "1", 2)) { - dlclose(library); - } -} -inline void* test_platform_get_proc_address(test_platform_dl_handle library, const char* name) { - assert(library); - assert(name); - return dlsym(library, name); -} -inline const char* test_platform_get_proc_address_error([[maybe_unused]] const char* name) { return dlerror(); } -#endif - -class FromVoidStarFunc { - private: - void* function; - - public: - FromVoidStarFunc(void* function) : function(function) {} - FromVoidStarFunc(PFN_vkVoidFunction function) : function(reinterpret_cast(function)) {} - - template - operator T() { - return reinterpret_cast(function); - } -}; - -struct LibraryWrapper { - explicit LibraryWrapper() noexcept {} - explicit LibraryWrapper(std::filesystem::path const& lib_path) noexcept : lib_path(lib_path) { - lib_handle = test_platform_open_library(lib_path.native().c_str()); - if (lib_handle == nullptr) { - test_platform_open_library_print_error(lib_path); - assert(lib_handle != nullptr && "Must be able to open library"); - } - } - ~LibraryWrapper() noexcept { - if (lib_handle != nullptr) { - test_platform_close_library(lib_handle); - lib_handle = nullptr; - } - } - LibraryWrapper(LibraryWrapper const& wrapper) = delete; - LibraryWrapper& operator=(LibraryWrapper const& wrapper) = delete; - LibraryWrapper(LibraryWrapper&& wrapper) noexcept : lib_handle(wrapper.lib_handle), lib_path(wrapper.lib_path) { - wrapper.lib_handle = nullptr; - } - LibraryWrapper& operator=(LibraryWrapper&& wrapper) noexcept { - if (this != &wrapper) { - if (lib_handle != nullptr) { - test_platform_close_library(lib_handle); - } - lib_handle = wrapper.lib_handle; - lib_path = wrapper.lib_path; - wrapper.lib_handle = nullptr; - } - return *this; - } - FromVoidStarFunc get_symbol(const char* symbol_name) const { - assert(lib_handle != nullptr && "Cannot get symbol with null library handle"); - void* symbol = test_platform_get_proc_address(lib_handle, symbol_name); - if (symbol == nullptr) { - fprintf(stderr, "Unable to open symbol %s: %s\n", symbol_name, test_platform_get_proc_address_error(symbol_name)); - assert(symbol != nullptr && "Must be able to get symbol"); - } - return FromVoidStarFunc(symbol); - } - - explicit operator bool() const noexcept { return lib_handle != nullptr; } - - test_platform_dl_handle lib_handle = nullptr; - std::filesystem::path lib_path; -}; - -template -PFN_vkVoidFunction to_vkVoidFunction(T func) { - return reinterpret_cast(func); -} -template -struct FRAMEWORK_EXPORT DispatchableHandle { - DispatchableHandle() { - auto ptr_handle = new VK_LOADER_DATA; - set_loader_magic_value(ptr_handle); - handle = reinterpret_cast(ptr_handle); - } - ~DispatchableHandle() { - if (handle) { - delete reinterpret_cast(handle); - } - handle = nullptr; - } - DispatchableHandle(DispatchableHandle const&) = delete; - DispatchableHandle& operator=(DispatchableHandle const&) = delete; - DispatchableHandle(DispatchableHandle&& other) noexcept : handle(other.handle) { other.handle = nullptr; } - DispatchableHandle& operator=(DispatchableHandle&& other) noexcept { - if (handle) { - delete reinterpret_cast(handle); - } - handle = other.handle; - other.handle = nullptr; - return *this; - } - bool operator==(T base_handle) { return base_handle == handle; } - bool operator!=(T base_handle) { return base_handle != handle; } - - T handle = nullptr; -}; - -// Stream operator for VkResult so GTEST will print out error codes as strings (automatically) -inline std::ostream& operator<<(std::ostream& os, const VkResult& result) { - switch (result) { - case (VK_SUCCESS): - return os << "VK_SUCCESS"; - case (VK_NOT_READY): - return os << "VK_NOT_READY"; - case (VK_TIMEOUT): - return os << "VK_TIMEOUT"; - case (VK_EVENT_SET): - return os << "VK_EVENT_SET"; - case (VK_EVENT_RESET): - return os << "VK_EVENT_RESET"; - case (VK_INCOMPLETE): - return os << "VK_INCOMPLETE"; - case (VK_ERROR_OUT_OF_HOST_MEMORY): - return os << "VK_ERROR_OUT_OF_HOST_MEMORY"; - case (VK_ERROR_OUT_OF_DEVICE_MEMORY): - return os << "VK_ERROR_OUT_OF_DEVICE_MEMORY"; - case (VK_ERROR_INITIALIZATION_FAILED): - return os << "VK_ERROR_INITIALIZATION_FAILED"; - case (VK_ERROR_DEVICE_LOST): - return os << "VK_ERROR_DEVICE_LOST"; - case (VK_ERROR_MEMORY_MAP_FAILED): - return os << "VK_ERROR_MEMORY_MAP_FAILED"; - case (VK_ERROR_LAYER_NOT_PRESENT): - return os << "VK_ERROR_LAYER_NOT_PRESENT"; - case (VK_ERROR_EXTENSION_NOT_PRESENT): - return os << "VK_ERROR_EXTENSION_NOT_PRESENT"; - case (VK_ERROR_FEATURE_NOT_PRESENT): - return os << "VK_ERROR_FEATURE_NOT_PRESENT"; - case (VK_ERROR_INCOMPATIBLE_DRIVER): - return os << "VK_ERROR_INCOMPATIBLE_DRIVER"; - case (VK_ERROR_TOO_MANY_OBJECTS): - return os << "VK_ERROR_TOO_MANY_OBJECTS"; - case (VK_ERROR_FORMAT_NOT_SUPPORTED): - return os << "VK_ERROR_FORMAT_NOT_SUPPORTED"; - case (VK_ERROR_FRAGMENTED_POOL): - return os << "VK_ERROR_FRAGMENTED_POOL"; - case (VK_ERROR_UNKNOWN): - return os << "VK_ERROR_UNKNOWN"; - case (VK_ERROR_OUT_OF_POOL_MEMORY): - return os << "VK_ERROR_OUT_OF_POOL_MEMORY"; - case (VK_ERROR_INVALID_EXTERNAL_HANDLE): - return os << "VK_ERROR_INVALID_EXTERNAL_HANDLE"; - case (VK_ERROR_FRAGMENTATION): - return os << "VK_ERROR_FRAGMENTATION"; - case (VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS): - return os << "VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS"; - case (VK_ERROR_SURFACE_LOST_KHR): - return os << "VK_ERROR_SURFACE_LOST_KHR"; - case (VK_ERROR_NATIVE_WINDOW_IN_USE_KHR): - return os << "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR"; - case (VK_SUBOPTIMAL_KHR): - return os << "VK_SUBOPTIMAL_KHR"; - case (VK_ERROR_OUT_OF_DATE_KHR): - return os << "VK_ERROR_OUT_OF_DATE_KHR"; - case (VK_ERROR_INCOMPATIBLE_DISPLAY_KHR): - return os << "VK_ERROR_INCOMPATIBLE_DISPLAY_KHR"; - case (VK_ERROR_VALIDATION_FAILED_EXT): - return os << "VK_ERROR_VALIDATION_FAILED_EXT"; - case (VK_ERROR_INVALID_SHADER_NV): - return os << "VK_ERROR_INVALID_SHADER_NV"; - case (VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT): - return os << "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT"; - case (VK_ERROR_NOT_PERMITTED_EXT): - return os << "VK_ERROR_NOT_PERMITTED_EXT"; - case (VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT): - return os << "VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT"; - case (VK_THREAD_IDLE_KHR): - return os << "VK_THREAD_IDLE_KHR"; - case (VK_THREAD_DONE_KHR): - return os << "VK_THREAD_DONE_KHR"; - case (VK_OPERATION_DEFERRED_KHR): - return os << "VK_OPERATION_DEFERRED_KHR"; - case (VK_OPERATION_NOT_DEFERRED_KHR): - return os << "VK_OPERATION_NOT_DEFERRED_KHR"; - case (VK_PIPELINE_COMPILE_REQUIRED_EXT): - return os << "VK_PIPELINE_COMPILE_REQUIRED_EXT"; - case (VK_RESULT_MAX_ENUM): - return os << "VK_RESULT_MAX_ENUM"; - case (VK_ERROR_COMPRESSION_EXHAUSTED_EXT): - return os << "VK_ERROR_COMPRESSION_EXHAUSTED_EXT"; - case (VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR): - return os << "VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR"; - case (VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR): - return os << "VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR"; - case (VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR): - return os << "VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR"; - case (VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR): - return os << "VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR"; - case (VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR): - return os << "VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR"; - case (VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR): - return os << "VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR"; - case (VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR): - return os << "VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR"; - case (VK_ERROR_INCOMPATIBLE_SHADER_BINARY_EXT): - return os << "VK_ERROR_INCOMPATIBLE_SHADER_BINARY_EXT"; - case (VK_PIPELINE_BINARY_MISSING_KHR): - return os << "VK_PIPELINE_BINARY_MISSING_KHR"; - case (VK_ERROR_NOT_ENOUGH_SPACE_KHR): - return os << "VK_ERROR_NOT_ENOUGH_SPACE_KHR"; - } - return os << static_cast(result); -} - -const char* get_platform_wsi_extension([[maybe_unused]] const char* api_selection); - -bool string_eq(const char* a, const char* b) noexcept; -bool string_eq(const char* a, const char* b, size_t len) noexcept; - -inline std::string version_to_string(uint32_t version) { - std::string out = std::to_string(VK_API_VERSION_MAJOR(version)) + "." + std::to_string(VK_API_VERSION_MINOR(version)) + "." + - std::to_string(VK_API_VERSION_PATCH(version)); - if (VK_API_VERSION_VARIANT(version) != 0) out += std::to_string(VK_API_VERSION_VARIANT(version)) + "." + out; - return out; -} - -// Macro to ease the definition of variables with builder member functions -// type = type of the variable -// name = name of the variable -// default_value = value to default initialize, use {} if nothing else makes sense -#define BUILDER_VALUE_WITH_DEFAULT(type, name, default_value) \ - type name = default_value; \ - auto set_##name(type const& name)->decltype(*this) { \ - this->name = name; \ - return *this; \ - } - -#define BUILDER_VALUE(type, name) BUILDER_VALUE_WITH_DEFAULT(type, name, {}) - -// Macro to ease the definition of vectors with builder member functions -// type = type of the variable -// name = name of the variable -// singular_name = used for the `add_singular_name` member function -#define BUILDER_VECTOR(type, name, singular_name) \ - std::vector name; \ - auto add_##singular_name(type const& singular_name)->decltype(*this) { \ - this->name.push_back(singular_name); \ - return *this; \ - } \ - auto add_##singular_name##s(std::vector const& singular_name)->decltype(*this) { \ - for (auto& elem : singular_name) this->name.push_back(elem); \ - return *this; \ - } -// Like BUILDER_VECTOR but for move only types - where passing in means giving up ownership -#define BUILDER_VECTOR_MOVE_ONLY(type, name, singular_name) \ - std::vector name; \ - auto add_##singular_name(type&& singular_name)->decltype(*this) { \ - this->name.push_back(std::move(singular_name)); \ - return *this; \ - } - -struct ManifestVersion { - BUILDER_VALUE_WITH_DEFAULT(uint32_t, major, 1) - BUILDER_VALUE_WITH_DEFAULT(uint32_t, minor, 0) - BUILDER_VALUE_WITH_DEFAULT(uint32_t, patch, 0) - - std::string get_version_str() const noexcept { - return std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(patch); - } -}; - -// ManifestICD builder -struct ManifestICD { - BUILDER_VALUE(ManifestVersion, file_format_version) - BUILDER_VALUE(uint32_t, api_version) - BUILDER_VALUE(std::filesystem::path, lib_path) - BUILDER_VALUE(bool, is_portability_driver) - BUILDER_VALUE(std::string, library_arch) - std::string get_manifest_str() const; -}; - -// ManifestLayer builder -struct ManifestLayer { - struct LayerDescription { - enum class Type { INSTANCE, GLOBAL, DEVICE }; - std::string get_type_str(Type layer_type) const { - if (layer_type == Type::GLOBAL) - return "GLOBAL"; - else if (layer_type == Type::DEVICE) - return "DEVICE"; - else // default - return "INSTANCE"; - } - struct FunctionOverride { - BUILDER_VALUE(std::string, vk_func) - BUILDER_VALUE(std::string, override_name) - - void get_manifest_str(JsonWriter& writer) const { writer.AddKeyedString(vk_func, override_name); } - }; - struct Extension { - Extension() noexcept {} - Extension(std::string name, uint32_t spec_version = 0, std::vector entrypoints = {}) noexcept - : name(name), spec_version(spec_version), entrypoints(entrypoints) {} - std::string name; - uint32_t spec_version = 0; - std::vector entrypoints; - void get_manifest_str(JsonWriter& writer) const; - }; - BUILDER_VALUE(std::string, name) - BUILDER_VALUE_WITH_DEFAULT(Type, type, Type::INSTANCE) - BUILDER_VALUE(std::filesystem::path, lib_path) - BUILDER_VALUE_WITH_DEFAULT(uint32_t, api_version, VK_API_VERSION_1_0) - BUILDER_VALUE(uint32_t, implementation_version) - BUILDER_VALUE(std::string, description) - BUILDER_VECTOR(FunctionOverride, functions, function) - BUILDER_VECTOR(Extension, instance_extensions, instance_extension) - BUILDER_VECTOR(Extension, device_extensions, device_extension) - BUILDER_VALUE(std::string, enable_environment) - BUILDER_VALUE(std::string, disable_environment) - BUILDER_VECTOR(std::string, component_layers, component_layer) - BUILDER_VECTOR(std::string, blacklisted_layers, blacklisted_layer) - BUILDER_VECTOR(std::filesystem::path, override_paths, override_path) - BUILDER_VECTOR(FunctionOverride, pre_instance_functions, pre_instance_function) - BUILDER_VECTOR(std::string, app_keys, app_key) - BUILDER_VALUE(std::string, library_arch) - - void get_manifest_str(JsonWriter& writer) const; - VkLayerProperties get_layer_properties() const; - }; - BUILDER_VALUE(ManifestVersion, file_format_version) - BUILDER_VECTOR(LayerDescription, layers, layer) - - std::string get_manifest_str() const; -}; - -struct Extension { - BUILDER_VALUE(std::string, extensionName) - BUILDER_VALUE_WITH_DEFAULT(uint32_t, specVersion, VK_API_VERSION_1_0) - - Extension(const char* name, uint32_t specVersion = VK_API_VERSION_1_0) noexcept - : extensionName(name), specVersion(specVersion) {} - Extension(std::string extensionName, uint32_t specVersion = VK_API_VERSION_1_0) noexcept - : extensionName(extensionName), specVersion(specVersion) {} - - VkExtensionProperties get() const noexcept { - VkExtensionProperties props{}; - copy_string_to_char_array(extensionName, &props.extensionName[0], VK_MAX_EXTENSION_NAME_SIZE); - props.specVersion = specVersion; - return props; - } -}; - -struct MockQueueFamilyProperties { - BUILDER_VALUE(VkQueueFamilyProperties, properties) - BUILDER_VALUE(bool, support_present) - - VkQueueFamilyProperties get() const noexcept { return properties; } -}; - -struct InstanceCreateInfo { - BUILDER_VALUE(VkInstanceCreateInfo, instance_info) - BUILDER_VALUE(VkApplicationInfo, application_info) - BUILDER_VALUE(std::string, app_name) - BUILDER_VALUE(std::string, engine_name) - BUILDER_VALUE(uint32_t, flags) - BUILDER_VALUE(uint32_t, app_version) - BUILDER_VALUE(uint32_t, engine_version) - BUILDER_VALUE_WITH_DEFAULT(uint32_t, api_version, VK_API_VERSION_1_0) - BUILDER_VECTOR(const char*, enabled_layers, layer) - BUILDER_VECTOR(const char*, enabled_extensions, extension) - // tell the get() function to not provide `application_info` - BUILDER_VALUE_WITH_DEFAULT(bool, fill_in_application_info, true) - - InstanceCreateInfo(); - - VkInstanceCreateInfo* get() noexcept; - - InstanceCreateInfo& set_api_version(uint32_t major, uint32_t minor, uint32_t patch); - - InstanceCreateInfo& setup_WSI(const char* api_selection = nullptr); -}; - -struct DeviceQueueCreateInfo { - DeviceQueueCreateInfo(); - DeviceQueueCreateInfo(const VkDeviceQueueCreateInfo* create_info); - - BUILDER_VALUE(VkDeviceQueueCreateInfo, queue_create_info) - BUILDER_VECTOR(float, priorities, priority) - - VkDeviceQueueCreateInfo get() noexcept; -}; - -struct DeviceCreateInfo { - DeviceCreateInfo() = default; - DeviceCreateInfo(const VkDeviceCreateInfo* create_info); - - BUILDER_VALUE(VkDeviceCreateInfo, dev) - BUILDER_VECTOR(const char*, enabled_extensions, extension) - BUILDER_VECTOR(const char*, enabled_layers, layer) - BUILDER_VECTOR(DeviceQueueCreateInfo, queue_info_details, device_queue) - - VkDeviceCreateInfo* get() noexcept; - - private: - std::vector device_queue_infos; -}; - -inline bool operator==(const VkExtent3D& a, const VkExtent3D& b) { - return a.width == b.width && a.height == b.height && a.depth == b.depth; -} -inline bool operator!=(const VkExtent3D& a, const VkExtent3D& b) { return !(a == b); } - -inline bool operator==(const VkQueueFamilyProperties& a, const VkQueueFamilyProperties& b) { - return a.minImageTransferGranularity == b.minImageTransferGranularity && a.queueCount == b.queueCount && - a.queueFlags == b.queueFlags && a.timestampValidBits == b.timestampValidBits; -} -inline bool operator!=(const VkQueueFamilyProperties& a, const VkQueueFamilyProperties& b) { return !(a == b); } - -inline bool operator==(const VkQueueFamilyProperties& a, const VkQueueFamilyProperties2& b) { return a == b.queueFamilyProperties; } -inline bool operator!=(const VkQueueFamilyProperties& a, const VkQueueFamilyProperties2& b) { return a != b.queueFamilyProperties; } - -inline bool operator==(const VkLayerProperties& a, const VkLayerProperties& b) { - return string_eq(a.layerName, b.layerName, 256) && string_eq(a.description, b.description, 256) && - a.implementationVersion == b.implementationVersion && a.specVersion == b.specVersion; -} -inline bool operator!=(const VkLayerProperties& a, const VkLayerProperties& b) { return !(a == b); } - -inline bool operator==(const VkExtensionProperties& a, const VkExtensionProperties& b) { - return string_eq(a.extensionName, b.extensionName, 256) && a.specVersion == b.specVersion; -} -inline bool operator!=(const VkExtensionProperties& a, const VkExtensionProperties& b) { return !(a == b); } - -inline bool operator==(const VkPhysicalDeviceFeatures& feats1, const VkPhysicalDeviceFeatures2& feats2) { - return feats1.robustBufferAccess == feats2.features.robustBufferAccess && - feats1.fullDrawIndexUint32 == feats2.features.fullDrawIndexUint32 && - feats1.imageCubeArray == feats2.features.imageCubeArray && feats1.independentBlend == feats2.features.independentBlend && - feats1.geometryShader == feats2.features.geometryShader && - feats1.tessellationShader == feats2.features.tessellationShader && - feats1.sampleRateShading == feats2.features.sampleRateShading && feats1.dualSrcBlend == feats2.features.dualSrcBlend && - feats1.logicOp == feats2.features.logicOp && feats1.multiDrawIndirect == feats2.features.multiDrawIndirect && - feats1.drawIndirectFirstInstance == feats2.features.drawIndirectFirstInstance && - feats1.depthClamp == feats2.features.depthClamp && feats1.depthBiasClamp == feats2.features.depthBiasClamp && - feats1.fillModeNonSolid == feats2.features.fillModeNonSolid && feats1.depthBounds == feats2.features.depthBounds && - feats1.wideLines == feats2.features.wideLines && feats1.largePoints == feats2.features.largePoints && - feats1.alphaToOne == feats2.features.alphaToOne && feats1.multiViewport == feats2.features.multiViewport && - feats1.samplerAnisotropy == feats2.features.samplerAnisotropy && - feats1.textureCompressionETC2 == feats2.features.textureCompressionETC2 && - feats1.textureCompressionASTC_LDR == feats2.features.textureCompressionASTC_LDR && - feats1.textureCompressionBC == feats2.features.textureCompressionBC && - feats1.occlusionQueryPrecise == feats2.features.occlusionQueryPrecise && - feats1.pipelineStatisticsQuery == feats2.features.pipelineStatisticsQuery && - feats1.vertexPipelineStoresAndAtomics == feats2.features.vertexPipelineStoresAndAtomics && - feats1.fragmentStoresAndAtomics == feats2.features.fragmentStoresAndAtomics && - feats1.shaderTessellationAndGeometryPointSize == feats2.features.shaderTessellationAndGeometryPointSize && - feats1.shaderImageGatherExtended == feats2.features.shaderImageGatherExtended && - feats1.shaderStorageImageExtendedFormats == feats2.features.shaderStorageImageExtendedFormats && - feats1.shaderStorageImageMultisample == feats2.features.shaderStorageImageMultisample && - feats1.shaderStorageImageReadWithoutFormat == feats2.features.shaderStorageImageReadWithoutFormat && - feats1.shaderStorageImageWriteWithoutFormat == feats2.features.shaderStorageImageWriteWithoutFormat && - feats1.shaderUniformBufferArrayDynamicIndexing == feats2.features.shaderUniformBufferArrayDynamicIndexing && - feats1.shaderSampledImageArrayDynamicIndexing == feats2.features.shaderSampledImageArrayDynamicIndexing && - feats1.shaderStorageBufferArrayDynamicIndexing == feats2.features.shaderStorageBufferArrayDynamicIndexing && - feats1.shaderStorageImageArrayDynamicIndexing == feats2.features.shaderStorageImageArrayDynamicIndexing && - feats1.shaderClipDistance == feats2.features.shaderClipDistance && - feats1.shaderCullDistance == feats2.features.shaderCullDistance && - feats1.shaderFloat64 == feats2.features.shaderFloat64 && feats1.shaderInt64 == feats2.features.shaderInt64 && - feats1.shaderInt16 == feats2.features.shaderInt16 && - feats1.shaderResourceResidency == feats2.features.shaderResourceResidency && - feats1.shaderResourceMinLod == feats2.features.shaderResourceMinLod && - feats1.sparseBinding == feats2.features.sparseBinding && - feats1.sparseResidencyBuffer == feats2.features.sparseResidencyBuffer && - feats1.sparseResidencyImage2D == feats2.features.sparseResidencyImage2D && - feats1.sparseResidencyImage3D == feats2.features.sparseResidencyImage3D && - feats1.sparseResidency2Samples == feats2.features.sparseResidency2Samples && - feats1.sparseResidency4Samples == feats2.features.sparseResidency4Samples && - feats1.sparseResidency8Samples == feats2.features.sparseResidency8Samples && - feats1.sparseResidency16Samples == feats2.features.sparseResidency16Samples && - feats1.sparseResidencyAliased == feats2.features.sparseResidencyAliased && - feats1.variableMultisampleRate == feats2.features.variableMultisampleRate && - feats1.inheritedQueries == feats2.features.inheritedQueries; -} - -inline bool operator==(const VkPhysicalDeviceMemoryProperties& props1, const VkPhysicalDeviceMemoryProperties2& props2) { - bool equal = true; - equal = equal && props1.memoryTypeCount == props2.memoryProperties.memoryTypeCount; - equal = equal && props1.memoryHeapCount == props2.memoryProperties.memoryHeapCount; - for (uint32_t i = 0; i < props1.memoryHeapCount; ++i) { - equal = equal && props1.memoryHeaps[i].size == props2.memoryProperties.memoryHeaps[i].size; - equal = equal && props1.memoryHeaps[i].flags == props2.memoryProperties.memoryHeaps[i].flags; - } - for (uint32_t i = 0; i < props1.memoryTypeCount; ++i) { - equal = equal && props1.memoryTypes[i].propertyFlags == props2.memoryProperties.memoryTypes[i].propertyFlags; - equal = equal && props1.memoryTypes[i].heapIndex == props2.memoryProperties.memoryTypes[i].heapIndex; - } - return equal; -} -inline bool operator==(const VkSparseImageFormatProperties& props1, const VkSparseImageFormatProperties& props2) { - return props1.aspectMask == props2.aspectMask && props1.imageGranularity.width == props2.imageGranularity.width && - props1.imageGranularity.height == props2.imageGranularity.height && - props1.imageGranularity.depth == props2.imageGranularity.depth && props1.flags == props2.flags; -} -inline bool operator==(const VkSparseImageFormatProperties& props1, const VkSparseImageFormatProperties2& props2) { - return props1 == props2.properties; -} -inline bool operator==(const VkExternalMemoryProperties& props1, const VkExternalMemoryProperties& props2) { - return props1.externalMemoryFeatures == props2.externalMemoryFeatures && - props1.exportFromImportedHandleTypes == props2.exportFromImportedHandleTypes && - props1.compatibleHandleTypes == props2.compatibleHandleTypes; -} -inline bool operator==(const VkExternalSemaphoreProperties& props1, const VkExternalSemaphoreProperties& props2) { - return props1.externalSemaphoreFeatures == props2.externalSemaphoreFeatures && - props1.exportFromImportedHandleTypes == props2.exportFromImportedHandleTypes && - props1.compatibleHandleTypes == props2.compatibleHandleTypes; -} -inline bool operator==(const VkExternalFenceProperties& props1, const VkExternalFenceProperties& props2) { - return props1.externalFenceFeatures == props2.externalFenceFeatures && - props1.exportFromImportedHandleTypes == props2.exportFromImportedHandleTypes && - props1.compatibleHandleTypes == props2.compatibleHandleTypes; -} -inline bool operator==(const VkSurfaceCapabilitiesKHR& props1, const VkSurfaceCapabilitiesKHR& props2) { - return props1.minImageCount == props2.minImageCount && props1.maxImageCount == props2.maxImageCount && - props1.currentExtent.width == props2.currentExtent.width && props1.currentExtent.height == props2.currentExtent.height && - props1.minImageExtent.width == props2.minImageExtent.width && - props1.minImageExtent.height == props2.minImageExtent.height && - props1.maxImageExtent.width == props2.maxImageExtent.width && - props1.maxImageExtent.height == props2.maxImageExtent.height && - props1.maxImageArrayLayers == props2.maxImageArrayLayers && props1.supportedTransforms == props2.supportedTransforms && - props1.currentTransform == props2.currentTransform && props1.supportedCompositeAlpha == props2.supportedCompositeAlpha && - props1.supportedUsageFlags == props2.supportedUsageFlags; -} -inline bool operator==(const VkSurfacePresentScalingCapabilitiesEXT& caps1, const VkSurfacePresentScalingCapabilitiesEXT& caps2) { - return caps1.supportedPresentScaling == caps2.supportedPresentScaling && - caps1.supportedPresentGravityX == caps2.supportedPresentGravityX && - caps1.supportedPresentGravityY == caps2.supportedPresentGravityY && - caps1.minScaledImageExtent.width == caps2.minScaledImageExtent.width && - caps1.minScaledImageExtent.height == caps2.minScaledImageExtent.height && - caps1.maxScaledImageExtent.width == caps2.maxScaledImageExtent.width && - caps1.maxScaledImageExtent.height == caps2.maxScaledImageExtent.height; -} -inline bool operator==(const VkSurfaceFormatKHR& format1, const VkSurfaceFormatKHR& format2) { - return format1.format == format2.format && format1.colorSpace == format2.colorSpace; -} -inline bool operator==(const VkSurfaceFormatKHR& format1, const VkSurfaceFormat2KHR& format2) { - return format1 == format2.surfaceFormat; -} -inline bool operator==(const VkDisplayPropertiesKHR& props1, const VkDisplayPropertiesKHR& props2) { - return props1.display == props2.display && props1.physicalDimensions.width == props2.physicalDimensions.width && - props1.physicalDimensions.height == props2.physicalDimensions.height && - props1.physicalResolution.width == props2.physicalResolution.width && - props1.physicalResolution.height == props2.physicalResolution.height && - props1.supportedTransforms == props2.supportedTransforms && props1.planeReorderPossible == props2.planeReorderPossible && - props1.persistentContent == props2.persistentContent; -} -inline bool operator==(const VkDisplayPropertiesKHR& props1, const VkDisplayProperties2KHR& props2) { - return props1 == props2.displayProperties; -} -inline bool operator==(const VkDisplayModePropertiesKHR& disp1, const VkDisplayModePropertiesKHR& disp2) { - return disp1.displayMode == disp2.displayMode && disp1.parameters.visibleRegion.width == disp2.parameters.visibleRegion.width && - disp1.parameters.visibleRegion.height == disp2.parameters.visibleRegion.height && - disp1.parameters.refreshRate == disp2.parameters.refreshRate; -} - -inline bool operator==(const VkDisplayModePropertiesKHR& disp1, const VkDisplayModeProperties2KHR& disp2) { - return disp1 == disp2.displayModeProperties; -} -inline bool operator==(const VkDisplayPlaneCapabilitiesKHR& caps1, const VkDisplayPlaneCapabilitiesKHR& caps2) { - return caps1.supportedAlpha == caps2.supportedAlpha && caps1.minSrcPosition.x == caps2.minSrcPosition.x && - caps1.minSrcPosition.y == caps2.minSrcPosition.y && caps1.maxSrcPosition.x == caps2.maxSrcPosition.x && - caps1.maxSrcPosition.y == caps2.maxSrcPosition.y && caps1.minSrcExtent.width == caps2.minSrcExtent.width && - caps1.minSrcExtent.height == caps2.minSrcExtent.height && caps1.maxSrcExtent.width == caps2.maxSrcExtent.width && - caps1.maxSrcExtent.height == caps2.maxSrcExtent.height && caps1.minDstPosition.x == caps2.minDstPosition.x && - caps1.minDstPosition.y == caps2.minDstPosition.y && caps1.maxDstPosition.x == caps2.maxDstPosition.x && - caps1.maxDstPosition.y == caps2.maxDstPosition.y && caps1.minDstExtent.width == caps2.minDstExtent.width && - caps1.minDstExtent.height == caps2.minDstExtent.height && caps1.maxDstExtent.width == caps2.maxDstExtent.width && - caps1.maxDstExtent.height == caps2.maxDstExtent.height; -} - -inline bool operator==(const VkDisplayPlaneCapabilitiesKHR& caps1, const VkDisplayPlaneCapabilities2KHR& caps2) { - return caps1 == caps2.capabilities; -} -inline bool operator==(const VkDisplayPlanePropertiesKHR& props1, const VkDisplayPlanePropertiesKHR& props2) { - return props1.currentDisplay == props2.currentDisplay && props1.currentStackIndex == props2.currentStackIndex; -} -inline bool operator==(const VkDisplayPlanePropertiesKHR& props1, const VkDisplayPlaneProperties2KHR& props2) { - return props1 == props2.displayPlaneProperties; -} -inline bool operator==(const VkExtent2D& ext1, const VkExtent2D& ext2) { - return ext1.height == ext2.height && ext1.width == ext2.width; -} -// Allow comparison of vectors of different types as long as their elements are comparable (just has to make sure to only apply when -// T != U) -template >> -bool operator==(const std::vector& a, const std::vector& b) { - return std::equal(a.begin(), a.end(), b.begin(), b.end(), [](const auto& left, const auto& right) { return left == right; }); -} - -struct VulkanFunction { - std::string name; - 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; - for (uint32_t i = 0; i < expected.size(); i++) { - auto found = std::find_if(std::begin(returned), std::end(returned), - [&](T elem) { return string_eq(*(expected.begin() + i), elem.layerName); }); - if (found == std::end(returned)) return false; - } - return true; -} -template -bool check_permutation(std::initializer_list expected, std::vector const& returned) { - if (expected.size() != returned.size()) return false; - for (uint32_t i = 0; i < expected.size(); i++) { - auto found = std::find_if(std::begin(returned), std::end(returned), - [&](T elem) { return string_eq(*(expected.begin() + i), elem.layerName); }); - if (found == std::end(returned)) return false; - } - return true; -} - -inline bool contains(std::vector const& vec, const char* name) { - return std::any_of(std::begin(vec), std::end(vec), - [name](VkExtensionProperties const& elem) { return string_eq(name, elem.extensionName); }); -} -inline bool contains(std::vector const& vec, const char* name) { - return std::any_of(std::begin(vec), std::end(vec), - [name](VkLayerProperties const& elem) { return string_eq(name, elem.layerName); }); -} - -#if defined(__linux__) || defined(__GNU__) - -// find application path + name. Path cannot be longer than 1024, returns NULL if it is greater than that. -inline std::string test_platform_executable_path() { - std::string buffer; - buffer.resize(1024); - ssize_t count = readlink("/proc/self/exe", &buffer[0], buffer.size()); - if (count == -1) return NULL; - if (count == 0) return NULL; - buffer[count] = '\0'; - buffer.resize(count); - return buffer; -} -#elif defined(__APPLE__) -#include -inline std::string test_platform_executable_path() { - std::string buffer; - buffer.resize(1024); - pid_t pid = getpid(); - int ret = proc_pidpath(pid, &buffer[0], static_cast(buffer.size())); - if (ret <= 0) return NULL; - buffer[ret] = '\0'; - buffer.resize(ret); - return buffer; -} -#elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) -#include -inline std::string test_platform_executable_path() { - int mib[] = { - CTL_KERN, -#if defined(__NetBSD__) - KERN_PROC_ARGS, - -1, - KERN_PROC_PATHNAME, -#else - KERN_PROC, - KERN_PROC_PATHNAME, - -1, -#endif - }; - std::string buffer; - buffer.resize(1024); - size_t size = buffer.size(); - if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &buffer[0], &size, NULL, 0) < 0) { - return NULL; - } - buffer.resize(size); - - return buffer; -} -#elif defined(__Fuchsia__) || defined(__OpenBSD__) -inline std::string test_platform_executable_path() { return {}; } -#elif defined(__QNX__) - -#ifndef SYSCONFDIR -#define SYSCONFDIR "/etc" -#endif - -#include -#include - -inline std::string test_platform_executable_path() { - std::string buffer; - buffer.resize(1024); - int fd = open("/proc/self/exefile", O_RDONLY); - ssize_t rdsize; - - if (fd == -1) { - return NULL; - } - - rdsize = read(fd, &buffer[0], buffer.size()); - if (rdsize < 0) { - return NULL; - } - buffer[rdsize] = 0x00; - close(fd); - buffer.resize(rdsize); - - return buffer; -} -#endif // defined (__QNX__) -#if defined(WIN32) -inline std::string test_platform_executable_path() { - std::string buffer; - buffer.resize(1024); - DWORD ret = GetModuleFileName(NULL, static_cast(&buffer[0]), (DWORD)buffer.size()); - if (ret == 0) return NULL; - if (ret > buffer.size()) return NULL; - buffer.resize(ret); - buffer[ret] = '\0'; - return narrow(std::filesystem::path(buffer).native()); -} - -#endif diff --git a/tests/framework/util/CMakeLists.txt b/tests/framework/util/CMakeLists.txt new file mode 100644 index 000000000..3860f9932 --- /dev/null +++ b/tests/framework/util/CMakeLists.txt @@ -0,0 +1,85 @@ +# ~~~ +# Copyright (c) 2021 Valve Corporation +# Copyright (c) 2021 LunarG, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ~~~ + +add_library(testing_framework_util STATIC + builder_defines.h + dispatch_table.cpp + dispatch_table.h + dispatchable_handle.h + dynamic_library_wrapper.cpp + dynamic_library_wrapper.h + env_var_wrapper.cpp + env_var_wrapper.h + equality_helpers.cpp + equality_helpers.h + folder_manager.cpp + folder_manager.h + functions.h + get_executable_path.cpp + get_executable_path.h + json_writer.cpp + json_writer.h + manifest_builders.cpp + manifest_builders.h + platform_wsi.cpp + platform_wsi.h + test_defines.h + vulkan_object_wrappers.cpp + vulkan_object_wrappers.h + wide_char_handling.cpp + wide_char_handling.h + ) +target_link_libraries(testing_framework_util PUBLIC loader_common_options Vulkan::Headers gtest) + +if(UNIX OR APPLE) + target_link_libraries(testing_framework_util PUBLIC ${CMAKE_DL_LIBS}) +endif() + +if(UNIX) + target_compile_options(testing_framework_util PUBLIC -fPIC) +endif() +# Gives access to all headers in this folder, the framework folder, the framework binary folder, and the loader/generated folder +target_include_directories(testing_framework_util + PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/.." "${CMAKE_CURRENT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}") + +if (UNIX) + if (LOADER_ENABLE_ADDRESS_SANITIZER) + target_compile_options(testing_framework_util PUBLIC -fsanitize=address,undefined) + target_link_options(testing_framework_util PUBLIC -fsanitize=address,undefined) + endif() + if (LOADER_ENABLE_THREAD_SANITIZER) + target_compile_options(testing_framework_util PUBLIC -fsanitize=thread) + target_link_options(testing_framework_util PUBLIC -fsanitize=thread) + target_compile_options(gtest PUBLIC -fsanitize=thread) + target_link_options(gtest PUBLIC -fsanitize=thread) + endif() +endif() + +if (MSVC) + # silence hidden class member warnings in test framework + target_compile_options(testing_framework_util PUBLIC /wd4458) + # Make sure exception handling is enabled for the test framework + target_compile_options(testing_framework_util PUBLIC /EHsc) +endif() + +# Add a compiler definition for the path to framework_config.h with the correct config +target_compile_definitions(testing_framework_util PUBLIC FRAMEWORK_CONFIG_HEADER="${FRAMEWORK_CONFIG_HEADER_PATH}") + +if (APPLE_STATIC_LOADER) + target_compile_definitions(testing_framework_util PUBLIC "APPLE_STATIC_LOADER=1") + target_link_libraries(testing_framework_util PRIVATE vulkan) +endif() diff --git a/tests/framework/util/builder_defines.h b/tests/framework/util/builder_defines.h new file mode 100644 index 000000000..c727c81b9 --- /dev/null +++ b/tests/framework/util/builder_defines.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2025 The Khronos Group Inc. + * Copyright (c) 2025 Valve Corporation + * Copyright (c) 2025 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * The above copyright notice(s) and this permission notice shall be included in + * all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + */ + +#pragma once + +#include + +// Macro to ease the definition of variables with builder member functions +// type = type of the variable +// name = name of the variable +// default_value = value to default initialize, use {} if nothing else makes sense +#define BUILDER_VALUE_WITH_DEFAULT(type, name, default_value) \ + type name = default_value; \ + auto set_##name(type const& name)->decltype(*this) { \ + this->name = name; \ + return *this; \ + } + +#define BUILDER_VALUE(type, name) BUILDER_VALUE_WITH_DEFAULT(type, name, {}) + +// Macro to ease the definition of vectors with builder member functions +// type = type of the variable +// name = name of the variable +// singular_name = used for the `add_singular_name` member function +#define BUILDER_VECTOR(type, name, singular_name) \ + std::vector name; \ + auto add_##singular_name(type const& singular_name)->decltype(*this) { \ + this->name.push_back(singular_name); \ + return *this; \ + } \ + auto add_##singular_name##s(std::vector const& singular_name)->decltype(*this) { \ + for (auto& elem : singular_name) this->name.push_back(elem); \ + return *this; \ + } +// Like BUILDER_VECTOR but for move only types - where passing in means giving up ownership +#define BUILDER_VECTOR_MOVE_ONLY(type, name, singular_name) \ + std::vector name; \ + auto add_##singular_name(type&& singular_name)->decltype(*this) { \ + this->name.push_back(std::move(singular_name)); \ + return *this; \ + } diff --git a/tests/framework/util/dispatch_table.cpp b/tests/framework/util/dispatch_table.cpp new file mode 100644 index 000000000..60901afc1 --- /dev/null +++ b/tests/framework/util/dispatch_table.cpp @@ -0,0 +1,174 @@ +// VulkanFunctions - loads vulkan functions for tests to use +/* + * Copyright (c) 2025 The Khronos Group Inc. + * Copyright (c) 2025 Valve Corporation + * Copyright (c) 2025 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * The above copyright notice(s) and this permission notice shall be included in + * all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + */ + +#include "dispatch_table.h" + +#include "env_var_wrapper.h" + +std::filesystem::path get_loader_path() { + auto loader_path = std::filesystem::path(FRAMEWORK_VULKAN_LIBRARY_PATH); + auto env_var_res = get_env_var("VK_LOADER_TEST_LOADER_PATH", false); + if (!env_var_res.empty()) { + loader_path = std::filesystem::path(env_var_res); + } + return loader_path; +} + +void init_vulkan_functions(VulkanFunctions& funcs) { +#if defined(APPLE_STATIC_LOADER) +#define GPA(name) name +#else +#define GPA(name) funcs.loader.get_symbol(#name) +#endif + + // clang-format off + funcs.vkGetInstanceProcAddr = GPA(vkGetInstanceProcAddr); + funcs.vkEnumerateInstanceExtensionProperties = GPA(vkEnumerateInstanceExtensionProperties); + funcs.vkEnumerateInstanceLayerProperties = GPA(vkEnumerateInstanceLayerProperties); + funcs.vkEnumerateInstanceVersion = GPA(vkEnumerateInstanceVersion); + funcs.vkCreateInstance = GPA(vkCreateInstance); + funcs.vkDestroyInstance = GPA(vkDestroyInstance); + funcs.vkEnumeratePhysicalDevices = GPA(vkEnumeratePhysicalDevices); + funcs.vkEnumeratePhysicalDeviceGroups = GPA(vkEnumeratePhysicalDeviceGroups); + funcs.vkGetPhysicalDeviceFeatures = GPA(vkGetPhysicalDeviceFeatures); + funcs.vkGetPhysicalDeviceFeatures2 = GPA(vkGetPhysicalDeviceFeatures2); + funcs.vkGetPhysicalDeviceFormatProperties = GPA(vkGetPhysicalDeviceFormatProperties); + funcs.vkGetPhysicalDeviceFormatProperties2 = GPA(vkGetPhysicalDeviceFormatProperties2); + funcs.vkGetPhysicalDeviceImageFormatProperties = GPA(vkGetPhysicalDeviceImageFormatProperties); + funcs.vkGetPhysicalDeviceImageFormatProperties2 = GPA(vkGetPhysicalDeviceImageFormatProperties2); + funcs.vkGetPhysicalDeviceSparseImageFormatProperties = GPA(vkGetPhysicalDeviceSparseImageFormatProperties); + funcs.vkGetPhysicalDeviceSparseImageFormatProperties2 = GPA(vkGetPhysicalDeviceSparseImageFormatProperties2); + funcs.vkGetPhysicalDeviceProperties = GPA(vkGetPhysicalDeviceProperties); + funcs.vkGetPhysicalDeviceProperties2 = GPA(vkGetPhysicalDeviceProperties2); + funcs.vkGetPhysicalDeviceQueueFamilyProperties = GPA(vkGetPhysicalDeviceQueueFamilyProperties); + funcs.vkGetPhysicalDeviceQueueFamilyProperties2 = GPA(vkGetPhysicalDeviceQueueFamilyProperties2); + funcs.vkGetPhysicalDeviceMemoryProperties = GPA(vkGetPhysicalDeviceMemoryProperties); + funcs.vkGetPhysicalDeviceMemoryProperties2 = GPA(vkGetPhysicalDeviceMemoryProperties2); + funcs.vkGetPhysicalDeviceSurfaceSupportKHR = GPA(vkGetPhysicalDeviceSurfaceSupportKHR); + funcs.vkGetPhysicalDeviceSurfaceFormatsKHR = GPA(vkGetPhysicalDeviceSurfaceFormatsKHR); + funcs.vkGetPhysicalDeviceSurfacePresentModesKHR = GPA(vkGetPhysicalDeviceSurfacePresentModesKHR); + funcs.vkGetPhysicalDeviceSurfaceCapabilitiesKHR = GPA(vkGetPhysicalDeviceSurfaceCapabilitiesKHR); + funcs.vkEnumerateDeviceExtensionProperties = GPA(vkEnumerateDeviceExtensionProperties); + funcs.vkEnumerateDeviceLayerProperties = GPA(vkEnumerateDeviceLayerProperties); + funcs.vkGetPhysicalDeviceExternalBufferProperties = GPA(vkGetPhysicalDeviceExternalBufferProperties); + funcs.vkGetPhysicalDeviceExternalFenceProperties = GPA(vkGetPhysicalDeviceExternalFenceProperties); + funcs.vkGetPhysicalDeviceExternalSemaphoreProperties = GPA(vkGetPhysicalDeviceExternalSemaphoreProperties); + + funcs.vkDestroySurfaceKHR = GPA(vkDestroySurfaceKHR); + funcs.vkGetDeviceProcAddr = GPA(vkGetDeviceProcAddr); + funcs.vkCreateDevice = GPA(vkCreateDevice); + + funcs.vkCreateHeadlessSurfaceEXT = GPA(vkCreateHeadlessSurfaceEXT); + funcs.vkCreateDisplayPlaneSurfaceKHR = GPA(vkCreateDisplayPlaneSurfaceKHR); + funcs.vkGetPhysicalDeviceDisplayPropertiesKHR = GPA(vkGetPhysicalDeviceDisplayPropertiesKHR); + funcs.vkGetPhysicalDeviceDisplayPlanePropertiesKHR = GPA(vkGetPhysicalDeviceDisplayPlanePropertiesKHR); + funcs.vkGetDisplayPlaneSupportedDisplaysKHR = GPA(vkGetDisplayPlaneSupportedDisplaysKHR); + funcs.vkGetDisplayModePropertiesKHR = GPA(vkGetDisplayModePropertiesKHR); + funcs.vkCreateDisplayModeKHR = GPA(vkCreateDisplayModeKHR); + funcs.vkGetDisplayPlaneCapabilitiesKHR = GPA(vkGetDisplayPlaneCapabilitiesKHR); + funcs.vkGetPhysicalDevicePresentRectanglesKHR = GPA(vkGetPhysicalDevicePresentRectanglesKHR); + funcs.vkGetPhysicalDeviceDisplayProperties2KHR = GPA(vkGetPhysicalDeviceDisplayProperties2KHR); + funcs.vkGetPhysicalDeviceDisplayPlaneProperties2KHR = GPA(vkGetPhysicalDeviceDisplayPlaneProperties2KHR); + funcs.vkGetDisplayModeProperties2KHR = GPA(vkGetDisplayModeProperties2KHR); + funcs.vkGetDisplayPlaneCapabilities2KHR = GPA(vkGetDisplayPlaneCapabilities2KHR); + funcs.vkGetPhysicalDeviceSurfaceCapabilities2KHR = GPA(vkGetPhysicalDeviceSurfaceCapabilities2KHR); + funcs.vkGetPhysicalDeviceSurfaceFormats2KHR = GPA(vkGetPhysicalDeviceSurfaceFormats2KHR); + +#if defined(VK_USE_PLATFORM_ANDROID_KHR) + funcs.vkCreateAndroidSurfaceKHR = GPA(vkCreateAndroidSurfaceKHR); +#endif // VK_USE_PLATFORM_ANDROID_KHR +#if defined(VK_USE_PLATFORM_DIRECTFB_EXT) + funcs.vkCreateDirectFBSurfaceEXT = GPA(vkCreateDirectFBSurfaceEXT); + funcs.vkGetPhysicalDeviceDirectFBPresentationSupportEXT = GPA(vkGetPhysicalDeviceDirectFBPresentationSupportEXT); +#endif // VK_USE_PLATFORM_DIRECTFB_EXT +#if defined(VK_USE_PLATFORM_FUCHSIA) + funcs.vkCreateImagePipeSurfaceFUCHSIA = GPA(vkCreateImagePipeSurfaceFUCHSIA); +#endif // VK_USE_PLATFORM_FUCHSIA +#if defined(VK_USE_PLATFORM_GGP) + funcs.vkCreateStreamDescriptorSurfaceGGP = GPA(vkCreateStreamDescriptorSurfaceGGP); +#endif // VK_USE_PLATFORM_GGP +#if defined(VK_USE_PLATFORM_IOS_MVK) + funcs.vkCreateIOSSurfaceMVK = GPA(vkCreateIOSSurfaceMVK); +#endif // VK_USE_PLATFORM_IOS_MVK +#if defined(VK_USE_PLATFORM_MACOS_MVK) + funcs.vkCreateMacOSSurfaceMVK = GPA(vkCreateMacOSSurfaceMVK); +#endif // VK_USE_PLATFORM_MACOS_MVK +#if defined(VK_USE_PLATFORM_METAL_EXT) + funcs.vkCreateMetalSurfaceEXT = GPA(vkCreateMetalSurfaceEXT); +#endif // VK_USE_PLATFORM_METAL_EXT +#if defined(VK_USE_PLATFORM_SCREEN_QNX) + funcs.vkCreateScreenSurfaceQNX = GPA(vkCreateScreenSurfaceQNX); + funcs.vkGetPhysicalDeviceScreenPresentationSupportQNX = GPA(vkGetPhysicalDeviceScreenPresentationSupportQNX); +#endif // VK_USE_PLATFORM_SCREEN_QNX +#if defined(VK_USE_PLATFORM_WAYLAND_KHR) + funcs.vkCreateWaylandSurfaceKHR = GPA(vkCreateWaylandSurfaceKHR); + funcs.vkGetPhysicalDeviceWaylandPresentationSupportKHR = GPA(vkGetPhysicalDeviceWaylandPresentationSupportKHR); +#endif // VK_USE_PLATFORM_WAYLAND_KHR +#if defined(VK_USE_PLATFORM_XCB_KHR) + funcs.vkCreateXcbSurfaceKHR = GPA(vkCreateXcbSurfaceKHR); + funcs.vkGetPhysicalDeviceXcbPresentationSupportKHR = GPA(vkGetPhysicalDeviceXcbPresentationSupportKHR); +#endif // VK_USE_PLATFORM_XCB_KHR +#if defined(VK_USE_PLATFORM_XLIB_KHR) + funcs.vkCreateXlibSurfaceKHR = GPA(vkCreateXlibSurfaceKHR); + funcs.vkGetPhysicalDeviceXlibPresentationSupportKHR = GPA(vkGetPhysicalDeviceXlibPresentationSupportKHR); +#endif // VK_USE_PLATFORM_XLIB_KHR +#if defined(VK_USE_PLATFORM_WIN32_KHR) + funcs.vkCreateWin32SurfaceKHR = GPA(vkCreateWin32SurfaceKHR); + funcs.vkGetPhysicalDeviceWin32PresentationSupportKHR = GPA(vkGetPhysicalDeviceWin32PresentationSupportKHR); +#endif // VK_USE_PLATFORM_WIN32_KHR + funcs.vkDestroyDevice = GPA(vkDestroyDevice); + funcs.vkGetDeviceQueue = GPA(vkGetDeviceQueue); +#undef GPA + // clang-format on +} + +#if defined(APPLE_STATIC_LOADER) +VulkanFunctions::VulkanFunctions() { +#else +VulkanFunctions::VulkanFunctions() : loader(get_loader_path()) { +#endif + init_vulkan_functions(*this); +} + +void VulkanFunctions::load_instance_functions(VkInstance instance) { + vkCreateDebugReportCallbackEXT = FromVoidStarFunc(vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT")); + vkDestroyDebugReportCallbackEXT = FromVoidStarFunc(vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT")); + vkCreateDebugUtilsMessengerEXT = FromVoidStarFunc(vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT")); + vkDestroyDebugUtilsMessengerEXT = FromVoidStarFunc(vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT")); +} + +DeviceFunctions::DeviceFunctions(const VulkanFunctions& vulkan_functions, VkDevice device) { + vkGetDeviceProcAddr = vulkan_functions.vkGetDeviceProcAddr; + vkDestroyDevice = load(device, "vkDestroyDevice"); + vkGetDeviceQueue = load(device, "vkGetDeviceQueue"); + vkCreateCommandPool = load(device, "vkCreateCommandPool"); + vkAllocateCommandBuffers = load(device, "vkAllocateCommandBuffers"); + vkDestroyCommandPool = load(device, "vkDestroyCommandPool"); + vkCreateSwapchainKHR = load(device, "vkCreateSwapchainKHR"); + vkGetSwapchainImagesKHR = load(device, "vkGetSwapchainImagesKHR"); + vkDestroySwapchainKHR = load(device, "vkDestroySwapchainKHR"); +} diff --git a/tests/framework/util/dispatch_table.h b/tests/framework/util/dispatch_table.h new file mode 100644 index 000000000..265416532 --- /dev/null +++ b/tests/framework/util/dispatch_table.h @@ -0,0 +1,178 @@ +// VulkanFunctions - loads vulkan functions for tests to use +/* + * Copyright (c) 2025 The Khronos Group Inc. + * Copyright (c) 2025 Valve Corporation + * Copyright (c) 2025 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * The above copyright notice(s) and this permission notice shall be included in + * all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + */ + +#pragma once + +#include + +#include "functions.h" +#include "dynamic_library_wrapper.h" + +struct VulkanFunctions { +#if !defined(APPLE_STATIC_LOADER) + LibraryWrapper loader; +#endif + // Pre-Instance + PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = nullptr; + PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties = nullptr; + PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties = nullptr; + PFN_vkEnumerateInstanceVersion vkEnumerateInstanceVersion = nullptr; + PFN_vkCreateInstance vkCreateInstance = nullptr; + + // Instance + PFN_vkDestroyInstance vkDestroyInstance = nullptr; + PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices = nullptr; + PFN_vkEnumeratePhysicalDeviceGroups vkEnumeratePhysicalDeviceGroups = nullptr; + PFN_vkGetPhysicalDeviceFeatures vkGetPhysicalDeviceFeatures = nullptr; + PFN_vkGetPhysicalDeviceFeatures2 vkGetPhysicalDeviceFeatures2 = nullptr; + PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties = nullptr; + PFN_vkGetPhysicalDeviceFormatProperties2 vkGetPhysicalDeviceFormatProperties2 = nullptr; + PFN_vkGetPhysicalDeviceImageFormatProperties vkGetPhysicalDeviceImageFormatProperties = nullptr; + PFN_vkGetPhysicalDeviceImageFormatProperties2 vkGetPhysicalDeviceImageFormatProperties2 = nullptr; + PFN_vkGetPhysicalDeviceSparseImageFormatProperties vkGetPhysicalDeviceSparseImageFormatProperties = nullptr; + PFN_vkGetPhysicalDeviceSparseImageFormatProperties2 vkGetPhysicalDeviceSparseImageFormatProperties2 = nullptr; + PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties = nullptr; + PFN_vkGetPhysicalDeviceProperties2 vkGetPhysicalDeviceProperties2 = nullptr; + PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties = nullptr; + PFN_vkGetPhysicalDeviceQueueFamilyProperties2 vkGetPhysicalDeviceQueueFamilyProperties2 = nullptr; + PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties = nullptr; + PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2 = nullptr; + PFN_vkGetPhysicalDeviceSurfaceSupportKHR vkGetPhysicalDeviceSurfaceSupportKHR = nullptr; + PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR = nullptr; + PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR = nullptr; + PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR = nullptr; + PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties = nullptr; + PFN_vkEnumerateDeviceLayerProperties vkEnumerateDeviceLayerProperties = nullptr; + PFN_vkGetPhysicalDeviceExternalBufferProperties vkGetPhysicalDeviceExternalBufferProperties = nullptr; + PFN_vkGetPhysicalDeviceExternalFenceProperties vkGetPhysicalDeviceExternalFenceProperties = nullptr; + PFN_vkGetPhysicalDeviceExternalSemaphoreProperties vkGetPhysicalDeviceExternalSemaphoreProperties = nullptr; + + PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr = nullptr; + PFN_vkCreateDevice vkCreateDevice = nullptr; + + // WSI + PFN_vkCreateHeadlessSurfaceEXT vkCreateHeadlessSurfaceEXT = nullptr; + PFN_vkCreateDisplayPlaneSurfaceKHR vkCreateDisplayPlaneSurfaceKHR = nullptr; + PFN_vkGetPhysicalDeviceDisplayPropertiesKHR vkGetPhysicalDeviceDisplayPropertiesKHR = nullptr; + PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR vkGetPhysicalDeviceDisplayPlanePropertiesKHR = nullptr; + PFN_vkGetDisplayPlaneSupportedDisplaysKHR vkGetDisplayPlaneSupportedDisplaysKHR = nullptr; + PFN_vkGetDisplayModePropertiesKHR vkGetDisplayModePropertiesKHR = nullptr; + PFN_vkCreateDisplayModeKHR vkCreateDisplayModeKHR = nullptr; + PFN_vkGetDisplayPlaneCapabilitiesKHR vkGetDisplayPlaneCapabilitiesKHR = nullptr; + PFN_vkGetPhysicalDevicePresentRectanglesKHR vkGetPhysicalDevicePresentRectanglesKHR = nullptr; + PFN_vkGetPhysicalDeviceDisplayProperties2KHR vkGetPhysicalDeviceDisplayProperties2KHR = nullptr; + PFN_vkGetPhysicalDeviceDisplayPlaneProperties2KHR vkGetPhysicalDeviceDisplayPlaneProperties2KHR = nullptr; + PFN_vkGetDisplayModeProperties2KHR vkGetDisplayModeProperties2KHR = nullptr; + PFN_vkGetDisplayPlaneCapabilities2KHR vkGetDisplayPlaneCapabilities2KHR = nullptr; + PFN_vkGetPhysicalDeviceSurfaceCapabilities2KHR vkGetPhysicalDeviceSurfaceCapabilities2KHR = nullptr; + PFN_vkGetPhysicalDeviceSurfaceFormats2KHR vkGetPhysicalDeviceSurfaceFormats2KHR = nullptr; + +#if defined(VK_USE_PLATFORM_ANDROID_KHR) + PFN_vkCreateAndroidSurfaceKHR vkCreateAndroidSurfaceKHR = nullptr; +#endif // VK_USE_PLATFORM_ANDROID_KHR +#if defined(VK_USE_PLATFORM_DIRECTFB_EXT) + PFN_vkCreateDirectFBSurfaceEXT vkCreateDirectFBSurfaceEXT = nullptr; + PFN_vkGetPhysicalDeviceDirectFBPresentationSupportEXT vkGetPhysicalDeviceDirectFBPresentationSupportEXT = nullptr; +#endif // VK_USE_PLATFORM_DIRECTFB_EXT +#if defined(VK_USE_PLATFORM_FUCHSIA) + PFN_vkCreateImagePipeSurfaceFUCHSIA vkCreateImagePipeSurfaceFUCHSIA = nullptr; +#endif // VK_USE_PLATFORM_FUCHSIA +#if defined(VK_USE_PLATFORM_GGP) + PFN_vkCreateStreamDescriptorSurfaceGGP vkCreateStreamDescriptorSurfaceGGP = nullptr; +#endif // VK_USE_PLATFORM_GGP +#if defined(VK_USE_PLATFORM_IOS_MVK) + PFN_vkCreateIOSSurfaceMVK vkCreateIOSSurfaceMVK = nullptr; +#endif // VK_USE_PLATFORM_IOS_MVK +#if defined(VK_USE_PLATFORM_MACOS_MVK) + PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK = nullptr; +#endif // VK_USE_PLATFORM_MACOS_MVK +#if defined(VK_USE_PLATFORM_METAL_EXT) + PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT = nullptr; +#endif // VK_USE_PLATFORM_METAL_EXT +#if defined(VK_USE_PLATFORM_SCREEN_QNX) + PFN_vkCreateScreenSurfaceQNX vkCreateScreenSurfaceQNX = nullptr; + PFN_vkGetPhysicalDeviceScreenPresentationSupportQNX vkGetPhysicalDeviceScreenPresentationSupportQNX = nullptr; +#endif // VK_USE_PLATFORM_SCREEN_QNX +#if defined(VK_USE_PLATFORM_WAYLAND_KHR) + PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR = nullptr; + PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR vkGetPhysicalDeviceWaylandPresentationSupportKHR = nullptr; +#endif // VK_USE_PLATFORM_WAYLAND_KHR +#if defined(VK_USE_PLATFORM_XCB_KHR) + PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR = nullptr; + PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR vkGetPhysicalDeviceXcbPresentationSupportKHR = nullptr; +#endif // VK_USE_PLATFORM_XCB_KHR +#if defined(VK_USE_PLATFORM_XLIB_KHR) + PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR = nullptr; + PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR vkGetPhysicalDeviceXlibPresentationSupportKHR = nullptr; +#endif // VK_USE_PLATFORM_XLIB_KHR +#if defined(VK_USE_PLATFORM_WIN32_KHR) + PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR = nullptr; + PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR vkGetPhysicalDeviceWin32PresentationSupportKHR = nullptr; +#endif // VK_USE_PLATFORM_WIN32_KHR + PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR = nullptr; + + // instance extensions functions (can only be loaded with a valid instance) + PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT = nullptr; // Null unless the extension is enabled + PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT = nullptr; // Null unless the extension is enabled + PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT = nullptr; // Null unless the extension is enabled + PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallbackEXT = nullptr; // Null unless the extension is enabled + + // device functions + PFN_vkDestroyDevice vkDestroyDevice = nullptr; + PFN_vkGetDeviceQueue vkGetDeviceQueue = nullptr; + + VulkanFunctions(); + + void load_instance_functions(VkInstance instance); + + FromVoidStarFunc load(VkInstance inst, const char* func_name) const { + return FromVoidStarFunc(vkGetInstanceProcAddr(inst, func_name)); + } + + FromVoidStarFunc load(VkDevice device, const char* func_name) const { + return FromVoidStarFunc(vkGetDeviceProcAddr(device, func_name)); + } +}; + +struct DeviceFunctions { + PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr = nullptr; + PFN_vkDestroyDevice vkDestroyDevice = nullptr; + PFN_vkGetDeviceQueue vkGetDeviceQueue = nullptr; + PFN_vkCreateCommandPool vkCreateCommandPool = nullptr; + PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers = nullptr; + PFN_vkDestroyCommandPool vkDestroyCommandPool = nullptr; + PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR = nullptr; + PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR = nullptr; + PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR = nullptr; + + DeviceFunctions() = default; + DeviceFunctions(const VulkanFunctions& vulkan_functions, VkDevice device); + + FromVoidStarFunc load(VkDevice device, const char* func_name) const { + return FromVoidStarFunc(vkGetDeviceProcAddr(device, func_name)); + } +}; diff --git a/tests/framework/util/dispatchable_handle.h b/tests/framework/util/dispatchable_handle.h new file mode 100644 index 000000000..98085bb7d --- /dev/null +++ b/tests/framework/util/dispatchable_handle.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2025 The Khronos Group Inc. + * Copyright (c) 2025 Valve Corporation + * Copyright (c) 2025 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * The above copyright notice(s) and this permission notice shall be included in + * all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + */ + +#pragma once + +#include + +#include "test_defines.h" + +template +struct FRAMEWORK_EXPORT DispatchableHandle { + DispatchableHandle() { + auto ptr_handle = new VK_LOADER_DATA; + set_loader_magic_value(ptr_handle); + handle = reinterpret_cast(ptr_handle); + } + ~DispatchableHandle() { + if (handle) { + delete reinterpret_cast(handle); + } + handle = nullptr; + } + DispatchableHandle(DispatchableHandle const&) = delete; + DispatchableHandle& operator=(DispatchableHandle const&) = delete; + DispatchableHandle(DispatchableHandle&& other) noexcept : handle(other.handle) { other.handle = nullptr; } + DispatchableHandle& operator=(DispatchableHandle&& other) noexcept { + if (handle) { + delete reinterpret_cast(handle); + } + handle = other.handle; + other.handle = nullptr; + return *this; + } + bool operator==(T base_handle) { return base_handle == handle; } + bool operator!=(T base_handle) { return base_handle != handle; } + + T handle = nullptr; +}; diff --git a/tests/framework/util/dynamic_library_wrapper.cpp b/tests/framework/util/dynamic_library_wrapper.cpp new file mode 100644 index 000000000..e868d1607 --- /dev/null +++ b/tests/framework/util/dynamic_library_wrapper.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2025 The Khronos Group Inc. + * Copyright (c) 2025 Valve Corporation + * Copyright (c) 2025 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * The above copyright notice(s) and this permission notice shall be included in + * all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + */ + +#include "dynamic_library_wrapper.h" + +#include "test_defines.h" +#include "env_var_wrapper.h" + +#include +#include +#include + +#include + +namespace detail { + +#if defined(_WIN32) +typedef HMODULE test_platform_dl_handle; + +std::string error_code_to_string(DWORD error_code) { + LPSTR lpMsgBuf{}; + size_t MsgBufSize = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), lpMsgBuf, 0, nullptr); + std::string code_str(lpMsgBuf, MsgBufSize); + LocalFree(lpMsgBuf); + return code_str; +} +std::wstring wide_error_code_to_string(DWORD error_code) { + LPWSTR lpwMsgBuf{}; + size_t MsgBufSize = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), lpwMsgBuf, 0, nullptr); + std::wstring code_str(lpwMsgBuf, MsgBufSize); + LocalFree(lpwMsgBuf); + return code_str; +} +#elif TESTING_COMMON_UNIX_PLATFORMS +#include + +typedef void* test_platform_dl_handle; +#endif +} // namespace detail + +LibraryWrapper::LibraryWrapper() noexcept {} +LibraryWrapper::LibraryWrapper(std::filesystem::path const& lib_path) noexcept : lib_path(lib_path.lexically_normal()) { +#if defined(_WIN32) + auto wide_path = lib_path.native(); // Get a std::wstring first + // Try loading the library the original way first. + lib_handle = LoadLibraryW(wide_path.c_str()); + DWORD last_error = GetLastError(); + // If that failed because of path limitations, prepend a special sequence to opt into longer path support + if (lib_handle == nullptr && last_error == ERROR_FILENAME_EXCED_RANGE) { + // "\\?\" is a special prefix for filenames that tells winapi to ignore path limits, so we can workaround path limitations + // without relying on system level changes. + wide_path = L"\\\\?\\" + wide_path; + last_error = 0; + lib_handle = LoadLibraryW(wide_path.c_str()); + last_error = GetLastError(); + } + // If that failed, then try loading it with broader search folders. + if (lib_handle == nullptr && last_error == ERROR_MOD_NOT_FOUND) { + last_error = 0; + lib_handle = + LoadLibraryExW(wide_path.c_str(), nullptr, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR); + last_error = GetLastError(); + } + // If neither of the above worked, print an error message and abort + if (lib_handle == nullptr) { + auto error_msg = detail::wide_error_code_to_string(last_error); + std::wcerr << L"Unable to open library: " << wide_path << L" due to: " << error_msg << L"\n"; + abort(); + } +#elif TESTING_COMMON_UNIX_PLATFORMS + lib_handle = dlopen(lib_path.string().c_str(), RTLD_LAZY | RTLD_LOCAL); + if (lib_handle == nullptr) { + std::wcerr << "Unable to open library: " << lib_path << " due to: " << dlerror() << "\n"; + abort(); + } +#else +#error "Unhandled platform in dynamic_library_wrapper.cpp!" +#endif +} + +LibraryWrapper::~LibraryWrapper() noexcept { close_library(); } + +LibraryWrapper::LibraryWrapper(LibraryWrapper&& wrapper) noexcept : lib_handle(wrapper.lib_handle), lib_path(wrapper.lib_path) { + wrapper.lib_handle = nullptr; +} +LibraryWrapper& LibraryWrapper::operator=(LibraryWrapper&& wrapper) noexcept { + if (this != &wrapper) { + lib_handle = wrapper.lib_handle; + lib_path = wrapper.lib_path; + wrapper.lib_handle = nullptr; + } + return *this; +} + +void LibraryWrapper::close_library() noexcept { + auto loader_disable_dynamic_library_unloading_env_var = get_env_var("VK_LOADER_DISABLE_DYNAMIC_LIBRARY_UNLOADING", false); + if (loader_disable_dynamic_library_unloading_env_var.empty() || loader_disable_dynamic_library_unloading_env_var != "1") { + if (lib_handle != nullptr) { +#if defined(_WIN32) + FreeLibrary(lib_handle); +#elif TESTING_COMMON_UNIX_PLATFORMS + dlclose(lib_handle); +#else +#error "Unhandled platform in dynamic_library_wrapper.cpp!" +#endif + lib_handle = nullptr; + } + } +} + +FromVoidStarFunc LibraryWrapper::get_symbol(const char* symbol_name) const { + if (symbol_name == nullptr) { + std::cerr << "LibraryWrapper::get_symbol called with a null symbol_name!\n"; + abort(); + } + if (lib_handle == nullptr) { + std::cerr << "LibraryWrapper::get_symbol called when lib_handle is nullptr!\n"; + abort(); + } + +#if defined(_WIN32) + void* symbol = reinterpret_cast(GetProcAddress(lib_handle, symbol_name)); + if (symbol == nullptr) { + auto last_error = detail::error_code_to_string(GetLastError()); + std::cerr << "Unable to get symbol " << symbol_name << " in library " << lib_path.string() << " with to error code " + << last_error << "\n"; + abort(); + } +#elif TESTING_COMMON_UNIX_PLATFORMS + void* symbol = dlsym(lib_handle, symbol_name); + if (symbol == nullptr) { + std::cerr << "Unable to get symbol " << symbol_name << " in library " << lib_path.string() << " due to " << dlerror() + << "\n"; + abort(); + } +#else +#error "Unhandled platform in dynamic_library_wrapper.cpp!" +#endif + return FromVoidStarFunc(symbol); +} diff --git a/tests/framework/util/dynamic_library_wrapper.h b/tests/framework/util/dynamic_library_wrapper.h new file mode 100644 index 000000000..35b72adbe --- /dev/null +++ b/tests/framework/util/dynamic_library_wrapper.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2025 The Khronos Group Inc. + * Copyright (c) 2025 Valve Corporation + * Copyright (c) 2025 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * The above copyright notice(s) and this permission notice shall be included in + * all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + */ + +#pragma once + +#include + +#include "test_defines.h" + +#include "functions.h" + +#if defined(_WIN32) +#include +#include +#include +#elif TESTING_COMMON_UNIX_PLATFORMS +#include +#include +#include +#include +#include +#endif + +struct LibraryWrapper { + explicit LibraryWrapper() noexcept; + explicit LibraryWrapper(std::filesystem::path const& lib_path) noexcept; + ~LibraryWrapper() noexcept; + LibraryWrapper(LibraryWrapper const& wrapper) = delete; + LibraryWrapper& operator=(LibraryWrapper const& wrapper) = delete; + LibraryWrapper(LibraryWrapper&& wrapper) noexcept; + LibraryWrapper& operator=(LibraryWrapper&& wrapper) noexcept; + FromVoidStarFunc get_symbol(const char* symbol_name) const; + + std::filesystem::path const& get_path() const { return lib_path; } + explicit operator bool() const noexcept { return lib_handle != nullptr; } + + private: + void close_library() noexcept; + +#if defined(_WIN32) + HMODULE lib_handle = nullptr; +#elif TESTING_COMMON_UNIX_PLATFORMS + void* lib_handle = nullptr; +#else +#error "Unhandled platform in dynamic_library_wrapper.h!" +#endif + std::filesystem::path lib_path; +}; diff --git a/tests/framework/util/env_var_wrapper.cpp b/tests/framework/util/env_var_wrapper.cpp new file mode 100644 index 000000000..f9df38994 --- /dev/null +++ b/tests/framework/util/env_var_wrapper.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2025 The Khronos Group Inc. + * Copyright (c) 2025 Valve Corporation + * Copyright (c) 2025 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * The above copyright notice(s) and this permission notice shall be included in + * all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + */ + +#include "env_var_wrapper.h" + +#include + +#include + +#include "wide_char_handling.h" + +// NOTE: get_env_var() are only intended for test framework code, all test code MUST use +// EnvVarWrapper + +#if defined(_WIN32) +#include +#include +#include + +// Windows specific error handling logic +const long ERROR_SETENV_FAILED = 10543; // chosen at random, attempts to not conflict +const long ERROR_REMOVEDIRECTORY_FAILED = 10544; // chosen at random, attempts to not conflict +const char* win_api_error_str(LSTATUS status) { + if (status == ERROR_INVALID_FUNCTION) return "ERROR_INVALID_FUNCTION"; + if (status == ERROR_FILE_NOT_FOUND) return "ERROR_FILE_NOT_FOUND"; + if (status == ERROR_PATH_NOT_FOUND) return "ERROR_PATH_NOT_FOUND"; + if (status == ERROR_TOO_MANY_OPEN_FILES) return "ERROR_TOO_MANY_OPEN_FILES"; + if (status == ERROR_ACCESS_DENIED) return "ERROR_ACCESS_DENIED"; + if (status == ERROR_INVALID_HANDLE) return "ERROR_INVALID_HANDLE"; + if (status == ERROR_ENVVAR_NOT_FOUND) return "ERROR_ENVVAR_NOT_FOUND"; + if (status == ERROR_SETENV_FAILED) return "ERROR_SETENV_FAILED"; + return "UNKNOWN ERROR"; +} +std::string error_code_to_string(DWORD error_code) { + LPSTR lpMsgBuf{}; + size_t MsgBufSize = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), lpMsgBuf, 0, nullptr); + std::string code_str(lpMsgBuf, MsgBufSize); + LocalFree(lpMsgBuf); + return code_str; +} + +void print_error_message(LSTATUS status, const char* function_name, std::string optional_message = "") { + DWORD dw = GetLastError(); + + std::cerr << function_name << " failed with " << win_api_error_str(status) << ": " << error_code_to_string(dw); + if (optional_message != "") { + std::cerr << " | " << optional_message; + } + std::cerr << "\n"; +} + +// get_env_var() - returns a std::string of `name`. if report_failure is true, then it will log to stderr that it didn't find the +// env-var +std::string get_env_var(std::string const& name, bool report_failure) { + std::wstring name_utf16 = widen(name); + DWORD value_size = GetEnvironmentVariableW(name_utf16.c_str(), nullptr, 0); + if (0 == value_size) { + if (report_failure) print_error_message(ERROR_ENVVAR_NOT_FOUND, "GetEnvironmentVariableW"); + return {}; + } + std::wstring value(value_size, L'\0'); + if (GetEnvironmentVariableW(name_utf16.c_str(), &value[0], value_size) != value_size - 1) { + return {}; + } + return narrow(value); +} +#elif TESTING_COMMON_UNIX_PLATFORMS + +// get_env_var() - returns a std::string of `name`. if report_failure is true, then it will log to stderr that it didn't find the +// env-var +std::string get_env_var(std::string const& name, bool report_failure) { + char* ret = getenv(name.c_str()); + if (ret == nullptr) { + if (report_failure) std::cerr << "Failed to get environment variable:" << name << "\n"; + return std::string(); + } + return ret; +} +#endif + +std::vector split_env_var_as_list(std::string const& env_var_contents) { + std::vector items; + size_t start = 0; + size_t len = 0; + for (size_t i = 0; i < env_var_contents.size(); i++) { + if (env_var_contents[i] == OS_ENV_VAR_LIST_SEPARATOR) { + if (len != 0) { + // only push back non empty strings + items.push_back(env_var_contents.substr(start, len)); + } + start = i + 1; + len = 0; + } else { + len++; + } + } + items.push_back(env_var_contents.substr(start, len)); + return items; +} + +/* + * Wrapper around Environment Variables with common operations + * Since Environment Variables leak between tests, there needs to be RAII code to remove them during test cleanup + + */ + +EnvVarWrapper::EnvVarWrapper(std::string const& name) noexcept : name(name) { + initial_value = get_env_var(name, false); + remove_env_var(); +} +EnvVarWrapper::EnvVarWrapper(std::string const& name, std::string const& value) noexcept : name(name), cur_value(value) { + initial_value = get_env_var(name, false); + set_env_var(); +} +EnvVarWrapper::~EnvVarWrapper() noexcept { + remove_env_var(); + if (!initial_value.empty()) { + set_new_value(initial_value); + } +} + +void EnvVarWrapper::set_new_value(std::string const& value) { + cur_value = value; + set_env_var(); +} +void EnvVarWrapper::add_to_list(std::string const& list_item) { + if (!cur_value.empty()) { + cur_value += OS_ENV_VAR_LIST_SEPARATOR; + } + cur_value += list_item; + set_env_var(); +} +#if defined(_WIN32) +void EnvVarWrapper::add_to_list(std::wstring const& list_item) { + if (!cur_value.empty()) { + cur_value += OS_ENV_VAR_LIST_SEPARATOR; + } + cur_value += narrow(list_item); + set_env_var(); +} +#endif +void EnvVarWrapper::remove_value() const { remove_env_var(); } +const char* EnvVarWrapper::get() const { return name.c_str(); } +const char* EnvVarWrapper::value() const { return cur_value.c_str(); } + +#if defined(_WIN32) + +void EnvVarWrapper::set_env_var() const { + BOOL ret = SetEnvironmentVariableW(widen(name).c_str(), widen(cur_value).c_str()); + if (ret == 0) { + print_error_message(ERROR_SETENV_FAILED, "SetEnvironmentVariableW"); + } +} +void EnvVarWrapper::remove_env_var() const { SetEnvironmentVariableW(widen(name).c_str(), nullptr); } + +#elif TESTING_COMMON_UNIX_PLATFORMS + +void EnvVarWrapper::set_env_var() const { setenv(name.c_str(), cur_value.c_str(), 1); } +void EnvVarWrapper::remove_env_var() const { unsetenv(name.c_str()); } +#endif diff --git a/tests/framework/util/env_var_wrapper.h b/tests/framework/util/env_var_wrapper.h new file mode 100644 index 000000000..2a0ecca7a --- /dev/null +++ b/tests/framework/util/env_var_wrapper.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2025 The Khronos Group Inc. + * Copyright (c) 2025 Valve Corporation + * Copyright (c) 2025 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * The above copyright notice(s) and this permission notice shall be included in + * all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + */ + +#pragma once + +#include +#include + +#include "test_defines.h" + +// Environment variable list separator - not for filesystem paths +#if defined(_WIN32) +static const char OS_ENV_VAR_LIST_SEPARATOR = ';'; +#elif TESTING_COMMON_UNIX_PLATFORMS +static const char OS_ENV_VAR_LIST_SEPARATOR = ':'; +#else +#error "Unhandled platform in env_var_wrapper.h!" +#endif + +// get_env_var() - returns the contents of env-var `name` as a std::string +// If report_failure is true, then it will log to stderr that it didn't find the env-var +std::string get_env_var(std::string const& name, bool report_failure = true); + +// split_env_var_as_list() - split env_var_contents into separate elements using the platform +// specific env-var list separator. +std::vector split_env_var_as_list(std::string const& env_var_contents); + +/* + * Wrapper around Environment Variables with common operations + * Since Environment Variables leak between tests, there needs to be RAII code to remove them during test cleanup + + */ + +// Wrapper to set & remove env-vars automatically +struct EnvVarWrapper { + // Constructor which unsets the env-var + EnvVarWrapper(std::string const& name) noexcept; + // Constructor which set the env-var to the specified value + EnvVarWrapper(std::string const& name, std::string const& value) noexcept; + ~EnvVarWrapper() noexcept; + + // delete copy operators + EnvVarWrapper(const EnvVarWrapper&) = delete; + EnvVarWrapper& operator=(const EnvVarWrapper&) = delete; + + void set_new_value(std::string const& value); + void add_to_list(std::string const& list_item); +#if defined(_WIN32) + void add_to_list(std::wstring const& list_item); +#endif + void remove_value() const; + const char* get() const; + const char* value() const; + + private: + std::string name; + std::string cur_value; + std::string initial_value; + + void set_env_var() const; + void remove_env_var() const; +}; diff --git a/tests/framework/util/equality_helpers.cpp b/tests/framework/util/equality_helpers.cpp new file mode 100644 index 000000000..4a98599d4 --- /dev/null +++ b/tests/framework/util/equality_helpers.cpp @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2025 The Khronos Group Inc. + * Copyright (c) 2025 Valve Corporation + * Copyright (c) 2025 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * The above copyright notice(s) and this permission notice shall be included in + * all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + */ + +#include "equality_helpers.h" + +bool operator==(const VkExtent3D& a, const VkExtent3D& b) { + return a.width == b.width && a.height == b.height && a.depth == b.depth; +} +bool operator!=(const VkExtent3D& a, const VkExtent3D& b) { return !(a == b); } + +bool operator==(const VkQueueFamilyProperties& a, const VkQueueFamilyProperties& b) { + return a.minImageTransferGranularity == b.minImageTransferGranularity && a.queueCount == b.queueCount && + a.queueFlags == b.queueFlags && a.timestampValidBits == b.timestampValidBits; +} +bool operator!=(const VkQueueFamilyProperties& a, const VkQueueFamilyProperties& b) { return !(a == b); } + +bool operator==(const VkQueueFamilyProperties& a, const VkQueueFamilyProperties2& b) { return a == b.queueFamilyProperties; } +bool operator!=(const VkQueueFamilyProperties& a, const VkQueueFamilyProperties2& b) { return a != b.queueFamilyProperties; } + +bool operator==(const VkLayerProperties& a, const VkLayerProperties& b) { + return string_eq(a.layerName, b.layerName, 256) && string_eq(a.description, b.description, 256) && + a.implementationVersion == b.implementationVersion && a.specVersion == b.specVersion; +} +bool operator!=(const VkLayerProperties& a, const VkLayerProperties& b) { return !(a == b); } + +bool operator==(const VkExtensionProperties& a, const VkExtensionProperties& b) { + return string_eq(a.extensionName, b.extensionName, 256) && a.specVersion == b.specVersion; +} +bool operator!=(const VkExtensionProperties& a, const VkExtensionProperties& b) { return !(a == b); } + +bool operator==(const VkPhysicalDeviceFeatures& feats1, const VkPhysicalDeviceFeatures2& feats2) { + return feats1.robustBufferAccess == feats2.features.robustBufferAccess && + feats1.fullDrawIndexUint32 == feats2.features.fullDrawIndexUint32 && + feats1.imageCubeArray == feats2.features.imageCubeArray && feats1.independentBlend == feats2.features.independentBlend && + feats1.geometryShader == feats2.features.geometryShader && + feats1.tessellationShader == feats2.features.tessellationShader && + feats1.sampleRateShading == feats2.features.sampleRateShading && feats1.dualSrcBlend == feats2.features.dualSrcBlend && + feats1.logicOp == feats2.features.logicOp && feats1.multiDrawIndirect == feats2.features.multiDrawIndirect && + feats1.drawIndirectFirstInstance == feats2.features.drawIndirectFirstInstance && + feats1.depthClamp == feats2.features.depthClamp && feats1.depthBiasClamp == feats2.features.depthBiasClamp && + feats1.fillModeNonSolid == feats2.features.fillModeNonSolid && feats1.depthBounds == feats2.features.depthBounds && + feats1.wideLines == feats2.features.wideLines && feats1.largePoints == feats2.features.largePoints && + feats1.alphaToOne == feats2.features.alphaToOne && feats1.multiViewport == feats2.features.multiViewport && + feats1.samplerAnisotropy == feats2.features.samplerAnisotropy && + feats1.textureCompressionETC2 == feats2.features.textureCompressionETC2 && + feats1.textureCompressionASTC_LDR == feats2.features.textureCompressionASTC_LDR && + feats1.textureCompressionBC == feats2.features.textureCompressionBC && + feats1.occlusionQueryPrecise == feats2.features.occlusionQueryPrecise && + feats1.pipelineStatisticsQuery == feats2.features.pipelineStatisticsQuery && + feats1.vertexPipelineStoresAndAtomics == feats2.features.vertexPipelineStoresAndAtomics && + feats1.fragmentStoresAndAtomics == feats2.features.fragmentStoresAndAtomics && + feats1.shaderTessellationAndGeometryPointSize == feats2.features.shaderTessellationAndGeometryPointSize && + feats1.shaderImageGatherExtended == feats2.features.shaderImageGatherExtended && + feats1.shaderStorageImageExtendedFormats == feats2.features.shaderStorageImageExtendedFormats && + feats1.shaderStorageImageMultisample == feats2.features.shaderStorageImageMultisample && + feats1.shaderStorageImageReadWithoutFormat == feats2.features.shaderStorageImageReadWithoutFormat && + feats1.shaderStorageImageWriteWithoutFormat == feats2.features.shaderStorageImageWriteWithoutFormat && + feats1.shaderUniformBufferArrayDynamicIndexing == feats2.features.shaderUniformBufferArrayDynamicIndexing && + feats1.shaderSampledImageArrayDynamicIndexing == feats2.features.shaderSampledImageArrayDynamicIndexing && + feats1.shaderStorageBufferArrayDynamicIndexing == feats2.features.shaderStorageBufferArrayDynamicIndexing && + feats1.shaderStorageImageArrayDynamicIndexing == feats2.features.shaderStorageImageArrayDynamicIndexing && + feats1.shaderClipDistance == feats2.features.shaderClipDistance && + feats1.shaderCullDistance == feats2.features.shaderCullDistance && + feats1.shaderFloat64 == feats2.features.shaderFloat64 && feats1.shaderInt64 == feats2.features.shaderInt64 && + feats1.shaderInt16 == feats2.features.shaderInt16 && + feats1.shaderResourceResidency == feats2.features.shaderResourceResidency && + feats1.shaderResourceMinLod == feats2.features.shaderResourceMinLod && + feats1.sparseBinding == feats2.features.sparseBinding && + feats1.sparseResidencyBuffer == feats2.features.sparseResidencyBuffer && + feats1.sparseResidencyImage2D == feats2.features.sparseResidencyImage2D && + feats1.sparseResidencyImage3D == feats2.features.sparseResidencyImage3D && + feats1.sparseResidency2Samples == feats2.features.sparseResidency2Samples && + feats1.sparseResidency4Samples == feats2.features.sparseResidency4Samples && + feats1.sparseResidency8Samples == feats2.features.sparseResidency8Samples && + feats1.sparseResidency16Samples == feats2.features.sparseResidency16Samples && + feats1.sparseResidencyAliased == feats2.features.sparseResidencyAliased && + feats1.variableMultisampleRate == feats2.features.variableMultisampleRate && + feats1.inheritedQueries == feats2.features.inheritedQueries; +} + +bool operator==(const VkPhysicalDeviceMemoryProperties& props1, const VkPhysicalDeviceMemoryProperties2& props2) { + bool equal = true; + equal = equal && props1.memoryTypeCount == props2.memoryProperties.memoryTypeCount; + equal = equal && props1.memoryHeapCount == props2.memoryProperties.memoryHeapCount; + for (uint32_t i = 0; i < props1.memoryHeapCount; ++i) { + equal = equal && props1.memoryHeaps[i].size == props2.memoryProperties.memoryHeaps[i].size; + equal = equal && props1.memoryHeaps[i].flags == props2.memoryProperties.memoryHeaps[i].flags; + } + for (uint32_t i = 0; i < props1.memoryTypeCount; ++i) { + equal = equal && props1.memoryTypes[i].propertyFlags == props2.memoryProperties.memoryTypes[i].propertyFlags; + equal = equal && props1.memoryTypes[i].heapIndex == props2.memoryProperties.memoryTypes[i].heapIndex; + } + return equal; +} +bool operator==(const VkSparseImageFormatProperties& props1, const VkSparseImageFormatProperties& props2) { + return props1.aspectMask == props2.aspectMask && props1.imageGranularity.width == props2.imageGranularity.width && + props1.imageGranularity.height == props2.imageGranularity.height && + props1.imageGranularity.depth == props2.imageGranularity.depth && props1.flags == props2.flags; +} +bool operator==(const VkSparseImageFormatProperties& props1, const VkSparseImageFormatProperties2& props2) { + return props1 == props2.properties; +} +bool operator==(const VkExternalMemoryProperties& props1, const VkExternalMemoryProperties& props2) { + return props1.externalMemoryFeatures == props2.externalMemoryFeatures && + props1.exportFromImportedHandleTypes == props2.exportFromImportedHandleTypes && + props1.compatibleHandleTypes == props2.compatibleHandleTypes; +} +bool operator==(const VkExternalSemaphoreProperties& props1, const VkExternalSemaphoreProperties& props2) { + return props1.externalSemaphoreFeatures == props2.externalSemaphoreFeatures && + props1.exportFromImportedHandleTypes == props2.exportFromImportedHandleTypes && + props1.compatibleHandleTypes == props2.compatibleHandleTypes; +} +bool operator==(const VkExternalFenceProperties& props1, const VkExternalFenceProperties& props2) { + return props1.externalFenceFeatures == props2.externalFenceFeatures && + props1.exportFromImportedHandleTypes == props2.exportFromImportedHandleTypes && + props1.compatibleHandleTypes == props2.compatibleHandleTypes; +} +bool operator==(const VkSurfaceCapabilitiesKHR& props1, const VkSurfaceCapabilitiesKHR& props2) { + return props1.minImageCount == props2.minImageCount && props1.maxImageCount == props2.maxImageCount && + props1.currentExtent.width == props2.currentExtent.width && props1.currentExtent.height == props2.currentExtent.height && + props1.minImageExtent.width == props2.minImageExtent.width && + props1.minImageExtent.height == props2.minImageExtent.height && + props1.maxImageExtent.width == props2.maxImageExtent.width && + props1.maxImageExtent.height == props2.maxImageExtent.height && + props1.maxImageArrayLayers == props2.maxImageArrayLayers && props1.supportedTransforms == props2.supportedTransforms && + props1.currentTransform == props2.currentTransform && props1.supportedCompositeAlpha == props2.supportedCompositeAlpha && + props1.supportedUsageFlags == props2.supportedUsageFlags; +} +bool operator==(const VkSurfacePresentScalingCapabilitiesEXT& caps1, const VkSurfacePresentScalingCapabilitiesEXT& caps2) { + return caps1.supportedPresentScaling == caps2.supportedPresentScaling && + caps1.supportedPresentGravityX == caps2.supportedPresentGravityX && + caps1.supportedPresentGravityY == caps2.supportedPresentGravityY && + caps1.minScaledImageExtent.width == caps2.minScaledImageExtent.width && + caps1.minScaledImageExtent.height == caps2.minScaledImageExtent.height && + caps1.maxScaledImageExtent.width == caps2.maxScaledImageExtent.width && + caps1.maxScaledImageExtent.height == caps2.maxScaledImageExtent.height; +} +bool operator==(const VkSurfaceFormatKHR& format1, const VkSurfaceFormatKHR& format2) { + return format1.format == format2.format && format1.colorSpace == format2.colorSpace; +} +bool operator==(const VkSurfaceFormatKHR& format1, const VkSurfaceFormat2KHR& format2) { return format1 == format2.surfaceFormat; } +bool operator==(const VkDisplayPropertiesKHR& props1, const VkDisplayPropertiesKHR& props2) { + return props1.display == props2.display && props1.physicalDimensions.width == props2.physicalDimensions.width && + props1.physicalDimensions.height == props2.physicalDimensions.height && + props1.physicalResolution.width == props2.physicalResolution.width && + props1.physicalResolution.height == props2.physicalResolution.height && + props1.supportedTransforms == props2.supportedTransforms && props1.planeReorderPossible == props2.planeReorderPossible && + props1.persistentContent == props2.persistentContent; +} +bool operator==(const VkDisplayPropertiesKHR& props1, const VkDisplayProperties2KHR& props2) { + return props1 == props2.displayProperties; +} +bool operator==(const VkDisplayModePropertiesKHR& disp1, const VkDisplayModePropertiesKHR& disp2) { + return disp1.displayMode == disp2.displayMode && disp1.parameters.visibleRegion.width == disp2.parameters.visibleRegion.width && + disp1.parameters.visibleRegion.height == disp2.parameters.visibleRegion.height && + disp1.parameters.refreshRate == disp2.parameters.refreshRate; +} + +bool operator==(const VkDisplayModePropertiesKHR& disp1, const VkDisplayModeProperties2KHR& disp2) { + return disp1 == disp2.displayModeProperties; +} +bool operator==(const VkDisplayPlaneCapabilitiesKHR& caps1, const VkDisplayPlaneCapabilitiesKHR& caps2) { + return caps1.supportedAlpha == caps2.supportedAlpha && caps1.minSrcPosition.x == caps2.minSrcPosition.x && + caps1.minSrcPosition.y == caps2.minSrcPosition.y && caps1.maxSrcPosition.x == caps2.maxSrcPosition.x && + caps1.maxSrcPosition.y == caps2.maxSrcPosition.y && caps1.minSrcExtent.width == caps2.minSrcExtent.width && + caps1.minSrcExtent.height == caps2.minSrcExtent.height && caps1.maxSrcExtent.width == caps2.maxSrcExtent.width && + caps1.maxSrcExtent.height == caps2.maxSrcExtent.height && caps1.minDstPosition.x == caps2.minDstPosition.x && + caps1.minDstPosition.y == caps2.minDstPosition.y && caps1.maxDstPosition.x == caps2.maxDstPosition.x && + caps1.maxDstPosition.y == caps2.maxDstPosition.y && caps1.minDstExtent.width == caps2.minDstExtent.width && + caps1.minDstExtent.height == caps2.minDstExtent.height && caps1.maxDstExtent.width == caps2.maxDstExtent.width && + caps1.maxDstExtent.height == caps2.maxDstExtent.height; +} + +bool operator==(const VkDisplayPlaneCapabilitiesKHR& caps1, const VkDisplayPlaneCapabilities2KHR& caps2) { + return caps1 == caps2.capabilities; +} +bool operator==(const VkDisplayPlanePropertiesKHR& props1, const VkDisplayPlanePropertiesKHR& props2) { + return props1.currentDisplay == props2.currentDisplay && props1.currentStackIndex == props2.currentStackIndex; +} +bool operator==(const VkDisplayPlanePropertiesKHR& props1, const VkDisplayPlaneProperties2KHR& props2) { + return props1 == props2.displayPlaneProperties; +} +bool operator==(const VkExtent2D& ext1, const VkExtent2D& ext2) { return ext1.height == ext2.height && ext1.width == ext2.width; } diff --git a/tests/framework/util/equality_helpers.h b/tests/framework/util/equality_helpers.h new file mode 100644 index 000000000..5c38f5031 --- /dev/null +++ b/tests/framework/util/equality_helpers.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2025 The Khronos Group Inc. + * Copyright (c) 2025 Valve Corporation + * Copyright (c) 2025 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * The above copyright notice(s) and this permission notice shall be included in + * all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + */ + +#pragma once + +#include + +#include +#include + +#include + +inline bool string_eq(const char* a, const char* b) noexcept { return a && b && strcmp(a, b) == 0; } +inline bool string_eq(const char* a, const char* b, size_t len) noexcept { return a && b && strncmp(a, b, len) == 0; } + +bool operator==(const VkExtent3D& a, const VkExtent3D& b); +bool operator!=(const VkExtent3D& a, const VkExtent3D& b); + +bool operator==(const VkQueueFamilyProperties& a, const VkQueueFamilyProperties& b); +bool operator!=(const VkQueueFamilyProperties& a, const VkQueueFamilyProperties& b); + +bool operator==(const VkQueueFamilyProperties& a, const VkQueueFamilyProperties2& b); +bool operator!=(const VkQueueFamilyProperties& a, const VkQueueFamilyProperties2& b); + +bool operator==(const VkLayerProperties& a, const VkLayerProperties& b); +bool operator!=(const VkLayerProperties& a, const VkLayerProperties& b); + +bool operator==(const VkExtensionProperties& a, const VkExtensionProperties& b); +bool operator!=(const VkExtensionProperties& a, const VkExtensionProperties& b); + +bool operator==(const VkPhysicalDeviceFeatures& feats1, const VkPhysicalDeviceFeatures2& feats2); + +bool operator==(const VkPhysicalDeviceMemoryProperties& props1, const VkPhysicalDeviceMemoryProperties2& props2); +bool operator==(const VkSparseImageFormatProperties& props1, const VkSparseImageFormatProperties& props2); +bool operator==(const VkSparseImageFormatProperties& props1, const VkSparseImageFormatProperties2& props2); +bool operator==(const VkExternalMemoryProperties& props1, const VkExternalMemoryProperties& props2); +bool operator==(const VkExternalSemaphoreProperties& props1, const VkExternalSemaphoreProperties& props2); +bool operator==(const VkExternalFenceProperties& props1, const VkExternalFenceProperties& props2); +bool operator==(const VkSurfaceCapabilitiesKHR& props1, const VkSurfaceCapabilitiesKHR& props2); +bool operator==(const VkSurfacePresentScalingCapabilitiesEXT& caps1, const VkSurfacePresentScalingCapabilitiesEXT& caps2); +bool operator==(const VkSurfaceFormatKHR& format1, const VkSurfaceFormatKHR& format2); +bool operator==(const VkSurfaceFormatKHR& format1, const VkSurfaceFormat2KHR& format2); +bool operator==(const VkDisplayPropertiesKHR& props1, const VkDisplayPropertiesKHR& props2); +bool operator==(const VkDisplayPropertiesKHR& props1, const VkDisplayProperties2KHR& props2); +bool operator==(const VkDisplayModePropertiesKHR& disp1, const VkDisplayModePropertiesKHR& disp2); + +bool operator==(const VkDisplayModePropertiesKHR& disp1, const VkDisplayModeProperties2KHR& disp2); +bool operator==(const VkDisplayPlaneCapabilitiesKHR& caps1, const VkDisplayPlaneCapabilitiesKHR& caps2); + +bool operator==(const VkDisplayPlaneCapabilitiesKHR& caps1, const VkDisplayPlaneCapabilities2KHR& caps2); +bool operator==(const VkDisplayPlanePropertiesKHR& props1, const VkDisplayPlanePropertiesKHR& props2); +bool operator==(const VkDisplayPlanePropertiesKHR& props1, const VkDisplayPlaneProperties2KHR& props2); +bool operator==(const VkExtent2D& ext1, const VkExtent2D& ext2); + +// Allow comparison of vectors of different types as long as their elements are comparable (just has to make sure to only apply when +// T != U) +template >> +bool operator==(const std::vector& a, const std::vector& b) { + return std::equal(a.begin(), a.end(), b.begin(), b.end(), [](const auto& left, const auto& right) { return left == right; }); +} diff --git a/tests/framework/util/folder_manager.cpp b/tests/framework/util/folder_manager.cpp new file mode 100644 index 000000000..c406309ed --- /dev/null +++ b/tests/framework/util/folder_manager.cpp @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2025 The Khronos Group Inc. + * Copyright (c) 2025 Valve Corporation + * Copyright (c) 2025 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * The above copyright notice(s) and this permission notice shall be included in + * all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + */ + +#include "folder_manager.h" + +#include +#include + +#include "gtest/gtest.h" + +#include "util/json_writer.h" +#include "util/env_var_wrapper.h" + +namespace fs { +Folder::Folder(std::filesystem::path root_path, std::string name) noexcept : folder((root_path / name).lexically_normal()) { + clear(); + // Don't actually create the folder yet, as we will do it on demand +} +Folder::~Folder() noexcept { clear(); } +Folder::Folder(Folder&& other) noexcept : actually_created(other.actually_created), folder(other.folder) { other.folder.clear(); } +Folder& Folder::operator=(Folder&& other) noexcept { + folder = other.folder; + actually_created = other.actually_created; + other.folder.clear(); + return *this; +} + +void Folder::check_if_first_use() { + if (!actually_created) { + if (!::testing::internal::InDeathTestChild()) { + std::error_code err; + if (!std::filesystem::create_directories(folder, err)) { + std::cerr << "Failed to create folder " << folder << " because " << err.message() << "\n"; + } + } + actually_created = true; + } +} + +std::filesystem::path Folder::write_manifest(std::filesystem::path const& name, std::string const& contents) { + check_if_first_use(); + std::filesystem::path out_path = (folder / name).lexically_normal(); + if (!::testing::internal::InDeathTestChild()) { + auto file = std::ofstream(out_path, std::ios_base::trunc | std::ios_base::out); + if (!file) { + std::cerr << "Failed to create manifest " << name << " at " << out_path << "\n"; + return out_path; + } + file << contents << std::endl; + } + insert_file_to_tracking(name); + return out_path; +} + +// close file handle, delete file, remove `name` from managed file list. +void Folder::remove(std::filesystem::path const& name) { + check_if_first_use(); + std::filesystem::path out_path = folder / name; + if (!::testing::internal::InDeathTestChild()) { + std::error_code err; + if (!std::filesystem::remove(out_path, err)) { + std::cerr << "Failed to remove file " << name << " at " << out_path << " because " << err.message() << "\n"; + } + } + + auto found = std::find(added_files.begin(), added_files.end(), name); + if (found != added_files.end()) { + added_files.erase(found); + } else { + std::cout << "File " << name << " not in tracked files of folder " << folder << ".\n"; + } +} + +// copy file into this folder +std::filesystem::path Folder::copy_file(std::filesystem::path const& file, std::filesystem::path const& new_name) { + check_if_first_use(); + insert_file_to_tracking(new_name); + + auto new_filepath = folder / new_name; + if (!::testing::internal::InDeathTestChild()) { + std::error_code err; + if (!std::filesystem::copy_file(file, new_filepath, err)) { + std::cerr << "Failed to copy file " << file << " to " << new_filepath << " because " << err.message() << "\n"; + } + } + return new_filepath; +} + +std::vector Folder::get_files() const { return added_files; } + +std::filesystem::path Folder::add_symlink(std::filesystem::path const& target, std::filesystem::path const& link_name) { + check_if_first_use(); + + if (!::testing::internal::InDeathTestChild()) { + std::error_code err; + std::filesystem::create_symlink(target, folder / link_name, err); + if (err.value() != 0) { + std::cerr << "Failed to create symlink with target" << target << " with name " << folder / link_name << " because " + << err.message() << "\n"; + } + } + insert_file_to_tracking(link_name); + return folder / link_name; +} +void Folder::insert_file_to_tracking(std::filesystem::path const& name) { + auto found = std::find(added_files.begin(), added_files.end(), name); + if (found != added_files.end()) { + std::cout << "Overwriting manifest " << name << ". Was this intended?\n"; + } else { + added_files.emplace_back(name); + } +} + +void Folder::clear() const noexcept { + if (!::testing::internal::InDeathTestChild()) { + std::error_code err; + std::filesystem::remove_all(folder, err); + if (err.value() != 0) { + std::cerr << "Failed to remove folder " << folder << " because " << err.message() << "\n"; + } + } +} + +std::filesystem::path category_path_name(ManifestCategory category) { + if (category == ManifestCategory::settings) return "settings.d"; + if (category == ManifestCategory::implicit_layer) return "implicit_layer.d"; + if (category == ManifestCategory::explicit_layer) + return "explicit_layer.d"; + else + return "icd.d"; +} + +FileSystemManager::FileSystemManager() + : root_folder(std::filesystem::path(TEST_EXECUTION_DIRECTORY), + std::string(::testing::UnitTest::GetInstance()->GetInstance()->current_test_suite()->name()) + "_" + + ::testing::UnitTest::GetInstance()->current_test_info()->name()) { + // Clean out test folder in case a previous run's files are still around + root_folder.clear(); + + auto const& root = root_folder.location(); + folders.try_emplace(ManifestLocation::null, root, std::string("null_dir")); + folders.try_emplace(ManifestLocation::driver, root, std::string("icd_manifests")); + folders.try_emplace(ManifestLocation::driver_env_var, root, std::string("icd_env_vars_manifests")); + folders.try_emplace(ManifestLocation::explicit_layer, root, std::string("explicit_layer_manifests")); + folders.try_emplace(ManifestLocation::explicit_layer_env_var, root, std::string("explicit_env_var_layer_folder")); + folders.try_emplace(ManifestLocation::explicit_layer_add_env_var, root, std::string("explicit_add_env_var_layer_folder")); + folders.try_emplace(ManifestLocation::implicit_layer, root, std::string("implicit_layer_manifests")); + folders.try_emplace(ManifestLocation::implicit_layer_env_var, root, std::string("implicit_env_var_layer_manifests")); + folders.try_emplace(ManifestLocation::implicit_layer_add_env_var, root, std::string("implicit_add_env_var_layer_manifests")); + folders.try_emplace(ManifestLocation::override_layer, root, std::string("override_layer_manifests")); +#if defined(_WIN32) + folders.try_emplace(ManifestLocation::windows_app_package, root, std::string("app_package_manifests")); +#endif +#if defined(__APPLE__) + folders.try_emplace(ManifestLocation::macos_bundle, root, std::string("macos_bundle")); +#endif + folders.try_emplace(ManifestLocation::settings_location, root, std::string("settings_location")); + folders.try_emplace(ManifestLocation::unsecured_driver, root, std::string("unsecured_driver")); + folders.try_emplace(ManifestLocation::unsecured_explicit_layer, root, std::string("unsecured_explicit_layer")); + folders.try_emplace(ManifestLocation::unsecured_implicit_layer, root, std::string("unsecured_implicit_layer")); + folders.try_emplace(ManifestLocation::unsecured_settings, root, std::string("unsecured_settings")); +} + +fs::Folder& FileSystemManager::get_folder(ManifestLocation location) noexcept { return folders.at(location); } +fs::Folder const& FileSystemManager::get_folder(ManifestLocation location) const noexcept { return folders.at(location); } + +Folder* FileSystemManager::get_folder_for_given_path(std::filesystem::path const& given_path) noexcept { + auto normalized_path = given_path.lexically_normal(); + for (auto const& [manifest_location, folder] : folders) { + if (folder.location() == normalized_path) { + return &folders.at(manifest_location); + } + } + if (redirected_paths.count(given_path.string()) > 0) { + return &folders.at(redirected_paths.at(normalized_path.string())); + } + for (auto const& [redirected_path_str, redirected_location] : redirected_paths) { + std::filesystem::path redirected_path = redirected_path_str; + const auto mismatch_pair = + std::mismatch(normalized_path.begin(), normalized_path.end(), redirected_path.begin(), redirected_path.end()); + if (mismatch_pair.second == redirected_path.end()) return &folders.at(redirected_location); + } + return nullptr; +} + +bool FileSystemManager::is_folder_path(std::filesystem::path const& path) const noexcept { + for (auto const& [location, folder] : folders) { + if (path == folder.location()) { + return true; + } + } + return false; +} + +void FileSystemManager::add_path_redirect(std::filesystem::path const& redirected_path, ManifestLocation location) { + redirected_paths[redirected_path.string()] = location; +} + +std::filesystem::path FileSystemManager::get_real_path_of_redirected_path(std::filesystem::path const& path) const { + if (redirected_paths.count(path.string()) > 0) { + return folders.at(redirected_paths.at(path.lexically_normal().string())).location(); + } + for (auto const& [redirected_path_str, redirected_location] : redirected_paths) { + std::filesystem::path redirected_path = redirected_path_str; + const auto mismatch_pair = std::mismatch(path.begin(), path.end(), redirected_path.begin(), redirected_path.end()); + if (mismatch_pair.second == redirected_path.end()) return folders.at(redirected_location).location(); + } + return std::filesystem::path{}; +} + +std::filesystem::path FileSystemManager::get_path_redirect_by_manifest_location(ManifestLocation location) const { + for (auto const& [path, redirected_location] : redirected_paths) { + if (redirected_location == location) { + return path; + } + } + return folders.at(ManifestLocation::null).location(); +} + +} // namespace fs diff --git a/tests/framework/util/folder_manager.h b/tests/framework/util/folder_manager.h new file mode 100644 index 000000000..68e69c211 --- /dev/null +++ b/tests/framework/util/folder_manager.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2025 The Khronos Group Inc. + * Copyright (c) 2025 Valve Corporation + * Copyright (c) 2025 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * The above copyright notice(s) and this permission notice shall be included in + * all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + */ + +#pragma once + +#include +#include +#include +#include + +#include "test_defines.h" + +namespace fs { + +class Folder { + public: + explicit Folder(std::filesystem::path root_path, std::string name) noexcept; + ~Folder() noexcept; + Folder(Folder const&) = delete; + Folder& operator=(Folder const&) = delete; + Folder(Folder&& other) noexcept; + Folder& operator=(Folder&& other) noexcept; + + // Add a manifest to the folder + std::filesystem::path write_manifest(std::filesystem::path const& name, std::string const& contents); + + // close file handle, delete file, remove `name` from managed file list. + void remove(std::filesystem::path const& name); + + // Remove all contents in the path + void clear() const noexcept; + + // copy file into this folder with name `new_name`. Returns the full path of the file that was copied + std::filesystem::path copy_file(std::filesystem::path const& file, std::filesystem::path const& new_name); + + // location of the managed folder + std::filesystem::path const& location() const { return folder; } + + std::vector get_files() const; + + // Create a symlink in this folder to target with the filename set to link_name + std::filesystem::path add_symlink(std::filesystem::path const& target, std::filesystem::path const& link_name); + + private: + bool actually_created = false; + std::filesystem::path folder; + std::vector added_files; + + void insert_file_to_tracking(std::filesystem::path const& name); + void check_if_first_use(); +}; + +class FileSystemManager { + fs::Folder root_folder; + std::unordered_map folders; + + // paths that this folder appears as to the loader (ex /usr/share/vulkan/explicit_layer.d is in the ManifestLocation::icd folder + std::unordered_map redirected_paths; + + public: + FileSystemManager(); + + Folder& get_folder(ManifestLocation location) noexcept; + Folder const& get_folder(ManifestLocation location) const noexcept; + + // Gets a pointer to the folder that given_path points to. This includes redirected paths as well as the exact path of folders + Folder* get_folder_for_given_path(std::filesystem::path const& given_path) noexcept; + + bool is_folder_path(std::filesystem::path const& path) const noexcept; + + void add_path_redirect(std::filesystem::path const& apparent_path, ManifestLocation location); + + // Returns the first redirected path pointing at by a manifest location. Returns the NULL location if one isn't found + std::filesystem::path get_path_redirect_by_manifest_location(ManifestLocation location) const; + + // Returns the real path that a redirected path points to. Returns an empty path if no redirect is found + std::filesystem::path get_real_path_of_redirected_path(std::filesystem::path const& redirected_path) const; +}; + +} // namespace fs diff --git a/tests/framework/util/functions.h b/tests/framework/util/functions.h new file mode 100644 index 000000000..5debba9fe --- /dev/null +++ b/tests/framework/util/functions.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2025 The Khronos Group Inc. + * Copyright (c) 2025 Valve Corporation + * Copyright (c) 2025 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * The above copyright notice(s) and this permission notice shall be included in + * all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + */ + +#pragma once + +#include + +#include + +class FromVoidStarFunc { + void* function; + + public: + FromVoidStarFunc(void* function) : function(function) {} + FromVoidStarFunc(PFN_vkVoidFunction function) : function(reinterpret_cast(function)) {} + + template + operator T() { + return reinterpret_cast(function); + } +}; + +template +PFN_vkVoidFunction to_vkVoidFunction(T func) { + return reinterpret_cast(func); +} + +struct VulkanFunction { + std::string name; + PFN_vkVoidFunction function = nullptr; +}; diff --git a/tests/framework/util/get_executable_path.cpp b/tests/framework/util/get_executable_path.cpp new file mode 100644 index 000000000..9edb5d38d --- /dev/null +++ b/tests/framework/util/get_executable_path.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2025 The Khronos Group Inc. + * Copyright (c) 2025 Valve Corporation + * Copyright (c) 2025 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * The above copyright notice(s) and this permission notice shall be included in + * all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + */ + +#include "get_executable_path.h" + +#if defined(__linux__) || defined(__GNU__) + +#include + +// find application path + name. Path cannot be longer than 1024, returns NULL if it is greater than that. +std::string test_platform_executable_path() { + std::string buffer; + buffer.resize(1024); + ssize_t count = readlink("/proc/self/exe", &buffer[0], buffer.size()); + if (count == -1) return NULL; + if (count == 0) return NULL; + buffer[count] = '\0'; + buffer.resize(count); + return buffer; +} +#elif defined(__APPLE__) + +#include +#include + +std::string test_platform_executable_path() { + std::string buffer; + buffer.resize(1024); + pid_t pid = getpid(); + int ret = proc_pidpath(pid, &buffer[0], static_cast(buffer.size())); + if (ret <= 0) return NULL; + buffer[ret] = '\0'; + buffer.resize(ret); + return buffer; +} +#elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) +#include +std::string test_platform_executable_path() { + int mib[] = { + CTL_KERN, +#if defined(__NetBSD__) + KERN_PROC_ARGS, + -1, + KERN_PROC_PATHNAME, +#else + KERN_PROC, + KERN_PROC_PATHNAME, + -1, +#endif + }; + std::string buffer; + buffer.resize(1024); + size_t size = buffer.size(); + if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &buffer[0], &size, NULL, 0) < 0) { + return NULL; + } + buffer.resize(size); + + return buffer; +} +#elif defined(__Fuchsia__) || defined(__OpenBSD__) +std::string test_platform_executable_path() { return {}; } +#elif defined(__QNX__) + +#ifndef SYSCONFDIR +#define SYSCONFDIR "/etc" +#endif + +#include +#include + +std::string test_platform_executable_path() { + std::string buffer; + buffer.resize(1024); + int fd = open("/proc/self/exefile", O_RDONLY); + ssize_t rdsize; + + if (fd == -1) { + return NULL; + } + + rdsize = read(fd, &buffer[0], buffer.size()); + if (rdsize < 0) { + return NULL; + } + buffer[rdsize] = 0x00; + close(fd); + buffer.resize(rdsize); + + return buffer; +} +#endif // defined (__QNX__) + +#if defined(_WIN32) + +#include + +#include + +#include "wide_char_handling.h" + +std::string test_platform_executable_path() { + std::string buffer; + buffer.resize(1024); + DWORD ret = GetModuleFileName(NULL, static_cast(&buffer[0]), (DWORD)buffer.size()); + if (ret == 0) return NULL; + if (ret > buffer.size()) return NULL; + buffer.resize(ret); + buffer[ret] = '\0'; + return narrow(std::filesystem::path(buffer).native()); +} + +#endif diff --git a/tests/framework/util/get_executable_path.h b/tests/framework/util/get_executable_path.h new file mode 100644 index 000000000..bb296e54f --- /dev/null +++ b/tests/framework/util/get_executable_path.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2025 The Khronos Group Inc. + * Copyright (c) 2025 Valve Corporation + * Copyright (c) 2025 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * The above copyright notice(s) and this permission notice shall be included in + * all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + */ + +#pragma once + +#include + +// find application path + name. Path cannot be longer than 1024, returns NULL if it is greater than that. +std::string test_platform_executable_path(); diff --git a/tests/framework/util/json_writer.cpp b/tests/framework/util/json_writer.cpp new file mode 100644 index 000000000..1a955c09a --- /dev/null +++ b/tests/framework/util/json_writer.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2023 The Khronos Group Inc. + * Copyright (c) 2023 Valve Corporation + * Copyright (c) 2023 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * The above copyright notice(s) and this permission notice shall be included in + * all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + * Author: Charles Giessen + */ + +#include "json_writer.h" + +#include "wide_char_handling.h" + +void JsonWriter::StartObject() { + CommaAndNewLine(); + Indent(); + output += "{"; + stack.push(false); +} +void JsonWriter::StartKeyedObject(std::string const& key) { + CommaAndNewLine(); + Indent(); + output += "\"" + key + "\": {"; + stack.push(false); +} +void JsonWriter::EndObject() { + stack.pop(); + output += "\n"; + Indent(); + output += "}"; +} +void JsonWriter::StartKeyedArray(std::string const& key) { + CommaAndNewLine(); + Indent(); + output += "\"" + key + "\": ["; + stack.push(false); +} +void JsonWriter::StartArray() { + CommaAndNewLine(); + Indent(); + output += "["; + stack.push(false); +} +void JsonWriter::EndArray() { + stack.pop(); + output += "\n"; + Indent(); + output += "]"; +} + +void JsonWriter::AddKeyedString(std::string const& key, std::string const& value) { + CommaAndNewLine(); + Indent(); + output += "\"" + key + "\": \"" + escape(value) + "\""; +} +void JsonWriter::AddString(std::string const& value) { + CommaAndNewLine(); + Indent(); + output += "\"" + escape(value) + "\""; +} +#if defined(_WIN32) +void JsonWriter::AddKeyedString(std::string const& key, std::wstring const& value) { + CommaAndNewLine(); + Indent(); + output += "\"" + key + "\": \"" + escape(narrow(value)) + "\""; +} +void JsonWriter::AddString(std::wstring const& value) { + CommaAndNewLine(); + Indent(); + output += "\"" + escape(narrow(value)) + "\""; +} +#endif + +void JsonWriter::AddKeyedBool(std::string const& key, bool value) { + CommaAndNewLine(); + Indent(); + output += "\"" + key + "\": " + (value ? "true" : "false"); +} +void JsonWriter::AddBool(bool value) { + CommaAndNewLine(); + Indent(); + output += std::string(value ? "true" : "false"); +} + +void JsonWriter::AddKeyedNumber(std::string const& key, double number) { + CommaAndNewLine(); + Indent(); + output += "\"" + key + "\": " + std::to_string(number); +} +void JsonWriter::AddNumber(double number) { + CommaAndNewLine(); + Indent(); + output += std::to_string(number); +} + +void JsonWriter::AddKeyedInteger(std::string const& key, int64_t number) { + CommaAndNewLine(); + Indent(); + output += "\"" + key + "\": " + std::to_string(number); +} +void JsonWriter::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 +std::string JsonWriter::escape(std::string const& in_path) { + std::string out; + for (auto& c : in_path) { + if (c == '\\') + out += "\\\\"; + else + out += c; + } + return out; +} +std::string JsonWriter::escape(std::filesystem::path const& in_path) { return escape(narrow(in_path.native())); } + +void JsonWriter::CommaAndNewLine() { + if (stack.size() > 0) { + if (stack.top() == false) { + stack.top() = true; + } else { + output += ","; + } + output += "\n"; + } +} +void JsonWriter::Indent() { + for (uint32_t i = 0; i < stack.size(); i++) { + output += '\t'; + } +} diff --git a/tests/framework/util/json_writer.h b/tests/framework/util/json_writer.h new file mode 100644 index 000000000..a5c31545a --- /dev/null +++ b/tests/framework/util/json_writer.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2023 The Khronos Group Inc. + * Copyright (c) 2023 Valve Corporation + * Copyright (c) 2023 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * The above copyright notice(s) and this permission notice shall be included in + * all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + * Author: Charles Giessen + */ + +#pragma once + +#include +#include +#include +#include + +#include "wide_char_handling.h" + +// Utility class to simplify the writing of JSON manifest files + +struct JsonWriter { + std::string output; + + // the bool represents whether an object has been written & a comma is needed + std::stack stack; + + void StartObject(); + void StartKeyedObject(std::string const& key); + void EndObject(); + void StartKeyedArray(std::string const& key); + void StartArray(); + void EndArray(); + + void AddKeyedString(std::string const& key, std::string const& value); + void AddString(std::string const& value); +#if defined(_WIN32) + void AddKeyedString(std::string const& key, std::wstring const& value); + void AddString(std::wstring const& value); +#endif + + void AddKeyedBool(std::string const& key, bool value); + void AddBool(bool value); + + void AddKeyedNumber(std::string const& key, double number); + void AddNumber(double number); + + void AddKeyedInteger(std::string const& key, int64_t number); + void AddInteger(int64_t 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); + static std::string escape(std::filesystem::path const& in_path); + + private: + void CommaAndNewLine(); + void Indent(); +}; diff --git a/tests/framework/util/manifest_builders.cpp b/tests/framework/util/manifest_builders.cpp new file mode 100644 index 000000000..6666a5769 --- /dev/null +++ b/tests/framework/util/manifest_builders.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2025 The Khronos Group Inc. + * Copyright (c) 2025 Valve Corporation + * Copyright (c) 2025 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * The above copyright notice(s) and this permission notice shall be included in + * all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + */ + +#include "manifest_builders.h" + +#include "json_writer.h" + +template +void print_object_of_t(JsonWriter& writer, const char* object_name, std::vector const& vec) { + if (vec.size() == 0) return; + writer.StartKeyedObject(object_name); + for (auto& element : vec) { + element.get_manifest_str(writer); + } + writer.EndObject(); +} + +template +void print_array_of_t(JsonWriter& writer, const char* object_name, std::vector const& vec) { + if (vec.size() == 0) return; + writer.StartKeyedArray(object_name); + for (auto& element : vec) { + element.get_manifest_str(writer); + } + writer.EndArray(); +} +void print_vector_of_strings(JsonWriter& writer, const char* object_name, std::vector const& strings) { + if (strings.size() == 0) return; + writer.StartKeyedArray(object_name); + for (auto const& str : strings) { + writer.AddString(std::filesystem::path(str).native()); + } + writer.EndArray(); +} +void print_vector_of_strings(JsonWriter& writer, const char* object_name, std::vector const& paths) { + if (paths.size() == 0) return; + writer.StartKeyedArray(object_name); + for (auto const& path : paths) { + writer.AddString(path.native()); + } + writer.EndArray(); +} + +std::string version_to_string(uint32_t version) { + std::string out = std::to_string(VK_API_VERSION_MAJOR(version)) + "." + std::to_string(VK_API_VERSION_MINOR(version)) + "." + + std::to_string(VK_API_VERSION_PATCH(version)); + if (VK_API_VERSION_VARIANT(version) != 0) out += std::to_string(VK_API_VERSION_VARIANT(version)) + "." + out; + return out; +} + +std::string to_text(bool b) { return b ? std::string("true") : std::string("false"); } + +std::string ManifestICD::get_manifest_str() const { + JsonWriter writer; + writer.StartObject(); + writer.AddKeyedString("file_format_version", file_format_version.get_version_str()); + writer.StartKeyedObject("ICD"); + writer.AddKeyedString("library_path", lib_path.native()); + writer.AddKeyedString("api_version", version_to_string(api_version)); + writer.AddKeyedBool("is_portability_driver", is_portability_driver); + if (!library_arch.empty()) writer.AddKeyedString("library_arch", library_arch); + writer.EndObject(); + writer.EndObject(); + return writer.output; +} + +void ManifestLayer::LayerDescription::FunctionOverride::get_manifest_str(JsonWriter& writer) const { + writer.AddKeyedString(vk_func, override_name); +} + +void ManifestLayer::LayerDescription::Extension::get_manifest_str(JsonWriter& writer) const { + writer.StartObject(); + writer.AddKeyedString("name", name); + writer.AddKeyedString("spec_version", std::to_string(spec_version)); + writer.AddKeyedString("spec_version", std::to_string(spec_version)); + print_vector_of_strings(writer, "entrypoints", entrypoints); + writer.EndObject(); +} + +void ManifestLayer::LayerDescription::get_manifest_str(JsonWriter& writer) const { + writer.AddKeyedString("name", name); + writer.AddKeyedString("type", get_type_str(type)); + if (!lib_path.empty()) { + writer.AddKeyedString("library_path", lib_path.native()); + } + writer.AddKeyedString("api_version", version_to_string(api_version)); + writer.AddKeyedString("implementation_version", std::to_string(implementation_version)); + writer.AddKeyedString("description", description); + print_object_of_t(writer, "functions", functions); + print_array_of_t(writer, "instance_extensions", instance_extensions); + print_array_of_t(writer, "device_extensions", device_extensions); + if (!enable_environment.empty()) { + writer.StartKeyedObject("enable_environment"); + writer.AddKeyedString(enable_environment, "1"); + writer.EndObject(); + } + if (!disable_environment.empty()) { + writer.StartKeyedObject("disable_environment"); + writer.AddKeyedString(disable_environment, "1"); + writer.EndObject(); + } + print_vector_of_strings(writer, "component_layers", component_layers); + print_vector_of_strings(writer, "blacklisted_layers", blacklisted_layers); + print_vector_of_strings(writer, "override_paths", override_paths); + print_vector_of_strings(writer, "app_keys", app_keys); + print_object_of_t(writer, "pre_instance_functions", pre_instance_functions); + if (!library_arch.empty()) { + writer.AddKeyedString("library_arch", library_arch); + } +} + +VkLayerProperties ManifestLayer::LayerDescription::get_layer_properties() const { + VkLayerProperties properties{}; + name.copy(properties.layerName, VK_MAX_EXTENSION_NAME_SIZE); + description.copy(properties.description, VK_MAX_EXTENSION_NAME_SIZE); + properties.implementationVersion = implementation_version; + properties.specVersion = api_version; + return properties; +} + +std::string ManifestLayer::get_manifest_str() const { + JsonWriter writer; + writer.StartObject(); + writer.AddKeyedString("file_format_version", file_format_version.get_version_str()); + if (layers.size() == 1) { + writer.StartKeyedObject("layer"); + layers.at(0).get_manifest_str(writer); + writer.EndObject(); + } else { + writer.StartKeyedArray("layers"); + for (size_t i = 0; i < layers.size(); i++) { + writer.StartObject(); + layers.at(i).get_manifest_str(writer); + writer.EndObject(); + } + writer.EndArray(); + } + writer.EndObject(); + return writer.output; +} diff --git a/tests/framework/util/manifest_builders.h b/tests/framework/util/manifest_builders.h new file mode 100644 index 000000000..bc559dda7 --- /dev/null +++ b/tests/framework/util/manifest_builders.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2025 The Khronos Group Inc. + * Copyright (c) 2025 Valve Corporation + * Copyright (c) 2025 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * The above copyright notice(s) and this permission notice shall be included in + * all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + */ + +#pragma once + +#include +#include +#include + +#include + +#include "builder_defines.h" + +// Forward declaration since we take it by reference only +class JsonWriter; + +struct ManifestVersion { + BUILDER_VALUE_WITH_DEFAULT(uint32_t, major, 1) + BUILDER_VALUE_WITH_DEFAULT(uint32_t, minor, 0) + BUILDER_VALUE_WITH_DEFAULT(uint32_t, patch, 0) + + std::string get_version_str() const noexcept { + return std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(patch); + } +}; + +// ManifestICD builder +struct ManifestICD { + BUILDER_VALUE(ManifestVersion, file_format_version) + BUILDER_VALUE(uint32_t, api_version) + BUILDER_VALUE(std::filesystem::path, lib_path) + BUILDER_VALUE(bool, is_portability_driver) + BUILDER_VALUE(std::string, library_arch) + std::string get_manifest_str() const; +}; + +// ManifestLayer builder +struct ManifestLayer { + struct LayerDescription { + enum class Type { INSTANCE, GLOBAL, DEVICE }; + std::string get_type_str(Type layer_type) const { + if (layer_type == Type::GLOBAL) + return "GLOBAL"; + else if (layer_type == Type::DEVICE) + return "DEVICE"; + else // default + return "INSTANCE"; + } + struct FunctionOverride { + BUILDER_VALUE(std::string, vk_func) + BUILDER_VALUE(std::string, override_name) + + void get_manifest_str(JsonWriter& writer) const; + }; + struct Extension { + Extension() noexcept {} + Extension(std::string name, uint32_t spec_version = 0, std::vector entrypoints = {}) noexcept + : name(name), spec_version(spec_version), entrypoints(entrypoints) {} + std::string name; + uint32_t spec_version = 0; + std::vector entrypoints; + void get_manifest_str(JsonWriter& writer) const; + }; + BUILDER_VALUE(std::string, name) + BUILDER_VALUE_WITH_DEFAULT(Type, type, Type::INSTANCE) + BUILDER_VALUE(std::filesystem::path, lib_path) + BUILDER_VALUE_WITH_DEFAULT(uint32_t, api_version, VK_API_VERSION_1_0) + BUILDER_VALUE(uint32_t, implementation_version) + BUILDER_VALUE(std::string, description) + BUILDER_VECTOR(FunctionOverride, functions, function) + BUILDER_VECTOR(Extension, instance_extensions, instance_extension) + BUILDER_VECTOR(Extension, device_extensions, device_extension) + BUILDER_VALUE(std::string, enable_environment) + BUILDER_VALUE(std::string, disable_environment) + BUILDER_VECTOR(std::string, component_layers, component_layer) + BUILDER_VECTOR(std::string, blacklisted_layers, blacklisted_layer) + BUILDER_VECTOR(std::filesystem::path, override_paths, override_path) + BUILDER_VECTOR(FunctionOverride, pre_instance_functions, pre_instance_function) + BUILDER_VECTOR(std::string, app_keys, app_key) + BUILDER_VALUE(std::string, library_arch) + + void get_manifest_str(JsonWriter& writer) const; + VkLayerProperties get_layer_properties() const; + }; + BUILDER_VALUE(ManifestVersion, file_format_version) + BUILDER_VECTOR(LayerDescription, layers, layer) + + std::string get_manifest_str() const; +}; diff --git a/tests/framework/util/platform_wsi.cpp b/tests/framework/util/platform_wsi.cpp new file mode 100644 index 000000000..748abdcf2 --- /dev/null +++ b/tests/framework/util/platform_wsi.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2025 The Khronos Group Inc. + * Copyright (c) 2025 Valve Corporation + * Copyright (c) 2025 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * The above copyright notice(s) and this permission notice shall be included in + * all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + */ + +#include "platform_wsi.h" + +#include "equality_helpers.h" + +const char* get_platform_wsi_extension([[maybe_unused]] const char* api_selection) { +#if defined(VK_USE_PLATFORM_ANDROID_KHR) + return "VK_KHR_android_surface"; +#elif defined(VK_USE_PLATFORM_DIRECTFB_EXT) + return "VK_EXT_directfb_surface"; +#elif defined(VK_USE_PLATFORM_FUCHSIA) + return "VK_FUCHSIA_imagepipe_surface"; +#elif defined(VK_USE_PLATFORM_GGP) + return "VK_GGP_stream_descriptor_surface"; +#elif defined(VK_USE_PLATFORM_IOS_MVK) + return "VK_MVK_ios_surface"; +#elif defined(VK_USE_PLATFORM_MACOS_MVK) || defined(VK_USE_PLATFORM_METAL_EXT) +#if defined(VK_USE_PLATFORM_MACOS_MVK) + if (string_eq(api_selection, "VK_USE_PLATFORM_MACOS_MVK")) return "VK_MVK_macos_surface"; +#endif +#if defined(VK_USE_PLATFORM_METAL_EXT) + if (string_eq(api_selection, "VK_USE_PLATFORM_METAL_EXT")) return "VK_EXT_metal_surface"; + return "VK_EXT_metal_surface"; +#endif +#elif defined(VK_USE_PLATFORM_SCREEN_QNX) + return "VK_QNX_screen_surface"; +#elif defined(VK_USE_PLATFORM_VI_NN) + return "VK_NN_vi_surface"; +#elif defined(VK_USE_PLATFORM_XCB_KHR) || defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_WAYLAND_KHR) +#if defined(VK_USE_PLATFORM_XCB_KHR) + if (string_eq(api_selection, "VK_USE_PLATFORM_XCB_KHR")) return "VK_KHR_xcb_surface"; +#endif +#if defined(VK_USE_PLATFORM_XLIB_KHR) + if (string_eq(api_selection, "VK_USE_PLATFORM_XLIB_KHR")) return "VK_KHR_xlib_surface"; +#endif +#if defined(VK_USE_PLATFORM_WAYLAND_KHR) + if (string_eq(api_selection, "VK_USE_PLATFORM_WAYLAND_KHR")) return "VK_KHR_wayland_surface"; +#endif +#if defined(VK_USE_PLATFORM_XCB_KHR) + return "VK_KHR_xcb_surface"; +#endif +#elif defined(VK_USE_PLATFORM_WIN32_KHR) + return "VK_KHR_win32_surface"; +#else + return "VK_KHR_display"; +#endif +} diff --git a/tests/framework/util/platform_wsi.h b/tests/framework/util/platform_wsi.h new file mode 100644 index 000000000..66b7b507e --- /dev/null +++ b/tests/framework/util/platform_wsi.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2025 The Khronos Group Inc. + * Copyright (c) 2025 Valve Corporation + * Copyright (c) 2025 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * The above copyright notice(s) and this permission notice shall be included in + * all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + */ + +#pragma once + +const char* get_platform_wsi_extension([[maybe_unused]] const char* api_selection); diff --git a/tests/framework/util/test_defines.h b/tests/framework/util/test_defines.h new file mode 100644 index 000000000..63d6a32e9 --- /dev/null +++ b/tests/framework/util/test_defines.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2025 The Khronos Group Inc. + * Copyright (c) 2025 Valve Corporation + * Copyright (c) 2025 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * The above copyright notice(s) and this permission notice shall be included in + * all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + */ + +#pragma once + +#if defined(__GNUC__) && __GNUC__ >= 4 +#define FRAMEWORK_EXPORT __attribute__((visibility("default"))) +#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590) +#define FRAMEWORK_EXPORT __attribute__((visibility("default"))) +#elif defined(_WIN32) +#define FRAMEWORK_EXPORT __declspec(dllexport) +#else +#define FRAMEWORK_EXPORT +#endif + +// Set of platforms with a common set of functionality which is queried throughout the program +#if defined(__linux__) || defined(__APPLE__) || defined(__Fuchsia__) || defined(__QNX__) || defined(__FreeBSD__) || \ + defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__GNU__) +#define TESTING_COMMON_UNIX_PLATFORMS 1 +#else +#define TESTING_COMMON_UNIX_PLATFORMS 0 +#endif + +#include FRAMEWORK_CONFIG_HEADER + +enum class ManifestCategory { implicit_layer, explicit_layer, icd, settings }; + +// Controls whether to create a manifest and where to put it +enum class ManifestDiscoveryType { + generic, // put the manifest in the regular locations + unsecured_generic, // put the manifest in a user folder rather than system + none, // Do not write the manifest anywhere (for Direct Driver Loading) + null_dir, // put the manifest in the 'null_dir' which the loader does not search in (D3DKMT for instance) + env_var, // use the corresponding env-var for it + add_env_var, // use the corresponding add-env-var for it + override_folder, // add to a special folder for the override layer to use +#if defined(_WIN32) + windows_app_package, // let the app package search find it +#endif +#if defined(__APPLE__) + macos_bundle, // place it in a location only accessible to macos bundles +#endif +}; + +enum class LibraryPathType { + absolute, // default for testing - the exact path of the binary + relative, // Relative to the manifest file + default_search_paths, // Dont add any path information to the library_path - force the use of the default search paths +}; + +// Locations that files can go in the test framework +enum class ManifestLocation { + null, + driver, + driver_env_var, + explicit_layer, + explicit_layer_env_var, + explicit_layer_add_env_var, + implicit_layer, + implicit_layer_env_var, + implicit_layer_add_env_var, + override_layer, +#if defined(_WIN32) + windows_app_package, +#endif +#if defined(__APPLE__) + macos_bundle, +#endif + settings_location, + unsecured_driver, + unsecured_explicit_layer, + unsecured_implicit_layer, + unsecured_settings, +}; diff --git a/tests/framework/util/vulkan_object_wrappers.cpp b/tests/framework/util/vulkan_object_wrappers.cpp new file mode 100644 index 000000000..0223ff68f --- /dev/null +++ b/tests/framework/util/vulkan_object_wrappers.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2025 The Khronos Group Inc. + * Copyright (c) 2025 Valve Corporation + * Copyright (c) 2025 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * The above copyright notice(s) and this permission notice shall be included in + * all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + */ + +#include "vulkan_object_wrappers.h" + +#include "platform_wsi.h" + +VkExtensionProperties Extension::get() const noexcept { + VkExtensionProperties props{}; + extensionName.copy(props.extensionName, VK_MAX_EXTENSION_NAME_SIZE); + props.specVersion = specVersion; + return props; +} + +InstanceCreateInfo::InstanceCreateInfo() { + instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + application_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; +} + +VkInstanceCreateInfo* InstanceCreateInfo::get() noexcept { + if (fill_in_application_info) { + application_info.pApplicationName = app_name.c_str(); + application_info.pEngineName = engine_name.c_str(); + application_info.applicationVersion = app_version; + application_info.engineVersion = engine_version; + application_info.apiVersion = api_version; + instance_info.pApplicationInfo = &application_info; + } + instance_info.flags = flags; + instance_info.enabledLayerCount = static_cast(enabled_layers.size()); + instance_info.ppEnabledLayerNames = enabled_layers.data(); + instance_info.enabledExtensionCount = static_cast(enabled_extensions.size()); + instance_info.ppEnabledExtensionNames = enabled_extensions.data(); + return &instance_info; +} +InstanceCreateInfo& InstanceCreateInfo::set_api_version(uint32_t major, uint32_t minor, uint32_t patch) { + this->api_version = VK_MAKE_API_VERSION(0, major, minor, patch); + return *this; +} +InstanceCreateInfo& InstanceCreateInfo::setup_WSI(const char* api_selection) { + add_extensions({"VK_KHR_surface", get_platform_wsi_extension(api_selection)}); + return *this; +} + +DeviceQueueCreateInfo::DeviceQueueCreateInfo() { queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; } +DeviceQueueCreateInfo::DeviceQueueCreateInfo(const VkDeviceQueueCreateInfo* create_info) { + queue_create_info = *create_info; + for (uint32_t i = 0; i < create_info->queueCount; i++) { + priorities.push_back(create_info->pQueuePriorities[i]); + } +} + +VkDeviceQueueCreateInfo DeviceQueueCreateInfo::get() noexcept { + queue_create_info.pQueuePriorities = priorities.data(); + queue_create_info.queueCount = 1; + queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + return queue_create_info; +} + +DeviceCreateInfo::DeviceCreateInfo(const VkDeviceCreateInfo* create_info) { + dev = *create_info; + for (uint32_t i = 0; i < create_info->enabledExtensionCount; i++) { + enabled_extensions.push_back(create_info->ppEnabledExtensionNames[i]); + } + for (uint32_t i = 0; i < create_info->enabledLayerCount; i++) { + enabled_layers.push_back(create_info->ppEnabledLayerNames[i]); + } + for (uint32_t i = 0; i < create_info->queueCreateInfoCount; i++) { + device_queue_infos.push_back(create_info->pQueueCreateInfos[i]); + } +} + +VkDeviceCreateInfo* DeviceCreateInfo::get() noexcept { + dev.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + dev.enabledLayerCount = static_cast(enabled_layers.size()); + dev.ppEnabledLayerNames = enabled_layers.data(); + dev.enabledExtensionCount = static_cast(enabled_extensions.size()); + dev.ppEnabledExtensionNames = enabled_extensions.data(); + uint32_t index = 0; + for (auto& queue : queue_info_details) { + queue.queue_create_info.queueFamilyIndex = index++; + queue.queue_create_info.queueCount = 1; + device_queue_infos.push_back(queue.get()); + } + + dev.queueCreateInfoCount = static_cast(device_queue_infos.size()); + dev.pQueueCreateInfos = device_queue_infos.data(); + return &dev; +} diff --git a/tests/framework/util/vulkan_object_wrappers.h b/tests/framework/util/vulkan_object_wrappers.h new file mode 100644 index 000000000..7cc923515 --- /dev/null +++ b/tests/framework/util/vulkan_object_wrappers.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2025 The Khronos Group Inc. + * Copyright (c) 2025 Valve Corporation + * Copyright (c) 2025 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * The above copyright notice(s) and this permission notice shall be included in + * all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + */ + +#pragma once + +#include +#include + +#include + +#include "builder_defines.h" + +struct Extension { + BUILDER_VALUE(std::string, extensionName) + BUILDER_VALUE_WITH_DEFAULT(uint32_t, specVersion, VK_API_VERSION_1_0) + + Extension(const char* name, uint32_t specVersion = VK_API_VERSION_1_0) noexcept + : extensionName(name), specVersion(specVersion) {} + Extension(std::string extensionName, uint32_t specVersion = VK_API_VERSION_1_0) noexcept + : extensionName(extensionName), specVersion(specVersion) {} + + VkExtensionProperties get() const noexcept; +}; + +struct MockQueueFamilyProperties { + BUILDER_VALUE(VkQueueFamilyProperties, properties) + BUILDER_VALUE(bool, support_present) + + VkQueueFamilyProperties get() const noexcept { return properties; } +}; + +struct InstanceCreateInfo { + BUILDER_VALUE(VkInstanceCreateInfo, instance_info) + BUILDER_VALUE(VkApplicationInfo, application_info) + BUILDER_VALUE(std::string, app_name) + BUILDER_VALUE(std::string, engine_name) + BUILDER_VALUE(uint32_t, flags) + BUILDER_VALUE(uint32_t, app_version) + BUILDER_VALUE(uint32_t, engine_version) + BUILDER_VALUE_WITH_DEFAULT(uint32_t, api_version, VK_API_VERSION_1_0) + BUILDER_VECTOR(const char*, enabled_layers, layer) + BUILDER_VECTOR(const char*, enabled_extensions, extension) + // tell the get() function to not provide `application_info` + BUILDER_VALUE_WITH_DEFAULT(bool, fill_in_application_info, true) + + InstanceCreateInfo(); + + VkInstanceCreateInfo* get() noexcept; + + InstanceCreateInfo& set_api_version(uint32_t major, uint32_t minor, uint32_t patch); + + InstanceCreateInfo& setup_WSI(const char* api_selection = nullptr); +}; + +struct DeviceQueueCreateInfo { + DeviceQueueCreateInfo(); + DeviceQueueCreateInfo(const VkDeviceQueueCreateInfo* create_info); + + BUILDER_VALUE(VkDeviceQueueCreateInfo, queue_create_info) + BUILDER_VECTOR(float, priorities, priority) + + VkDeviceQueueCreateInfo get() noexcept; +}; + +struct DeviceCreateInfo { + DeviceCreateInfo() = default; + DeviceCreateInfo(const VkDeviceCreateInfo* create_info); + + BUILDER_VALUE(VkDeviceCreateInfo, dev) + BUILDER_VECTOR(const char*, enabled_extensions, extension) + BUILDER_VECTOR(const char*, enabled_layers, layer) + BUILDER_VECTOR(DeviceQueueCreateInfo, queue_info_details, device_queue) + + VkDeviceCreateInfo* get() noexcept; + + private: + std::vector device_queue_infos; +}; diff --git a/tests/framework/util/wide_char_handling.cpp b/tests/framework/util/wide_char_handling.cpp new file mode 100644 index 000000000..ac4fb0bff --- /dev/null +++ b/tests/framework/util/wide_char_handling.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2025 The Khronos Group Inc. + * Copyright (c) 2025 Valve Corporation + * Copyright (c) 2025 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * The above copyright notice(s) and this permission notice shall be included in + * all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + */ + +#include "wide_char_handling.h" + +#if defined(_WIN32) + +#include "Windows.h" + +std::string narrow(const std::wstring& utf16) { + if (utf16.empty()) { + return {}; + } + int size = WideCharToMultiByte(CP_UTF8, 0, utf16.data(), static_cast(utf16.size()), nullptr, 0, nullptr, nullptr); + if (size <= 0) { + return {}; + } + std::string utf8(size, '\0'); + if (WideCharToMultiByte(CP_UTF8, 0, utf16.data(), static_cast(utf16.size()), &utf8[0], size, nullptr, nullptr) != size) { + return {}; + } + return utf8; +} + +std::wstring widen(const std::string& utf8) { + if (utf8.empty()) { + return {}; + } + int size = MultiByteToWideChar(CP_UTF8, 0, utf8.data(), static_cast(utf8.size()), nullptr, 0); + if (size <= 0) { + return {}; + } + std::wstring utf16(size, L'\0'); + if (MultiByteToWideChar(CP_UTF8, 0, utf8.data(), static_cast(utf8.size()), &utf16[0], size) != size) { + return {}; + } + return utf16; +} +#else +std::string narrow(const std::string& utf16) { return utf16; } +std::string widen(const std::string& utf8) { return utf8; } +#endif diff --git a/tests/framework/util/wide_char_handling.h b/tests/framework/util/wide_char_handling.h new file mode 100644 index 000000000..805afe14c --- /dev/null +++ b/tests/framework/util/wide_char_handling.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2025 The Khronos Group Inc. + * Copyright (c) 2025 Valve Corporation + * Copyright (c) 2025 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * The above copyright notice(s) and this permission notice shall be included in + * all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + */ + +#pragma once + +#include + +// Define it here so that json_writer.h has access to these functions +#if defined(_WIN32) +// Convert an UTF-16 wstring to an UTF-8 string +std::string narrow(const std::wstring& utf16); +// Convert an UTF-8 string to an UTF-16 wstring +std::wstring widen(const std::string& utf8); +#else +// Do nothing passthrough for the sake of Windows & UTF-16 +std::string narrow(const std::string& utf16); +// Do nothing passthrough for the sake of Windows & UTF-16 +std::string widen(const std::string& utf8); +#endif diff --git a/tests/live_verification/dynamic_loader_behavior/dynamic_library.h b/tests/live_verification/dynamic_loader_behavior/dynamic_library.h index 1d88d306e..b4bff319b 100644 --- a/tests/live_verification/dynamic_loader_behavior/dynamic_library.h +++ b/tests/live_verification/dynamic_loader_behavior/dynamic_library.h @@ -25,7 +25,12 @@ * Author: Charles Giessen */ -#include "test_util.h" +#include +#include + +#include "util/test_defines.h" +#include "util/functions.h" +#include "util/dynamic_library_wrapper.h" extern "C" { @@ -38,7 +43,7 @@ using InitFunction = void (*)(); FRAMEWORK_EXPORT void init(); }; -#if defined(WIN32) +#if defined(_WIN32) #if !defined(LIB_EXT) #define LIB_EXT "dll" #endif diff --git a/tests/live_verification/dynamic_loader_behavior/test_dynamic_linking.cpp b/tests/live_verification/dynamic_loader_behavior/test_dynamic_linking.cpp index 2d2d6b35c..943917ec3 100644 --- a/tests/live_verification/dynamic_loader_behavior/test_dynamic_linking.cpp +++ b/tests/live_verification/dynamic_loader_behavior/test_dynamic_linking.cpp @@ -42,4 +42,4 @@ int main() { std::cout << "Success\n"; #endif return 0; -} \ No newline at end of file +} diff --git a/tests/loader_alloc_callback_tests.cpp b/tests/loader_alloc_callback_tests.cpp index 1701bfb92..f9eba0f2d 100644 --- a/tests/loader_alloc_callback_tests.cpp +++ b/tests/loader_alloc_callback_tests.cpp @@ -440,7 +440,9 @@ TEST(Allocation, CreateInstanceIntentionalAllocFailInvalidManifests) { auto file_name = std::string("invalid_implicit_layer_") + std::to_string(i) + ".json"; std::filesystem::path new_path = env.get_folder(ManifestLocation::implicit_layer).write_manifest(file_name, invalid_jsons[i]); - env.platform_shim->add_manifest(ManifestCategory::implicit_layer, new_path); +#if defined(WIN32) + env.platform_shim->add_manifest_to_registry(ManifestCategory::implicit_layer, new_path); +#endif } const char* layer_name = "VkLayerImplicit0"; diff --git a/tests/loader_envvar_tests.cpp b/tests/loader_envvar_tests.cpp index c94c2ecdd..85a23ddf2 100644 --- a/tests/loader_envvar_tests.cpp +++ b/tests/loader_envvar_tests.cpp @@ -28,6 +28,8 @@ #include "test_environment.h" +#include "util/wide_char_handling.h" + // Don't support vk_icdNegotiateLoaderICDInterfaceVersion // Loader calls vk_icdGetInstanceProcAddr second // does not support vk_icdGetInstanceProcAddr @@ -120,18 +122,25 @@ TEST(EnvVarICDOverrideSetup, TestOnlyDriverEnvVar) { phys_dev_count = 5; ASSERT_EQ(inst2->vkEnumeratePhysicalDevices(inst2.inst, &phys_dev_count, phys_devs_array.data()), VK_SUCCESS); ASSERT_EQ(phys_dev_count, 5U); +} - env.debug_log.clear(); +// Test VK_DRIVER_FILES environment variable with elelvated privileges +TEST(EnvVarICDOverrideSetup, TestOnlyDriverEnvVarRunningWithElevatedPrivileges) { + FrameworkEnvironment env{FrameworkSettings{}.set_run_as_if_with_elevated_privleges(true)}; + env.add_icd(TestICDDetails(TEST_ICD_PATH_EXPORT_NONE).set_discovery_type(ManifestDiscoveryType::env_var)); + env.get_test_icd(0).add_physical_device("pd0"); - env.platform_shim->set_elevated_privilege(true); + for (uint32_t add = 0; add < 2; ++add) { + env.add_icd(TestICDDetails(TEST_ICD_PATH_EXPORT_NONE).set_discovery_type(ManifestDiscoveryType::env_var)) + .add_physical_device("pd" + std::to_string(add) + "0") + .add_physical_device("pd" + std::to_string(add) + "1"); + } - InstWrapper inst3{env.vulkan_functions}; - FillDebugUtilsCreateDetails(inst3.create_info, env.debug_log); - inst3.CheckCreate(VK_ERROR_INCOMPATIBLE_DRIVER); + InstWrapper inst{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(VK_ERROR_INCOMPATIBLE_DRIVER); EXPECT_TRUE(env.debug_log.find("vkCreateInstance: Found no drivers!")); - - env.platform_shim->set_elevated_privilege(false); } // Test VK_DRIVER_FILES environment variable containing a path to a folder @@ -157,27 +166,30 @@ TEST(EnvVarICDOverrideSetup, TestOnlyDriverEnvVarInFolder) { .add_physical_device("pd" + std::to_string(add) + "1"); } - env.debug_log.clear(); - InstWrapper inst2{env.vulkan_functions}; - FillDebugUtilsCreateDetails(inst2.create_info, env.debug_log); inst2.CheckCreate(); phys_dev_count = 5; ASSERT_EQ(inst2->vkEnumeratePhysicalDevices(inst2.inst, &phys_dev_count, phys_devs_array.data()), VK_SUCCESS); ASSERT_EQ(phys_dev_count, 5U); +} +// Test VK_DRIVER_FILES environment variable containing a path to a folder with elevated privileges +TEST(EnvVarICDOverrideSetup, TestOnlyDriverEnvVarInFolderWithElevatedPrivileges) { + FrameworkEnvironment env{FrameworkSettings{}.set_run_as_if_with_elevated_privleges(true)}; + env.add_icd(TestICDDetails(TEST_ICD_PATH_EXPORT_NONE).set_discovery_type(ManifestDiscoveryType::env_var).set_is_dir(false)); + env.get_test_icd(0).add_physical_device("pd0"); - env.debug_log.clear(); - - env.platform_shim->set_elevated_privilege(true); + for (uint32_t add = 0; add < 2; ++add) { + env.add_icd(TestICDDetails(TEST_ICD_PATH_EXPORT_NONE).set_discovery_type(ManifestDiscoveryType::env_var)) + .add_physical_device("pd" + std::to_string(add) + "0") + .add_physical_device("pd" + std::to_string(add) + "1"); + } - InstWrapper inst3{env.vulkan_functions}; - FillDebugUtilsCreateDetails(inst3.create_info, env.debug_log); - inst3.CheckCreate(VK_ERROR_INCOMPATIBLE_DRIVER); + InstWrapper inst{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(VK_ERROR_INCOMPATIBLE_DRIVER); EXPECT_TRUE(env.debug_log.find("vkCreateInstance: Found no drivers!")); - - env.platform_shim->set_elevated_privilege(false); } #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__GNU__) || defined(__QNX__) @@ -203,14 +215,13 @@ TEST(EnvVarICDOverrideSetup, XDG) { // Set up a layer path that includes default and user-specified locations, // so that the test app can find them. Include some badly specified elements as well. // Need to redirect the 'home' directory - std::filesystem::path HOME = "/home/fake_home"; - EnvVarWrapper home_env_var{"HOME", HOME}; - EnvVarWrapper xdg_config_dirs_env_var{"XDG_CONFIG_DIRS", ":/tmp/goober:::::/tmp/goober/::::"}; - EnvVarWrapper xdg_config_home_env_var{"XDG_CONFIG_HOME", ":/tmp/goober:::::/tmp/goober2/::::"}; - EnvVarWrapper xdg_data_dirs_env_var{"XDG_DATA_DIRS", "::::/tmp/goober3:/tmp/goober4/with spaces:::"}; - EnvVarWrapper xdg_data_home_env_var{"XDG_DATA_HOME", "::::/tmp/goober3:/tmp/goober4/with spaces:::"}; - FrameworkEnvironment env{}; + FrameworkEnvironment env{FrameworkSettings{} + .set_home_env_var("/home/fake_home") + .set_xdg_config_dirs_env_var(":/tmp/goober:::::/tmp/goober/::::") + .set_xdg_config_home_env_var(":/tmp/goober:::::/tmp/goober2/::::") + .set_xdg_data_dirs_env_var("::::/tmp/goober3:/tmp/goober4/with spaces:::") + .set_xdg_data_home_env_var("::::/tmp/goober3:/tmp/goober4/with spaces:::")}; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device("physical_device_0"); InstWrapper inst{env.vulkan_functions}; @@ -232,9 +243,7 @@ TEST(EnvVarICDOverrideSetup, XDGContainsJsonFile) { // Set up a layer path that includes default and user-specified locations, // so that the test app can find them. Include some badly specified elements as well. // Need to redirect the 'home' directory - EnvVarWrapper xdg_config_dirs_env_var{"XDG_CONFIG_DIRS", "bad_file.json"}; - - FrameworkEnvironment env{}; + FrameworkEnvironment env{FrameworkSettings{}.set_xdg_config_dirs_env_var("bad_file.json")}; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device("physical_device_0"); InstWrapper inst{env.vulkan_functions}; @@ -249,24 +258,27 @@ TEST(EnvVarICDOverrideSetup, TestOnlyAddDriverEnvVar) { env.add_icd(TestICDDetails(TEST_ICD_PATH_EXPORT_NONE).set_discovery_type(ManifestDiscoveryType::add_env_var)); env.get_test_icd(0).physical_devices.emplace_back("pd0"); - InstWrapper inst1{env.vulkan_functions}; - FillDebugUtilsCreateDetails(inst1.create_info, env.debug_log); - inst1.CheckCreate(); + InstWrapper inst{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(); std::array phys_devs_array; uint32_t phys_dev_count = 1; - ASSERT_EQ(inst1->vkEnumeratePhysicalDevices(inst1.inst, &phys_dev_count, phys_devs_array.data()), VK_SUCCESS); + ASSERT_EQ(inst->vkEnumeratePhysicalDevices(inst.inst, &phys_dev_count, phys_devs_array.data()), VK_SUCCESS); ASSERT_EQ(phys_dev_count, 1U); +} - env.platform_shim->set_elevated_privilege(true); +// Test VK_ADD_DRIVER_FILES environment variable with elelvated privileges +TEST(EnvVarICDOverrideSetup, TestOnlyAddDriverEnvVarRunningWithElevatedPrivileges) { + FrameworkEnvironment env{FrameworkSettings{}.set_run_as_if_with_elevated_privleges(true)}; + env.add_icd(TestICDDetails(TEST_ICD_PATH_EXPORT_NONE).set_discovery_type(ManifestDiscoveryType::add_env_var)); + env.get_test_icd(0).physical_devices.emplace_back("pd0"); - InstWrapper inst2{env.vulkan_functions}; - FillDebugUtilsCreateDetails(inst2.create_info, env.debug_log); - inst2.CheckCreate(VK_ERROR_INCOMPATIBLE_DRIVER); + InstWrapper inst{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(VK_ERROR_INCOMPATIBLE_DRIVER); EXPECT_TRUE(env.debug_log.find("vkCreateInstance: Found no drivers!")); - - env.platform_shim->set_elevated_privilege(false); } // Test Both VK_DRIVER_FILES and VK_ADD_DRIVER_FILES environment variable @@ -295,7 +307,7 @@ TEST(EnvVarICDOverrideSetup, TestBothDriverEnvVars) { TEST(EnvVarICDOverrideSetup, TestOnlyLayerEnvVar) { FrameworkEnvironment env{}; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device("physical_device_0"); - env.platform_shim->redirect_path("/tmp/carol", env.get_folder(ManifestLocation::explicit_layer_env_var).location()); + env.file_system_manager.add_path_redirect("/tmp/carol", ManifestLocation::explicit_layer_env_var); const char* layer_name = "TestLayer"; env.add_explicit_layer( @@ -312,35 +324,51 @@ TEST(EnvVarICDOverrideSetup, TestOnlyLayerEnvVar) { std::string vk_layer_path = ":/tmp/carol::::/:"; vk_layer_path += (HOME / "/ with spaces/:::::/tandy:"); EnvVarWrapper layer_path_env_var{"VK_LAYER_PATH", vk_layer_path}; - InstWrapper inst1{env.vulkan_functions}; - inst1.create_info.add_layer(layer_name); - FillDebugUtilsCreateDetails(inst1.create_info, env.debug_log); - inst1.CheckCreate(); + InstWrapper inst{env.vulkan_functions}; + inst.create_info.add_layer(layer_name); + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(); // look for VK_LAYER_PATHS EXPECT_TRUE(env.debug_log.find("/tmp/carol")); EXPECT_TRUE(env.debug_log.find("/tandy")); EXPECT_TRUE(env.debug_log.find((HOME / "/ with spaces/"))); +} +// Test VK_LAYER_PATH environment variable with elevated privileges +TEST(EnvVarICDOverrideSetup, TestOnlyLayerEnvVarRunningWithElevatedPrivileges) { + FrameworkEnvironment env{FrameworkSettings{}.set_run_as_if_with_elevated_privleges(true)}; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device("physical_device_0"); + env.file_system_manager.add_path_redirect("/tmp/carol", ManifestLocation::explicit_layer_env_var); - env.debug_log.clear(); + const char* layer_name = "TestLayer"; + env.add_explicit_layer( + TestLayerDetails(ManifestLayer{}.add_layer( + ManifestLayer::LayerDescription{}.set_name(layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "test_layer.json") + .set_discovery_type(ManifestDiscoveryType::env_var)); - env.platform_shim->set_elevated_privilege(true); + // Now set up a layer path that includes default and user-specified locations, + // so that the test app can find them. Include some badly specified elements as well. + // Need to redirect the 'home' directory + std::filesystem::path HOME = "/home/fake_home"; + EnvVarWrapper home_env_var{"HOME", HOME}; + std::string vk_layer_path = ":/tmp/carol::::/:"; + vk_layer_path += (HOME / "/ with spaces/:::::/tandy:"); + EnvVarWrapper layer_path_env_var{"VK_LAYER_PATH", vk_layer_path}; - InstWrapper inst2{env.vulkan_functions}; - inst2.create_info.add_layer(layer_name); - FillDebugUtilsCreateDetails(inst2.create_info, env.debug_log); - inst2.CheckCreate(VK_ERROR_LAYER_NOT_PRESENT); + InstWrapper inst{env.vulkan_functions}; + inst.create_info.add_layer(layer_name); + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(VK_ERROR_LAYER_NOT_PRESENT); EXPECT_FALSE(env.debug_log.find("/tmp/carol")); - - env.platform_shim->set_elevated_privilege(false); } // Test VK_ADD_LAYER_PATH environment variable TEST(EnvVarICDOverrideSetup, TestOnlyAddLayerEnvVar) { FrameworkEnvironment env{}; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device("physical_device_0"); - env.platform_shim->redirect_path("/tmp/carol", env.get_folder(ManifestLocation::explicit_layer_add_env_var).location()); + env.file_system_manager.add_path_redirect("/tmp/carol", ManifestLocation::explicit_layer_add_env_var); const char* layer_name = "TestLayer"; env.add_explicit_layer( @@ -358,35 +386,52 @@ TEST(EnvVarICDOverrideSetup, TestOnlyAddLayerEnvVar) { vk_layer_path += (HOME / "/ with spaces/:::::/tandy:"); EnvVarWrapper add_layer_path_env_var{"VK_ADD_LAYER_PATH", vk_layer_path}; - InstWrapper inst1{env.vulkan_functions}; - inst1.create_info.add_layer(layer_name); - FillDebugUtilsCreateDetails(inst1.create_info, env.debug_log); - inst1.CheckCreate(); + InstWrapper inst{env.vulkan_functions}; + inst.create_info.add_layer(layer_name); + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(); // look for VK_ADD_LAYER_PATHS EXPECT_TRUE(env.debug_log.find("/tmp/carol")); EXPECT_TRUE(env.debug_log.find("/tandy")); EXPECT_TRUE(env.debug_log.find((HOME / "/ with spaces/"))); +} - env.debug_log.clear(); +// Test VK_ADD_LAYER_PATH environment variable with elevated privileges +TEST(EnvVarICDOverrideSetup, TestOnlyAddLayerEnvVarRunningWithElevatedPrivileges) { + FrameworkEnvironment env{FrameworkSettings{}.set_run_as_if_with_elevated_privleges(true)}; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device("physical_device_0"); + env.file_system_manager.add_path_redirect("/tmp/carol", ManifestLocation::explicit_layer_add_env_var); - env.platform_shim->set_elevated_privilege(true); + const char* layer_name = "TestLayer"; + env.add_explicit_layer( + TestLayerDetails(ManifestLayer{}.add_layer( + ManifestLayer::LayerDescription{}.set_name(layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "test_layer.json") + .set_discovery_type(ManifestDiscoveryType::add_env_var)); - InstWrapper inst2{env.vulkan_functions}; - inst2.create_info.add_layer(layer_name); - FillDebugUtilsCreateDetails(inst2.create_info, env.debug_log); - inst2.CheckCreate(VK_ERROR_LAYER_NOT_PRESENT); + // Set up a layer path that includes default and user-specified locations, + // so that the test app can find them. Include some badly specified elements as well. + // Need to redirect the 'home' directory + std::filesystem::path HOME = "/home/fake_home"; + EnvVarWrapper home_env_var{"HOME", HOME}; + std::string vk_layer_path = ":/tmp/carol::::/:"; + vk_layer_path += (HOME / "/ with spaces/:::::/tandy:"); + EnvVarWrapper add_layer_path_env_var{"VK_ADD_LAYER_PATH", vk_layer_path}; - EXPECT_FALSE(env.debug_log.find("/tmp/carol")); + InstWrapper inst{env.vulkan_functions}; + inst.create_info.add_layer(layer_name); + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(VK_ERROR_LAYER_NOT_PRESENT); - env.platform_shim->set_elevated_privilege(false); + EXPECT_FALSE(env.debug_log.find("/tmp/carol")); } // Test VK_IMPLICIT_LAYER_PATH environment variable TEST(EnvVarICDOverrideSetup, TestOnlyImplicitLayerEnvVar) { FrameworkEnvironment env{}; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device("physical_device_0"); - env.platform_shim->redirect_path("/tmp/carol", env.get_folder(ManifestLocation::implicit_layer_env_var).location()); + env.file_system_manager.add_path_redirect("/tmp/carol", ManifestLocation::implicit_layer_env_var); const char* layer_name = "TestLayer"; env.add_implicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} @@ -404,39 +449,57 @@ TEST(EnvVarICDOverrideSetup, TestOnlyImplicitLayerEnvVar) { std::string vk_implicit_layer_path = ":/tmp/carol::::/:"; vk_implicit_layer_path += (HOME / "/ with spaces/:::::/tandy:"); EnvVarWrapper implicit_layer_path_env_var{"VK_IMPLICIT_LAYER_PATH", vk_implicit_layer_path}; - InstWrapper inst1{env.vulkan_functions}; - FillDebugUtilsCreateDetails(inst1.create_info, env.debug_log); - inst1.CheckCreate(); + InstWrapper inst{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(); - auto active_layers1 = inst1.GetActiveLayers(inst1.GetPhysDev(), 1); - ASSERT_TRUE(string_eq(active_layers1.at(0).layerName, layer_name)); + auto active_layers = inst.GetActiveLayers(inst.GetPhysDev(), 1); + ASSERT_TRUE(string_eq(active_layers.at(0).layerName, layer_name)); // look for VK_IMPLICIT_LAYER_PATHS EXPECT_TRUE(env.debug_log.find("/tmp/carol")); EXPECT_TRUE(env.debug_log.find("/tandy")); EXPECT_TRUE(env.debug_log.find((HOME / "/ with spaces/"))); +} - env.debug_log.clear(); +// Test VK_IMPLICIT_LAYER_PATH environment variable run with elevated privileges +TEST(EnvVarICDOverrideSetup, TestOnlyImplicitLayerEnvVarRunningWithElevatedPrivileges) { + FrameworkEnvironment env{FrameworkSettings{}.set_run_as_if_with_elevated_privleges(true)}; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device("physical_device_0"); + env.file_system_manager.add_path_redirect("/tmp/carol", ManifestLocation::implicit_layer_env_var); + + const char* layer_name = "TestLayer"; + env.add_implicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_name(layer_name) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_disable_environment("DISABLE_ENV")), + "test_layer.json") + .set_discovery_type(ManifestDiscoveryType::env_var)); - env.platform_shim->set_elevated_privilege(true); + // Now set up a layer path that includes default and user-specified locations, + // so that the test app can find them. Include some badly specified elements as well. + // Need to redirect the 'home' directory + std::filesystem::path HOME = "/home/fake_home"; + EnvVarWrapper home_env_var{"HOME", HOME}; + std::string vk_implicit_layer_path = ":/tmp/carol::::/:"; + vk_implicit_layer_path += (HOME / "/ with spaces/:::::/tandy:"); + EnvVarWrapper implicit_layer_path_env_var{"VK_IMPLICIT_LAYER_PATH", vk_implicit_layer_path}; - InstWrapper inst2{env.vulkan_functions}; - FillDebugUtilsCreateDetails(inst2.create_info, env.debug_log); - inst2.CheckCreate(); + InstWrapper inst{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(); - auto active_layers2 = inst2.GetActiveLayers(inst2.GetPhysDev(), 0); - ASSERT_TRUE(active_layers2.empty()); + auto active_layers = inst.GetActiveLayers(inst.GetPhysDev(), 0); + ASSERT_TRUE(active_layers.empty()); EXPECT_FALSE(env.debug_log.find("/tmp/carol")); - - env.platform_shim->set_elevated_privilege(false); } // Test VK_ADD_IMPLICIT_LAYER_PATH environment variable TEST(EnvVarICDOverrideSetup, TestOnlyAddImplicitLayerEnvVar) { FrameworkEnvironment env{}; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device("physical_device_0"); - env.platform_shim->redirect_path("/tmp/carol", env.get_folder(ManifestLocation::implicit_layer_add_env_var).location()); + env.file_system_manager.add_path_redirect("/tmp/carol", ManifestLocation::implicit_layer_add_env_var); const char* layer_name = "TestLayer"; env.add_implicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} @@ -453,7 +516,7 @@ TEST(EnvVarICDOverrideSetup, TestOnlyAddImplicitLayerEnvVar) { EnvVarWrapper home_env_var{"HOME", HOME}; std::string vk_add_implicit_layer_path = ":/tmp/carol::::/:"; vk_add_implicit_layer_path += (HOME / "/ with spaces/:::::/tandy:"); - EnvVarWrapper add_implicit_layer_path_env_var{"VK_ADD_LAYER_PATH", vk_add_implicit_layer_path}; + EnvVarWrapper add_implicit_layer_path_env_var{"VK_ADD_IMPLICIT_LAYER_PATH", vk_add_implicit_layer_path}; InstWrapper inst1{env.vulkan_functions}; FillDebugUtilsCreateDetails(inst1.create_info, env.debug_log); @@ -466,21 +529,39 @@ TEST(EnvVarICDOverrideSetup, TestOnlyAddImplicitLayerEnvVar) { EXPECT_TRUE(env.debug_log.find("/tmp/carol")); EXPECT_TRUE(env.debug_log.find("/tandy")); EXPECT_TRUE(env.debug_log.find((HOME / "/ with spaces/"))); +} - env.debug_log.clear(); +// Test VK_ADD_IMPLICIT_LAYER_PATH environment variable running with elevated privileges +TEST(EnvVarICDOverrideSetup, TestOnlyAddImplicitLayerEnvVarWithElevatedPrivileges) { + FrameworkEnvironment env{FrameworkSettings{}.set_run_as_if_with_elevated_privleges(true)}; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device("physical_device_0"); + env.file_system_manager.add_path_redirect("/tmp/carol", ManifestLocation::implicit_layer_add_env_var); - env.platform_shim->set_elevated_privilege(true); + const char* layer_name = "TestLayer"; + env.add_implicit_layer(TestLayerDetails(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{} + .set_name(layer_name) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_disable_environment("DISABLE_ENV")), + "test_layer.json") + .set_discovery_type(ManifestDiscoveryType::add_env_var)); - InstWrapper inst2{env.vulkan_functions}; - FillDebugUtilsCreateDetails(inst2.create_info, env.debug_log); - inst2.CheckCreate(); + // Set up a layer path that includes default and user-specified locations, + // so that the test app can find them. Include some badly specified elements as well. + // Need to redirect the 'home' directory + std::filesystem::path HOME = "/home/fake_home"; + EnvVarWrapper home_env_var{"HOME", HOME}; + std::string vk_add_implicit_layer_path = ":/tmp/carol::::/:"; + vk_add_implicit_layer_path += (HOME / "/ with spaces/:::::/tandy:"); + EnvVarWrapper add_implicit_layer_path_env_var{"VK_ADD_IMPLICIT_LAYER_PATH", vk_add_implicit_layer_path}; - auto active_layers2 = inst2.GetActiveLayers(inst2.GetPhysDev(), 0); - ASSERT_TRUE(active_layers2.empty()); + InstWrapper inst{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(); - EXPECT_FALSE(env.debug_log.find("/tmp/carol")); + auto active_layers = inst.GetActiveLayers(inst.GetPhysDev(), 0); + ASSERT_TRUE(active_layers.empty()); - env.platform_shim->set_elevated_privilege(false); + EXPECT_FALSE(env.debug_log.find("/tmp/carol")); } #endif diff --git a/tests/loader_layer_tests.cpp b/tests/loader_layer_tests.cpp index 0fae6ce96..3babc5961 100644 --- a/tests/loader_layer_tests.cpp +++ b/tests/loader_layer_tests.cpp @@ -27,6 +27,9 @@ #include "test_environment.h" +#include "util/test_defines.h" +#include "util/get_executable_path.h" + void CheckLogForLayerString(FrameworkEnvironment& env, const char* implicit_layer_name, bool check_for_enable) { { InstWrapper inst{env.vulkan_functions}; @@ -968,7 +971,8 @@ TEST(ImplicitLayers, DuplicateLayers) { .set_description("actually_layer_1") .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) .set_disable_environment("if_you_can")), - "regular_layer_1.json")); + "regular_layer_1.json") + .set_discovery_type(ManifestDiscoveryType::add_env_var)); auto& layer1 = env.get_test_layer(0); layer1.set_description("actually_layer_1"); layer1.set_make_spurious_log_in_create_instance("actually_layer_1"); @@ -979,17 +983,10 @@ TEST(ImplicitLayers, DuplicateLayers) { .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) .set_disable_environment("if_you_can")), "regular_layer_1.json") - // use override folder as just a folder and manually add it to the implicit layer search paths - .set_discovery_type(ManifestDiscoveryType::override_folder)); + .set_discovery_type(ManifestDiscoveryType::generic)); auto& layer2 = env.get_test_layer(1); layer2.set_description("actually_layer_2"); layer2.set_make_spurious_log_in_create_instance("actually_layer_2"); -#if defined(WIN32) - env.platform_shim->add_manifest(ManifestCategory::implicit_layer, env.get_folder(ManifestLocation::override_layer).location()); -#elif COMMON_UNIX_PLATFORMS - env.platform_shim->redirect_path(std::filesystem::path(USER_LOCAL_SHARE_DIR "/vulkan/implicit_layer.d"), - env.get_folder(ManifestLocation::override_layer).location()); -#endif auto layer_props = env.GetLayerProperties(2); ASSERT_TRUE(string_eq(same_layer_name_1, layer_props[0].layerName)); @@ -1926,7 +1923,7 @@ TEST(OverrideMetaLayer, AppKeysDoesNotContainCurrentApplication) { } } -TEST(OverrideMetaLayer, RunningWithElevatedPrivilegesFromSecureLocation) { +TEST(OverrideMetaLayer, RunningWithRegularPrivilegesFromSecureLocation) { FrameworkEnvironment env; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device({}); @@ -1950,38 +1947,58 @@ TEST(OverrideMetaLayer, RunningWithElevatedPrivilegesFromSecureLocation) { .add_override_path(override_layer_folder.location())), "meta_test_layer.json"}); - { // try with no elevated privileges - auto layer_props = env.GetLayerProperties(2); - EXPECT_TRUE(check_permutation({regular_layer_name, lunarg_meta_layer_name}, layer_props)); + // try with no elevated privileges + auto layer_props = env.GetLayerProperties(2); + EXPECT_TRUE(check_permutation({regular_layer_name, lunarg_meta_layer_name}, layer_props)); - InstWrapper inst{env.vulkan_functions}; - inst.create_info.set_api_version(1, 1, 0); - FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); - inst.CheckCreate(); - ASSERT_TRUE(env.debug_log.find(std::string("Insert instance layer \"") + regular_layer_name)); - auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 2); - EXPECT_TRUE(check_permutation({regular_layer_name, lunarg_meta_layer_name}, layer_props)); - env.debug_log.clear(); - } + InstWrapper inst{env.vulkan_functions}; + inst.create_info.set_api_version(1, 1, 0); + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(); + ASSERT_TRUE(env.debug_log.find(std::string("Insert instance layer \"") + regular_layer_name)); + auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 2); + EXPECT_TRUE(check_permutation({regular_layer_name, lunarg_meta_layer_name}, layer_props)); +} - env.platform_shim->set_elevated_privilege(true); +TEST(OverrideMetaLayer, RunningWithElevatedPrivilegesFromSecureLocation) { + FrameworkEnvironment env{FrameworkSettings{}.set_run_as_if_with_elevated_privleges(true)}; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device({}); - { // try with elevated privileges - auto layer_props = env.GetLayerProperties(2); - EXPECT_TRUE(check_permutation({regular_layer_name, lunarg_meta_layer_name}, layer_props)); + auto& override_layer_folder = env.get_folder(ManifestLocation::override_layer); - InstWrapper inst{env.vulkan_functions}; - inst.create_info.set_api_version(1, 1, 0); - FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); - inst.CheckCreate(); - ASSERT_TRUE(env.debug_log.find(std::string("Insert instance layer \"") + regular_layer_name)); - auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 2); - EXPECT_TRUE(check_permutation({regular_layer_name, lunarg_meta_layer_name}, active_layer_props)); - } + const char* regular_layer_name = "VK_LAYER_TestLayer_1"; + override_layer_folder.write_manifest("regular_test_layer.json", + ManifestLayer{} + .add_layer(ManifestLayer::LayerDescription{} + .set_name(regular_layer_name) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_api_version(VK_MAKE_API_VERSION(0, 1, 1, 0))) + .get_manifest_str()); + auto override_folder_location = override_layer_folder.location().string(); + env.add_implicit_layer(TestLayerDetails{ + ManifestLayer{}.set_file_format_version({1, 2, 0}).add_layer(ManifestLayer::LayerDescription{} + .set_name(lunarg_meta_layer_name) + .set_api_version(VK_MAKE_API_VERSION(0, 1, 1, 0)) + .add_component_layer(regular_layer_name) + .set_disable_environment("DisableMeIfYouCan") + .add_override_path(override_layer_folder.location())), + "meta_test_layer.json"}); + + // try with elevated privileges + auto layer_props = env.GetLayerProperties(2); + EXPECT_TRUE(check_permutation({regular_layer_name, lunarg_meta_layer_name}, layer_props)); + + InstWrapper inst{env.vulkan_functions}; + inst.create_info.set_api_version(1, 1, 0); + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(); + ASSERT_TRUE(env.debug_log.find(std::string("Insert instance layer \"") + regular_layer_name)); + auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 2); + EXPECT_TRUE(check_permutation({regular_layer_name, lunarg_meta_layer_name}, active_layer_props)); } // Override layer should not be found and thus not loaded when running with elevated privileges -TEST(OverrideMetaLayer, RunningWithElevatedPrivilegesFromUnsecureLocation) { +TEST(OverrideMetaLayer, RunningWithRegularPrivilegesFromUnsecureLocation) { FrameworkEnvironment env; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device({}); @@ -2005,32 +2022,51 @@ TEST(OverrideMetaLayer, RunningWithElevatedPrivilegesFromUnsecureLocation) { "meta_test_layer.json"} .set_discovery_type(ManifestDiscoveryType::unsecured_generic)); - { // try with no elevated privileges - auto layer_props = env.GetLayerProperties(2); - EXPECT_TRUE(check_permutation({regular_layer_name, lunarg_meta_layer_name}, layer_props)); + auto layer_props = env.GetLayerProperties(2); + EXPECT_TRUE(check_permutation({regular_layer_name, lunarg_meta_layer_name}, layer_props)); - InstWrapper inst{env.vulkan_functions}; - inst.create_info.set_api_version(1, 1, 0); - FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); - inst.CheckCreate(); - ASSERT_TRUE(env.debug_log.find(std::string("Insert instance layer \"") + regular_layer_name)); - env.debug_log.clear(); - auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 2); - EXPECT_TRUE(check_permutation({regular_layer_name, lunarg_meta_layer_name}, active_layer_props)); - } + InstWrapper inst{env.vulkan_functions}; + inst.create_info.set_api_version(1, 1, 0); + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(); + ASSERT_TRUE(env.debug_log.find(std::string("Insert instance layer \"") + regular_layer_name)); + env.debug_log.clear(); + auto active_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 2); + EXPECT_TRUE(check_permutation({regular_layer_name, lunarg_meta_layer_name}, active_layer_props)); +} - env.platform_shim->set_elevated_privilege(true); +TEST(OverrideMetaLayer, RunningWithElevatedPrivilegesFromUnsecureLocation) { + FrameworkEnvironment env{FrameworkSettings{}.set_run_as_if_with_elevated_privleges(true)}; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device({}); - { // try with no elevated privileges - ASSERT_NO_FATAL_FAILURE(env.GetLayerProperties(0)); + auto& override_layer_folder = env.get_folder(ManifestLocation::override_layer); - InstWrapper inst{env.vulkan_functions}; - inst.create_info.set_api_version(1, 1, 0); - FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); - inst.CheckCreate(); - ASSERT_FALSE(env.debug_log.find(std::string("Insert instance layer \"") + regular_layer_name)); - ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0)); - } + const char* regular_layer_name = "VK_LAYER_TestLayer_1"; + override_layer_folder.write_manifest("regular_test_layer.json", + ManifestLayer{} + .add_layer(ManifestLayer::LayerDescription{} + .set_name(regular_layer_name) + .set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2) + .set_api_version(VK_MAKE_API_VERSION(0, 1, 1, 0))) + .get_manifest_str()); + env.add_implicit_layer(TestLayerDetails{ + ManifestLayer{}.set_file_format_version({1, 2, 0}).add_layer(ManifestLayer::LayerDescription{} + .set_name(lunarg_meta_layer_name) + .set_api_version(VK_MAKE_API_VERSION(0, 1, 1, 0)) + .add_component_layer(regular_layer_name) + .set_disable_environment("DisableMeIfYouCan") + .add_override_path(override_layer_folder.location())), + "meta_test_layer.json"} + .set_discovery_type(ManifestDiscoveryType::unsecured_generic)); + + ASSERT_NO_FATAL_FAILURE(env.GetLayerProperties(0)); + + InstWrapper inst{env.vulkan_functions}; + inst.create_info.set_api_version(1, 1, 0); + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(); + ASSERT_FALSE(env.debug_log.find(std::string("Insert instance layer \"") + regular_layer_name)); + ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0)); } // Makes sure explicit layers can't override pre-instance functions even if enabled by the override layer @@ -2825,6 +2861,16 @@ TEST(ExplicitLayers, CorrectOrderOfApplicationEnabledLayers) { } } +// Helpers +bool contains(std::vector const& vec, const char* name) { + return std::any_of(std::begin(vec), std::end(vec), + [name](VkExtensionProperties const& elem) { return string_eq(name, elem.extensionName); }); +} +bool contains(std::vector const& vec, const char* name) { + return std::any_of(std::begin(vec), std::end(vec), + [name](VkLayerProperties const& elem) { return string_eq(name, elem.layerName); }); +} + TEST(LayerExtensions, ImplicitNoAdditionalInstanceExtension) { FrameworkEnvironment env; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA)).add_physical_device({}); @@ -5380,34 +5426,31 @@ TEST(TestLayers, AllowFilterWithImplicitLayer) { auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1); ASSERT_TRUE(string_eq(layers.at(0).layerName, layer_name)); } + env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration( + LoaderSettingsLayerConfiguration{} + .set_name(layer_name) + .set_control("on") + .set_path(env.get_shimmed_layer_manifest_path(0)) + .set_treat_as_implicit_manifest(true))); + env.update_loader_settings(env.loader_settings); { - env.loader_settings.add_app_specific_setting(AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration( - LoaderSettingsLayerConfiguration{} - .set_name(layer_name) - .set_control("on") - .set_path(env.get_shimmed_layer_manifest_path(0)) - .set_treat_as_implicit_manifest(true))); - env.update_loader_settings(env.loader_settings); - InstWrapper inst{env.vulkan_functions}; inst.CheckCreate(); auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1); ASSERT_TRUE(string_eq(layers.at(0).layerName, layer_name)); } + env.loader_settings.app_specific_settings.at(0).layer_configurations.at(0).set_control("off"); + env.update_loader_settings(env.loader_settings); { - env.loader_settings.app_specific_settings.at(0).layer_configurations.at(0).set_control("off"); - env.update_loader_settings(env.loader_settings); - InstWrapper inst{env.vulkan_functions}; inst.CheckCreate(); ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0)); } + env.loader_settings.app_specific_settings.at(0).layer_configurations.at(0).set_control("auto"); + env.update_loader_settings(env.loader_settings); { - env.loader_settings.app_specific_settings.at(0).layer_configurations.at(0).set_control("auto"); - env.update_loader_settings(env.loader_settings); - InstWrapper inst{env.vulkan_functions}; inst.CheckCreate(); @@ -5417,7 +5460,7 @@ TEST(TestLayers, AllowFilterWithImplicitLayer) { env.remove_loader_settings(); - // Set the disable_environment variable + // Set the layer specific disable_environment variable - layer should never load if this is set EnvVarWrapper set_disable_env_var{disable_env_var, "1"}; { @@ -5441,7 +5484,6 @@ TEST(TestLayers, AllowFilterWithImplicitLayer) { InstWrapper inst{env.vulkan_functions}; inst.CheckCreate(); - // layer's disable_environment takes priority ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0)); } { diff --git a/tests/loader_regression_tests.cpp b/tests/loader_regression_tests.cpp index c2d652f6c..60b108812 100644 --- a/tests/loader_regression_tests.cpp +++ b/tests/loader_regression_tests.cpp @@ -3872,7 +3872,7 @@ TEST(DuplicateRegistryEntries, Layers) { auto null_path = env.get_folder(ManifestLocation::null).location() / "test_layer.json"; - env.platform_shim->add_manifest(ManifestCategory::explicit_layer, null_path); + env.platform_shim->add_manifest_to_registry(ManifestCategory::explicit_layer, null_path); const char* layer_name = "TestLayer"; env.add_explicit_layer( @@ -3890,7 +3890,7 @@ TEST(DuplicateRegistryEntries, Layers) { TEST(DuplicateRegistryEntries, Drivers) { FrameworkEnvironment env{}; auto null_path = env.get_folder(ManifestLocation::null).location() / "test_icd_0.json"; - env.platform_shim->add_manifest(ManifestCategory::icd, null_path); + env.platform_shim->add_manifest_to_registry(ManifestCategory::icd, null_path); env.add_icd(TestICDDetails{TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA}.set_discovery_type(ManifestDiscoveryType::null_dir)) .add_physical_device("physical_device_0") @@ -3908,8 +3908,6 @@ TEST(DuplicateRegistryEntries, Drivers) { TEST(LibraryLoading, SystemLocations) { FrameworkEnvironment env{}; - EnvVarWrapper ld_library_path("LD_LIBRARY_PATH", env.get_folder(ManifestLocation::driver).location().string()); - ld_library_path.add_to_list(env.get_folder(ManifestLocation::explicit_layer).location()); auto& driver = env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2).set_library_path_type(LibraryPathType::default_search_paths)) .add_physical_device({}); @@ -3941,16 +3939,13 @@ TEST(LibraryLoading, SystemLocations) { } #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) -// Check that valid symlinks do not cause the loader to crash when directly in an XDG env-var -TEST(ManifestDiscovery, ValidSymlinkInXDGEnvVar) { - FrameworkEnvironment env{FrameworkSettings{}.set_enable_default_search_paths(false)}; +// Check that valid symlinks do not cause the loader to crash when found in an unsecure location +TEST(ManifestDiscovery, ValidSymlinkInUnsecureLocation) { + FrameworkEnvironment env; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA).set_discovery_type(ManifestDiscoveryType::override_folder)) .add_physical_device({}); - auto symlink_path = - env.get_folder(ManifestLocation::driver_env_var).add_symlink(env.get_icd_manifest_path(0), "symlink_to_driver.json"); - - EnvVarWrapper xdg_config_dirs_env_var{"XDG_CONFIG_DIRS", symlink_path}; + env.add_symlink(ManifestLocation::unsecured_driver, env.get_icd_manifest_path(0), "symlink_to_driver.json"); InstWrapper inst{env.vulkan_functions}; FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); @@ -3959,29 +3954,23 @@ TEST(ManifestDiscovery, ValidSymlinkInXDGEnvVar) { // Check that valid symlinks do not cause the loader to crash TEST(ManifestDiscovery, ValidSymlink) { - FrameworkEnvironment env{FrameworkSettings{}.set_enable_default_search_paths(false)}; + FrameworkEnvironment env{FrameworkSettings{}}; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA).set_discovery_type(ManifestDiscoveryType::override_folder)) .add_physical_device({}); - auto symlink_path = - env.get_folder(ManifestLocation::driver_env_var).add_symlink(env.get_icd_manifest_path(0), "symlink_to_driver.json"); - - env.platform_shim->set_fake_path(ManifestCategory::icd, env.get_folder(ManifestLocation::driver_env_var).location()); + env.add_symlink(ManifestLocation::driver, env.get_icd_manifest_path(0), "symlink_to_driver.json"); InstWrapper inst{env.vulkan_functions}; FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); inst.CheckCreate(); } -// Check that invalid symlinks do not cause the loader to crash when directly in an XDG env-var -TEST(ManifestDiscovery, InvalidSymlinkXDGEnvVar) { - FrameworkEnvironment env{FrameworkSettings{}.set_enable_default_search_paths(false)}; - - auto symlink_path = - env.get_folder(ManifestLocation::driver) - .add_symlink(env.get_folder(ManifestLocation::driver).location() / "nothing_here.json", "symlink_to_nothing.json"); +// Check that invalid symlinks do not cause the loader to crash when found in an unsecure location +TEST(ManifestDiscovery, InvalidSymlinkInUnsecureLocation) { + FrameworkEnvironment env; - EnvVarWrapper xdg_config_dirs_env_var{symlink_path}; + env.add_symlink(ManifestLocation::unsecured_driver, env.get_folder(ManifestLocation::driver).location() / "nothing_here.json", + "symlink_to_nothing.json"); InstWrapper inst{env.vulkan_functions}; FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); @@ -3990,13 +3979,10 @@ TEST(ManifestDiscovery, InvalidSymlinkXDGEnvVar) { // Check that invalid symlinks do not cause the loader to crash TEST(ManifestDiscovery, InvalidSymlink) { - FrameworkEnvironment env{FrameworkSettings{}.set_enable_default_search_paths(false)}; - - auto symlink_path = - env.get_folder(ManifestLocation::driver_env_var) - .add_symlink(env.get_folder(ManifestLocation::driver).location() / "nothing_here.json", "symlink_to_nothing.json"); + FrameworkEnvironment env; - env.platform_shim->set_fake_path(ManifestCategory::icd, env.get_folder(ManifestLocation::driver_env_var).location()); + env.add_symlink(ManifestLocation::driver, env.get_folder(ManifestLocation::driver).location() / "nothing_here.json", + "symlink_to_nothing.json"); InstWrapper inst{env.vulkan_functions}; FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); @@ -4220,7 +4206,9 @@ TEST(InvalidManifest, ICD) { for (size_t i = 0; i < invalid_jsons.size(); i++) { auto file_name = std::string("invalid_driver_") + std::to_string(i) + ".json"; std::filesystem::path new_path = env.get_folder(ManifestLocation::driver).write_manifest(file_name, invalid_jsons[i]); - env.platform_shim->add_manifest(ManifestCategory::icd, new_path); +#if defined(WIN32) + env.platform_shim->add_manifest_to_registry(ManifestCategory::icd, new_path); +#endif } InstWrapper inst{env.vulkan_functions}; @@ -4245,7 +4233,9 @@ TEST(InvalidManifest, Layer) { auto file_name = std::string("invalid_implicit_layer_") + std::to_string(i) + ".json"; std::filesystem::path new_path = env.get_folder(ManifestLocation::implicit_layer).write_manifest(file_name, invalid_jsons[i]); - env.platform_shim->add_manifest(ManifestCategory::implicit_layer, new_path); +#if defined(WIN32) + env.platform_shim->add_manifest_to_registry(ManifestCategory::implicit_layer, new_path); +#endif } InstWrapper inst{env.vulkan_functions}; diff --git a/tests/loader_settings_tests.cpp b/tests/loader_settings_tests.cpp index adc2ae995..8c1fcb90d 100644 --- a/tests/loader_settings_tests.cpp +++ b/tests/loader_settings_tests.cpp @@ -29,31 +29,33 @@ #include -std::string get_settings_location_log_message([[maybe_unused]] FrameworkEnvironment const& env, - [[maybe_unused]] bool use_secure = false) { +#include "util/get_executable_path.h" +#include "util/json_writer.h" +#include "util/test_defines.h" + +std::string get_settings_location_log_message([[maybe_unused]] FrameworkEnvironment const& env, bool use_secure = true) { std::string s = "Using layer configurations found in loader settings from "; #if defined(WIN32) - return s + (env.get_folder(ManifestLocation::settings_location).location() / "vk_loader_settings.json").string(); -#elif COMMON_UNIX_PLATFORMS - if (use_secure) - return s + "/etc/vulkan/loader_settings.d/vk_loader_settings.json"; - else - return s + "/home/fake_home/.local/share/vulkan/loader_settings.d/vk_loader_settings.json"; + ManifestLocation settings_location = use_secure ? ManifestLocation::settings_location : ManifestLocation::unsecured_settings; + return s + (env.get_folder(settings_location).location() / "vk_loader_settings.json").string(); +#elif TESTING_COMMON_UNIX_PLATFORMS + return s + (use_secure ? env.secure_manifest_base_location : env.unsecure_manifest_base_location) + + "/vulkan/loader_settings.d/vk_loader_settings.json"; #endif } +std::string get_unsecure_settings_location_log_message(FrameworkEnvironment const& env) { + return get_settings_location_log_message(env, false); +} -std::string get_settings_not_in_use_log_message([[maybe_unused]] FrameworkEnvironment const& env, - [[maybe_unused]] bool use_secure = false) { +std::string get_settings_not_in_use_log_message([[maybe_unused]] FrameworkEnvironment const& env, bool use_secure) { 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"; + ManifestLocation settings_location = use_secure ? ManifestLocation::settings_location : ManifestLocation::unsecured_settings; + return s + (env.get_folder(settings_location).location() / "vk_loader_settings.json").string(); +#elif TESTING_COMMON_UNIX_PLATFORMS + return s + (use_secure ? env.secure_manifest_base_location : env.unsecure_manifest_base_location) + + "/vulkan/loader_settings.d/vk_loader_settings.json\" but did not contain any valid settings."; #endif - return s + "\" but did not contain any valid settings."; } enum class LayerType { exp, @@ -124,39 +126,56 @@ TEST(SettingsFile, SettingsInUnsecuredLocation) { ManifestLayer::LayerDescription{}.set_name(regular_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), "regular_test_layer.json"} .set_discovery_type(ManifestDiscoveryType::override_folder)); - env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting( - AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(LoaderSettingsLayerConfiguration{} - .set_name(regular_layer_name) - .set_path(env.get_layer_manifest_path()) - .set_control("on")))); - { - auto layer_props = env.GetLayerProperties(1); - EXPECT_TRUE(string_eq(layer_props.at(0).layerName, regular_layer_name)); + env.update_loader_settings( + env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting( + AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(LoaderSettingsLayerConfiguration{} + .set_name(regular_layer_name) + .set_path(env.get_layer_manifest_path()) + .set_control("on"))), + false); - InstWrapper inst{env.vulkan_functions}; - FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); - inst.CheckCreate(); + auto layer_props = env.GetLayerProperties(1); + EXPECT_TRUE(string_eq(layer_props.at(0).layerName, regular_layer_name)); - ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env))); - env.debug_log.clear(); - auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1); - ASSERT_TRUE(string_eq(layers.at(0).layerName, regular_layer_name)); - } - env.platform_shim->set_elevated_privilege(true); - { - ASSERT_NO_FATAL_FAILURE(env.GetLayerProperties(0)); + InstWrapper inst{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(); - InstWrapper inst{env.vulkan_functions}; - FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); - inst.CheckCreate(); + ASSERT_TRUE(env.debug_log.find(get_unsecure_settings_location_log_message(env))); + env.debug_log.clear(); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1); + ASSERT_TRUE(string_eq(layers.at(0).layerName, regular_layer_name)); +} - ASSERT_FALSE(env.debug_log.find(get_settings_location_log_message(env))); - ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0)); - } +TEST(SettingsFile, SettingsInUnsecuredLocationRunningWithElevatedPrivileges) { + FrameworkEnvironment env{FrameworkSettings{}.set_run_as_if_with_elevated_privleges(true)}; + env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)).add_physical_device({}); + const char* regular_layer_name = "VK_LAYER_TestLayer_0"; + env.add_explicit_layer(TestLayerDetails{ + ManifestLayer{}.add_layer( + ManifestLayer::LayerDescription{}.set_name(regular_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), + "regular_test_layer.json"} + .set_discovery_type(ManifestDiscoveryType::override_folder)); + env.update_loader_settings( + env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting( + AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(LoaderSettingsLayerConfiguration{} + .set_name(regular_layer_name) + .set_path(env.get_layer_manifest_path()) + .set_control("on"))), + false); + + ASSERT_NO_FATAL_FAILURE(env.GetLayerProperties(0)); + + InstWrapper inst{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(); + + ASSERT_FALSE(env.debug_log.find(get_unsecure_settings_location_log_message(env))); + ASSERT_NO_FATAL_FAILURE(inst.GetActiveLayers(inst.GetPhysDev(), 0)); } TEST(SettingsFile, SettingsInSecuredLocation) { - FrameworkEnvironment env{FrameworkSettings{}.set_secure_loader_settings(true)}; + FrameworkEnvironment env{FrameworkSettings{}.set_run_as_if_with_elevated_privleges(true)}; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)).add_physical_device({}); env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)); const char* regular_layer_name = "VK_LAYER_TestLayer_0"; @@ -170,33 +189,18 @@ TEST(SettingsFile, SettingsInSecuredLocation) { .set_name(regular_layer_name) .set_path(env.get_layer_manifest_path()) .set_control("on")))); - { - auto layer_props = env.GetLayerProperties(1); - EXPECT_TRUE(string_eq(layer_props.at(0).layerName, regular_layer_name)); - - InstWrapper inst{env.vulkan_functions}; - FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); - inst.CheckCreate(); - ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env, true))); - env.debug_log.clear(); - auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1); - ASSERT_TRUE(string_eq(layers.at(0).layerName, regular_layer_name)); - } - env.platform_shim->set_elevated_privilege(true); - { - auto layer_props = env.GetLayerProperties(1); - EXPECT_TRUE(string_eq(layer_props.at(0).layerName, regular_layer_name)); + auto layer_props = env.GetLayerProperties(1); + EXPECT_TRUE(string_eq(layer_props.at(0).layerName, regular_layer_name)); - InstWrapper inst{env.vulkan_functions}; - FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); - inst.CheckCreate(); + InstWrapper inst{env.vulkan_functions}; + FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); + inst.CheckCreate(); - ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env, true))); - env.debug_log.clear(); - auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1); - ASSERT_TRUE(string_eq(layers.at(0).layerName, regular_layer_name)); - } + ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env))); + env.debug_log.clear(); + auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1); + ASSERT_TRUE(string_eq(layers.at(0).layerName, regular_layer_name)); } // Make sure settings file can have multiple sets of settings @@ -505,7 +509,7 @@ TEST(SettingsFile, LayerListIsEmpty) { writer.EndArray(); writer.EndObject(); writer.EndObject(); - env.write_settings_file(writer.output); + env.write_settings_file(writer.output, true); auto layer_props = env.GetLayerProperties(1); ASSERT_TRUE(string_eq(layer_props.at(0).layerName, implicit_layer_name)); @@ -513,7 +517,7 @@ TEST(SettingsFile, LayerListIsEmpty) { InstWrapper inst{env.vulkan_functions}; FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); inst.CheckCreate(); - ASSERT_TRUE(env.debug_log.find(get_settings_not_in_use_log_message(env))); + ASSERT_TRUE(env.debug_log.find(get_settings_not_in_use_log_message(env, true))); auto actice_layer_props = inst.GetActiveLayers(inst.GetPhysDev(), 1); ASSERT_TRUE(string_eq(actice_layer_props.at(0).layerName, implicit_layer_name)); } @@ -552,7 +556,7 @@ TEST(SettingsFile, InvalidSettingsFile) { ASSERT_TRUE(fuzzer_output_json_file.is_open()); std::stringstream fuzzer_output_json; fuzzer_output_json << fuzzer_output_json_file.rdbuf(); - env.write_settings_file(fuzzer_output_json.str()); + env.write_settings_file(fuzzer_output_json.str(), true); check_integrity(); } @@ -563,7 +567,7 @@ TEST(SettingsFile, InvalidSettingsFile) { writer.StartObject(); writer.AddKeyedString("file_format_version", "0.0.0"); writer.EndObject(); - env.write_settings_file(writer.output); + env.write_settings_file(writer.output, true); check_integrity(); } @@ -577,7 +581,7 @@ TEST(SettingsFile, InvalidSettingsFile) { writer.StartKeyedObject("settings"); writer.EndObject(); writer.EndObject(); - env.write_settings_file(writer.output); + env.write_settings_file(writer.output, true); check_integrity(); } @@ -594,7 +598,7 @@ TEST(SettingsFile, InvalidSettingsFile) { writer.EndObject(); } writer.EndObject(); - env.write_settings_file(writer.output); + env.write_settings_file(writer.output, true); check_integrity(); } @@ -1773,8 +1777,8 @@ TEST(SettingsFile, EnvVarsWork_VK_LOADER_LAYERS_DISABLE) { TEST(SettingsFile, MultipleKeysInRegistryInUnsecureLocation) { FrameworkEnvironment env{}; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)).add_physical_device({}); - env.platform_shim->add_unsecured_manifest(ManifestCategory::settings, "jank_path"); - env.platform_shim->add_unsecured_manifest(ManifestCategory::settings, "jank_path2"); + env.platform_shim->add_unsecured_manifest_to_registry(ManifestCategory::settings, "jank_path"); + env.platform_shim->add_unsecured_manifest_to_registry(ManifestCategory::settings, "jank_path2"); const char* regular_layer_name = "VK_LAYER_TestLayer_0"; env.add_explicit_layer(TestLayerDetails{ @@ -1782,11 +1786,13 @@ TEST(SettingsFile, MultipleKeysInRegistryInUnsecureLocation) { ManifestLayer::LayerDescription{}.set_name(regular_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), "regular_test_layer.json"} .set_discovery_type(ManifestDiscoveryType::override_folder)); - env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting( - AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(LoaderSettingsLayerConfiguration{} - .set_name(regular_layer_name) - .set_path(env.get_layer_manifest_path()) - .set_control("on")))); + env.update_loader_settings( + env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting( + AppSpecificSettings{}.add_stderr_log_filter("all").add_layer_configuration(LoaderSettingsLayerConfiguration{} + .set_name(regular_layer_name) + .set_path(env.get_layer_manifest_path()) + .set_control("on"))), + false); env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)); auto layer_props = env.GetLayerProperties(1); @@ -1796,16 +1802,16 @@ TEST(SettingsFile, MultipleKeysInRegistryInUnsecureLocation) { FillDebugUtilsCreateDetails(inst.create_info, env.debug_log); inst.CheckCreate(); - ASSERT_TRUE(env.debug_log.find(get_settings_location_log_message(env))); + ASSERT_TRUE(env.debug_log.find(get_unsecure_settings_location_log_message(env))); auto layers = inst.GetActiveLayers(inst.GetPhysDev(), 1); ASSERT_TRUE(string_eq(layers.at(0).layerName, regular_layer_name)); } TEST(SettingsFile, MultipleKeysInRegistryInSecureLocation) { - FrameworkEnvironment env{FrameworkSettings{}.set_secure_loader_settings(true)}; + FrameworkEnvironment env{FrameworkSettings{}.set_run_as_if_with_elevated_privleges(true)}; env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)).add_physical_device({}); - env.platform_shim->add_manifest(ManifestCategory::settings, "jank_path"); - env.platform_shim->add_manifest(ManifestCategory::settings, "jank_path2"); + env.platform_shim->add_manifest_to_registry(ManifestCategory::settings, "jank_path"); + env.platform_shim->add_manifest_to_registry(ManifestCategory::settings, "jank_path2"); const char* regular_layer_name = "VK_LAYER_TestLayer_0"; env.add_explicit_layer(TestLayerDetails{ @@ -1821,7 +1827,6 @@ TEST(SettingsFile, MultipleKeysInRegistryInSecureLocation) { env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)); // Make sure it works if the settings file is in the HKEY_LOCAL_MACHINE - env.platform_shim->set_elevated_privilege(true); env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2)); { auto layer_props = env.GetLayerProperties(1); @@ -2571,14 +2576,14 @@ TEST(SettingsFile, StderrLog_NoOutput) { ManifestLayer{}.add_layer( ManifestLayer::LayerDescription{}.set_name(explicit_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), "explicit_test_layer1.json"}); - env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting( + env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting( AppSpecificSettings{} .add_layer_configuration(LoaderSettingsLayerConfiguration{} .set_name(explicit_layer_name) .set_path(env.get_shimmed_layer_manifest_path()) .set_control("auto")) .add_layer_configuration( - LoaderSettingsLayerConfiguration{}.set_name("VK_LAYER_missing").set_path("/road/to/nowhere").set_control("auto")))); + LoaderSettingsLayerConfiguration{}.set_name("VK_LAYER_missing").set_path("/road/to/nowhere").set_control("auto"))); env.loader_settings.app_specific_settings.at(0).stderr_log = {""}; env.update_loader_settings(env.loader_settings); @@ -2662,14 +2667,14 @@ TEST(SettingsFile, NoStderr_log_but_VK_LOADER_DEBUG) { ManifestLayer::LayerDescription{}.set_name(explicit_layer_name).set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)), "explicit_test_layer1.json"}); - env.update_loader_settings(env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting( + env.loader_settings.set_file_format_version({1, 0, 0}).add_app_specific_setting( AppSpecificSettings{} .add_layer_configuration(LoaderSettingsLayerConfiguration{} .set_name(explicit_layer_name) .set_path(env.get_shimmed_layer_manifest_path()) .set_control("auto")) .add_layer_configuration( - LoaderSettingsLayerConfiguration{}.set_name("VK_LAYER_missing").set_path("/road/to/nowhere").set_control("auto")))); + LoaderSettingsLayerConfiguration{}.set_name("VK_LAYER_missing").set_path("/road/to/nowhere").set_control("auto"))); env.loader_settings.app_specific_settings.at(0).stderr_log = {}; env.update_loader_settings(env.loader_settings); @@ -3176,7 +3181,7 @@ TEST(SettingsFile, InvalidAdditionalDriversField) { writer.EndArray(); writer.EndObject(); writer.EndObject(); - env.write_settings_file(writer.output); + env.write_settings_file(writer.output, true); InstWrapper inst{env.vulkan_functions}; inst.CheckCreate(); @@ -3377,7 +3382,6 @@ TEST(SettingsFile, DriverConfigurationsAndAdditionalDrivers) { 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(); diff --git a/tests/loader_testing_main.cpp b/tests/loader_testing_main.cpp index 018bea434..263c1e36f 100644 --- a/tests/loader_testing_main.cpp +++ b/tests/loader_testing_main.cpp @@ -27,6 +27,8 @@ #include "test_environment.h" +#include "util/test_defines.h" + // Makes any failed assertion throw, allowing for graceful cleanup of resources instead of hard aborts class ThrowListener : public testing::EmptyTestEventListener { void OnTestPartResult(const testing::TestPartResult& result) override { @@ -70,14 +72,16 @@ int main(int argc, char** argv) { EnvVarWrapper vk_loader_layers_disable_env_var{"VK_LOADER_LAYERS_DISABLE"}; EnvVarWrapper vk_loader_debug_env_var{"VK_LOADER_DEBUG"}; EnvVarWrapper vk_loader_disable_inst_ext_filter_env_var{"VK_LOADER_DISABLE_INST_EXT_FILTER"}; + EnvVarWrapper vk_loader_disable_select_env_var{"VK_LOADER_DISABLE_SELECT"}; -#if COMMON_UNIX_PLATFORMS + // even though apple shouldn't have XDG env-vars set, the loader looks for them so we have to clear them +#if TESTING_COMMON_UNIX_PLATFORMS // Set only one of the 4 XDG variables to /etc, let everything else be empty - EnvVarWrapper xdg_config_home_env_var{"XDG_CONFIG_HOME", ETC_DIR}; + EnvVarWrapper xdg_config_home_env_var{"XDG_CONFIG_HOME"}; EnvVarWrapper xdg_config_dirs_env_var{"XDG_CONFIG_DIRS"}; EnvVarWrapper xdg_data_home_env_var{"XDG_DATA_HOME"}; EnvVarWrapper xdg_data_dirs_env_var{"XDG_DATA_DIRS"}; - EnvVarWrapper home_env_var{"HOME", HOME_DIR}; + EnvVarWrapper home_env_var{"HOME", "/home/test_home_directory"}; #endif ::testing::InitGoogleTest(&argc, argv); ::testing::UnitTest::GetInstance()->listeners().Append(new ThrowListener); diff --git a/tests/loader_unknown_ext_tests.cpp b/tests/loader_unknown_ext_tests.cpp index 782275682..162ceabd3 100644 --- a/tests/loader_unknown_ext_tests.cpp +++ b/tests/loader_unknown_ext_tests.cpp @@ -27,6 +27,8 @@ #include "test_environment.h" +#include "framework/util/dispatchable_handle.h" + enum class TestConfig { add_layer_implementation, add_layer_interception, diff --git a/tests/loader_version_tests.cpp b/tests/loader_version_tests.cpp index c13c7f60a..e097ff3ce 100644 --- a/tests/loader_version_tests.cpp +++ b/tests/loader_version_tests.cpp @@ -338,9 +338,9 @@ TEST(MultipleICDConfig, Basic) { env.get_test_icd(1).physical_devices.at(0).properties.deviceType = VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU; env.get_test_icd(2).physical_devices.at(0).properties.deviceType = VK_PHYSICAL_DEVICE_TYPE_CPU; - copy_string_to_char_array("dev0", env.get_test_icd(0).physical_devices.at(0).properties.deviceName, VK_MAX_EXTENSION_NAME_SIZE); - copy_string_to_char_array("dev1", env.get_test_icd(1).physical_devices.at(0).properties.deviceName, VK_MAX_EXTENSION_NAME_SIZE); - copy_string_to_char_array("dev2", env.get_test_icd(2).physical_devices.at(0).properties.deviceName, VK_MAX_EXTENSION_NAME_SIZE); + std::string("dev0").copy(env.get_test_icd(0).physical_devices.at(0).properties.deviceName, VK_MAX_EXTENSION_NAME_SIZE); + std::string("dev1").copy(env.get_test_icd(1).physical_devices.at(0).properties.deviceName, VK_MAX_EXTENSION_NAME_SIZE); + std::string("dev2").copy(env.get_test_icd(2).physical_devices.at(0).properties.deviceName, VK_MAX_EXTENSION_NAME_SIZE); InstWrapper inst{env.vulkan_functions}; inst.CheckCreate();