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#include "test_util.h" 29 30#if defined(WIN32) 31#include <strsafe.h> 32const char* win_api_error_str(LSTATUS status) { 33 if (status == ERROR_INVALID_FUNCTION) return "ERROR_INVALID_FUNCTION"; 34 if (status == ERROR_FILE_NOT_FOUND) return "ERROR_FILE_NOT_FOUND"; 35 if (status == ERROR_PATH_NOT_FOUND) return "ERROR_PATH_NOT_FOUND"; 36 if (status == ERROR_TOO_MANY_OPEN_FILES) return "ERROR_TOO_MANY_OPEN_FILES"; 37 if (status == ERROR_ACCESS_DENIED) return "ERROR_ACCESS_DENIED"; 38 if (status == ERROR_INVALID_HANDLE) return "ERROR_INVALID_HANDLE"; 39 if (status == ERROR_ENVVAR_NOT_FOUND) return "ERROR_ENVVAR_NOT_FOUND"; 40 if (status == ERROR_SETENV_FAILED) return "ERROR_SETENV_FAILED"; 41 return "UNKNOWN ERROR"; 42} 43 44void print_error_message(LSTATUS status, const char* function_name, std::string optional_message) { 45 LPVOID lpMsgBuf; 46 DWORD dw = GetLastError(); 47 48 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, dw, 49 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast<LPTSTR>(&lpMsgBuf), 0, nullptr); 50 51 std::cerr << function_name << " failed with " << win_api_error_str(status) << ": " 52 << std::string(reinterpret_cast<LPTSTR>(lpMsgBuf)); 53 if (optional_message != "") { 54 std::cerr << " | " << optional_message; 55 } 56 std::cerr << "\n"; 57 LocalFree(lpMsgBuf); 58} 59 60void EnvVarWrapper::set_env_var() { 61 BOOL ret = SetEnvironmentVariableW(widen(name).c_str(), widen(cur_value).c_str()); 62 if (ret == 0) { 63 print_error_message(ERROR_SETENV_FAILED, "SetEnvironmentVariableW"); 64 } 65} 66void EnvVarWrapper::remove_env_var() const { SetEnvironmentVariableW(widen(name).c_str(), nullptr); } 67std::string get_env_var(std::string const& name, bool report_failure) { 68 std::wstring name_utf16 = widen(name); 69 DWORD value_size = GetEnvironmentVariableW(name_utf16.c_str(), nullptr, 0); 70 if (0 == value_size) { 71 if (report_failure) print_error_message(ERROR_ENVVAR_NOT_FOUND, "GetEnvironmentVariableW"); 72 return {}; 73 } 74 std::wstring value(value_size, L'\0'); 75 if (GetEnvironmentVariableW(name_utf16.c_str(), &value[0], value_size) != value_size - 1) { 76 return {}; 77 } 78 return narrow(value); 79} 80#elif COMMON_UNIX_PLATFORMS 81 82void EnvVarWrapper::set_env_var() { setenv(name.c_str(), cur_value.c_str(), 1); } 83void EnvVarWrapper::remove_env_var() const { unsetenv(name.c_str()); } 84std::string get_env_var(std::string const& name, bool report_failure) { 85 char* ret = getenv(name.c_str()); 86 if (ret == nullptr) { 87 if (report_failure) std::cerr << "Failed to get environment variable:" << name << "\n"; 88 return std::string(); 89 } 90 return ret; 91} 92#endif 93 94template <typename T> 95void print_object_of_t(JsonWriter& writer, const char* object_name, std::vector<T> const& vec) { 96 if (vec.size() == 0) return; 97 writer.StartKeyedObject(object_name); 98 for (auto& element : vec) { 99 element.get_manifest_str(writer); 100 } 101 writer.EndObject(); 102} 103 104template <typename T> 105void print_array_of_t(JsonWriter& writer, const char* object_name, std::vector<T> const& vec) { 106 if (vec.size() == 0) return; 107 writer.StartKeyedArray(object_name); 108 for (auto& element : vec) { 109 element.get_manifest_str(writer); 110 } 111 writer.EndArray(); 112} 113void print_vector_of_strings(JsonWriter& writer, const char* object_name, std::vector<std::string> const& strings) { 114 if (strings.size() == 0) return; 115 writer.StartKeyedArray(object_name); 116 for (auto& str : strings) { 117 writer.AddString(fs::fixup_backslashes_in_path(str)); 118 } 119 writer.EndArray(); 120} 121 122std::string to_text(bool b) { return b ? std::string("true") : std::string("false"); } 123 124std::string ManifestICD::get_manifest_str() const { 125 JsonWriter writer; 126 writer.StartObject(); 127 writer.AddKeyedString("file_format_version", file_format_version.get_version_str()); 128 writer.StartKeyedObject("ICD"); 129 writer.AddKeyedString("library_path", fs::fixup_backslashes_in_path(lib_path).str()); 130 writer.AddKeyedString("api_version", version_to_string(api_version)); 131 writer.AddKeyedBool("is_portability_driver", is_portability_driver); 132 if (!library_arch.empty()) writer.AddKeyedString("library_arch", library_arch); 133 writer.EndObject(); 134 writer.EndObject(); 135 return writer.output; 136} 137 138void ManifestLayer::LayerDescription::Extension::get_manifest_str(JsonWriter& writer) const { 139 writer.StartObject(); 140 writer.AddKeyedString("name", name); 141 writer.AddKeyedString("spec_version", std::to_string(spec_version)); 142 writer.AddKeyedString("spec_version", std::to_string(spec_version)); 143 print_vector_of_strings(writer, "entrypoints", entrypoints); 144 writer.EndObject(); 145} 146 147void ManifestLayer::LayerDescription::get_manifest_str(JsonWriter& writer) const { 148 writer.AddKeyedString("name", name); 149 writer.AddKeyedString("type", get_type_str(type)); 150 if (!lib_path.str().empty()) { 151 writer.AddKeyedString("library_path", fs::fixup_backslashes_in_path(lib_path.str())); 152 } 153 writer.AddKeyedString("api_version", version_to_string(api_version)); 154 writer.AddKeyedString("implementation_version", std::to_string(implementation_version)); 155 writer.AddKeyedString("description", description); 156 print_object_of_t(writer, "functions", functions); 157 print_array_of_t(writer, "instance_extensions", instance_extensions); 158 print_array_of_t(writer, "device_extensions", device_extensions); 159 if (!enable_environment.empty()) { 160 writer.StartKeyedObject("enable_environment"); 161 writer.AddKeyedString(enable_environment, "1"); 162 writer.EndObject(); 163 } 164 if (!disable_environment.empty()) { 165 writer.StartKeyedObject("disable_environment"); 166 writer.AddKeyedString(disable_environment, "1"); 167 writer.EndObject(); 168 } 169 print_vector_of_strings(writer, "component_layers", component_layers); 170 print_vector_of_strings(writer, "blacklisted_layers", blacklisted_layers); 171 print_vector_of_strings(writer, "override_paths", override_paths); 172 print_vector_of_strings(writer, "app_keys", app_keys); 173 print_object_of_t(writer, "pre_instance_functions", pre_instance_functions); 174 if (!library_arch.empty()) { 175 writer.AddKeyedString("library_arch", library_arch); 176 } 177} 178 179VkLayerProperties ManifestLayer::LayerDescription::get_layer_properties() const { 180 VkLayerProperties properties{}; 181 copy_string_to_char_array(name, properties.layerName, VK_MAX_EXTENSION_NAME_SIZE); 182 copy_string_to_char_array(description, properties.description, VK_MAX_EXTENSION_NAME_SIZE); 183 properties.implementationVersion = implementation_version; 184 properties.specVersion = api_version; 185 return properties; 186} 187 188std::string ManifestLayer::get_manifest_str() const { 189 JsonWriter writer; 190 writer.StartObject(); 191 writer.AddKeyedString("file_format_version", file_format_version.get_version_str()); 192 if (layers.size() == 1) { 193 writer.StartKeyedObject("layer"); 194 layers.at(0).get_manifest_str(writer); 195 writer.EndObject(); 196 } else { 197 writer.StartKeyedArray("layers"); 198 for (size_t i = 0; i < layers.size(); i++) { 199 writer.StartObject(); 200 layers.at(i).get_manifest_str(writer); 201 writer.EndObject(); 202 } 203 writer.EndArray(); 204 } 205 writer.EndObject(); 206 return writer.output; 207} 208 209namespace fs { 210std::string make_native(std::string const& in_path) { 211 std::string out; 212#if defined(WIN32) 213 for (auto& c : in_path) { 214 if (c == '/') 215 out += "\\"; 216 else 217 out += c; 218 } 219#elif COMMON_UNIX_PLATFORMS 220 for (size_t i = 0; i < in_path.size(); i++) { 221 if (i + 1 < in_path.size() && in_path[i] == '\\' && in_path[i + 1] == '\\') { 222 out += '/'; 223 i++; 224 } else 225 out += in_path[i]; 226 } 227#endif 228 return out; 229} 230 231// Json doesn't allow `\` in strings, it must be escaped. Thus we have to convert '\\' to '\\\\' in strings 232std::string fixup_backslashes_in_path(std::string const& in_path) { 233 std::string out; 234 for (auto& c : in_path) { 235 if (c == '\\') 236 out += "\\\\"; 237 else 238 out += c; 239 } 240 return out; 241} 242fs::path fixup_backslashes_in_path(fs::path const& in_path) { return fixup_backslashes_in_path(in_path.str()); } 243 244path& path::operator+=(path const& in) { 245 contents += in.contents; 246 return *this; 247} 248path& path::operator+=(std::string const& in) { 249 contents += in; 250 return *this; 251} 252path& path::operator+=(const char* in) { 253 contents += std::string{in}; 254 return *this; 255} 256path& path::operator/=(path const& in) { 257 if (contents.back() != path_separator && in.contents.front() != path_separator) contents += path_separator; 258 contents += in.contents; 259 return *this; 260} 261path& path::operator/=(std::string const& in) { 262 if (contents.back() != path_separator && in.front() != path_separator) contents += path_separator; 263 contents += in; 264 return *this; 265} 266path& path::operator/=(const char* in) { 267 std::string in_str{in}; 268 if (contents.back() != path_separator && in_str.front() != path_separator) contents += path_separator; 269 contents += in_str; 270 return *this; 271} 272path path::operator+(path const& in) const { 273 path new_path = contents; 274 new_path += in; 275 return new_path; 276} 277path path::operator+(std::string const& in) const { 278 path new_path = contents; 279 new_path += in; 280 return new_path; 281} 282path path::operator+(const char* in) const { 283 path new_path(contents); 284 new_path += in; 285 return new_path; 286} 287 288path path::operator/(path const& in) const { 289 path new_path = contents; 290 new_path /= in; 291 return new_path; 292} 293path path::operator/(std::string const& in) const { 294 path new_path = contents; 295 new_path /= in; 296 return new_path; 297} 298path path::operator/(const char* in) const { 299 path new_path(contents); 300 new_path /= in; 301 return new_path; 302} 303 304path path::parent_path() const { 305 auto last_div = contents.rfind(path_separator); 306 if (last_div == std::string::npos) return ""; 307 return path(contents.substr(0, last_div)); 308} 309bool path::has_parent_path() const { 310 auto last_div = contents.rfind(path_separator); 311 return last_div != std::string::npos; 312} 313path path::filename() const { 314 auto last_div = contents.rfind(path_separator); 315 return path(contents.substr(last_div + 1, contents.size() - last_div + 1)); 316} 317 318path path::extension() const { 319 auto last_div = contents.rfind(path_separator); 320 auto ext_div = contents.rfind('.'); 321 // Make sure to not get the special `.` and `..`, as well as any filename that being with a dot, like .profile 322 if (last_div + 1 == ext_div || (last_div + 2 == ext_div && contents[last_div + 1] == '.')) return path(""); 323 path temp = path(contents.substr(ext_div, contents.size() - ext_div + 1)); 324 325 return path(contents.substr(ext_div, contents.size() - ext_div + 1)); 326} 327 328path path::stem() const { 329 auto last_div = contents.rfind(path_separator); 330 auto ext_div = contents.rfind('.'); 331 if (last_div + 1 == ext_div || (last_div + 2 == ext_div && contents[last_div + 1] == '.')) { 332 return path(contents.substr(last_div + 1, contents.size() - last_div + 1)); 333 } 334 return path(contents.substr(last_div + 1, ext_div - last_div - 1)); 335} 336 337path& path::replace_filename(path const& replacement) { 338 *this = parent_path() / replacement.str(); 339 return *this; 340} 341 342// internal implementation helper for per-platform creating & destroying folders 343int create_folder(path const& path) { 344#if defined(WIN32) 345 return _wmkdir(widen(path.str()).c_str()); 346#else 347 mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); 348 return 0; 349#endif 350} 351 352int delete_folder_contents(path const& folder) { 353#if defined(WIN32) 354 std::wstring folder_utf16 = widen(folder.str()); 355 if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(folder_utf16.c_str()) && GetLastError() == ERROR_FILE_NOT_FOUND) { 356 // nothing to delete 357 return 0; 358 } 359 std::wstring search_path = folder_utf16 + L"/*.*"; 360 std::string s_p = folder.str() + "/"; 361 WIN32_FIND_DATAW fd; 362 HANDLE hFind = ::FindFirstFileW(search_path.c_str(), &fd); 363 if (hFind != INVALID_HANDLE_VALUE) { 364 do { 365 std::string file_name_utf8 = narrow(fd.cFileName); 366 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { 367 if (!string_eq(file_name_utf8.c_str(), ".") && !string_eq(file_name_utf8.c_str(), "..")) { 368 delete_folder(s_p + file_name_utf8); 369 } 370 } else { 371 std::string child_name = s_p + file_name_utf8; 372 DeleteFileW(widen(child_name).c_str()); 373 } 374 } while (::FindNextFileW(hFind, &fd)); 375 ::FindClose(hFind); 376 } 377 return 0; 378#else 379 DIR* dir = opendir(folder.c_str()); 380 if (!dir) { 381 return 0; 382 } 383 int ret = 0; 384 dirent* file; 385 while (!ret && (file = readdir(dir))) { 386 int ret2 = -1; 387 388 /* Skip the names "." and ".." as we don't want to recurse on them. */ 389 if (string_eq(file->d_name, ".") || string_eq(file->d_name, "..")) continue; 390 391 path file_path = folder / file->d_name; 392 struct stat statbuf; 393 if (!stat(file_path.c_str(), &statbuf)) { 394 if (S_ISDIR(statbuf.st_mode)) 395 ret2 = delete_folder(file_path); 396 else 397 ret2 = unlink(file_path.c_str()); 398 } 399 400 ret = ret2; 401 } 402 closedir(dir); 403 return ret; 404#endif 405} 406 407int delete_folder(path const& folder) { 408 int ret = delete_folder_contents(folder); 409 if (ret != 0) return ret; 410#if defined(WIN32) 411 _wrmdir(widen(folder.str()).c_str()); 412 return 0; 413#else 414 return rmdir(folder.c_str()); 415#endif 416} 417 418#if defined(WIN32) 419std::wstring native_path(const std::string& utf8) { return widen(utf8); } 420#else 421const std::string& native_path(const std::string& utf8) { return utf8; } 422#endif 423 424FolderManager::FolderManager(path root_path, std::string name) noexcept : folder(root_path / name) { 425 delete_folder_contents(folder); 426 create_folder(folder); 427} 428FolderManager::~FolderManager() noexcept { 429 if (folder.str().empty()) return; 430 auto list_of_files_to_delete = files; 431 // remove(file) modifies the files variable, copy the list before deleting it 432 // Note: the allocation tests currently leak the loaded driver handles because in an OOM scenario the loader doesn't bother 433 // removing those. Since this is in an OOM situation, it is a low priority to fix. It does have the effect that Windows will 434 // be unable to delete the binaries that were leaked. 435 for (auto& file : list_of_files_to_delete) { 436 remove(file); 437 } 438 delete_folder(folder); 439} 440FolderManager::FolderManager(FolderManager&& other) noexcept : folder(other.folder), files(other.files) { 441 other.folder.str().clear(); 442} 443FolderManager& FolderManager::operator=(FolderManager&& other) noexcept { 444 folder = other.folder; 445 files = other.files; 446 other.folder.str().clear(); 447 return *this; 448} 449 450path FolderManager::write_manifest(std::string const& name, std::string const& contents) { 451 path out_path = folder / name; 452 auto found = std::find(files.begin(), files.end(), name); 453 if (found != files.end()) { 454 std::cout << "Overwriting manifest " << name << ". Was this intended?\n"; 455 } else { 456 files.emplace_back(name); 457 } 458 auto file = std::ofstream(native_path(out_path.str()), std::ios_base::trunc | std::ios_base::out); 459 if (!file) { 460 std::cerr << "Failed to create manifest " << name << " at " << out_path.str() << "\n"; 461 return out_path; 462 } 463 file << contents << std::endl; 464 return out_path; 465} 466void FolderManager::add_existing_file(std::string const& file_name) { files.emplace_back(file_name); } 467 468// close file handle, delete file, remove `name` from managed file list. 469void FolderManager::remove(std::string const& name) { 470 path out_path = folder / name; 471 auto found = std::find(files.begin(), files.end(), name); 472 if (found != files.end()) { 473 int rc = std::remove(out_path.c_str()); 474 if (rc != 0) { 475 std::cerr << "Failed to remove file " << name << " at " << out_path.str() << "\n"; 476 } 477 478 files.erase(found); 479 480 } else { 481 std::cout << "Couldn't remove file " << name << " at " << out_path.str() << ".\n"; 482 } 483} 484 485// copy file into this folder 486path FolderManager::copy_file(path const& file, std::string const& new_name) { 487 auto new_filepath = folder / new_name; 488 auto found = std::find(files.begin(), files.end(), new_name); 489 if (found != files.end()) { 490 std::cout << "File location already contains" << new_name << ". Is this a bug?\n"; 491 } else if (file.str() == new_filepath.str()) { 492 std::cout << "Trying to copy " << new_name << " into itself. Is this a bug?\n"; 493 } else { 494 files.emplace_back(new_name); 495 } 496 std::ifstream src(native_path(file.str()), std::ios::binary); 497 if (!src) { 498 std::cerr << "Failed to create file " << file.str() << " for copying from\n"; 499 return new_filepath; 500 } 501 std::ofstream dst(native_path(new_filepath.str()), std::ios::binary); 502 if (!dst) { 503 std::cerr << "Failed to create file " << new_filepath.str() << " for copying to\n"; 504 return new_filepath; 505 } 506 dst << src.rdbuf(); 507 return new_filepath; 508} 509} // namespace fs 510 511const char* get_platform_wsi_extension([[maybe_unused]] const char* api_selection) { 512#if defined(VK_USE_PLATFORM_ANDROID_KHR) 513 return "VK_KHR_android_surface"; 514#elif defined(VK_USE_PLATFORM_DIRECTFB_EXT) 515 return "VK_EXT_directfb_surface"; 516#elif defined(VK_USE_PLATFORM_FUCHSIA) 517 return "VK_FUCHSIA_imagepipe_surface"; 518#elif defined(VK_USE_PLATFORM_GGP) 519 return "VK_GGP_stream_descriptor_surface"; 520#elif defined(VK_USE_PLATFORM_IOS_MVK) 521 return "VK_MVK_ios_surface"; 522#elif defined(VK_USE_PLATFORM_MACOS_MVK) || defined(VK_USE_PLATFORM_METAL_EXT) 523#if defined(VK_USE_PLATFORM_MACOS_MVK) 524 if (string_eq(api_selection, "VK_USE_PLATFORM_MACOS_MVK")) return "VK_MVK_macos_surface"; 525#endif 526#if defined(VK_USE_PLATFORM_METAL_EXT) 527 if (string_eq(api_selection, "VK_USE_PLATFORM_METAL_EXT")) return "VK_EXT_metal_surface"; 528 return "VK_EXT_metal_surface"; 529#endif 530#elif defined(VK_USE_PLATFORM_SCREEN_QNX) 531 return "VK_QNX_screen_surface"; 532#elif defined(VK_USE_PLATFORM_VI_NN) 533 return "VK_NN_vi_surface"; 534#elif defined(VK_USE_PLATFORM_XCB_KHR) || defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_WAYLAND_KHR) 535#if defined(VK_USE_PLATFORM_XCB_KHR) 536 if (string_eq(api_selection, "VK_USE_PLATFORM_XCB_KHR")) return "VK_KHR_xcb_surface"; 537#endif 538#if defined(VK_USE_PLATFORM_XLIB_KHR) 539 if (string_eq(api_selection, "VK_USE_PLATFORM_XLIB_KHR")) return "VK_KHR_xlib_surface"; 540#endif 541#if defined(VK_USE_PLATFORM_WAYLAND_KHR) 542 if (string_eq(api_selection, "VK_USE_PLATFORM_WAYLAND_KHR")) return "VK_KHR_wayland_surface"; 543#endif 544#if defined(VK_USE_PLATFORM_XCB_KHR) 545 return "VK_KHR_xcb_surface"; 546#endif 547#elif defined(VK_USE_PLATFORM_WIN32_KHR) 548 return "VK_KHR_win32_surface"; 549#else 550 return "VK_KHR_display"; 551#endif 552} 553 554bool string_eq(const char* a, const char* b) noexcept { return a && b && strcmp(a, b) == 0; } 555bool string_eq(const char* a, const char* b, size_t len) noexcept { return a && b && strncmp(a, b, len) == 0; } 556 557InstanceCreateInfo::InstanceCreateInfo() { 558 instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; 559 application_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; 560} 561 562VkInstanceCreateInfo* InstanceCreateInfo::get() noexcept { 563 if (fill_in_application_info) { 564 application_info.pApplicationName = app_name.c_str(); 565 application_info.pEngineName = engine_name.c_str(); 566 application_info.applicationVersion = app_version; 567 application_info.engineVersion = engine_version; 568 application_info.apiVersion = api_version; 569 instance_info.pApplicationInfo = &application_info; 570 } 571 instance_info.flags = flags; 572 instance_info.enabledLayerCount = static_cast<uint32_t>(enabled_layers.size()); 573 instance_info.ppEnabledLayerNames = enabled_layers.data(); 574 instance_info.enabledExtensionCount = static_cast<uint32_t>(enabled_extensions.size()); 575 instance_info.ppEnabledExtensionNames = enabled_extensions.data(); 576 return &instance_info; 577} 578InstanceCreateInfo& InstanceCreateInfo::set_api_version(uint32_t major, uint32_t minor, uint32_t patch) { 579 this->api_version = VK_MAKE_API_VERSION(0, major, minor, patch); 580 return *this; 581} 582InstanceCreateInfo& InstanceCreateInfo::setup_WSI(const char* api_selection) { 583 add_extensions({"VK_KHR_surface", get_platform_wsi_extension(api_selection)}); 584 return *this; 585} 586 587DeviceQueueCreateInfo::DeviceQueueCreateInfo() { queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; } 588DeviceQueueCreateInfo::DeviceQueueCreateInfo(const VkDeviceQueueCreateInfo* create_info) { 589 queue_create_info = *create_info; 590 for (uint32_t i = 0; i < create_info->queueCount; i++) { 591 priorities.push_back(create_info->pQueuePriorities[i]); 592 } 593} 594 595VkDeviceQueueCreateInfo DeviceQueueCreateInfo::get() noexcept { 596 queue_create_info.pQueuePriorities = priorities.data(); 597 queue_create_info.queueCount = 1; 598 queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; 599 return queue_create_info; 600} 601 602DeviceCreateInfo::DeviceCreateInfo(const VkDeviceCreateInfo* create_info) { 603 dev = *create_info; 604 for (uint32_t i = 0; i < create_info->enabledExtensionCount; i++) { 605 enabled_extensions.push_back(create_info->ppEnabledExtensionNames[i]); 606 } 607 for (uint32_t i = 0; i < create_info->enabledLayerCount; i++) { 608 enabled_layers.push_back(create_info->ppEnabledLayerNames[i]); 609 } 610 for (uint32_t i = 0; i < create_info->queueCreateInfoCount; i++) { 611 device_queue_infos.push_back(create_info->pQueueCreateInfos[i]); 612 } 613} 614 615VkDeviceCreateInfo* DeviceCreateInfo::get() noexcept { 616 dev.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; 617 dev.enabledLayerCount = static_cast<uint32_t>(enabled_layers.size()); 618 dev.ppEnabledLayerNames = enabled_layers.data(); 619 dev.enabledExtensionCount = static_cast<uint32_t>(enabled_extensions.size()); 620 dev.ppEnabledExtensionNames = enabled_extensions.data(); 621 uint32_t index = 0; 622 for (auto& queue : queue_info_details) { 623 queue.queue_create_info.queueFamilyIndex = index++; 624 queue.queue_create_info.queueCount = 1; 625 device_queue_infos.push_back(queue.get()); 626 } 627 628 dev.queueCreateInfoCount = static_cast<uint32_t>(device_queue_infos.size()); 629 dev.pQueueCreateInfos = device_queue_infos.data(); 630 return &dev; 631} 632 633#if defined(WIN32) 634std::string narrow(const std::wstring& utf16) { 635 if (utf16.empty()) { 636 return {}; 637 } 638 int size = WideCharToMultiByte(CP_UTF8, 0, utf16.data(), static_cast<int>(utf16.size()), nullptr, 0, nullptr, nullptr); 639 if (size <= 0) { 640 return {}; 641 } 642 std::string utf8(size, '\0'); 643 if (WideCharToMultiByte(CP_UTF8, 0, utf16.data(), static_cast<int>(utf16.size()), &utf8[0], size, nullptr, nullptr) != size) { 644 return {}; 645 } 646 return utf8; 647} 648 649std::wstring widen(const std::string& utf8) { 650 if (utf8.empty()) { 651 return {}; 652 } 653 int size = MultiByteToWideChar(CP_UTF8, 0, utf8.data(), static_cast<int>(utf8.size()), nullptr, 0); 654 if (size <= 0) { 655 return {}; 656 } 657 std::wstring utf16(size, L'\0'); 658 if (MultiByteToWideChar(CP_UTF8, 0, utf8.data(), static_cast<int>(utf8.size()), &utf16[0], size) != size) { 659 return {}; 660 } 661 return utf16; 662} 663#endif 664