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