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
110std::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
119struct 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
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
141    void set_new_value(std::string const& value) {
142        cur_value = value;
143        set_env_var();
144    }
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    }
152    void remove_value() const { remove_env_var(); }
153    const char* get() const { return name.c_str(); }
154    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)
174const long ERROR_SETENV_FAILED = 10543;           // chosen at random, attempts to not conflict
175const long ERROR_REMOVEDIRECTORY_FAILED = 10544;  // chosen at random, attempts to not conflict
176const char* win_api_error_str(LSTATUS status);
177void print_error_message(LSTATUS status, const char* function_name, std::string optional_message = "");
178#endif
179
180struct ManifestICD;    // forward declaration for FolderManager::write
181struct ManifestLayer;  // forward declaration for FolderManager::write
182
183namespace fs {
184std::string make_native(std::string const&);
185
186struct 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
243std::string fixup_backslashes_in_path(std::string const& in_path);
244fs::path fixup_backslashes_in_path(fs::path const& in_path);
245
246int create_folder(path const& path);
247int delete_folder(path const& folder);
248
249class 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
285inline 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
289std::string narrow(const std::wstring& utf16);
290// Convert an UTF-8 string to an UTF-16 wstring
291std::wstring widen(const std::string& utf8);
292#endif
293
294#if defined(WIN32)
295typedef HMODULE loader_platform_dl_handle;
296inline 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}
307inline 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}
312inline void loader_platform_close_library(loader_platform_dl_handle library) { FreeLibrary(library); }
313inline 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}
318inline 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
326typedef void* loader_platform_dl_handle;
327inline loader_platform_dl_handle loader_platform_open_library(const char* libPath) {
328    return dlopen(libPath, RTLD_LAZY | RTLD_LOCAL);
329}
330inline const char* loader_platform_open_library_error([[maybe_unused]] const char* libPath) { return dlerror(); }
331inline void loader_platform_close_library(loader_platform_dl_handle library) { dlclose(library); }
332inline 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}
337inline const char* loader_platform_get_proc_address_error([[maybe_unused]] const char* name) { return dlerror(); }
338#endif
339
340class 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
354struct 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
402template <typename T>
403PFN_vkVoidFunction to_vkVoidFunction(T func) {
404    return reinterpret_cast<PFN_vkVoidFunction>(func);
405}
406template <typename T>
407struct 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)
435inline 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
537const char* get_platform_wsi_extension([[maybe_unused]] const char* api_selection);
538
539bool string_eq(const char* a, const char* b) noexcept;
540bool string_eq(const char* a, const char* b, size_t len) noexcept;
541
542inline 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
584struct 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
595struct 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
605struct 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
658struct 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
675struct 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
682struct 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
705struct 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
715struct 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
730inline bool operator==(const VkExtent3D& a, const VkExtent3D& b) {
731    return a.width == b.width && a.height == b.height && a.depth == b.depth;
732}
733inline bool operator!=(const VkExtent3D& a, const VkExtent3D& b) { return !(a == b); }
734
735inline 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}
739inline bool operator!=(const VkQueueFamilyProperties& a, const VkQueueFamilyProperties& b) { return !(a == b); }
740
741inline 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}
745inline bool operator!=(const VkLayerProperties& a, const VkLayerProperties& b) { return !(a == b); }
746
747inline bool operator==(const VkExtensionProperties& a, const VkExtensionProperties& b) {
748    return string_eq(a.extensionName, b.extensionName, 256) && a.specVersion == b.specVersion;
749}
750inline bool operator!=(const VkExtensionProperties& a, const VkExtensionProperties& b) { return !(a == b); }
751
752struct VulkanFunction {
753    std::string name;
754    PFN_vkVoidFunction function = nullptr;
755};
756
757template <typename T, size_t U>
758bool 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}
767template <typename T>
768bool 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
778inline 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}
782inline 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.
790inline 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>
802inline 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>
814inline 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__)
838inline 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
846inline 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)
868inline 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
879inline 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