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