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>
win_api_error_str(LSTATUS status)32 const 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 
print_error_message(LSTATUS status, const char* function_name, std::string optional_message)44 void 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 
set_env_var()60 void 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 }
remove_env_var() const66 void EnvVarWrapper::remove_env_var() const { SetEnvironmentVariableW(widen(name).c_str(), nullptr); }
get_env_var(std::string const& name, bool report_failure)67 std::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 
set_env_var()82 void EnvVarWrapper::set_env_var() { setenv(name.c_str(), cur_value.c_str(), 1); }
remove_env_var() const83 void EnvVarWrapper::remove_env_var() const { unsetenv(name.c_str()); }
get_env_var(std::string const& name, bool report_failure)84 std::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 
94 template <typename T>
print_object_of_t(JsonWriter& writer, const char* object_name, std::vector<T> const& vec)95 void 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 
104 template <typename T>
print_array_of_t(JsonWriter& writer, const char* object_name, std::vector<T> const& vec)105 void 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 }
print_vector_of_strings(JsonWriter& writer, const char* object_name, std::vector<std::string> const& strings)113 void 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 
to_text(bool b)122 std::string to_text(bool b) { return b ? std::string("true") : std::string("false"); }
123 
get_manifest_str() const124 std::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 
get_manifest_str(JsonWriter& writer) const138 void 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 
get_manifest_str(JsonWriter& writer) const147 void 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 
get_layer_properties() const179 VkLayerProperties 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 
get_manifest_str() const188 std::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 
209 namespace fs {
make_native(std::string const& in_path)210 std::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
fixup_backslashes_in_path(std::string const& in_path)232 std::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 }
fixup_backslashes_in_path(fs::path const& in_path)242 fs::path fixup_backslashes_in_path(fs::path const& in_path) { return fixup_backslashes_in_path(in_path.str()); }
243 
operator +=(path const& in)244 path& path::operator+=(path const& in) {
245     contents += in.contents;
246     return *this;
247 }
operator +=(std::string const& in)248 path& path::operator+=(std::string const& in) {
249     contents += in;
250     return *this;
251 }
operator +=(const char* in)252 path& path::operator+=(const char* in) {
253     contents += std::string{in};
254     return *this;
255 }
operator /=(path const& in)256 path& 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 }
operator /=(std::string const& in)261 path& 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 }
operator /=(const char* in)266 path& 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 }
operator +(path const& in) const272 path path::operator+(path const& in) const {
273     path new_path = contents;
274     new_path += in;
275     return new_path;
276 }
operator +(std::string const& in) const277 path path::operator+(std::string const& in) const {
278     path new_path = contents;
279     new_path += in;
280     return new_path;
281 }
operator +(const char* in) const282 path path::operator+(const char* in) const {
283     path new_path(contents);
284     new_path += in;
285     return new_path;
286 }
287 
operator /(path const& in) const288 path path::operator/(path const& in) const {
289     path new_path = contents;
290     new_path /= in;
291     return new_path;
292 }
operator /(std::string const& in) const293 path path::operator/(std::string const& in) const {
294     path new_path = contents;
295     new_path /= in;
296     return new_path;
297 }
operator /(const char* in) const298 path path::operator/(const char* in) const {
299     path new_path(contents);
300     new_path /= in;
301     return new_path;
302 }
303 
parent_path() const304 path 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 }
has_parent_path() const309 bool path::has_parent_path() const {
310     auto last_div = contents.rfind(path_separator);
311     return last_div != std::string::npos;
312 }
filename() const313 path 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 
extension() const318 path 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 
stem() const328 path 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 
replace_filename(path const& replacement)337 path& 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
create_folder(path const& path)343 int 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 
delete_folder_contents(path const& folder)352 int 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 
delete_folder(path const& folder)407 int 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)
native_path(const std::string& utf8)419 std::wstring native_path(const std::string& utf8) { return widen(utf8); }
420 #else
native_path(const std::string& utf8)421 const std::string& native_path(const std::string& utf8) { return utf8; }
422 #endif
423 
424 FolderManager::FolderManager(path root_path, std::string name) noexcept : folder(root_path / name) {
425     delete_folder_contents(folder);
426     create_folder(folder);
427 }
428 FolderManager::~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 }
files(other.files)440 FolderManager::FolderManager(FolderManager&& other) noexcept : folder(other.folder), files(other.files) {
441     other.folder.str().clear();
442 }
443 FolderManager& FolderManager::operator=(FolderManager&& other) noexcept {
444     folder = other.folder;
445     files = other.files;
446     other.folder.str().clear();
447     return *this;
448 }
449 
write_manifest(std::string const& name, std::string const& contents)450 path 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 }
add_existing_file(std::string const& file_name)466 void 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.
remove(std::string const& name)469 void 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
copy_file(path const& file, std::string const& new_name)486 path 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 
get_platform_wsi_extension([[maybe_unused]] const char* api_selection)511 const 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 
554 bool string_eq(const char* a, const char* b) noexcept { return a && b && strcmp(a, b) == 0; }
555 bool string_eq(const char* a, const char* b, size_t len) noexcept { return a && b && strncmp(a, b, len) == 0; }
556 
InstanceCreateInfo()557 InstanceCreateInfo::InstanceCreateInfo() {
558     instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
559     application_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
560 }
561 
562 VkInstanceCreateInfo* 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 }
set_api_version(uint32_t major, uint32_t minor, uint32_t patch)578 InstanceCreateInfo& 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 }
setup_WSI(const char* api_selection)582 InstanceCreateInfo& InstanceCreateInfo::setup_WSI(const char* api_selection) {
583     add_extensions({"VK_KHR_surface", get_platform_wsi_extension(api_selection)});
584     return *this;
585 }
586 
DeviceQueueCreateInfo()587 DeviceQueueCreateInfo::DeviceQueueCreateInfo() { queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; }
DeviceQueueCreateInfo(const VkDeviceQueueCreateInfo* create_info)588 DeviceQueueCreateInfo::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 
595 VkDeviceQueueCreateInfo 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 
DeviceCreateInfo(const VkDeviceCreateInfo* create_info)602 DeviceCreateInfo::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 
615 VkDeviceCreateInfo* 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)
narrow(const std::wstring& utf16)634 std::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 
widen(const std::string& utf8)649 std::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