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  * path abstraction class - modelled after C++17's filesystem::path
35  * FolderManager - manages the contents of a folder, cleaning up when needed
36  * per-platform library loading - mirrors the vk_loader_platform
37  * LibraryWrapper - RAII wrapper for a library
38  * DispatchableHandle - RAII wrapper for vulkan dispatchable handle objects
39  * ostream overload for VkResult - prettifies googletest output
40  * Instance & Device create info helpers
41  * operator == overloads for many vulkan structs - more concise tests
42  */
43 #pragma once
44 
45 #include <algorithm>
46 #include <array>
47 #include <iostream>
48 #include <fstream>
49 #include <ostream>
50 #include <string>
51 #include <vector>
52 #include <unordered_map>
53 #include <utility>
54 #include <memory>
55 #include <functional>
56 
57 #include <cassert>
58 #include <cstring>
59 #include <ctime>
60 #include <inttypes.h>
61 #include <stdio.h>
62 #include <stdint.h>
63 
64 // Set of platforms with a common set of functionality which is queried throughout the program
65 #if defined(__linux__) || defined(__APPLE__) || defined(__Fuchsia__) || defined(__QNX__) || defined(__FreeBSD__) || \
66     defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__GNU__)
67 #define COMMON_UNIX_PLATFORMS 1
68 #else
69 #define COMMON_UNIX_PLATFORMS 0
70 #endif
71 
72 #if defined(WIN32)
73 #include <direct.h>
74 #include <windows.h>
75 #include <strsafe.h>
76 #elif COMMON_UNIX_PLATFORMS
77 #include <dirent.h>
78 #include <sys/types.h>
79 #include <sys/stat.h>
80 #include <unistd.h>
81 #include <dlfcn.h>
82 
83 // Prevent macro collisions from <sys/types.h>
84 #undef major
85 #undef minor
86 
87 #endif
88 
89 #include <vulkan/vulkan.h>
90 #include <vulkan/vk_icd.h>
91 #include <vulkan/vk_layer.h>
92 
93 #include "framework_config.h"
94 
95 #if defined(__GNUC__) && __GNUC__ >= 4
96 #define FRAMEWORK_EXPORT __attribute__((visibility("default")))
97 #elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590)
98 #define FRAMEWORK_EXPORT __attribute__((visibility("default")))
99 #elif defined(WIN32)
100 #define FRAMEWORK_EXPORT __declspec(dllexport)
101 #else
102 #define FRAMEWORK_EXPORT
103 #endif
104 
105 #include "json_writer.h"
106 
107 // 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
108 //     env-var
109 // NOTE: This is only intended for test framework code, all test code MUST use EnvVarWrapper
110 std::string get_env_var(std::string const& name, bool report_failure = true);
111 
112 /*
113  * Wrapper around Environment Variables with common operations
114  * Since Environment Variables leak between tests, there needs to be RAII code to remove them during test cleanup
115 
116  */
117 
118 // Wrapper to set & remove env-vars automatically
119 struct EnvVarWrapper {
120     // Constructor which unsets the env-var
121     EnvVarWrapper(std::string const& name) noexcept : name(name) {
122         initial_value = get_env_var(name, false);
123         remove_env_var();
124     }
125     // Constructor which set the env-var to the specified value
cur_value(value)126     EnvVarWrapper(std::string const& name, std::string const& value) noexcept : name(name), cur_value(value) {
127         initial_value = get_env_var(name, false);
128         set_env_var();
129     }
130     ~EnvVarWrapper() noexcept {
131         remove_env_var();
132         if (!initial_value.empty()) {
133             set_new_value(initial_value);
134         }
135     }
136 
137     // delete copy operators
138     EnvVarWrapper(const EnvVarWrapper&) = delete;
139     EnvVarWrapper& operator=(const EnvVarWrapper&) = delete;
140 
set_new_value(std::string const& value)141     void set_new_value(std::string const& value) {
142         cur_value = value;
143         set_env_var();
144     }
add_to_list(std::string const& list_item)145     void add_to_list(std::string const& list_item) {
146         if (!cur_value.empty()) {
147             cur_value += OS_ENV_VAR_LIST_SEPARATOR;
148         }
149         cur_value += list_item;
150         set_env_var();
151     }
remove_value() const152     void remove_value() const { remove_env_var(); }
get() const153     const char* get() const { return name.c_str(); }
value() const154     const char* value() const { return cur_value.c_str(); }
155 
156    private:
157     std::string name;
158     std::string cur_value;
159     std::string initial_value;
160 
161     void set_env_var();
162     void remove_env_var() const;
163 #if defined(WIN32)
164     // Environment variable list separator - not for filesystem paths
165     const char OS_ENV_VAR_LIST_SEPARATOR = ';';
166 #elif COMMON_UNIX_PLATFORMS
167     // Environment variable list separator - not for filesystem paths
168     const char OS_ENV_VAR_LIST_SEPARATOR = ':';
169 #endif
170 };
171 
172 // Windows specific error handling logic
173 #if defined(WIN32)
174 const long ERROR_SETENV_FAILED = 10543;           // chosen at random, attempts to not conflict
175 const long ERROR_REMOVEDIRECTORY_FAILED = 10544;  // chosen at random, attempts to not conflict
176 const char* win_api_error_str(LSTATUS status);
177 void print_error_message(LSTATUS status, const char* function_name, std::string optional_message = "");
178 #endif
179 
180 struct ManifestICD;    // forward declaration for FolderManager::write
181 struct ManifestLayer;  // forward declaration for FolderManager::write
182 
183 namespace fs {
184 std::string make_native(std::string const&);
185 
186 struct path {
187 #if defined(WIN32)
188     static const char path_separator = '\\';
189 #elif COMMON_UNIX_PLATFORMS
190     static const char path_separator = '/';
191 #endif
192 
193    public:
194     path() {}
195     path(std::string const& in) : contents(make_native(in)) {}
196     path(const char* in) : contents(make_native(std::string(in))) {}
197 
198     // concat paths without directoryseperator
199     path& operator+=(path const& in);
200     path& operator+=(std::string const& in);
201     path& operator+=(const char* in);
202 
203     // append paths with directoryseperator
204     path& operator/=(path const& in);
205     path& operator/=(std::string const& in);
206     path& operator/=(const char* in);
207 
208     // concat paths without directory seperator
209     path operator+(path const& in) const;
210     path operator+(std::string const& in) const;
211     path operator+(const char* in) const;
212 
213     // append paths with directory seperator
214     path operator/(path const& in) const;
215     path operator/(std::string const& in) const;
216     path operator/(const char* in) const;
217 
218     // accessors
219     path parent_path() const;      // Everything before the last path separator, if there is one.
220     bool has_parent_path() const;  // True if the path contains more than just a filename.
221     path filename() const;         // Everything after the last path separator.
222     path extension() const;        // The file extension, if it has one.
223     path stem() const;             // The filename minus the extension.
224 
225     // modifiers
226     path& replace_filename(path const& replacement);
227 
228     // get c style string
229     const char* c_str() const { return contents.c_str(); }
230     // get C++ style string
231     std::string const& str() const { return contents; }
232     std::string& str() { return contents; }
233     size_t size() const { return contents.size(); }
234 
235     // equality
236     bool operator==(path const& other) const noexcept { return contents == other.contents; }
237     bool operator!=(path const& other) const noexcept { return !(*this == other); }
238 
239    private:
240     std::string contents;
241 };
242 
243 std::string fixup_backslashes_in_path(std::string const& in_path);
244 fs::path fixup_backslashes_in_path(fs::path const& in_path);
245 
246 int create_folder(path const& path);
247 int delete_folder(path const& folder);
248 
249 class FolderManager {
250    public:
251     explicit FolderManager(path root_path, std::string name) noexcept;
252     ~FolderManager() noexcept;
253     FolderManager(FolderManager const&) = delete;
254     FolderManager& operator=(FolderManager const&) = delete;
255     FolderManager(FolderManager&& other) noexcept;
256     FolderManager& operator=(FolderManager&& other) noexcept;
257 
258     // Add a manifest to the folder
259     path write_manifest(std::string const& name, std::string const& contents);
260 
261     // Add an already existing file to the manager, so it will be cleaned up automatically
262     void add_existing_file(std::string const& file_name);
263 
264     // close file handle, delete file, remove `name` from managed file list.
265     void remove(std::string const& name);
266 
267     // copy file into this folder with name `new_name`. Returns the full path of the file that was copied
268     path copy_file(path const& file, std::string const& new_name);
269 
270     // location of the managed folder
271     path location() const { return folder; }
272 
273     std::vector<std::string> get_files() const { return files; }
274 
275    private:
276     path folder;
277     std::vector<std::string> files;
278 };
279 }  // namespace fs
280 
281 // copy the contents of a std::string into a char array and add a null terminator at the end
282 // src - std::string to read from
283 // dst - char array to write to
284 // size_dst - number of characters in the dst array
285 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; }
286 
287 #if defined(WIN32)
288 // Convert an UTF-16 wstring to an UTF-8 string
289 std::string narrow(const std::wstring& utf16);
290 // Convert an UTF-8 string to an UTF-16 wstring
291 std::wstring widen(const std::string& utf8);
292 #endif
293 
294 #if defined(WIN32)
295 typedef HMODULE loader_platform_dl_handle;
296 inline loader_platform_dl_handle loader_platform_open_library(const char* lib_path) {
297     std::wstring lib_path_utf16 = widen(lib_path);
298     // Try loading the library the original way first.
299     loader_platform_dl_handle lib_handle = LoadLibraryW(lib_path_utf16.c_str());
300     if (lib_handle == nullptr && GetLastError() == ERROR_MOD_NOT_FOUND) {
301         // If that failed, then try loading it with broader search folders.
302         lib_handle =
303             LoadLibraryExW(lib_path_utf16.c_str(), nullptr, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR);
304     }
305     return lib_handle;
306 }
307 inline char* loader_platform_open_library_error(const char* libPath) {
308     static char errorMsg[164];
309     snprintf(errorMsg, 163, "Failed to open dynamic library \"%s\" with error %lu", libPath, GetLastError());
310     return errorMsg;
311 }
312 inline void loader_platform_close_library(loader_platform_dl_handle library) { FreeLibrary(library); }
313 inline void* loader_platform_get_proc_address(loader_platform_dl_handle library, const char* name) {
314     assert(library);
315     assert(name);
316     return reinterpret_cast<void*>(GetProcAddress(library, name));
317 }
318 inline char* loader_platform_get_proc_address_error(const char* name) {
319     static char errorMsg[120];
320     snprintf(errorMsg, 119, "Failed to find function \"%s\" in dynamic library", name);
321     return errorMsg;
322 }
323 
324 #elif COMMON_UNIX_PLATFORMS
325 
326 typedef void* loader_platform_dl_handle;
327 inline loader_platform_dl_handle loader_platform_open_library(const char* libPath) {
328     return dlopen(libPath, RTLD_LAZY | RTLD_LOCAL);
329 }
330 inline const char* loader_platform_open_library_error([[maybe_unused]] const char* libPath) { return dlerror(); }
331 inline void loader_platform_close_library(loader_platform_dl_handle library) { dlclose(library); }
332 inline void* loader_platform_get_proc_address(loader_platform_dl_handle library, const char* name) {
333     assert(library);
334     assert(name);
335     return dlsym(library, name);
336 }
337 inline const char* loader_platform_get_proc_address_error([[maybe_unused]] const char* name) { return dlerror(); }
338 #endif
339 
340 class FromVoidStarFunc {
341    private:
342     void* function;
343 
344    public:
345     FromVoidStarFunc(void* function) : function(function) {}
346     FromVoidStarFunc(PFN_vkVoidFunction function) : function(reinterpret_cast<void*>(function)) {}
347 
348     template <typename T>
349     operator T() {
350         return reinterpret_cast<T>(function);
351     }
352 };
353 
354 struct LibraryWrapper {
355     explicit LibraryWrapper() noexcept {}
356     explicit LibraryWrapper(fs::path const& lib_path) noexcept : lib_path(lib_path) {
357         lib_handle = loader_platform_open_library(lib_path.c_str());
358         if (lib_handle == nullptr) {
359             fprintf(stderr, "Unable to open library %s: %s\n", lib_path.c_str(),
360                     loader_platform_open_library_error(lib_path.c_str()));
361             assert(lib_handle != nullptr && "Must be able to open library");
362         }
363     }
364     ~LibraryWrapper() noexcept {
365         if (lib_handle != nullptr) {
366             loader_platform_close_library(lib_handle);
367             lib_handle = nullptr;
368         }
369     }
370     LibraryWrapper(LibraryWrapper const& wrapper) = delete;
371     LibraryWrapper& operator=(LibraryWrapper const& wrapper) = delete;
372     LibraryWrapper(LibraryWrapper&& wrapper) noexcept : lib_handle(wrapper.lib_handle), lib_path(wrapper.lib_path) {
373         wrapper.lib_handle = nullptr;
374     }
375     LibraryWrapper& operator=(LibraryWrapper&& wrapper) noexcept {
376         if (this != &wrapper) {
377             if (lib_handle != nullptr) {
378                 loader_platform_close_library(lib_handle);
379             }
380             lib_handle = wrapper.lib_handle;
381             lib_path = wrapper.lib_path;
382             wrapper.lib_handle = nullptr;
383         }
384         return *this;
385     }
386     FromVoidStarFunc get_symbol(const char* symbol_name) const {
387         assert(lib_handle != nullptr && "Cannot get symbol with null library handle");
388         void* symbol = loader_platform_get_proc_address(lib_handle, symbol_name);
389         if (symbol == nullptr) {
390             fprintf(stderr, "Unable to open symbol %s: %s\n", symbol_name, loader_platform_get_proc_address_error(symbol_name));
391             assert(symbol != nullptr && "Must be able to get symbol");
392         }
393         return FromVoidStarFunc(symbol);
394     }
395 
396     explicit operator bool() const noexcept { return lib_handle != nullptr; }
397 
398     loader_platform_dl_handle lib_handle = nullptr;
399     fs::path lib_path;
400 };
401 
402 template <typename T>
403 PFN_vkVoidFunction to_vkVoidFunction(T func) {
404     return reinterpret_cast<PFN_vkVoidFunction>(func);
405 }
406 template <typename T>
407 struct FRAMEWORK_EXPORT DispatchableHandle {
408     DispatchableHandle() {
409         auto ptr_handle = new VK_LOADER_DATA;
410         set_loader_magic_value(ptr_handle);
411         handle = reinterpret_cast<T>(ptr_handle);
412     }
413     ~DispatchableHandle() {
414         delete reinterpret_cast<VK_LOADER_DATA*>(handle);
415         handle = nullptr;
416     }
417     DispatchableHandle(DispatchableHandle const&) = delete;
418     DispatchableHandle& operator=(DispatchableHandle const&) = delete;
419     DispatchableHandle(DispatchableHandle&& other) noexcept : handle(other.handle) { other.handle = nullptr; }
420     DispatchableHandle& operator=(DispatchableHandle&& other) noexcept {
421         if (this != &other) {
422             delete reinterpret_cast<VK_LOADER_DATA*>(handle);
423             handle = other.handle;
424             other.handle = nullptr;
425         }
426         return *this;
427     }
428     bool operator==(T base_handle) { return base_handle == handle; }
429     bool operator!=(T base_handle) { return base_handle != handle; }
430 
431     T handle = nullptr;
432 };
433 
434 // Stream operator for VkResult so GTEST will print out error codes as strings (automatically)
435 inline std::ostream& operator<<(std::ostream& os, const VkResult& result) {
436     switch (result) {
437         case (VK_SUCCESS):
438             return os << "VK_SUCCESS";
439         case (VK_NOT_READY):
440             return os << "VK_NOT_READY";
441         case (VK_TIMEOUT):
442             return os << "VK_TIMEOUT";
443         case (VK_EVENT_SET):
444             return os << "VK_EVENT_SET";
445         case (VK_EVENT_RESET):
446             return os << "VK_EVENT_RESET";
447         case (VK_INCOMPLETE):
448             return os << "VK_INCOMPLETE";
449         case (VK_ERROR_OUT_OF_HOST_MEMORY):
450             return os << "VK_ERROR_OUT_OF_HOST_MEMORY";
451         case (VK_ERROR_OUT_OF_DEVICE_MEMORY):
452             return os << "VK_ERROR_OUT_OF_DEVICE_MEMORY";
453         case (VK_ERROR_INITIALIZATION_FAILED):
454             return os << "VK_ERROR_INITIALIZATION_FAILED";
455         case (VK_ERROR_DEVICE_LOST):
456             return os << "VK_ERROR_DEVICE_LOST";
457         case (VK_ERROR_MEMORY_MAP_FAILED):
458             return os << "VK_ERROR_MEMORY_MAP_FAILED";
459         case (VK_ERROR_LAYER_NOT_PRESENT):
460             return os << "VK_ERROR_LAYER_NOT_PRESENT";
461         case (VK_ERROR_EXTENSION_NOT_PRESENT):
462             return os << "VK_ERROR_EXTENSION_NOT_PRESENT";
463         case (VK_ERROR_FEATURE_NOT_PRESENT):
464             return os << "VK_ERROR_FEATURE_NOT_PRESENT";
465         case (VK_ERROR_INCOMPATIBLE_DRIVER):
466             return os << "VK_ERROR_INCOMPATIBLE_DRIVER";
467         case (VK_ERROR_TOO_MANY_OBJECTS):
468             return os << "VK_ERROR_TOO_MANY_OBJECTS";
469         case (VK_ERROR_FORMAT_NOT_SUPPORTED):
470             return os << "VK_ERROR_FORMAT_NOT_SUPPORTED";
471         case (VK_ERROR_FRAGMENTED_POOL):
472             return os << "VK_ERROR_FRAGMENTED_POOL";
473         case (VK_ERROR_UNKNOWN):
474             return os << "VK_ERROR_UNKNOWN";
475         case (VK_ERROR_OUT_OF_POOL_MEMORY):
476             return os << "VK_ERROR_OUT_OF_POOL_MEMORY";
477         case (VK_ERROR_INVALID_EXTERNAL_HANDLE):
478             return os << "VK_ERROR_INVALID_EXTERNAL_HANDLE";
479         case (VK_ERROR_FRAGMENTATION):
480             return os << "VK_ERROR_FRAGMENTATION";
481         case (VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS):
482             return os << "VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS";
483         case (VK_ERROR_SURFACE_LOST_KHR):
484             return os << "VK_ERROR_SURFACE_LOST_KHR";
485         case (VK_ERROR_NATIVE_WINDOW_IN_USE_KHR):
486             return os << "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR";
487         case (VK_SUBOPTIMAL_KHR):
488             return os << "VK_SUBOPTIMAL_KHR";
489         case (VK_ERROR_OUT_OF_DATE_KHR):
490             return os << "VK_ERROR_OUT_OF_DATE_KHR";
491         case (VK_ERROR_INCOMPATIBLE_DISPLAY_KHR):
492             return os << "VK_ERROR_INCOMPATIBLE_DISPLAY_KHR";
493         case (VK_ERROR_VALIDATION_FAILED_EXT):
494             return os << "VK_ERROR_VALIDATION_FAILED_EXT";
495         case (VK_ERROR_INVALID_SHADER_NV):
496             return os << "VK_ERROR_INVALID_SHADER_NV";
497         case (VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT):
498             return os << "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT";
499         case (VK_ERROR_NOT_PERMITTED_EXT):
500             return os << "VK_ERROR_NOT_PERMITTED_EXT";
501         case (VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT):
502             return os << "VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT";
503         case (VK_THREAD_IDLE_KHR):
504             return os << "VK_THREAD_IDLE_KHR";
505         case (VK_THREAD_DONE_KHR):
506             return os << "VK_THREAD_DONE_KHR";
507         case (VK_OPERATION_DEFERRED_KHR):
508             return os << "VK_OPERATION_DEFERRED_KHR";
509         case (VK_OPERATION_NOT_DEFERRED_KHR):
510             return os << "VK_OPERATION_NOT_DEFERRED_KHR";
511         case (VK_PIPELINE_COMPILE_REQUIRED_EXT):
512             return os << "VK_PIPELINE_COMPILE_REQUIRED_EXT";
513         case (VK_RESULT_MAX_ENUM):
514             return os << "VK_RESULT_MAX_ENUM";
515         case (VK_ERROR_COMPRESSION_EXHAUSTED_EXT):
516             return os << "VK_ERROR_COMPRESSION_EXHAUSTED_EXT";
517         case (VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR):
518             return os << "VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR";
519         case (VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR):
520             return os << "VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR";
521         case (VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR):
522             return os << "VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR";
523         case (VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR):
524             return os << "VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR";
525         case (VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR):
526             return os << "VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR";
527         case (VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR):
528             return os << "VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR";
529         case (VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR):
530             return os << "VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR";
531         case (VK_ERROR_INCOMPATIBLE_SHADER_BINARY_EXT):
532             return os << "VK_ERROR_INCOMPATIBLE_SHADER_BINARY_EXT";
533     }
534     return os << static_cast<int32_t>(result);
535 }
536 
537 const char* get_platform_wsi_extension([[maybe_unused]] const char* api_selection);
538 
539 bool string_eq(const char* a, const char* b) noexcept;
540 bool string_eq(const char* a, const char* b, size_t len) noexcept;
541 
542 inline std::string version_to_string(uint32_t version) {
543     std::string out = std::to_string(VK_API_VERSION_MAJOR(version)) + "." + std::to_string(VK_API_VERSION_MINOR(version)) + "." +
544                       std::to_string(VK_API_VERSION_PATCH(version));
545     if (VK_API_VERSION_VARIANT(version) != 0) out += std::to_string(VK_API_VERSION_VARIANT(version)) + "." + out;
546     return out;
547 }
548 
549 // Macro to ease the definition of variables with builder member functions
550 // class_name = class the member variable is apart of
551 // type = type of the variable
552 // name = name of the variable
553 // default_value = value to default initialize, use {} if nothing else makes sense
554 #define BUILDER_VALUE(class_name, type, name, default_value) \
555     type name = default_value;                               \
556     class_name& set_##name(type const& name) {               \
557         this->name = name;                                   \
558         return *this;                                        \
559     }
560 
561 // Macro to ease the definition of vectors with builder member functions
562 // class_name = class the member variable is apart of
563 // type = type of the variable
564 // name = name of the variable
565 // singular_name = used for the `add_singular_name` member function
566 #define BUILDER_VECTOR(class_name, type, name, singular_name)                    \
567     std::vector<type> name;                                                      \
568     class_name& add_##singular_name(type const& singular_name) {                 \
569         this->name.push_back(singular_name);                                     \
570         return *this;                                                            \
571     }                                                                            \
572     class_name& add_##singular_name##s(std::vector<type> const& singular_name) { \
573         for (auto& elem : singular_name) this->name.push_back(elem);             \
574         return *this;                                                            \
575     }
576 // Like BUILDER_VECTOR but for move only types - where passing in means giving up ownership
577 #define BUILDER_VECTOR_MOVE_ONLY(class_name, type, name, singular_name) \
578     std::vector<type> name;                                             \
579     class_name& add_##singular_name(type&& singular_name) {             \
580         this->name.push_back(std::move(singular_name));                 \
581         return *this;                                                   \
582     }
583 
584 struct ManifestVersion {
585     BUILDER_VALUE(ManifestVersion, uint32_t, major, 1)
586     BUILDER_VALUE(ManifestVersion, uint32_t, minor, 0)
587     BUILDER_VALUE(ManifestVersion, uint32_t, patch, 0)
588 
589     std::string get_version_str() const noexcept {
590         return std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(patch);
591     }
592 };
593 
594 // ManifestICD builder
595 struct ManifestICD {
596     BUILDER_VALUE(ManifestICD, ManifestVersion, file_format_version, {})
597     BUILDER_VALUE(ManifestICD, uint32_t, api_version, 0)
598     BUILDER_VALUE(ManifestICD, fs::path, lib_path, {})
599     BUILDER_VALUE(ManifestICD, bool, is_portability_driver, false)
600     BUILDER_VALUE(ManifestICD, std::string, library_arch, "")
601     std::string get_manifest_str() const;
602 };
603 
604 // ManifestLayer builder
605 struct ManifestLayer {
606     struct LayerDescription {
607         enum class Type { INSTANCE, GLOBAL, DEVICE };
608         std::string get_type_str(Type layer_type) const {
609             if (layer_type == Type::GLOBAL)
610                 return "GLOBAL";
611             else if (layer_type == Type::DEVICE)
612                 return "DEVICE";
613             else  // default
614                 return "INSTANCE";
615         }
616         struct FunctionOverride {
617             BUILDER_VALUE(FunctionOverride, std::string, vk_func, {})
618             BUILDER_VALUE(FunctionOverride, std::string, override_name, {})
619 
620             void get_manifest_str(JsonWriter& writer) const { writer.AddKeyedString(vk_func, override_name); }
621         };
622         struct Extension {
623             Extension() noexcept {}
624             Extension(std::string name, uint32_t spec_version = 0, std::vector<std::string> entrypoints = {}) noexcept
625                 : name(name), spec_version(spec_version), entrypoints(entrypoints) {}
626             std::string name;
627             uint32_t spec_version = 0;
628             std::vector<std::string> entrypoints;
629             void get_manifest_str(JsonWriter& writer) const;
630         };
631         BUILDER_VALUE(LayerDescription, std::string, name, {})
632         BUILDER_VALUE(LayerDescription, Type, type, Type::INSTANCE)
633         BUILDER_VALUE(LayerDescription, fs::path, lib_path, {})
634         BUILDER_VALUE(LayerDescription, uint32_t, api_version, VK_API_VERSION_1_0)
635         BUILDER_VALUE(LayerDescription, uint32_t, implementation_version, 0)
636         BUILDER_VALUE(LayerDescription, std::string, description, {})
637         BUILDER_VECTOR(LayerDescription, FunctionOverride, functions, function)
638         BUILDER_VECTOR(LayerDescription, Extension, instance_extensions, instance_extension)
639         BUILDER_VECTOR(LayerDescription, Extension, device_extensions, device_extension)
640         BUILDER_VALUE(LayerDescription, std::string, enable_environment, {})
641         BUILDER_VALUE(LayerDescription, std::string, disable_environment, {})
642         BUILDER_VECTOR(LayerDescription, std::string, component_layers, component_layer)
643         BUILDER_VECTOR(LayerDescription, std::string, blacklisted_layers, blacklisted_layer)
644         BUILDER_VECTOR(LayerDescription, std::string, override_paths, override_path)
645         BUILDER_VECTOR(LayerDescription, FunctionOverride, pre_instance_functions, pre_instance_function)
646         BUILDER_VECTOR(LayerDescription, std::string, app_keys, app_key)
647         BUILDER_VALUE(LayerDescription, std::string, library_arch, "")
648 
649         void get_manifest_str(JsonWriter& writer) const;
650         VkLayerProperties get_layer_properties() const;
651     };
652     BUILDER_VALUE(ManifestLayer, ManifestVersion, file_format_version, {})
653     BUILDER_VECTOR(ManifestLayer, LayerDescription, layers, layer)
654 
655     std::string get_manifest_str() const;
656 };
657 
658 struct Extension {
659     BUILDER_VALUE(Extension, std::string, extensionName, {})
660     BUILDER_VALUE(Extension, uint32_t, specVersion, VK_API_VERSION_1_0)
661 
662     Extension(const char* name, uint32_t specVersion = VK_API_VERSION_1_0) noexcept
663         : extensionName(name), specVersion(specVersion) {}
664     Extension(std::string extensionName, uint32_t specVersion = VK_API_VERSION_1_0) noexcept
665         : extensionName(extensionName), specVersion(specVersion) {}
666 
667     VkExtensionProperties get() const noexcept {
668         VkExtensionProperties props{};
669         copy_string_to_char_array(extensionName, &props.extensionName[0], VK_MAX_EXTENSION_NAME_SIZE);
670         props.specVersion = specVersion;
671         return props;
672     }
673 };
674 
675 struct MockQueueFamilyProperties {
676     BUILDER_VALUE(MockQueueFamilyProperties, VkQueueFamilyProperties, properties, {})
677     BUILDER_VALUE(MockQueueFamilyProperties, bool, support_present, false)
678 
679     VkQueueFamilyProperties get() const noexcept { return properties; }
680 };
681 
682 struct InstanceCreateInfo {
683     BUILDER_VALUE(InstanceCreateInfo, VkInstanceCreateInfo, instance_info, {})
684     BUILDER_VALUE(InstanceCreateInfo, VkApplicationInfo, application_info, {})
685     BUILDER_VALUE(InstanceCreateInfo, std::string, app_name, {})
686     BUILDER_VALUE(InstanceCreateInfo, std::string, engine_name, {})
687     BUILDER_VALUE(InstanceCreateInfo, uint32_t, flags, 0)
688     BUILDER_VALUE(InstanceCreateInfo, uint32_t, app_version, 0)
689     BUILDER_VALUE(InstanceCreateInfo, uint32_t, engine_version, 0)
690     BUILDER_VALUE(InstanceCreateInfo, uint32_t, api_version, VK_API_VERSION_1_0)
691     BUILDER_VECTOR(InstanceCreateInfo, const char*, enabled_layers, layer)
692     BUILDER_VECTOR(InstanceCreateInfo, const char*, enabled_extensions, extension)
693     // tell the get() function to not provide `application_info`
694     BUILDER_VALUE(InstanceCreateInfo, bool, fill_in_application_info, true)
695 
696     InstanceCreateInfo();
697 
698     VkInstanceCreateInfo* get() noexcept;
699 
700     InstanceCreateInfo& set_api_version(uint32_t major, uint32_t minor, uint32_t patch);
701 
702     InstanceCreateInfo& setup_WSI(const char* api_selection = nullptr);
703 };
704 
705 struct DeviceQueueCreateInfo {
706     DeviceQueueCreateInfo();
707     DeviceQueueCreateInfo(const VkDeviceQueueCreateInfo* create_info);
708 
709     BUILDER_VALUE(DeviceQueueCreateInfo, VkDeviceQueueCreateInfo, queue_create_info, {})
710     BUILDER_VECTOR(DeviceQueueCreateInfo, float, priorities, priority)
711 
712     VkDeviceQueueCreateInfo get() noexcept;
713 };
714 
715 struct DeviceCreateInfo {
716     DeviceCreateInfo() = default;
717     DeviceCreateInfo(const VkDeviceCreateInfo* create_info);
718 
719     BUILDER_VALUE(DeviceCreateInfo, VkDeviceCreateInfo, dev, {})
720     BUILDER_VECTOR(DeviceCreateInfo, const char*, enabled_extensions, extension)
721     BUILDER_VECTOR(DeviceCreateInfo, const char*, enabled_layers, layer)
722     BUILDER_VECTOR(DeviceCreateInfo, DeviceQueueCreateInfo, queue_info_details, device_queue)
723 
724     VkDeviceCreateInfo* get() noexcept;
725 
726    private:
727     std::vector<VkDeviceQueueCreateInfo> device_queue_infos;
728 };
729 
730 inline bool operator==(const VkExtent3D& a, const VkExtent3D& b) {
731     return a.width == b.width && a.height == b.height && a.depth == b.depth;
732 }
733 inline bool operator!=(const VkExtent3D& a, const VkExtent3D& b) { return !(a == b); }
734 
735 inline bool operator==(const VkQueueFamilyProperties& a, const VkQueueFamilyProperties& b) {
736     return a.minImageTransferGranularity == b.minImageTransferGranularity && a.queueCount == b.queueCount &&
737            a.queueFlags == b.queueFlags && a.timestampValidBits == b.timestampValidBits;
738 }
739 inline bool operator!=(const VkQueueFamilyProperties& a, const VkQueueFamilyProperties& b) { return !(a == b); }
740 
741 inline bool operator==(const VkLayerProperties& a, const VkLayerProperties& b) {
742     return string_eq(a.layerName, b.layerName, 256) && string_eq(a.description, b.description, 256) &&
743            a.implementationVersion == b.implementationVersion && a.specVersion == b.specVersion;
744 }
745 inline bool operator!=(const VkLayerProperties& a, const VkLayerProperties& b) { return !(a == b); }
746 
747 inline bool operator==(const VkExtensionProperties& a, const VkExtensionProperties& b) {
748     return string_eq(a.extensionName, b.extensionName, 256) && a.specVersion == b.specVersion;
749 }
750 inline bool operator!=(const VkExtensionProperties& a, const VkExtensionProperties& b) { return !(a == b); }
751 
752 struct VulkanFunction {
753     std::string name;
754     PFN_vkVoidFunction function = nullptr;
755 };
756 
757 template <typename T, size_t U>
758 bool check_permutation(std::initializer_list<const char*> expected, std::array<T, U> const& returned) {
759     if (expected.size() != returned.size()) return false;
760     for (uint32_t i = 0; i < expected.size(); i++) {
761         auto found = std::find_if(std::begin(returned), std::end(returned),
762                                   [&](T elem) { return string_eq(*(expected.begin() + i), elem.layerName); });
763         if (found == std::end(returned)) return false;
764     }
765     return true;
766 }
767 template <typename T>
768 bool check_permutation(std::initializer_list<const char*> expected, std::vector<T> const& returned) {
769     if (expected.size() != returned.size()) return false;
770     for (uint32_t i = 0; i < expected.size(); i++) {
771         auto found = std::find_if(std::begin(returned), std::end(returned),
772                                   [&](T elem) { return string_eq(*(expected.begin() + i), elem.layerName); });
773         if (found == std::end(returned)) return false;
774     }
775     return true;
776 }
777 
778 inline bool contains(std::vector<VkExtensionProperties> const& vec, const char* name) {
779     return std::any_of(std::begin(vec), std::end(vec),
780                        [name](VkExtensionProperties const& elem) { return string_eq(name, elem.extensionName); });
781 }
782 inline bool contains(std::vector<VkLayerProperties> const& vec, const char* name) {
783     return std::any_of(std::begin(vec), std::end(vec),
784                        [name](VkLayerProperties const& elem) { return string_eq(name, elem.layerName); });
785 }
786 
787 #if defined(__linux__) || defined(__GNU__)
788 
789 // find application path + name. Path cannot be longer than 1024, returns NULL if it is greater than that.
790 inline std::string test_platform_executable_path() {
791     std::string buffer;
792     buffer.resize(1024);
793     ssize_t count = readlink("/proc/self/exe", &buffer[0], buffer.size());
794     if (count == -1) return NULL;
795     if (count == 0) return NULL;
796     buffer[count] = '\0';
797     buffer.resize(count);
798     return buffer;
799 }
800 #elif defined(__APPLE__)
801 #include <libproc.h>
802 inline std::string test_platform_executable_path() {
803     std::string buffer;
804     buffer.resize(1024);
805     pid_t pid = getpid();
806     int ret = proc_pidpath(pid, &buffer[0], static_cast<uint32_t>(buffer.size()));
807     if (ret <= 0) return NULL;
808     buffer[ret] = '\0';
809     buffer.resize(ret);
810     return buffer;
811 }
812 #elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__)
813 #include <sys/sysctl.h>
814 inline std::string test_platform_executable_path() {
815     int mib[] = {
816         CTL_KERN,
817 #if defined(__NetBSD__)
818         KERN_PROC_ARGS,
819         -1,
820         KERN_PROC_PATHNAME,
821 #else
822         KERN_PROC,
823         KERN_PROC_PATHNAME,
824         -1,
825 #endif
826     };
827     std::string buffer;
828     buffer.resize(1024);
829     size_t size = buffer.size();
830     if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &buffer[0], &size, NULL, 0) < 0) {
831         return NULL;
832     }
833     buffer.resize(size);
834 
835     return buffer;
836 }
837 #elif defined(__Fuchsia__) || defined(__OpenBSD__)
838 inline std::string test_platform_executable_path() { return {}; }
839 #elif defined(__QNX__)
840 
841 #define SYSCONFDIR "/etc"
842 
843 #include <fcntl.h>
844 #include <sys/stat.h>
845 
846 inline std::string test_platform_executable_path() {
847     std::string buffer;
848     buffer.resize(1024);
849     int fd = open("/proc/self/exefile", O_RDONLY);
850     size_t rdsize;
851 
852     if (fd == -1) {
853         return NULL;
854     }
855 
856     rdsize = read(fd, &buffer[0], buffer.size());
857     if (rdsize == size) {
858         return NULL;
859     }
860     buffer[rdsize] = 0x00;
861     close(fd);
862     buffer.resize(rdsize);
863 
864     return buffer;
865 }
866 #endif  // defined (__QNX__)
867 #if defined(WIN32)
868 inline std::string test_platform_executable_path() {
869     std::string buffer;
870     buffer.resize(1024);
871     DWORD ret = GetModuleFileName(NULL, static_cast<LPSTR>(&buffer[0]), (DWORD)buffer.size());
872     if (ret == 0) return NULL;
873     if (ret > buffer.size()) return NULL;
874     buffer.resize(ret);
875     buffer[ret] = '\0';
876     return buffer;
877 }
878 
879 inline std::wstring conver_str_to_wstr(std::string const& input) {
880     std::wstring output{};
881     output.resize(input.size());
882     size_t characters_converted = 0;
883     mbstowcs_s(&characters_converted, &output[0], output.size() + 1, input.c_str(), input.size());
884     return output;
885 }
886 
887 #endif
888