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