• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2023 The Khronos Group Inc.
3  * Copyright (c) 2021-2023 Valve Corporation
4  * Copyright (c) 2021-2023 LunarG, Inc.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and/or associated documentation files (the "Materials"), to
8  * deal in the Materials without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Materials, and to permit persons to whom the Materials are
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice(s) and this permission notice shall be included in
14  * all copies or substantial portions of the Materials.
15  *
16  * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19  *
20  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
21  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
22  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE
23  * USE OR OTHER DEALINGS IN THE MATERIALS.
24  *
25  * Author: Charles Giessen <charles@lunarg.com>
26  */
27 
28 /*
29  * Contains all the utilities needed to make the framework and tests work.
30  * Contains:
31  * All the standard library includes and main platform specific includes
32  * Dll export macro
33  * Manifest ICD & Layer structs
34  * FolderManager - manages the contents of a folder, cleaning up when needed
35  * per-platform library loading - mirrors the vk_loader_platform
36  * LibraryWrapper - RAII wrapper for a library
37  * DispatchableHandle - RAII wrapper for vulkan dispatchable handle objects
38  * ostream overload for VkResult - prettifies googletest output
39  * Instance & Device create info helpers
40  * operator == overloads for many vulkan structs - more concise tests
41  */
42 #pragma once
43 
44 #include <algorithm>
45 #include <array>
46 #include <iostream>
47 #include <ostream>
48 #include <string>
49 #include <vector>
50 #include <unordered_map>
51 #include <filesystem>
52 
53 #include <cassert>
54 #include <cstring>
55 #include <ctime>
56 #include <inttypes.h>
57 #include <stdio.h>
58 #include <stdint.h>
59 
60 // Set of platforms with a common set of functionality which is queried throughout the program
61 #if defined(__linux__) || defined(__APPLE__) || defined(__Fuchsia__) || defined(__QNX__) || defined(__FreeBSD__) || \
62     defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__GNU__)
63 #define COMMON_UNIX_PLATFORMS 1
64 #else
65 #define COMMON_UNIX_PLATFORMS 0
66 #endif
67 
68 #if defined(WIN32)
69 #include <direct.h>
70 #include <windows.h>
71 #include <strsafe.h>
72 #elif COMMON_UNIX_PLATFORMS
73 #include <dirent.h>
74 #include <sys/types.h>
75 #include <sys/stat.h>
76 #include <unistd.h>
77 #include <dlfcn.h>
78 
79 // Prevent macro collisions from <sys/types.h>
80 #undef major
81 #undef minor
82 
83 #endif
84 
85 #include <vulkan/vulkan.h>
86 #include <vulkan/vk_icd.h>
87 #include <vulkan/vk_layer.h>
88 
89 #include FRAMEWORK_CONFIG_HEADER
90 
91 #if defined(__GNUC__) && __GNUC__ >= 4
92 #define FRAMEWORK_EXPORT __attribute__((visibility("default")))
93 #elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590)
94 #define FRAMEWORK_EXPORT __attribute__((visibility("default")))
95 #elif defined(WIN32)
96 #define FRAMEWORK_EXPORT __declspec(dllexport)
97 #else
98 #define FRAMEWORK_EXPORT
99 #endif
100 
101 // Define it here so that json_writer.h has access to these functions
102 #if defined(WIN32)
103 // Convert an UTF-16 wstring to an UTF-8 string
104 std::string narrow(const std::wstring& utf16);
105 // Convert an UTF-8 string to an UTF-16 wstring
106 std::wstring widen(const std::string& utf8);
107 #else
108 // Do nothing passthrough for the sake of Windows & UTF-16
109 std::string narrow(const std::string& utf16);
110 // Do nothing passthrough for the sake of Windows & UTF-16
111 std::string widen(const std::string& utf8);
112 #endif
113 
114 #include "json_writer.h"
115 
116 // 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
117 //     env-var
118 // NOTE: This is only intended for test framework code, all test code MUST use EnvVarWrapper
119 std::string get_env_var(std::string const& name, bool report_failure = true);
120 
121 /*
122  * Wrapper around Environment Variables with common operations
123  * Since Environment Variables leak between tests, there needs to be RAII code to remove them during test cleanup
124 
125  */
126 
127 // Wrapper to set & remove env-vars automatically
128 struct EnvVarWrapper {
129     // Constructor which unsets the env-var
EnvVarWrapperEnvVarWrapper130     EnvVarWrapper(std::string const& name) noexcept : name(name) {
131         initial_value = get_env_var(name, false);
132         remove_env_var();
133     }
134     // Constructor which set the env-var to the specified value
EnvVarWrapperEnvVarWrapper135     EnvVarWrapper(std::string const& name, std::string const& value) noexcept : name(name), cur_value(value) {
136         initial_value = get_env_var(name, false);
137         set_env_var();
138     }
~EnvVarWrapperEnvVarWrapper139     ~EnvVarWrapper() noexcept {
140         remove_env_var();
141         if (!initial_value.empty()) {
142             set_new_value(initial_value);
143         }
144     }
145 
146     // delete copy operators
147     EnvVarWrapper(const EnvVarWrapper&) = delete;
148     EnvVarWrapper& operator=(const EnvVarWrapper&) = delete;
149 
set_new_valueEnvVarWrapper150     void set_new_value(std::string const& value) {
151         cur_value = value;
152         set_env_var();
153     }
add_to_listEnvVarWrapper154     void add_to_list(std::string const& list_item) {
155         if (!cur_value.empty()) {
156             cur_value += OS_ENV_VAR_LIST_SEPARATOR;
157         }
158         cur_value += list_item;
159         set_env_var();
160     }
161 #if defined(WIN32)
add_to_listEnvVarWrapper162     void add_to_list(std::wstring const& list_item) {
163         if (!cur_value.empty()) {
164             cur_value += OS_ENV_VAR_LIST_SEPARATOR;
165         }
166         cur_value += narrow(list_item);
167         set_env_var();
168     }
169 #endif
remove_valueEnvVarWrapper170     void remove_value() const { remove_env_var(); }
getEnvVarWrapper171     const char* get() const { return name.c_str(); }
valueEnvVarWrapper172     const char* value() const { return cur_value.c_str(); }
173 
174    private:
175     std::string name;
176     std::string cur_value;
177     std::string initial_value;
178 
179     void set_env_var();
180     void remove_env_var() const;
181 #if defined(WIN32)
182     // Environment variable list separator - not for filesystem paths
183     const char OS_ENV_VAR_LIST_SEPARATOR = ';';
184 #elif COMMON_UNIX_PLATFORMS
185     // Environment variable list separator - not for filesystem paths
186     const char OS_ENV_VAR_LIST_SEPARATOR = ':';
187 #endif
188 };
189 
190 // Windows specific error handling logic
191 #if defined(WIN32)
192 const long ERROR_SETENV_FAILED = 10543;           // chosen at random, attempts to not conflict
193 const long ERROR_REMOVEDIRECTORY_FAILED = 10544;  // chosen at random, attempts to not conflict
194 const char* win_api_error_str(LSTATUS status);
195 void print_error_message(LSTATUS status, const char* function_name, std::string optional_message = "");
196 #endif
197 
198 struct ManifestICD;    // forward declaration for FolderManager::write
199 struct ManifestLayer;  // forward declaration for FolderManager::write
200 
201 namespace fs {
202 
203 int create_folder(std::filesystem::path const& path);
204 int delete_folder(std::filesystem::path const& folder);
205 
206 class FolderManager {
207    public:
208     explicit FolderManager(std::filesystem::path root_path, std::string name) noexcept;
209     ~FolderManager() noexcept;
210     FolderManager(FolderManager const&) = delete;
211     FolderManager& operator=(FolderManager const&) = delete;
212     FolderManager(FolderManager&& other) noexcept;
213     FolderManager& operator=(FolderManager&& other) noexcept;
214 
215     // Add a manifest to the folder
216     std::filesystem::path write_manifest(std::filesystem::path const& name, std::string const& contents);
217 
218     // Add an already existing file to the manager, so it will be cleaned up automatically
219     void add_existing_file(std::filesystem::path const& file_name);
220 
221     // close file handle, delete file, remove `name` from managed file list.
222     void remove(std::filesystem::path const& name);
223 
224     // copy file into this folder with name `new_name`. Returns the full path of the file that was copied
225     std::filesystem::path copy_file(std::filesystem::path const& file, std::filesystem::path const& new_name);
226 
227     // location of the managed folder
location()228     std::filesystem::path location() const { return folder; }
229 
get_files()230     std::vector<std::filesystem::path> get_files() const { return files; }
231 
232    private:
233     std::filesystem::path folder;
234     std::vector<std::filesystem::path> files;
235 };
236 }  // namespace fs
237 
238 // copy the contents of a std::string into a char array and add a null terminator at the end
239 // src - std::string to read from
240 // dst - char array to write to
241 // size_dst - number of characters in the dst array
copy_string_to_char_array(std::string const & src,char * dst,size_t size_dst)242 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; }
243 
244 #if defined(WIN32)
245 typedef HMODULE test_platform_dl_handle;
test_platform_open_library(const wchar_t * lib_path)246 inline test_platform_dl_handle test_platform_open_library(const wchar_t* lib_path) {
247     // Try loading the library the original way first.
248     test_platform_dl_handle lib_handle = LoadLibraryW(lib_path);
249     if (lib_handle == nullptr && GetLastError() == ERROR_MOD_NOT_FOUND) {
250         // If that failed, then try loading it with broader search folders.
251         lib_handle = LoadLibraryExW(lib_path, nullptr, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR);
252     }
253     return lib_handle;
254 }
test_platform_open_library_print_error(std::filesystem::path const & libPath)255 inline void test_platform_open_library_print_error(std::filesystem::path const& libPath) {
256     std::wcerr << L"Unable to open library: " << libPath << L" due to: " << std::to_wstring(GetLastError()) << L"\n";
257 }
test_platform_close_library(test_platform_dl_handle library)258 inline void test_platform_close_library(test_platform_dl_handle library) { FreeLibrary(library); }
test_platform_get_proc_address(test_platform_dl_handle library,const char * name)259 inline void* test_platform_get_proc_address(test_platform_dl_handle library, const char* name) {
260     assert(library);
261     assert(name);
262     return reinterpret_cast<void*>(GetProcAddress(library, name));
263 }
test_platform_get_proc_address_error(const char * name)264 inline char* test_platform_get_proc_address_error(const char* name) {
265     static char errorMsg[120];
266     snprintf(errorMsg, 119, "Failed to find function \"%s\" in dynamic library", name);
267     return errorMsg;
268 }
269 
270 #elif COMMON_UNIX_PLATFORMS
271 
272 typedef void* test_platform_dl_handle;
test_platform_open_library(const char * libPath)273 inline test_platform_dl_handle test_platform_open_library(const char* libPath) { return dlopen(libPath, RTLD_LAZY | RTLD_LOCAL); }
test_platform_open_library_print_error(std::filesystem::path const & libPath)274 inline void test_platform_open_library_print_error(std::filesystem::path const& libPath) {
275     std::wcerr << "Unable to open library: " << libPath << " due to: " << dlerror() << "\n";
276 }
test_platform_close_library(test_platform_dl_handle library)277 inline void test_platform_close_library(test_platform_dl_handle library) {
278     char* loader_disable_dynamic_library_unloading_env_var = getenv("VK_LOADER_DISABLE_DYNAMIC_LIBRARY_UNLOADING");
279     if (NULL == loader_disable_dynamic_library_unloading_env_var ||
280         0 != strncmp(loader_disable_dynamic_library_unloading_env_var, "1", 2)) {
281         dlclose(library);
282     }
283 }
test_platform_get_proc_address(test_platform_dl_handle library,const char * name)284 inline void* test_platform_get_proc_address(test_platform_dl_handle library, const char* name) {
285     assert(library);
286     assert(name);
287     return dlsym(library, name);
288 }
test_platform_get_proc_address_error(const char * name)289 inline const char* test_platform_get_proc_address_error([[maybe_unused]] const char* name) { return dlerror(); }
290 #endif
291 
292 class FromVoidStarFunc {
293    private:
294     void* function;
295 
296    public:
FromVoidStarFunc(void * function)297     FromVoidStarFunc(void* function) : function(function) {}
FromVoidStarFunc(PFN_vkVoidFunction function)298     FromVoidStarFunc(PFN_vkVoidFunction function) : function(reinterpret_cast<void*>(function)) {}
299 
300     template <typename T>
T()301     operator T() {
302         return reinterpret_cast<T>(function);
303     }
304 };
305 
306 struct LibraryWrapper {
LibraryWrapperLibraryWrapper307     explicit LibraryWrapper() noexcept {}
LibraryWrapperLibraryWrapper308     explicit LibraryWrapper(std::filesystem::path const& lib_path) noexcept : lib_path(lib_path) {
309         lib_handle = test_platform_open_library(lib_path.native().c_str());
310         if (lib_handle == nullptr) {
311             test_platform_open_library_print_error(lib_path);
312             assert(lib_handle != nullptr && "Must be able to open library");
313         }
314     }
~LibraryWrapperLibraryWrapper315     ~LibraryWrapper() noexcept {
316         if (lib_handle != nullptr) {
317             test_platform_close_library(lib_handle);
318             lib_handle = nullptr;
319         }
320     }
321     LibraryWrapper(LibraryWrapper const& wrapper) = delete;
322     LibraryWrapper& operator=(LibraryWrapper const& wrapper) = delete;
LibraryWrapperLibraryWrapper323     LibraryWrapper(LibraryWrapper&& wrapper) noexcept : lib_handle(wrapper.lib_handle), lib_path(wrapper.lib_path) {
324         wrapper.lib_handle = nullptr;
325     }
326     LibraryWrapper& operator=(LibraryWrapper&& wrapper) noexcept {
327         if (this != &wrapper) {
328             if (lib_handle != nullptr) {
329                 test_platform_close_library(lib_handle);
330             }
331             lib_handle = wrapper.lib_handle;
332             lib_path = wrapper.lib_path;
333             wrapper.lib_handle = nullptr;
334         }
335         return *this;
336     }
get_symbolLibraryWrapper337     FromVoidStarFunc get_symbol(const char* symbol_name) const {
338         assert(lib_handle != nullptr && "Cannot get symbol with null library handle");
339         void* symbol = test_platform_get_proc_address(lib_handle, symbol_name);
340         if (symbol == nullptr) {
341             fprintf(stderr, "Unable to open symbol %s: %s\n", symbol_name, test_platform_get_proc_address_error(symbol_name));
342             assert(symbol != nullptr && "Must be able to get symbol");
343         }
344         return FromVoidStarFunc(symbol);
345     }
346 
347     explicit operator bool() const noexcept { return lib_handle != nullptr; }
348 
349     test_platform_dl_handle lib_handle = nullptr;
350     std::filesystem::path lib_path;
351 };
352 
353 template <typename T>
to_vkVoidFunction(T func)354 PFN_vkVoidFunction to_vkVoidFunction(T func) {
355     return reinterpret_cast<PFN_vkVoidFunction>(func);
356 }
357 template <typename T>
358 struct FRAMEWORK_EXPORT DispatchableHandle {
DispatchableHandleDispatchableHandle359     DispatchableHandle() {
360         auto ptr_handle = new VK_LOADER_DATA;
361         set_loader_magic_value(ptr_handle);
362         handle = reinterpret_cast<T>(ptr_handle);
363     }
~DispatchableHandleDispatchableHandle364     ~DispatchableHandle() {
365         if (handle) {
366             delete reinterpret_cast<VK_LOADER_DATA*>(handle);
367         }
368         handle = nullptr;
369     }
370     DispatchableHandle(DispatchableHandle const&) = delete;
371     DispatchableHandle& operator=(DispatchableHandle const&) = delete;
DispatchableHandleDispatchableHandle372     DispatchableHandle(DispatchableHandle&& other) noexcept : handle(other.handle) { other.handle = nullptr; }
373     DispatchableHandle& operator=(DispatchableHandle&& other) noexcept {
374         if (handle) {
375             delete reinterpret_cast<VK_LOADER_DATA*>(handle);
376         }
377         handle = other.handle;
378         other.handle = nullptr;
379         return *this;
380     }
381     bool operator==(T base_handle) { return base_handle == handle; }
382     bool operator!=(T base_handle) { return base_handle != handle; }
383 
384     T handle = nullptr;
385 };
386 
387 // Stream operator for VkResult so GTEST will print out error codes as strings (automatically)
388 inline std::ostream& operator<<(std::ostream& os, const VkResult& result) {
389     switch (result) {
390         case (VK_SUCCESS):
391             return os << "VK_SUCCESS";
392         case (VK_NOT_READY):
393             return os << "VK_NOT_READY";
394         case (VK_TIMEOUT):
395             return os << "VK_TIMEOUT";
396         case (VK_EVENT_SET):
397             return os << "VK_EVENT_SET";
398         case (VK_EVENT_RESET):
399             return os << "VK_EVENT_RESET";
400         case (VK_INCOMPLETE):
401             return os << "VK_INCOMPLETE";
402         case (VK_ERROR_OUT_OF_HOST_MEMORY):
403             return os << "VK_ERROR_OUT_OF_HOST_MEMORY";
404         case (VK_ERROR_OUT_OF_DEVICE_MEMORY):
405             return os << "VK_ERROR_OUT_OF_DEVICE_MEMORY";
406         case (VK_ERROR_INITIALIZATION_FAILED):
407             return os << "VK_ERROR_INITIALIZATION_FAILED";
408         case (VK_ERROR_DEVICE_LOST):
409             return os << "VK_ERROR_DEVICE_LOST";
410         case (VK_ERROR_MEMORY_MAP_FAILED):
411             return os << "VK_ERROR_MEMORY_MAP_FAILED";
412         case (VK_ERROR_LAYER_NOT_PRESENT):
413             return os << "VK_ERROR_LAYER_NOT_PRESENT";
414         case (VK_ERROR_EXTENSION_NOT_PRESENT):
415             return os << "VK_ERROR_EXTENSION_NOT_PRESENT";
416         case (VK_ERROR_FEATURE_NOT_PRESENT):
417             return os << "VK_ERROR_FEATURE_NOT_PRESENT";
418         case (VK_ERROR_INCOMPATIBLE_DRIVER):
419             return os << "VK_ERROR_INCOMPATIBLE_DRIVER";
420         case (VK_ERROR_TOO_MANY_OBJECTS):
421             return os << "VK_ERROR_TOO_MANY_OBJECTS";
422         case (VK_ERROR_FORMAT_NOT_SUPPORTED):
423             return os << "VK_ERROR_FORMAT_NOT_SUPPORTED";
424         case (VK_ERROR_FRAGMENTED_POOL):
425             return os << "VK_ERROR_FRAGMENTED_POOL";
426         case (VK_ERROR_UNKNOWN):
427             return os << "VK_ERROR_UNKNOWN";
428         case (VK_ERROR_OUT_OF_POOL_MEMORY):
429             return os << "VK_ERROR_OUT_OF_POOL_MEMORY";
430         case (VK_ERROR_INVALID_EXTERNAL_HANDLE):
431             return os << "VK_ERROR_INVALID_EXTERNAL_HANDLE";
432         case (VK_ERROR_FRAGMENTATION):
433             return os << "VK_ERROR_FRAGMENTATION";
434         case (VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS):
435             return os << "VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS";
436         case (VK_ERROR_SURFACE_LOST_KHR):
437             return os << "VK_ERROR_SURFACE_LOST_KHR";
438         case (VK_ERROR_NATIVE_WINDOW_IN_USE_KHR):
439             return os << "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR";
440         case (VK_SUBOPTIMAL_KHR):
441             return os << "VK_SUBOPTIMAL_KHR";
442         case (VK_ERROR_OUT_OF_DATE_KHR):
443             return os << "VK_ERROR_OUT_OF_DATE_KHR";
444         case (VK_ERROR_INCOMPATIBLE_DISPLAY_KHR):
445             return os << "VK_ERROR_INCOMPATIBLE_DISPLAY_KHR";
446         case (VK_ERROR_VALIDATION_FAILED_EXT):
447             return os << "VK_ERROR_VALIDATION_FAILED_EXT";
448         case (VK_ERROR_INVALID_SHADER_NV):
449             return os << "VK_ERROR_INVALID_SHADER_NV";
450         case (VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT):
451             return os << "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT";
452         case (VK_ERROR_NOT_PERMITTED_EXT):
453             return os << "VK_ERROR_NOT_PERMITTED_EXT";
454         case (VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT):
455             return os << "VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT";
456         case (VK_THREAD_IDLE_KHR):
457             return os << "VK_THREAD_IDLE_KHR";
458         case (VK_THREAD_DONE_KHR):
459             return os << "VK_THREAD_DONE_KHR";
460         case (VK_OPERATION_DEFERRED_KHR):
461             return os << "VK_OPERATION_DEFERRED_KHR";
462         case (VK_OPERATION_NOT_DEFERRED_KHR):
463             return os << "VK_OPERATION_NOT_DEFERRED_KHR";
464         case (VK_PIPELINE_COMPILE_REQUIRED_EXT):
465             return os << "VK_PIPELINE_COMPILE_REQUIRED_EXT";
466         case (VK_RESULT_MAX_ENUM):
467             return os << "VK_RESULT_MAX_ENUM";
468         case (VK_ERROR_COMPRESSION_EXHAUSTED_EXT):
469             return os << "VK_ERROR_COMPRESSION_EXHAUSTED_EXT";
470         case (VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR):
471             return os << "VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR";
472         case (VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR):
473             return os << "VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR";
474         case (VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR):
475             return os << "VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR";
476         case (VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR):
477             return os << "VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR";
478         case (VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR):
479             return os << "VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR";
480         case (VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR):
481             return os << "VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR";
482         case (VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR):
483             return os << "VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR";
484         case (VK_ERROR_INCOMPATIBLE_SHADER_BINARY_EXT):
485             return os << "VK_ERROR_INCOMPATIBLE_SHADER_BINARY_EXT";
486         case (VK_PIPELINE_BINARY_MISSING_KHR):
487             return os << "VK_PIPELINE_BINARY_MISSING_KHR";
488         case (VK_ERROR_NOT_ENOUGH_SPACE_KHR):
489             return os << "VK_ERROR_NOT_ENOUGH_SPACE_KHR";
490     }
491     return os << static_cast<int32_t>(result);
492 }
493 
494 const char* get_platform_wsi_extension([[maybe_unused]] const char* api_selection);
495 
496 bool string_eq(const char* a, const char* b) noexcept;
497 bool string_eq(const char* a, const char* b, size_t len) noexcept;
498 
version_to_string(uint32_t version)499 inline std::string version_to_string(uint32_t version) {
500     std::string out = std::to_string(VK_API_VERSION_MAJOR(version)) + "." + std::to_string(VK_API_VERSION_MINOR(version)) + "." +
501                       std::to_string(VK_API_VERSION_PATCH(version));
502     if (VK_API_VERSION_VARIANT(version) != 0) out += std::to_string(VK_API_VERSION_VARIANT(version)) + "." + out;
503     return out;
504 }
505 
506 // Macro to ease the definition of variables with builder member functions
507 // class_name = class the member variable is apart of
508 // type = type of the variable
509 // name = name of the variable
510 // default_value = value to default initialize, use {} if nothing else makes sense
511 #define BUILDER_VALUE(class_name, type, name, default_value) \
512     type name = default_value;                               \
513     class_name& set_##name(type const& name) {               \
514         this->name = name;                                   \
515         return *this;                                        \
516     }
517 
518 // Macro to ease the definition of vectors with builder member functions
519 // class_name = class the member variable is apart of
520 // type = type of the variable
521 // name = name of the variable
522 // singular_name = used for the `add_singular_name` member function
523 #define BUILDER_VECTOR(class_name, type, name, singular_name)                    \
524     std::vector<type> name;                                                      \
525     class_name& add_##singular_name(type const& singular_name) {                 \
526         this->name.push_back(singular_name);                                     \
527         return *this;                                                            \
528     }                                                                            \
529     class_name& add_##singular_name##s(std::vector<type> const& singular_name) { \
530         for (auto& elem : singular_name) this->name.push_back(elem);             \
531         return *this;                                                            \
532     }
533 // Like BUILDER_VECTOR but for move only types - where passing in means giving up ownership
534 #define BUILDER_VECTOR_MOVE_ONLY(class_name, type, name, singular_name) \
535     std::vector<type> name;                                             \
536     class_name& add_##singular_name(type&& singular_name) {             \
537         this->name.push_back(std::move(singular_name));                 \
538         return *this;                                                   \
539     }
540 
541 struct ManifestVersion {
542     BUILDER_VALUE(ManifestVersion, uint32_t, major, 1)
543     BUILDER_VALUE(ManifestVersion, uint32_t, minor, 0)
544     BUILDER_VALUE(ManifestVersion, uint32_t, patch, 0)
545 
get_version_strManifestVersion546     std::string get_version_str() const noexcept {
547         return std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(patch);
548     }
549 };
550 
551 // ManifestICD builder
552 struct ManifestICD {
553     BUILDER_VALUE(ManifestICD, ManifestVersion, file_format_version, {})
554     BUILDER_VALUE(ManifestICD, uint32_t, api_version, 0)
555     BUILDER_VALUE(ManifestICD, std::filesystem::path, lib_path, {})
556     BUILDER_VALUE(ManifestICD, bool, is_portability_driver, false)
557     BUILDER_VALUE(ManifestICD, std::string, library_arch, "")
558     std::string get_manifest_str() const;
559 };
560 
561 // ManifestLayer builder
562 struct ManifestLayer {
563     struct LayerDescription {
564         enum class Type { INSTANCE, GLOBAL, DEVICE };
get_type_strManifestLayer::LayerDescription565         std::string get_type_str(Type layer_type) const {
566             if (layer_type == Type::GLOBAL)
567                 return "GLOBAL";
568             else if (layer_type == Type::DEVICE)
569                 return "DEVICE";
570             else  // default
571                 return "INSTANCE";
572         }
573         struct FunctionOverride {
574             BUILDER_VALUE(FunctionOverride, std::string, vk_func, {})
575             BUILDER_VALUE(FunctionOverride, std::string, override_name, {})
576 
get_manifest_strManifestLayer::LayerDescription::FunctionOverride577             void get_manifest_str(JsonWriter& writer) const { writer.AddKeyedString(vk_func, override_name); }
578         };
579         struct Extension {
ExtensionManifestLayer::LayerDescription::Extension580             Extension() noexcept {}
581             Extension(std::string name, uint32_t spec_version = 0, std::vector<std::string> entrypoints = {}) noexcept
nameManifestLayer::LayerDescription::Extension582                 : name(name), spec_version(spec_version), entrypoints(entrypoints) {}
583             std::string name;
584             uint32_t spec_version = 0;
585             std::vector<std::string> entrypoints;
586             void get_manifest_str(JsonWriter& writer) const;
587         };
588         BUILDER_VALUE(LayerDescription, std::string, name, {})
589         BUILDER_VALUE(LayerDescription, Type, type, Type::INSTANCE)
590         BUILDER_VALUE(LayerDescription, std::filesystem::path, lib_path, {})
591         BUILDER_VALUE(LayerDescription, uint32_t, api_version, VK_API_VERSION_1_0)
592         BUILDER_VALUE(LayerDescription, uint32_t, implementation_version, 0)
593         BUILDER_VALUE(LayerDescription, std::string, description, {})
594         BUILDER_VECTOR(LayerDescription, FunctionOverride, functions, function)
595         BUILDER_VECTOR(LayerDescription, Extension, instance_extensions, instance_extension)
596         BUILDER_VECTOR(LayerDescription, Extension, device_extensions, device_extension)
597         BUILDER_VALUE(LayerDescription, std::string, enable_environment, {})
598         BUILDER_VALUE(LayerDescription, std::string, disable_environment, {})
599         BUILDER_VECTOR(LayerDescription, std::string, component_layers, component_layer)
600         BUILDER_VECTOR(LayerDescription, std::string, blacklisted_layers, blacklisted_layer)
601         BUILDER_VECTOR(LayerDescription, std::filesystem::path, override_paths, override_path)
602         BUILDER_VECTOR(LayerDescription, FunctionOverride, pre_instance_functions, pre_instance_function)
603         BUILDER_VECTOR(LayerDescription, std::string, app_keys, app_key)
604         BUILDER_VALUE(LayerDescription, std::string, library_arch, "")
605 
606         void get_manifest_str(JsonWriter& writer) const;
607         VkLayerProperties get_layer_properties() const;
608     };
609     BUILDER_VALUE(ManifestLayer, ManifestVersion, file_format_version, {})
610     BUILDER_VECTOR(ManifestLayer, LayerDescription, layers, layer)
611 
612     std::string get_manifest_str() const;
613 };
614 
615 struct Extension {
616     BUILDER_VALUE(Extension, std::string, extensionName, {})
BUILDER_VALUEExtension617     BUILDER_VALUE(Extension, uint32_t, specVersion, VK_API_VERSION_1_0)
618 
619     Extension(const char* name, uint32_t specVersion = VK_API_VERSION_1_0) noexcept
620         : extensionName(name), specVersion(specVersion) {}
621     Extension(std::string extensionName, uint32_t specVersion = VK_API_VERSION_1_0) noexcept
extensionNameExtension622         : extensionName(extensionName), specVersion(specVersion) {}
623 
getExtension624     VkExtensionProperties get() const noexcept {
625         VkExtensionProperties props{};
626         copy_string_to_char_array(extensionName, &props.extensionName[0], VK_MAX_EXTENSION_NAME_SIZE);
627         props.specVersion = specVersion;
628         return props;
629     }
630 };
631 
632 struct MockQueueFamilyProperties {
633     BUILDER_VALUE(MockQueueFamilyProperties, VkQueueFamilyProperties, properties, {})
BUILDER_VALUEMockQueueFamilyProperties634     BUILDER_VALUE(MockQueueFamilyProperties, bool, support_present, false)
635 
636     VkQueueFamilyProperties get() const noexcept { return properties; }
637 };
638 
639 struct InstanceCreateInfo {
640     BUILDER_VALUE(InstanceCreateInfo, VkInstanceCreateInfo, instance_info, {})
641     BUILDER_VALUE(InstanceCreateInfo, VkApplicationInfo, application_info, {})
642     BUILDER_VALUE(InstanceCreateInfo, std::string, app_name, {})
643     BUILDER_VALUE(InstanceCreateInfo, std::string, engine_name, {})
644     BUILDER_VALUE(InstanceCreateInfo, uint32_t, flags, 0)
645     BUILDER_VALUE(InstanceCreateInfo, uint32_t, app_version, 0)
646     BUILDER_VALUE(InstanceCreateInfo, uint32_t, engine_version, 0)
647     BUILDER_VALUE(InstanceCreateInfo, uint32_t, api_version, VK_API_VERSION_1_0)
648     BUILDER_VECTOR(InstanceCreateInfo, const char*, enabled_layers, layer)
649     BUILDER_VECTOR(InstanceCreateInfo, const char*, enabled_extensions, extension)
650     // tell the get() function to not provide `application_info`
651     BUILDER_VALUE(InstanceCreateInfo, bool, fill_in_application_info, true)
652 
653     InstanceCreateInfo();
654 
655     VkInstanceCreateInfo* get() noexcept;
656 
657     InstanceCreateInfo& set_api_version(uint32_t major, uint32_t minor, uint32_t patch);
658 
659     InstanceCreateInfo& setup_WSI(const char* api_selection = nullptr);
660 };
661 
662 struct DeviceQueueCreateInfo {
663     DeviceQueueCreateInfo();
664     DeviceQueueCreateInfo(const VkDeviceQueueCreateInfo* create_info);
665 
666     BUILDER_VALUE(DeviceQueueCreateInfo, VkDeviceQueueCreateInfo, queue_create_info, {})
667     BUILDER_VECTOR(DeviceQueueCreateInfo, float, priorities, priority)
668 
669     VkDeviceQueueCreateInfo get() noexcept;
670 };
671 
672 struct DeviceCreateInfo {
673     DeviceCreateInfo() = default;
674     DeviceCreateInfo(const VkDeviceCreateInfo* create_info);
675 
676     BUILDER_VALUE(DeviceCreateInfo, VkDeviceCreateInfo, dev, {})
677     BUILDER_VECTOR(DeviceCreateInfo, const char*, enabled_extensions, extension)
678     BUILDER_VECTOR(DeviceCreateInfo, const char*, enabled_layers, layer)
679     BUILDER_VECTOR(DeviceCreateInfo, DeviceQueueCreateInfo, queue_info_details, device_queue)
680 
681     VkDeviceCreateInfo* get() noexcept;
682 
683    private:
684     std::vector<VkDeviceQueueCreateInfo> device_queue_infos;
685 };
686 
687 inline bool operator==(const VkExtent3D& a, const VkExtent3D& b) {
688     return a.width == b.width && a.height == b.height && a.depth == b.depth;
689 }
690 inline bool operator!=(const VkExtent3D& a, const VkExtent3D& b) { return !(a == b); }
691 
692 inline bool operator==(const VkQueueFamilyProperties& a, const VkQueueFamilyProperties& b) {
693     return a.minImageTransferGranularity == b.minImageTransferGranularity && a.queueCount == b.queueCount &&
694            a.queueFlags == b.queueFlags && a.timestampValidBits == b.timestampValidBits;
695 }
696 inline bool operator!=(const VkQueueFamilyProperties& a, const VkQueueFamilyProperties& b) { return !(a == b); }
697 
698 inline bool operator==(const VkQueueFamilyProperties& a, const VkQueueFamilyProperties2& b) { return a == b.queueFamilyProperties; }
699 inline bool operator!=(const VkQueueFamilyProperties& a, const VkQueueFamilyProperties2& b) { return a != b.queueFamilyProperties; }
700 
701 inline bool operator==(const VkLayerProperties& a, const VkLayerProperties& b) {
702     return string_eq(a.layerName, b.layerName, 256) && string_eq(a.description, b.description, 256) &&
703            a.implementationVersion == b.implementationVersion && a.specVersion == b.specVersion;
704 }
705 inline bool operator!=(const VkLayerProperties& a, const VkLayerProperties& b) { return !(a == b); }
706 
707 inline bool operator==(const VkExtensionProperties& a, const VkExtensionProperties& b) {
708     return string_eq(a.extensionName, b.extensionName, 256) && a.specVersion == b.specVersion;
709 }
710 inline bool operator!=(const VkExtensionProperties& a, const VkExtensionProperties& b) { return !(a == b); }
711 
712 inline bool operator==(const VkPhysicalDeviceFeatures& feats1, const VkPhysicalDeviceFeatures2& feats2) {
713     return feats1.robustBufferAccess == feats2.features.robustBufferAccess &&
714            feats1.fullDrawIndexUint32 == feats2.features.fullDrawIndexUint32 &&
715            feats1.imageCubeArray == feats2.features.imageCubeArray && feats1.independentBlend == feats2.features.independentBlend &&
716            feats1.geometryShader == feats2.features.geometryShader &&
717            feats1.tessellationShader == feats2.features.tessellationShader &&
718            feats1.sampleRateShading == feats2.features.sampleRateShading && feats1.dualSrcBlend == feats2.features.dualSrcBlend &&
719            feats1.logicOp == feats2.features.logicOp && feats1.multiDrawIndirect == feats2.features.multiDrawIndirect &&
720            feats1.drawIndirectFirstInstance == feats2.features.drawIndirectFirstInstance &&
721            feats1.depthClamp == feats2.features.depthClamp && feats1.depthBiasClamp == feats2.features.depthBiasClamp &&
722            feats1.fillModeNonSolid == feats2.features.fillModeNonSolid && feats1.depthBounds == feats2.features.depthBounds &&
723            feats1.wideLines == feats2.features.wideLines && feats1.largePoints == feats2.features.largePoints &&
724            feats1.alphaToOne == feats2.features.alphaToOne && feats1.multiViewport == feats2.features.multiViewport &&
725            feats1.samplerAnisotropy == feats2.features.samplerAnisotropy &&
726            feats1.textureCompressionETC2 == feats2.features.textureCompressionETC2 &&
727            feats1.textureCompressionASTC_LDR == feats2.features.textureCompressionASTC_LDR &&
728            feats1.textureCompressionBC == feats2.features.textureCompressionBC &&
729            feats1.occlusionQueryPrecise == feats2.features.occlusionQueryPrecise &&
730            feats1.pipelineStatisticsQuery == feats2.features.pipelineStatisticsQuery &&
731            feats1.vertexPipelineStoresAndAtomics == feats2.features.vertexPipelineStoresAndAtomics &&
732            feats1.fragmentStoresAndAtomics == feats2.features.fragmentStoresAndAtomics &&
733            feats1.shaderTessellationAndGeometryPointSize == feats2.features.shaderTessellationAndGeometryPointSize &&
734            feats1.shaderImageGatherExtended == feats2.features.shaderImageGatherExtended &&
735            feats1.shaderStorageImageExtendedFormats == feats2.features.shaderStorageImageExtendedFormats &&
736            feats1.shaderStorageImageMultisample == feats2.features.shaderStorageImageMultisample &&
737            feats1.shaderStorageImageReadWithoutFormat == feats2.features.shaderStorageImageReadWithoutFormat &&
738            feats1.shaderStorageImageWriteWithoutFormat == feats2.features.shaderStorageImageWriteWithoutFormat &&
739            feats1.shaderUniformBufferArrayDynamicIndexing == feats2.features.shaderUniformBufferArrayDynamicIndexing &&
740            feats1.shaderSampledImageArrayDynamicIndexing == feats2.features.shaderSampledImageArrayDynamicIndexing &&
741            feats1.shaderStorageBufferArrayDynamicIndexing == feats2.features.shaderStorageBufferArrayDynamicIndexing &&
742            feats1.shaderStorageImageArrayDynamicIndexing == feats2.features.shaderStorageImageArrayDynamicIndexing &&
743            feats1.shaderClipDistance == feats2.features.shaderClipDistance &&
744            feats1.shaderCullDistance == feats2.features.shaderCullDistance &&
745            feats1.shaderFloat64 == feats2.features.shaderFloat64 && feats1.shaderInt64 == feats2.features.shaderInt64 &&
746            feats1.shaderInt16 == feats2.features.shaderInt16 &&
747            feats1.shaderResourceResidency == feats2.features.shaderResourceResidency &&
748            feats1.shaderResourceMinLod == feats2.features.shaderResourceMinLod &&
749            feats1.sparseBinding == feats2.features.sparseBinding &&
750            feats1.sparseResidencyBuffer == feats2.features.sparseResidencyBuffer &&
751            feats1.sparseResidencyImage2D == feats2.features.sparseResidencyImage2D &&
752            feats1.sparseResidencyImage3D == feats2.features.sparseResidencyImage3D &&
753            feats1.sparseResidency2Samples == feats2.features.sparseResidency2Samples &&
754            feats1.sparseResidency4Samples == feats2.features.sparseResidency4Samples &&
755            feats1.sparseResidency8Samples == feats2.features.sparseResidency8Samples &&
756            feats1.sparseResidency16Samples == feats2.features.sparseResidency16Samples &&
757            feats1.sparseResidencyAliased == feats2.features.sparseResidencyAliased &&
758            feats1.variableMultisampleRate == feats2.features.variableMultisampleRate &&
759            feats1.inheritedQueries == feats2.features.inheritedQueries;
760 }
761 
762 inline bool operator==(const VkPhysicalDeviceMemoryProperties& props1, const VkPhysicalDeviceMemoryProperties2& props2) {
763     bool equal = true;
764     equal = equal && props1.memoryTypeCount == props2.memoryProperties.memoryTypeCount;
765     equal = equal && props1.memoryHeapCount == props2.memoryProperties.memoryHeapCount;
766     for (uint32_t i = 0; i < props1.memoryHeapCount; ++i) {
767         equal = equal && props1.memoryHeaps[i].size == props2.memoryProperties.memoryHeaps[i].size;
768         equal = equal && props1.memoryHeaps[i].flags == props2.memoryProperties.memoryHeaps[i].flags;
769     }
770     for (uint32_t i = 0; i < props1.memoryTypeCount; ++i) {
771         equal = equal && props1.memoryTypes[i].propertyFlags == props2.memoryProperties.memoryTypes[i].propertyFlags;
772         equal = equal && props1.memoryTypes[i].heapIndex == props2.memoryProperties.memoryTypes[i].heapIndex;
773     }
774     return equal;
775 }
776 inline bool operator==(const VkSparseImageFormatProperties& props1, const VkSparseImageFormatProperties& props2) {
777     return props1.aspectMask == props2.aspectMask && props1.imageGranularity.width == props2.imageGranularity.width &&
778            props1.imageGranularity.height == props2.imageGranularity.height &&
779            props1.imageGranularity.depth == props2.imageGranularity.depth && props1.flags == props2.flags;
780 }
781 inline bool operator==(const VkSparseImageFormatProperties& props1, const VkSparseImageFormatProperties2& props2) {
782     return props1 == props2.properties;
783 }
784 inline bool operator==(const VkExternalMemoryProperties& props1, const VkExternalMemoryProperties& props2) {
785     return props1.externalMemoryFeatures == props2.externalMemoryFeatures &&
786            props1.exportFromImportedHandleTypes == props2.exportFromImportedHandleTypes &&
787            props1.compatibleHandleTypes == props2.compatibleHandleTypes;
788 }
789 inline bool operator==(const VkExternalSemaphoreProperties& props1, const VkExternalSemaphoreProperties& props2) {
790     return props1.externalSemaphoreFeatures == props2.externalSemaphoreFeatures &&
791            props1.exportFromImportedHandleTypes == props2.exportFromImportedHandleTypes &&
792            props1.compatibleHandleTypes == props2.compatibleHandleTypes;
793 }
794 inline bool operator==(const VkExternalFenceProperties& props1, const VkExternalFenceProperties& props2) {
795     return props1.externalFenceFeatures == props2.externalFenceFeatures &&
796            props1.exportFromImportedHandleTypes == props2.exportFromImportedHandleTypes &&
797            props1.compatibleHandleTypes == props2.compatibleHandleTypes;
798 }
799 inline bool operator==(const VkSurfaceCapabilitiesKHR& props1, const VkSurfaceCapabilitiesKHR& props2) {
800     return props1.minImageCount == props2.minImageCount && props1.maxImageCount == props2.maxImageCount &&
801            props1.currentExtent.width == props2.currentExtent.width && props1.currentExtent.height == props2.currentExtent.height &&
802            props1.minImageExtent.width == props2.minImageExtent.width &&
803            props1.minImageExtent.height == props2.minImageExtent.height &&
804            props1.maxImageExtent.width == props2.maxImageExtent.width &&
805            props1.maxImageExtent.height == props2.maxImageExtent.height &&
806            props1.maxImageArrayLayers == props2.maxImageArrayLayers && props1.supportedTransforms == props2.supportedTransforms &&
807            props1.currentTransform == props2.currentTransform && props1.supportedCompositeAlpha == props2.supportedCompositeAlpha &&
808            props1.supportedUsageFlags == props2.supportedUsageFlags;
809 }
810 inline bool operator==(const VkSurfacePresentScalingCapabilitiesEXT& caps1, const VkSurfacePresentScalingCapabilitiesEXT& caps2) {
811     return caps1.supportedPresentScaling == caps2.supportedPresentScaling &&
812            caps1.supportedPresentGravityX == caps2.supportedPresentGravityX &&
813            caps1.supportedPresentGravityY == caps2.supportedPresentGravityY &&
814            caps1.minScaledImageExtent.width == caps2.minScaledImageExtent.width &&
815            caps1.minScaledImageExtent.height == caps2.minScaledImageExtent.height &&
816            caps1.maxScaledImageExtent.width == caps2.maxScaledImageExtent.width &&
817            caps1.maxScaledImageExtent.height == caps2.maxScaledImageExtent.height;
818 }
819 inline bool operator==(const VkSurfaceFormatKHR& format1, const VkSurfaceFormatKHR& format2) {
820     return format1.format == format2.format && format1.colorSpace == format2.colorSpace;
821 }
822 inline bool operator==(const VkSurfaceFormatKHR& format1, const VkSurfaceFormat2KHR& format2) {
823     return format1 == format2.surfaceFormat;
824 }
825 inline bool operator==(const VkDisplayPropertiesKHR& props1, const VkDisplayPropertiesKHR& props2) {
826     return props1.display == props2.display && props1.physicalDimensions.width == props2.physicalDimensions.width &&
827            props1.physicalDimensions.height == props2.physicalDimensions.height &&
828            props1.physicalResolution.width == props2.physicalResolution.width &&
829            props1.physicalResolution.height == props2.physicalResolution.height &&
830            props1.supportedTransforms == props2.supportedTransforms && props1.planeReorderPossible == props2.planeReorderPossible &&
831            props1.persistentContent == props2.persistentContent;
832 }
833 inline bool operator==(const VkDisplayPropertiesKHR& props1, const VkDisplayProperties2KHR& props2) {
834     return props1 == props2.displayProperties;
835 }
836 inline bool operator==(const VkDisplayModePropertiesKHR& disp1, const VkDisplayModePropertiesKHR& disp2) {
837     return disp1.displayMode == disp2.displayMode && disp1.parameters.visibleRegion.width == disp2.parameters.visibleRegion.width &&
838            disp1.parameters.visibleRegion.height == disp2.parameters.visibleRegion.height &&
839            disp1.parameters.refreshRate == disp2.parameters.refreshRate;
840 }
841 
842 inline bool operator==(const VkDisplayModePropertiesKHR& disp1, const VkDisplayModeProperties2KHR& disp2) {
843     return disp1 == disp2.displayModeProperties;
844 }
845 inline bool operator==(const VkDisplayPlaneCapabilitiesKHR& caps1, const VkDisplayPlaneCapabilitiesKHR& caps2) {
846     return caps1.supportedAlpha == caps2.supportedAlpha && caps1.minSrcPosition.x == caps2.minSrcPosition.x &&
847            caps1.minSrcPosition.y == caps2.minSrcPosition.y && caps1.maxSrcPosition.x == caps2.maxSrcPosition.x &&
848            caps1.maxSrcPosition.y == caps2.maxSrcPosition.y && caps1.minSrcExtent.width == caps2.minSrcExtent.width &&
849            caps1.minSrcExtent.height == caps2.minSrcExtent.height && caps1.maxSrcExtent.width == caps2.maxSrcExtent.width &&
850            caps1.maxSrcExtent.height == caps2.maxSrcExtent.height && caps1.minDstPosition.x == caps2.minDstPosition.x &&
851            caps1.minDstPosition.y == caps2.minDstPosition.y && caps1.maxDstPosition.x == caps2.maxDstPosition.x &&
852            caps1.maxDstPosition.y == caps2.maxDstPosition.y && caps1.minDstExtent.width == caps2.minDstExtent.width &&
853            caps1.minDstExtent.height == caps2.minDstExtent.height && caps1.maxDstExtent.width == caps2.maxDstExtent.width &&
854            caps1.maxDstExtent.height == caps2.maxDstExtent.height;
855 }
856 
857 inline bool operator==(const VkDisplayPlaneCapabilitiesKHR& caps1, const VkDisplayPlaneCapabilities2KHR& caps2) {
858     return caps1 == caps2.capabilities;
859 }
860 inline bool operator==(const VkDisplayPlanePropertiesKHR& props1, const VkDisplayPlanePropertiesKHR& props2) {
861     return props1.currentDisplay == props2.currentDisplay && props1.currentStackIndex == props2.currentStackIndex;
862 }
863 inline bool operator==(const VkDisplayPlanePropertiesKHR& props1, const VkDisplayPlaneProperties2KHR& props2) {
864     return props1 == props2.displayPlaneProperties;
865 }
866 inline bool operator==(const VkExtent2D& ext1, const VkExtent2D& ext2) {
867     return ext1.height == ext2.height && ext1.width == ext2.width;
868 }
869 // Allow comparison of vectors of different types as long as their elements are comparable (just has to make sure to only apply when
870 // T != U)
871 template <typename T, typename U, typename = std::enable_if_t<!std::is_same_v<T, U>>>
872 bool operator==(const std::vector<T>& a, const std::vector<U>& b) {
873     return std::equal(a.begin(), a.end(), b.begin(), b.end(), [](const auto& left, const auto& right) { return left == right; });
874 }
875 
876 struct VulkanFunction {
877     std::string name;
878     PFN_vkVoidFunction function = nullptr;
879 };
880 
881 template <typename T, size_t U>
check_permutation(std::initializer_list<const char * > expected,std::array<T,U> const & returned)882 bool check_permutation(std::initializer_list<const char*> expected, std::array<T, U> const& returned) {
883     if (expected.size() != returned.size()) return false;
884     for (uint32_t i = 0; i < expected.size(); i++) {
885         auto found = std::find_if(std::begin(returned), std::end(returned),
886                                   [&](T elem) { return string_eq(*(expected.begin() + i), elem.layerName); });
887         if (found == std::end(returned)) return false;
888     }
889     return true;
890 }
891 template <typename T>
check_permutation(std::initializer_list<const char * > expected,std::vector<T> const & returned)892 bool check_permutation(std::initializer_list<const char*> expected, std::vector<T> const& returned) {
893     if (expected.size() != returned.size()) return false;
894     for (uint32_t i = 0; i < expected.size(); i++) {
895         auto found = std::find_if(std::begin(returned), std::end(returned),
896                                   [&](T elem) { return string_eq(*(expected.begin() + i), elem.layerName); });
897         if (found == std::end(returned)) return false;
898     }
899     return true;
900 }
901 
contains(std::vector<VkExtensionProperties> const & vec,const char * name)902 inline bool contains(std::vector<VkExtensionProperties> const& vec, const char* name) {
903     return std::any_of(std::begin(vec), std::end(vec),
904                        [name](VkExtensionProperties const& elem) { return string_eq(name, elem.extensionName); });
905 }
contains(std::vector<VkLayerProperties> const & vec,const char * name)906 inline bool contains(std::vector<VkLayerProperties> const& vec, const char* name) {
907     return std::any_of(std::begin(vec), std::end(vec),
908                        [name](VkLayerProperties const& elem) { return string_eq(name, elem.layerName); });
909 }
910 
911 #if defined(__linux__) || defined(__GNU__)
912 
913 // find application path + name. Path cannot be longer than 1024, returns NULL if it is greater than that.
test_platform_executable_path()914 inline std::string test_platform_executable_path() {
915     std::string buffer;
916     buffer.resize(1024);
917     ssize_t count = readlink("/proc/self/exe", &buffer[0], buffer.size());
918     if (count == -1) return NULL;
919     if (count == 0) return NULL;
920     buffer[count] = '\0';
921     buffer.resize(count);
922     return buffer;
923 }
924 #elif defined(__APPLE__)
925 #include <libproc.h>
test_platform_executable_path()926 inline std::string test_platform_executable_path() {
927     std::string buffer;
928     buffer.resize(1024);
929     pid_t pid = getpid();
930     int ret = proc_pidpath(pid, &buffer[0], static_cast<uint32_t>(buffer.size()));
931     if (ret <= 0) return NULL;
932     buffer[ret] = '\0';
933     buffer.resize(ret);
934     return buffer;
935 }
936 #elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__)
937 #include <sys/sysctl.h>
test_platform_executable_path()938 inline std::string test_platform_executable_path() {
939     int mib[] = {
940         CTL_KERN,
941 #if defined(__NetBSD__)
942         KERN_PROC_ARGS,
943         -1,
944         KERN_PROC_PATHNAME,
945 #else
946         KERN_PROC,
947         KERN_PROC_PATHNAME,
948         -1,
949 #endif
950     };
951     std::string buffer;
952     buffer.resize(1024);
953     size_t size = buffer.size();
954     if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &buffer[0], &size, NULL, 0) < 0) {
955         return NULL;
956     }
957     buffer.resize(size);
958 
959     return buffer;
960 }
961 #elif defined(__Fuchsia__) || defined(__OpenBSD__)
test_platform_executable_path()962 inline std::string test_platform_executable_path() { return {}; }
963 #elif defined(__QNX__)
964 
965 #ifndef SYSCONFDIR
966 #define SYSCONFDIR "/etc"
967 #endif
968 
969 #include <fcntl.h>
970 #include <sys/stat.h>
971 
test_platform_executable_path()972 inline std::string test_platform_executable_path() {
973     std::string buffer;
974     buffer.resize(1024);
975     int fd = open("/proc/self/exefile", O_RDONLY);
976     ssize_t rdsize;
977 
978     if (fd == -1) {
979         return NULL;
980     }
981 
982     rdsize = read(fd, &buffer[0], buffer.size());
983     if (rdsize < 0) {
984         return NULL;
985     }
986     buffer[rdsize] = 0x00;
987     close(fd);
988     buffer.resize(rdsize);
989 
990     return buffer;
991 }
992 #endif  // defined (__QNX__)
993 #if defined(WIN32)
test_platform_executable_path()994 inline std::string test_platform_executable_path() {
995     std::string buffer;
996     buffer.resize(1024);
997     DWORD ret = GetModuleFileName(NULL, static_cast<LPSTR>(&buffer[0]), (DWORD)buffer.size());
998     if (ret == 0) return NULL;
999     if (ret > buffer.size()) return NULL;
1000     buffer.resize(ret);
1001     buffer[ret] = '\0';
1002     return narrow(std::filesystem::path(buffer).native());
1003 }
1004 
1005 #endif
1006