• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 The Khronos Group Inc.
3  * Copyright (c) 2021 Valve Corporation
4  * Copyright (c) 2021 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(std::string const & name,std::string const & value)60 void set_env_var(std::string const& name, std::string const& value) {
61     BOOL ret = SetEnvironmentVariableW(widen(name).c_str(), widen(value).c_str());
62     if (ret == 0) {
63         print_error_message(ERROR_SETENV_FAILED, "SetEnvironmentVariableW");
64     }
65 }
remove_env_var(std::string const & name)66 void remove_env_var(std::string const& name) { 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 defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
81 
set_env_var(std::string const & name,std::string const & value)82 void set_env_var(std::string const& name, std::string const& value) { setenv(name.c_str(), value.c_str(), 1); }
remove_env_var(std::string const & name)83 void remove_env_var(std::string const& name) { 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_list_of_t(std::string & out,const char * object_name,std::vector<T> const & vec)95 void print_list_of_t(std::string& out, const char* object_name, std::vector<T> const& vec) {
96     if (vec.size() > 0) {
97         out += std::string(",\n\t\t\"") + object_name + "\": {";
98         for (size_t i = 0; i < vec.size(); i++) {
99             if (i > 0) out += ",\t\t\t";
100             out += "\n\t\t\t" + vec.at(i).get_manifest_str();
101         }
102         out += "\n\t\t}";
103     }
104 }
105 
106 template <typename T>
print_vector_of_t(std::string & out,const char * object_name,std::vector<T> const & vec)107 void print_vector_of_t(std::string& out, const char* object_name, std::vector<T> const& vec) {
108     if (vec.size() > 0) {
109         out += std::string(",\n\t\t\"") + object_name + "\": [";
110         for (size_t i = 0; i < vec.size(); i++) {
111             if (i > 0) out += ",\t\t\t";
112             out += "\n\t\t\t" + vec.at(i).get_manifest_str();
113         }
114         out += "\n\t\t]";
115     }
116 }
print_vector_of_strings(std::string & out,const char * object_name,std::vector<std::string> const & strings)117 void print_vector_of_strings(std::string& out, const char* object_name, std::vector<std::string> const& strings) {
118     if (strings.size() > 0) {
119         out += std::string(",\n\t\t\"") + object_name + "\": [";
120         for (size_t i = 0; i < strings.size(); i++) {
121             if (i > 0) out += ",\t\t\t";
122             out += "\"" + fs::fixup_backslashes_in_path(strings.at(i)) + "\"";
123         }
124         out += "]";
125     }
126 }
127 
to_text(bool b)128 std::string to_text(bool b) { return b ? std::string("true") : std::string("false"); }
129 
get_manifest_str() const130 std::string ManifestICD::get_manifest_str() const {
131     std::string out;
132     out += "{\n";
133     out += "    " + file_format_version.get_version_str() + "\n";
134     out += "    \"ICD\": {\n";
135     out += "        \"library_path\": \"" + fs::fixup_backslashes_in_path(lib_path) + "\",\n";
136     out += "        \"api_version\": \"" + version_to_string(api_version) + "\",\n";
137     out += "        \"is_portability_driver\": " + to_text(is_portability_driver);
138     if (!library_arch.empty()) {
139         out += ",\n       \"library_arch\": \"" + library_arch + "\"\n";
140     } else {
141         out += "\n";
142     }
143     out += "    }\n";
144     out += "}\n";
145     return out;
146 }
147 
get_manifest_str() const148 std::string ManifestLayer::LayerDescription::Extension::get_manifest_str() const {
149     std::string out;
150     out += "{ \"name\":\"" + name + "\",\n\t\t\t\"spec_version\":\"" + std::to_string(spec_version) + "\"";
151     print_vector_of_strings(out, "entrypoints", entrypoints);
152     out += "\n\t\t\t}";
153     return out;
154 }
155 
get_manifest_str() const156 std::string ManifestLayer::LayerDescription::get_manifest_str() const {
157     std::string out;
158     out += "\t{\n";
159     out += "\t\t\"name\":\"" + name + "\",\n";
160     out += "\t\t\"type\":\"" + get_type_str(type) + "\",\n";
161     if (lib_path.size() > 0) {
162         out += "\t\t\"library_path\": \"" + fs::fixup_backslashes_in_path(lib_path.str()) + "\",\n";
163     }
164     out += "\t\t\"api_version\": \"" + version_to_string(api_version) + "\",\n";
165     out += "\t\t\"implementation_version\":\"" + std::to_string(implementation_version) + "\",\n";
166     out += "\t\t\"description\": \"" + description + "\"";
167     print_list_of_t(out, "functions", functions);
168     print_vector_of_t(out, "instance_extensions", instance_extensions);
169     print_vector_of_t(out, "device_extensions", device_extensions);
170     if (!enable_environment.empty()) {
171         out += ",\n\t\t\"enable_environment\": { \"" + enable_environment + "\": \"1\" }";
172     }
173     if (!disable_environment.empty()) {
174         out += ",\n\t\t\"disable_environment\": { \"" + disable_environment + "\": \"1\" }";
175     }
176     print_vector_of_strings(out, "component_layers", component_layers);
177     print_vector_of_strings(out, "blacklisted_layers", blacklisted_layers);
178     print_vector_of_strings(out, "override_paths", override_paths);
179     print_vector_of_strings(out, "app_keys", app_keys);
180     print_list_of_t(out, "pre_instance_functions", pre_instance_functions);
181     if (!library_arch.empty()) {
182         out += ",\n\t\t\"library_arch\": \"" + library_arch + "\"";
183     }
184     out += "\n\t}";
185 
186     return out;
187 }
188 
get_layer_properties() const189 VkLayerProperties ManifestLayer::LayerDescription::get_layer_properties() const {
190     VkLayerProperties properties{};
191     copy_string_to_char_array(name, properties.layerName, VK_MAX_EXTENSION_NAME_SIZE);
192     copy_string_to_char_array(description, properties.description, VK_MAX_EXTENSION_NAME_SIZE);
193     properties.implementationVersion = implementation_version;
194     properties.specVersion = api_version;
195     return properties;
196 }
197 
get_manifest_str() const198 std::string ManifestLayer::get_manifest_str() const {
199     std::string out;
200     out += "{\n";
201     out += "\t" + file_format_version.get_version_str() + "\n";
202     if (layers.size() == 1) {
203         out += "\t\"layer\": ";
204         out += layers.at(0).get_manifest_str() + "\n";
205     } else {
206         out += "\"\tlayers\": [";
207         for (size_t i = 0; i < layers.size(); i++) {
208             if (i > 0) out += ",";
209             out += "\n" + layers.at(0).get_manifest_str();
210         }
211         out += "\n]";
212     }
213     out += "}\n";
214     return out;
215 }
216 
217 namespace fs {
make_native(std::string const & in_path)218 std::string make_native(std::string const& in_path) {
219     std::string out;
220 #if defined(WIN32)
221     for (auto& c : in_path) {
222         if (c == '/')
223             out += "\\";
224         else
225             out += c;
226     }
227 #elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
228     for (size_t i = 0; i < in_path.size(); i++) {
229         if (i + 1 < in_path.size() && in_path[i] == '\\' && in_path[i + 1] == '\\') {
230             out += '/';
231             i++;
232         } else
233             out += in_path[i];
234     }
235 #endif
236     return out;
237 }
238 
239 // 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)240 std::string fixup_backslashes_in_path(std::string const& in_path) {
241     std::string out;
242     for (auto& c : in_path) {
243         if (c == '\\')
244             out += "\\\\";
245         else
246             out += c;
247     }
248     return out;
249 }
fixup_backslashes_in_path(fs::path const & in_path)250 fs::path fixup_backslashes_in_path(fs::path const& in_path) { return fixup_backslashes_in_path(in_path.str()); }
251 
operator +=(path const & in)252 path& path::operator+=(path const& in) {
253     contents += in.contents;
254     return *this;
255 }
operator +=(std::string const & in)256 path& path::operator+=(std::string const& in) {
257     contents += in;
258     return *this;
259 }
operator +=(const char * in)260 path& path::operator+=(const char* in) {
261     contents += std::string{in};
262     return *this;
263 }
operator /=(path const & in)264 path& path::operator/=(path const& in) {
265     if (contents.back() != path_separator && in.contents.front() != path_separator) contents += path_separator;
266     contents += in.contents;
267     return *this;
268 }
operator /=(std::string const & in)269 path& path::operator/=(std::string const& in) {
270     if (contents.back() != path_separator && in.front() != path_separator) contents += path_separator;
271     contents += in;
272     return *this;
273 }
operator /=(const char * in)274 path& path::operator/=(const char* in) {
275     std::string in_str{in};
276     if (contents.back() != path_separator && in_str.front() != path_separator) contents += path_separator;
277     contents += in_str;
278     return *this;
279 }
operator +(path const & in) const280 path path::operator+(path const& in) const {
281     path new_path = contents;
282     new_path += in;
283     return new_path;
284 }
operator +(std::string const & in) const285 path path::operator+(std::string const& in) const {
286     path new_path = contents;
287     new_path += in;
288     return new_path;
289 }
operator +(const char * in) const290 path path::operator+(const char* in) const {
291     path new_path(contents);
292     new_path += in;
293     return new_path;
294 }
295 
operator /(path const & in) const296 path path::operator/(path const& in) const {
297     path new_path = contents;
298     new_path /= in;
299     return new_path;
300 }
operator /(std::string const & in) const301 path path::operator/(std::string const& in) const {
302     path new_path = contents;
303     new_path /= in;
304     return new_path;
305 }
operator /(const char * in) const306 path path::operator/(const char* in) const {
307     path new_path(contents);
308     new_path /= in;
309     return new_path;
310 }
311 
parent_path() const312 path path::parent_path() const {
313     auto last_div = contents.rfind(path_separator);
314     if (last_div == std::string::npos) return "";
315     return path(contents.substr(0, last_div));
316 }
has_parent_path() const317 bool path::has_parent_path() const {
318     auto last_div = contents.rfind(path_separator);
319     return last_div != std::string::npos;
320 }
filename() const321 path path::filename() const {
322     auto last_div = contents.rfind(path_separator);
323     return path(contents.substr(last_div + 1, contents.size() - last_div + 1));
324 }
325 
extension() const326 path path::extension() const {
327     auto last_div = contents.rfind(path_separator);
328     auto ext_div = contents.rfind('.');
329     // Make sure to not get the special `.` and `..`, as well as any filename that being with a dot, like .profile
330     if (last_div + 1 == ext_div || (last_div + 2 == ext_div && contents[last_div + 1] == '.')) return path("");
331     path temp = path(contents.substr(ext_div, contents.size() - ext_div + 1));
332 
333     return path(contents.substr(ext_div, contents.size() - ext_div + 1));
334 }
335 
stem() const336 path path::stem() const {
337     auto last_div = contents.rfind(path_separator);
338     auto ext_div = contents.rfind('.');
339     if (last_div + 1 == ext_div || (last_div + 2 == ext_div && contents[last_div + 1] == '.')) {
340         return path(contents.substr(last_div + 1, contents.size() - last_div + 1));
341     }
342     return path(contents.substr(last_div + 1, ext_div - last_div - 1));
343 }
344 
replace_filename(path const & replacement)345 path& path::replace_filename(path const& replacement) {
346     *this = parent_path() / replacement.str();
347     return *this;
348 }
349 
350 // internal implementation helper for per-platform creating & destroying folders
create_folder(path const & path)351 int create_folder(path const& path) {
352 #if defined(WIN32)
353     return _wmkdir(widen(path.str()).c_str());
354 #else
355     mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
356     return 0;
357 #endif
358 }
359 
delete_folder_contents(path const & folder)360 int delete_folder_contents(path const& folder) {
361 #if defined(WIN32)
362     std::wstring folder_utf16 = widen(folder.str());
363     if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(folder_utf16.c_str()) && GetLastError() == ERROR_FILE_NOT_FOUND) {
364         // nothing to delete
365         return 0;
366     }
367     std::wstring search_path = folder_utf16 + L"/*.*";
368     std::string s_p = folder.str() + "/";
369     WIN32_FIND_DATAW fd;
370     HANDLE hFind = ::FindFirstFileW(search_path.c_str(), &fd);
371     if (hFind != INVALID_HANDLE_VALUE) {
372         do {
373             std::string file_name_utf8 = narrow(fd.cFileName);
374             if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
375                 if (!string_eq(file_name_utf8.c_str(), ".") && !string_eq(file_name_utf8.c_str(), "..")) {
376                     delete_folder(s_p + file_name_utf8);
377                 }
378             } else {
379                 std::string child_name = s_p + file_name_utf8;
380                 DeleteFileW(widen(child_name).c_str());
381             }
382         } while (::FindNextFileW(hFind, &fd));
383         ::FindClose(hFind);
384     }
385     return 0;
386 #else
387     DIR* dir = opendir(folder.c_str());
388     if (!dir) {
389         return 0;
390     }
391     int ret = 0;
392     dirent* file;
393     while (!ret && (file = readdir(dir))) {
394         int ret2 = -1;
395 
396         /* Skip the names "." and ".." as we don't want to recurse on them. */
397         if (string_eq(file->d_name, ".") || string_eq(file->d_name, "..")) continue;
398 
399         path file_path = folder / file->d_name;
400         struct stat statbuf;
401         if (!stat(file_path.c_str(), &statbuf)) {
402             if (S_ISDIR(statbuf.st_mode))
403                 ret2 = delete_folder(file_path);
404             else
405                 ret2 = unlink(file_path.c_str());
406         }
407 
408         ret = ret2;
409     }
410     closedir(dir);
411     return ret;
412 #endif
413 }
414 
delete_folder(path const & folder)415 int delete_folder(path const& folder) {
416     int ret = delete_folder_contents(folder);
417     if (ret != 0) return ret;
418 #if defined(WIN32)
419     _wrmdir(widen(folder.str()).c_str());
420     return 0;
421 #else
422     return rmdir(folder.c_str());
423 #endif
424 }
425 
426 #if defined(WIN32)
native_path(const std::string & utf8)427 std::wstring native_path(const std::string& utf8) { return widen(utf8); }
428 #else
native_path(const std::string & utf8)429 const std::string& native_path(const std::string& utf8) { return utf8; }
430 #endif
431 
FolderManager(path root_path,std::string name)432 FolderManager::FolderManager(path root_path, std::string name) noexcept : folder(root_path / name) {
433     delete_folder_contents(folder);
434     create_folder(folder);
435 }
~FolderManager()436 FolderManager::~FolderManager() noexcept {
437     if (folder.str().empty()) return;
438     auto list_of_files_to_delete = files;
439     // remove(file) modifies the files variable, copy the list before deleting it
440     // Note: the allocation tests currently leak the loaded driver handles because in an OOM scenario the loader doesn't bother
441     // removing those. Since this is in an OOM situation, it is a low priority to fix. It does have the effect that Windows will
442     // be unable to delete the binaries that were leaked.
443     for (auto& file : list_of_files_to_delete) {
444         remove(file);
445     }
446     delete_folder(folder);
447 }
FolderManager(FolderManager && other)448 FolderManager::FolderManager(FolderManager&& other) noexcept : folder(other.folder), files(other.files) {
449     other.folder.str().clear();
450 }
operator =(FolderManager && other)451 FolderManager& FolderManager::operator=(FolderManager&& other) noexcept {
452     folder = other.folder;
453     files = other.files;
454     other.folder.str().clear();
455     return *this;
456 }
457 
write_manifest(std::string const & name,std::string const & contents)458 path FolderManager::write_manifest(std::string const& name, std::string const& contents) {
459     path out_path = folder / name;
460     auto found = std::find(files.begin(), files.end(), name);
461     if (found != files.end()) {
462         std::cout << "Overwriting manifest " << name << ". Was this intended?\n";
463     } else {
464         files.emplace_back(name);
465     }
466     auto file = std::ofstream(native_path(out_path.str()), std::ios_base::trunc | std::ios_base::out);
467     if (!file) {
468         std::cerr << "Failed to create manifest " << name << " at " << out_path.str() << "\n";
469         return out_path;
470     }
471     file << contents << std::endl;
472     return out_path;
473 }
474 // close file handle, delete file, remove `name` from managed file list.
remove(std::string const & name)475 void FolderManager::remove(std::string const& name) {
476     path out_path = folder / name;
477     auto found = std::find(files.begin(), files.end(), name);
478     if (found != files.end()) {
479         int rc = std::remove(out_path.c_str());
480         if (rc != 0) {
481             std::cerr << "Failed to remove file " << name << " at " << out_path.str() << "\n";
482         }
483 
484         files.erase(found);
485 
486     } else {
487         std::cout << "Couldn't remove file " << name << " at " << out_path.str() << ".\n";
488     }
489 }
490 
491 // copy file into this folder
copy_file(path const & file,std::string const & new_name)492 path FolderManager::copy_file(path const& file, std::string const& new_name) {
493     auto new_filepath = folder / new_name;
494     auto found = std::find(files.begin(), files.end(), new_name);
495     if (found != files.end()) {
496         std::cout << "File location already contains" << new_name << ". Is this a bug?\n";
497     } else if (file.str() == new_filepath.str()) {
498         std::cout << "Trying to copy " << new_name << " into itself. Is this a bug?\n";
499     } else {
500         files.emplace_back(new_name);
501     }
502     std::ifstream src(native_path(file.str()), std::ios::binary);
503     if (!src) {
504         std::cerr << "Failed to create file " << file.str() << " for copying from\n";
505         return new_filepath;
506     }
507     std::ofstream dst(native_path(new_filepath.str()), std::ios::binary);
508     if (!dst) {
509         std::cerr << "Failed to create file " << new_filepath.str() << " for copying to\n";
510         return new_filepath;
511     }
512     dst << src.rdbuf();
513     return new_filepath;
514 }
515 }  // namespace fs
516 
string_eq(const char * a,const char * b)517 bool string_eq(const char* a, const char* b) noexcept { return strcmp(a, b) == 0; }
string_eq(const char * a,const char * b,size_t len)518 bool string_eq(const char* a, const char* b, size_t len) noexcept { return strncmp(a, b, len) == 0; }
519 
get_loader_path()520 fs::path get_loader_path() {
521     auto loader_path = fs::path(FRAMEWORK_VULKAN_LIBRARY_PATH);
522     auto env_var_res = get_env_var("VK_LOADER_TEST_LOADER_PATH", false);
523     if (!env_var_res.empty()) {
524         loader_path = fs::path(env_var_res);
525     }
526     return loader_path;
527 }
528 
VulkanFunctions()529 VulkanFunctions::VulkanFunctions() : loader(get_loader_path()) {
530     // clang-format off
531     vkGetInstanceProcAddr = loader.get_symbol("vkGetInstanceProcAddr");
532     vkEnumerateInstanceExtensionProperties = loader.get_symbol("vkEnumerateInstanceExtensionProperties");
533     vkEnumerateInstanceLayerProperties = loader.get_symbol("vkEnumerateInstanceLayerProperties");
534     vkEnumerateInstanceVersion = loader.get_symbol("vkEnumerateInstanceVersion");
535     vkCreateInstance = loader.get_symbol("vkCreateInstance");
536     vkDestroyInstance = loader.get_symbol("vkDestroyInstance");
537     vkEnumeratePhysicalDevices = loader.get_symbol("vkEnumeratePhysicalDevices");
538     vkEnumeratePhysicalDeviceGroups = loader.get_symbol("vkEnumeratePhysicalDeviceGroups");
539     vkGetPhysicalDeviceFeatures = loader.get_symbol("vkGetPhysicalDeviceFeatures");
540     vkGetPhysicalDeviceFeatures2 = loader.get_symbol("vkGetPhysicalDeviceFeatures2");
541     vkGetPhysicalDeviceFormatProperties = loader.get_symbol("vkGetPhysicalDeviceFormatProperties");
542     vkGetPhysicalDeviceFormatProperties2 = loader.get_symbol("vkGetPhysicalDeviceFormatProperties2");
543     vkGetPhysicalDeviceImageFormatProperties = loader.get_symbol("vkGetPhysicalDeviceImageFormatProperties");
544     vkGetPhysicalDeviceImageFormatProperties2 = loader.get_symbol("vkGetPhysicalDeviceImageFormatProperties2");
545     vkGetPhysicalDeviceSparseImageFormatProperties = loader.get_symbol("vkGetPhysicalDeviceSparseImageFormatProperties");
546     vkGetPhysicalDeviceSparseImageFormatProperties2 = loader.get_symbol("vkGetPhysicalDeviceSparseImageFormatProperties2");
547     vkGetPhysicalDeviceProperties = loader.get_symbol("vkGetPhysicalDeviceProperties");
548     vkGetPhysicalDeviceProperties2 = loader.get_symbol("vkGetPhysicalDeviceProperties2");
549     vkGetPhysicalDeviceQueueFamilyProperties = loader.get_symbol("vkGetPhysicalDeviceQueueFamilyProperties");
550     vkGetPhysicalDeviceQueueFamilyProperties2 = loader.get_symbol("vkGetPhysicalDeviceQueueFamilyProperties2");
551     vkGetPhysicalDeviceMemoryProperties = loader.get_symbol("vkGetPhysicalDeviceMemoryProperties");
552     vkGetPhysicalDeviceMemoryProperties2 = loader.get_symbol("vkGetPhysicalDeviceMemoryProperties2");
553     vkGetPhysicalDeviceSurfaceSupportKHR = loader.get_symbol("vkGetPhysicalDeviceSurfaceSupportKHR");
554     vkGetPhysicalDeviceSurfaceFormatsKHR = loader.get_symbol("vkGetPhysicalDeviceSurfaceFormatsKHR");
555     vkGetPhysicalDeviceSurfacePresentModesKHR = loader.get_symbol("vkGetPhysicalDeviceSurfacePresentModesKHR");
556     vkGetPhysicalDeviceSurfaceCapabilitiesKHR = loader.get_symbol("vkGetPhysicalDeviceSurfaceCapabilitiesKHR");
557     vkEnumerateDeviceExtensionProperties = loader.get_symbol("vkEnumerateDeviceExtensionProperties");
558     vkEnumerateDeviceLayerProperties = loader.get_symbol("vkEnumerateDeviceLayerProperties");
559     vkGetPhysicalDeviceExternalBufferProperties = loader.get_symbol("vkGetPhysicalDeviceExternalBufferProperties");
560     vkGetPhysicalDeviceExternalFenceProperties = loader.get_symbol("vkGetPhysicalDeviceExternalFenceProperties");
561     vkGetPhysicalDeviceExternalSemaphoreProperties = loader.get_symbol("vkGetPhysicalDeviceExternalSemaphoreProperties");
562 
563     vkDestroySurfaceKHR = loader.get_symbol("vkDestroySurfaceKHR");
564     vkGetDeviceProcAddr = loader.get_symbol("vkGetDeviceProcAddr");
565     vkCreateDevice = loader.get_symbol("vkCreateDevice");
566 
567     vkCreateHeadlessSurfaceEXT = loader.get_symbol("vkCreateHeadlessSurfaceEXT");
568     vkCreateDisplayPlaneSurfaceKHR = loader.get_symbol("vkCreateDisplayPlaneSurfaceKHR");
569     vkGetPhysicalDeviceDisplayPropertiesKHR = loader.get_symbol("vkGetPhysicalDeviceDisplayPropertiesKHR");
570     vkGetPhysicalDeviceDisplayPlanePropertiesKHR = loader.get_symbol("vkGetPhysicalDeviceDisplayPlanePropertiesKHR");
571     vkGetDisplayPlaneSupportedDisplaysKHR = loader.get_symbol("vkGetDisplayPlaneSupportedDisplaysKHR");
572     vkGetDisplayModePropertiesKHR = loader.get_symbol("vkGetDisplayModePropertiesKHR");
573     vkCreateDisplayModeKHR = loader.get_symbol("vkCreateDisplayModeKHR");
574     vkGetDisplayPlaneCapabilitiesKHR = loader.get_symbol("vkGetDisplayPlaneCapabilitiesKHR");
575     vkGetPhysicalDevicePresentRectanglesKHR = loader.get_symbol("vkGetPhysicalDevicePresentRectanglesKHR");
576     vkGetPhysicalDeviceDisplayProperties2KHR = loader.get_symbol("vkGetPhysicalDeviceDisplayProperties2KHR");
577     vkGetPhysicalDeviceDisplayPlaneProperties2KHR = loader.get_symbol("vkGetPhysicalDeviceDisplayPlaneProperties2KHR");
578     vkGetDisplayModeProperties2KHR = loader.get_symbol("vkGetDisplayModeProperties2KHR");
579     vkGetDisplayPlaneCapabilities2KHR = loader.get_symbol("vkGetDisplayPlaneCapabilities2KHR");
580     vkGetPhysicalDeviceSurfaceCapabilities2KHR = loader.get_symbol("vkGetPhysicalDeviceSurfaceCapabilities2KHR");
581     vkGetPhysicalDeviceSurfaceFormats2KHR = loader.get_symbol("vkGetPhysicalDeviceSurfaceFormats2KHR");
582 
583 #ifdef VK_USE_PLATFORM_ANDROID_KHR
584     vkCreateAndroidSurfaceKHR = loader.get_symbol("vkCreateAndroidSurfaceKHR");
585 #endif  // VK_USE_PLATFORM_ANDROID_KHR
586 #ifdef VK_USE_PLATFORM_DIRECTFB_EXT
587     vkCreateDirectFBSurfaceEXT = loader.get_symbol("vkCreateDirectFBSurfaceEXT");
588     vkGetPhysicalDeviceDirectFBPresentationSupportEXT = loader.get_symbol("vkGetPhysicalDeviceDirectFBPresentationSupportEXT");
589 #endif  // VK_USE_PLATFORM_DIRECTFB_EXT
590 #ifdef VK_USE_PLATFORM_FUCHSIA
591     vkCreateImagePipeSurfaceFUCHSIA = loader.get_symbol("vkCreateImagePipeSurfaceFUCHSIA");
592 #endif  // VK_USE_PLATFORM_FUCHSIA
593 #ifdef VK_USE_PLATFORM_GGP
594     vkCreateStreamDescriptorSurfaceGGP = loader.get_symbol("vkCreateStreamDescriptorSurfaceGGP");
595 #endif  // VK_USE_PLATFORM_GGP
596 #ifdef VK_USE_PLATFORM_IOS_MVK
597     vkCreateIOSSurfaceMVK = loader.get_symbol("vkCreateIOSSurfaceMVK");
598 #endif  // VK_USE_PLATFORM_IOS_MVK
599 #ifdef VK_USE_PLATFORM_MACOS_MVK
600     vkCreateMacOSSurfaceMVK = loader.get_symbol("vkCreateMacOSSurfaceMVK");
601 #endif  // VK_USE_PLATFORM_MACOS_MVK
602 #ifdef VK_USE_PLATFORM_METAL_EXT
603     vkCreateMetalSurfaceEXT = loader.get_symbol("vkCreateMetalSurfaceEXT");
604 #endif  // VK_USE_PLATFORM_METAL_EXT
605 #ifdef VK_USE_PLATFORM_SCREEN_QNX
606     vkCreateScreenSurfaceQNX = loader.get_symbol("vkCreateScreenSurfaceQNX");
607     vkGetPhysicalDeviceScreenPresentationSupportQNX = loader.get_symbol("vkGetPhysicalDeviceScreenPresentationSupportQNX");
608 #endif  // VK_USE_PLATFORM_SCREEN_QNX
609 #ifdef VK_USE_PLATFORM_WAYLAND_KHR
610     vkCreateWaylandSurfaceKHR = loader.get_symbol("vkCreateWaylandSurfaceKHR");
611     vkGetPhysicalDeviceWaylandPresentationSupportKHR = loader.get_symbol("vkGetPhysicalDeviceWaylandPresentationSupportKHR");
612 #endif  // VK_USE_PLATFORM_WAYLAND_KHR
613 #ifdef VK_USE_PLATFORM_XCB_KHR
614     vkCreateXcbSurfaceKHR = loader.get_symbol("vkCreateXcbSurfaceKHR");
615     vkGetPhysicalDeviceXcbPresentationSupportKHR = loader.get_symbol("vkGetPhysicalDeviceXcbPresentationSupportKHR");
616 #endif  // VK_USE_PLATFORM_XCB_KHR
617 #ifdef VK_USE_PLATFORM_XLIB_KHR
618     vkCreateXlibSurfaceKHR = loader.get_symbol("vkCreateXlibSurfaceKHR");
619     vkGetPhysicalDeviceXlibPresentationSupportKHR = loader.get_symbol("vkGetPhysicalDeviceXlibPresentationSupportKHR");
620 #endif  // VK_USE_PLATFORM_XLIB_KHR
621 #ifdef VK_USE_PLATFORM_WIN32_KHR
622     vkCreateWin32SurfaceKHR = loader.get_symbol("vkCreateWin32SurfaceKHR");
623     vkGetPhysicalDeviceWin32PresentationSupportKHR = loader.get_symbol("vkGetPhysicalDeviceWin32PresentationSupportKHR");
624 #endif  // VK_USE_PLATFORM_WIN32_KHR
625 
626     vkDestroyDevice = loader.get_symbol("vkDestroyDevice");
627     vkGetDeviceQueue = loader.get_symbol("vkGetDeviceQueue");
628 
629     // clang-format on
630 }
631 
DeviceFunctions(const VulkanFunctions & vulkan_functions,VkDevice device)632 DeviceFunctions::DeviceFunctions(const VulkanFunctions& vulkan_functions, VkDevice device) {
633     vkGetDeviceProcAddr = vulkan_functions.vkGetDeviceProcAddr;
634     vkDestroyDevice = load(device, "vkDestroyDevice");
635     vkGetDeviceQueue = load(device, "vkGetDeviceQueue");
636     vkCreateCommandPool = load(device, "vkCreateCommandPool");
637     vkAllocateCommandBuffers = load(device, "vkAllocateCommandBuffers");
638     vkDestroyCommandPool = load(device, "vkDestroyCommandPool");
639     vkCreateSwapchainKHR = load(device, "vkCreateSwapchainKHR");
640     vkDestroySwapchainKHR = load(device, "vkDestroySwapchainKHR");
641 }
642 
InstanceCreateInfo()643 InstanceCreateInfo::InstanceCreateInfo() {
644     instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
645     application_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
646 }
647 
get()648 VkInstanceCreateInfo* InstanceCreateInfo::get() noexcept {
649     if (fill_in_application_info) {
650         application_info.pApplicationName = app_name.c_str();
651         application_info.pEngineName = engine_name.c_str();
652         application_info.applicationVersion = app_version;
653         application_info.engineVersion = engine_version;
654         application_info.apiVersion = api_version;
655         instance_info.pApplicationInfo = &application_info;
656     }
657     instance_info.flags = flags;
658     instance_info.enabledLayerCount = static_cast<uint32_t>(enabled_layers.size());
659     instance_info.ppEnabledLayerNames = enabled_layers.data();
660     instance_info.enabledExtensionCount = static_cast<uint32_t>(enabled_extensions.size());
661     instance_info.ppEnabledExtensionNames = enabled_extensions.data();
662     return &instance_info;
663 }
set_api_version(uint32_t major,uint32_t minor,uint32_t patch)664 InstanceCreateInfo& InstanceCreateInfo::set_api_version(uint32_t major, uint32_t minor, uint32_t patch) {
665     this->api_version = VK_MAKE_API_VERSION(0, major, minor, patch);
666     return *this;
667 }
668 
DeviceQueueCreateInfo()669 DeviceQueueCreateInfo::DeviceQueueCreateInfo() { queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; }
670 
get()671 VkDeviceQueueCreateInfo DeviceQueueCreateInfo::get() noexcept {
672     queue_create_info.pQueuePriorities = priorities.data();
673     return queue_create_info;
674 }
675 
get()676 VkDeviceCreateInfo* DeviceCreateInfo::get() noexcept {
677     dev.enabledLayerCount = static_cast<uint32_t>(enabled_layers.size());
678     dev.ppEnabledLayerNames = enabled_layers.data();
679     dev.enabledExtensionCount = static_cast<uint32_t>(enabled_extensions.size());
680     dev.ppEnabledExtensionNames = enabled_extensions.data();
681     uint32_t index = 0;
682     for (auto& queue : queue_info_details) {
683         queue.queue_create_info.queueFamilyIndex = index++;
684         device_queue_infos.push_back(queue.get());
685     }
686 
687     dev.queueCreateInfoCount = static_cast<uint32_t>(device_queue_infos.size());
688     dev.pQueueCreateInfos = device_queue_infos.data();
689     return &dev;
690 }
691 
692 #if defined(WIN32)
narrow(const std::wstring & utf16)693 std::string narrow(const std::wstring& utf16) {
694     if (utf16.empty()) {
695         return {};
696     }
697     int size = WideCharToMultiByte(CP_UTF8, 0, utf16.data(), static_cast<int>(utf16.size()), nullptr, 0, nullptr, nullptr);
698     if (size <= 0) {
699         return {};
700     }
701     std::string utf8(size, '\0');
702     if (WideCharToMultiByte(CP_UTF8, 0, utf16.data(), static_cast<int>(utf16.size()), &utf8[0], size, nullptr, nullptr) != size) {
703         return {};
704     }
705     return utf8;
706 }
707 
widen(const std::string & utf8)708 std::wstring widen(const std::string& utf8) {
709     if (utf8.empty()) {
710         return {};
711     }
712     int size = MultiByteToWideChar(CP_UTF8, 0, utf8.data(), static_cast<int>(utf8.size()), nullptr, 0);
713     if (size <= 0) {
714         return {};
715     }
716     std::wstring utf16(size, L'\0');
717     if (MultiByteToWideChar(CP_UTF8, 0, utf8.data(), static_cast<int>(utf8.size()), &utf16[0], size) != size) {
718         return {};
719     }
720     return utf16;
721 }
722 #endif
723