vulkaninfo: fix device ext list having bad items
[platform/upstream/Vulkan-Tools.git] / vulkaninfo / vulkaninfo.h
1 /*
2  * Copyright (c) 2015-2020 The Khronos Group Inc.
3  * Copyright (c) 2015-2020 Valve Corporation
4  * Copyright (c) 2015-2020 LunarG, Inc.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
19  * Author: David Pinedo <david@lunarg.com>
20  * Author: Mark Lobodzinski <mark@lunarg.com>
21  * Author: Rene Lindsay <rene@lunarg.com>
22  * Author: Jeremy Kniager <jeremyk@lunarg.com>
23  * Author: Shannon McPherson <shannon@lunarg.com>
24  * Author: Bob Ellison <bob@lunarg.com>
25  * Author: Charles Giessen <charles@lunarg.com>
26  *
27  */
28
29 #include <algorithm>
30 #include <array>
31 #include <exception>
32 #include <iostream>
33 #include <fstream>
34 #include <memory>
35 #include <ostream>
36 #include <set>
37 #include <string>
38 #include <unordered_map>
39 #include <vector>
40 #include <utility>
41
42 #include <assert.h>
43 #include <stdint.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <cstring>
48
49 #ifdef __GNUC__
50 #ifndef _POSIX_C_SOURCE
51 #define _POSIX_C_SOURCE 200809L
52 #endif
53 #else
54 #define strndup(p, n) strdup(p)
55 #endif
56
57 #if defined(_WIN32)
58 #include <fcntl.h>
59 #include <io.h>
60 #include <windows.h>
61 #endif  // _WIN32
62
63 #if defined(__linux__) || defined(__APPLE__)
64 #include <dlfcn.h>
65 #endif
66
67 #if defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_XCB_KHR)
68 #include <X11/Xutil.h>
69 #endif
70
71 #if defined(VK_USE_PLATFORM_MACOS_MVK) || defined(VK_USE_PLATFORM_METAL_EXT)
72 #include "metal_view.h"
73 #endif
74
75 #include <vulkan/vulkan.h>
76
77 static const char *VkResultString(VkResult err);
78
79 // General error: Get file + line and a short message
80 struct FileLineException : std::runtime_error {
81     FileLineException(const std::string &arg, const char *file, int line) : runtime_error(arg) {
82         msg = std::string(file) + ":" + std::to_string(line) + ": " + arg;
83     }
84     ~FileLineException() throw() {}
85     const char *what() const throw() { return msg.c_str(); }
86
87   private:
88     std::string msg;
89 };
90 #define THROW_ERR(arg) throw FileLineException(arg, __FILE__, __LINE__);
91
92 // Vulkan function error: Get name of function, file, line, and the error code returned by the function
93 struct VulkanException : std::runtime_error {
94     VulkanException(const std::string &function, const char *file, int line, VkResult err) : runtime_error(function) {
95         msg = std::string(file) + ":" + std::to_string(line) + ":" + function + " failed with " + VkResultString(err);
96     }
97     ~VulkanException() throw() {}
98     const char *what() const throw() { return msg.c_str(); }
99
100   private:
101     std::string msg;
102 };
103 #define THROW_VK_ERR(func_name, err) throw VulkanException(func_name, __FILE__, __LINE__, err);
104
105 // global configuration
106 bool human_readable_output = true;
107 bool html_output = false;
108 bool json_output = false;
109 bool vkconfig_output = false;
110
111 #ifdef _WIN32
112
113 #define strdup _strdup
114
115 // Returns nonzero if the console is used only for this process. Will return
116 // zero if another process (such as cmd.exe) is also attached.
117 static int ConsoleIsExclusive(void) {
118     DWORD pids[2];
119     DWORD num_pids = GetConsoleProcessList(pids, ARRAYSIZE(pids));
120     return num_pids <= 1;
121 }
122
123 #define WAIT_FOR_CONSOLE_DESTROY                                            \
124     do {                                                                    \
125         if (ConsoleIsExclusive() && human_readable_output) Sleep(INFINITE); \
126     } while (0)
127 #else
128 #define WAIT_FOR_CONSOLE_DESTROY
129 #endif
130
131 #ifdef _WIN32
132
133 #define _CALL_PFN(pfn, ...) (pfn)
134 #define CALL_PFN(fncName) _CALL_PFN(User32Handles::pfn##fncName)
135
136 #define _CHECK_PFN(pfn, fncName)                                              \
137     do {                                                                      \
138         if (pfn == nullptr) {                                                 \
139             fprintf(stderr, "Failed to get %s function address!\n", fncName); \
140             WAIT_FOR_CONSOLE_DESTROY;                                         \
141             exit(1);                                                          \
142         }                                                                     \
143     } while (false)
144
145 #define _SET_PFN(dllHandle, pfnType, pfn, fncName)                           \
146     do {                                                                     \
147         pfn = reinterpret_cast<pfnType>(GetProcAddress(dllHandle, fncName)); \
148         _CHECK_PFN(pfn, fncName);                                            \
149     } while (false)
150
151 #define SET_PFN(dllHandle, fncName) _SET_PFN(User32Handles::dllHandle, PFN_##fncName, User32Handles::pfn##fncName, #fncName)
152
153 // User32 function declarations
154 typedef WINUSERAPI BOOL(WINAPI *PFN_AdjustWindowRect)(_Inout_ LPRECT, _In_ DWORD, _In_ BOOL);
155 typedef WINUSERAPI HWND(WINAPI *PFN_CreateWindowExA)(_In_ DWORD, _In_opt_ LPCSTR, _In_opt_ LPCSTR, _In_ DWORD, _In_ int, _In_ int,
156                                                      _In_ int, _In_ int, _In_opt_ HWND, _In_opt_ HMENU, _In_opt_ HINSTANCE,
157                                                      _In_opt_ LPVOID);
158 typedef WINUSERAPI LRESULT(WINAPI *PFN_DefWindowProcA)(_In_ HWND, _In_ UINT, _In_ WPARAM, _In_ LPARAM);
159 typedef WINUSERAPI BOOL(WINAPI *PFN_DestroyWindow)(_In_ HWND);
160 typedef WINUSERAPI HICON(WINAPI *PFN_LoadIconA)(_In_opt_ HINSTANCE, _In_ LPCSTR);
161 typedef WINUSERAPI ATOM(WINAPI *PFN_RegisterClassExA)(_In_ CONST WNDCLASSEXA *);
162
163 struct User32Handles {
164     // User32 function pointers
165     static PFN_AdjustWindowRect pfnAdjustWindowRect;
166     static PFN_CreateWindowExA pfnCreateWindowExA;
167     static PFN_DefWindowProcA pfnDefWindowProcA;
168     static PFN_DestroyWindow pfnDestroyWindow;
169     static PFN_LoadIconA pfnLoadIconA;
170     static PFN_RegisterClassExA pfnRegisterClassExA;
171
172     // User32 dll handle
173     static HMODULE user32DllHandle;
174 };
175
176 bool LoadUser32Dll() {
177     User32Handles::user32DllHandle = LoadLibraryExA("user32.dll", nullptr, 0);
178     if (User32Handles::user32DllHandle != NULL) {
179         SET_PFN(user32DllHandle, AdjustWindowRect);
180         SET_PFN(user32DllHandle, CreateWindowExA);
181         SET_PFN(user32DllHandle, DefWindowProcA);
182         SET_PFN(user32DllHandle, DestroyWindow);
183         SET_PFN(user32DllHandle, LoadIconA);
184         SET_PFN(user32DllHandle, RegisterClassExA);
185         return true;
186     }
187     return false;
188 }
189
190 void FreeUser32Dll() {
191     if (User32Handles::user32DllHandle != nullptr) {
192         FreeLibrary(User32Handles::user32DllHandle);
193         User32Handles::user32DllHandle = nullptr;
194     }
195 }
196 #endif  // _WIN32
197
198 const char *app_short_name = "vulkaninfo";
199
200 std::vector<const char *> get_c_str_array(std::vector<std::string> const &vec) {
201     std::vector<const char *> ret;
202     for (auto &str : vec) ret.push_back(str.c_str());
203     return ret;
204 }
205
206 static const char *VkDebugReportFlagsEXTString(const VkDebugReportFlagsEXT flags) {
207     switch (flags) {
208         case VK_DEBUG_REPORT_ERROR_BIT_EXT:
209             return "ERROR";
210         case VK_DEBUG_REPORT_WARNING_BIT_EXT:
211             return "WARNING";
212         case VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT:
213             return "PERF";
214         case VK_DEBUG_REPORT_INFORMATION_BIT_EXT:
215             return "INFO";
216         case VK_DEBUG_REPORT_DEBUG_BIT_EXT:
217             return "DEBUG";
218         default:
219             return "UNKNOWN";
220     }
221 }
222 static VKAPI_ATTR VkBool32 VKAPI_CALL DbgCallback(VkDebugReportFlagsEXT msgFlags, VkDebugReportObjectTypeEXT objType,
223                                                   uint64_t srcObject, size_t location, int32_t msgCode, const char *pLayerPrefix,
224                                                   const char *pMsg, void *pUserData) {
225     std::cerr << VkDebugReportFlagsEXTString(msgFlags) << ": [" << pLayerPrefix << "] Code " << msgCode << " : " << pMsg << "\n";
226
227     // True is reserved for layer developers, and MAY mean calls are not distributed down the layer chain after validation
228     // error. False SHOULD always be returned by apps:
229     return VK_FALSE;
230 }
231
232 // Helper for robustly executing the two-call pattern
233 template <typename T, typename F, typename... Ts>
234 auto GetVectorInit(const char *func_name, F &&f, T init, Ts &&... ts) -> std::vector<T> {
235     uint32_t count = 0;
236     std::vector<T> results;
237     VkResult err;
238     do {
239         err = f(ts..., &count, nullptr);
240         if (err) THROW_VK_ERR(func_name, err);
241         results.resize(count, init);
242         err = f(ts..., &count, results.data());
243         results.resize(count);
244     } while (err == VK_INCOMPLETE);
245     if (err) THROW_VK_ERR(func_name, err);
246     return results;
247 }
248
249 template <typename T, typename F, typename... Ts>
250 auto GetVector(const char *func_name, F &&f, Ts &&... ts) -> std::vector<T> {
251     return GetVectorInit(func_name, f, T(), ts...);
252 }
253
254 // ----------- Instance Setup ------- //
255 struct VkDll {
256     VkResult Initialize() {
257 #if defined(__linux__)
258         library = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL);
259         if (!library) library = dlopen("libvulkan.so.1", RTLD_NOW | RTLD_LOCAL);
260 #elif defined(_WIN32)
261         library = LoadLibrary(TEXT("vulkan-1.dll"));
262 #endif
263 #if !defined(__APPLE__)
264         if (library == 0) return VK_ERROR_INITIALIZATION_FAILED;
265 #endif
266         return VK_SUCCESS;
267     }
268     void Close() {
269 #if defined(__linux__)
270         dlclose(library);
271 #elif defined(_WIN32)
272         FreeLibrary(library);
273 #endif
274 #if !defined(__APPLE__)
275         library = 0;
276 #endif
277     }
278
279 #if defined(__APPLE__)
280 #define APPLE_FP(name) name
281 #else
282 #define APPLE_FP(nama) nullptr
283 #endif
284
285     // Function pointers, loaded from the dll
286     PFN_vkCreateInstance fp_vkCreateInstance = APPLE_FP(vkCreateInstance);
287     PFN_vkEnumerateInstanceExtensionProperties fp_vkEnumerateInstanceExtensionProperties =
288         APPLE_FP(vkEnumerateInstanceExtensionProperties);
289     PFN_vkEnumerateInstanceLayerProperties fp_vkEnumerateInstanceLayerProperties = APPLE_FP(vkEnumerateInstanceLayerProperties);
290     PFN_vkDestroyInstance fp_vkDestroyInstance = APPLE_FP(vkDestroyInstance);
291     PFN_vkEnumeratePhysicalDevices fp_vkEnumeratePhysicalDevices = APPLE_FP(vkEnumeratePhysicalDevices);
292     PFN_vkGetPhysicalDeviceFeatures fp_vkGetPhysicalDeviceFeatures = APPLE_FP(vkGetPhysicalDeviceFeatures);
293     PFN_vkGetPhysicalDeviceFormatProperties fp_vkGetPhysicalDeviceFormatProperties = APPLE_FP(vkGetPhysicalDeviceFormatProperties);
294     PFN_vkGetPhysicalDeviceImageFormatProperties fp_vkGetPhysicalDeviceImageFormatProperties =
295         APPLE_FP(vkGetPhysicalDeviceImageFormatProperties);
296     PFN_vkGetPhysicalDeviceProperties fp_vkGetPhysicalDeviceProperties = APPLE_FP(vkGetPhysicalDeviceProperties);
297     PFN_vkGetPhysicalDeviceQueueFamilyProperties fp_vkGetPhysicalDeviceQueueFamilyProperties =
298         APPLE_FP(vkGetPhysicalDeviceQueueFamilyProperties);
299     PFN_vkGetPhysicalDeviceMemoryProperties fp_vkGetPhysicalDeviceMemoryProperties = APPLE_FP(vkGetPhysicalDeviceMemoryProperties);
300     PFN_vkGetInstanceProcAddr fp_vkGetInstanceProcAddr = APPLE_FP(vkGetInstanceProcAddr);
301     PFN_vkGetDeviceProcAddr fp_vkGetDeviceProcAddr = APPLE_FP(vkGetDeviceProcAddr);
302     PFN_vkCreateDevice fp_vkCreateDevice = APPLE_FP(vkCreateDevice);
303     PFN_vkDestroyDevice fp_vkDestroyDevice = APPLE_FP(vkDestroyDevice);
304     PFN_vkEnumerateDeviceExtensionProperties fp_vkEnumerateDeviceExtensionProperties =
305         APPLE_FP(vkEnumerateDeviceExtensionProperties);
306     PFN_vkGetDeviceQueue fp_vkGetDeviceQueue = APPLE_FP(vkGetDeviceQueue);
307     PFN_vkCreateImage fp_vkCreateImage = APPLE_FP(vkCreateImage);
308     PFN_vkDestroyImage fp_vkDestroyImage = APPLE_FP(vkDestroyImage);
309     PFN_vkGetBufferMemoryRequirements fp_vkGetBufferMemoryRequirements = APPLE_FP(vkGetBufferMemoryRequirements);
310     PFN_vkGetImageMemoryRequirements fp_vkGetImageMemoryRequirements = APPLE_FP(vkGetImageMemoryRequirements);
311     PFN_vkGetImageSparseMemoryRequirements fp_vkGetImageSparseMemoryRequirements = APPLE_FP(vkGetImageSparseMemoryRequirements);
312     PFN_vkEnumerateInstanceVersion fp_vkEnumerateInstanceVersion = APPLE_FP(vkEnumerateInstanceVersion);
313     PFN_vkEnumeratePhysicalDeviceGroups fp_vkEnumeratePhysicalDeviceGroups = APPLE_FP(vkEnumeratePhysicalDeviceGroups);
314     PFN_vkGetPhysicalDeviceFeatures2 fp_vkGetPhysicalDeviceFeatures2 = APPLE_FP(vkGetPhysicalDeviceFeatures2);
315     PFN_vkGetPhysicalDeviceProperties2 fp_vkGetPhysicalDeviceProperties2 = APPLE_FP(vkGetPhysicalDeviceProperties2);
316     PFN_vkGetPhysicalDeviceFormatProperties2 fp_vkGetPhysicalDeviceFormatProperties2 =
317         APPLE_FP(vkGetPhysicalDeviceFormatProperties2);
318     PFN_vkGetPhysicalDeviceQueueFamilyProperties2 fp_vkGetPhysicalDeviceQueueFamilyProperties2 =
319         APPLE_FP(vkGetPhysicalDeviceQueueFamilyProperties2);
320     PFN_vkGetPhysicalDeviceMemoryProperties2 fp_vkGetPhysicalDeviceMemoryProperties2 =
321         APPLE_FP(vkGetPhysicalDeviceMemoryProperties2);
322     PFN_vkDestroySurfaceKHR fp_vkDestroySurfaceKHR = APPLE_FP(vkDestroySurfaceKHR);
323
324 #ifdef VK_USE_PLATFORM_XLIB_KHR
325     PFN_vkCreateXlibSurfaceKHR fp_vkCreateXlibSurfaceKHR = APPLE_FP(vkCreateXlibSurfaceKHR);
326 #endif  // VK_USE_PLATFORM_XLIB_KHR
327 #ifdef VK_USE_PLATFORM_XLIB_KHR
328     PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR fp_vkGetPhysicalDeviceXlibPresentationSupportKHR =
329         APPLE_FP(vkGetPhysicalDeviceXlibPresentationSupportKHR);
330 #endif  // VK_USE_PLATFORM_XLIB_KHR
331 #ifdef VK_USE_PLATFORM_XCB_KHR
332     PFN_vkCreateXcbSurfaceKHR fp_vkCreateXcbSurfaceKHR = APPLE_FP(vkCreateXcbSurfaceKHR);
333 #endif  // VK_USE_PLATFORM_XCB_KHR
334 #ifdef VK_USE_PLATFORM_XCB_KHR
335     PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR fp_vkGetPhysicalDeviceXcbPresentationSupportKHR =
336         APPLE_FP(vkGetPhysicalDeviceXcbPresentationSupportKHR);
337 #endif  // VK_USE_PLATFORM_XCB_KHR
338 #ifdef VK_USE_PLATFORM_WAYLAND_KHR
339     PFN_vkCreateWaylandSurfaceKHR fp_vkCreateWaylandSurfaceKHR = APPLE_FP(vkCreateWaylandSurfaceKHR);
340 #endif  // VK_USE_PLATFORM_WAYLAND_KHR
341 #ifdef VK_USE_PLATFORM_WAYLAND_KHR
342     PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR fp_vkGetPhysicalDeviceWaylandPresentationSupportKHR =
343         APPLE_FP(vkGetPhysicalDeviceWaylandPresentationSupportKHR);
344 #endif  // VK_USE_PLATFORM_WAYLAND_KHR
345 #ifdef VK_USE_PLATFORM_ANDROID_KHR
346     PFN_vkCreateAndroidSurfaceKHR fp_vkCreateAndroidSurfaceKHR = APPLE_FP(vkCreateAndroidSurfaceKHR);
347 #endif  // VK_USE_PLATFORM_ANDROID_KHR
348 #ifdef VK_USE_PLATFORM_WIN32_KHR
349     PFN_vkCreateWin32SurfaceKHR fp_vkCreateWin32SurfaceKHR = APPLE_FP(vkCreateWin32SurfaceKHR);
350 #endif  // VK_USE_PLATFORM_WIN32_KHR
351 #ifdef VK_USE_PLATFORM_WIN32_KHR
352     PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR fp_vkGetPhysicalDeviceWin32PresentationSupportKHR =
353         APPLE_FP(vkGetPhysicalDeviceWin32PresentationSupportKHR);
354 #endif  // VK_USE_PLATFORM_WIN32_KHR
355 #ifdef VK_USE_PLATFORM_MACOS_MVK
356     PFN_vkCreateMacOSSurfaceMVK fp_vkCreateMacOSSurfaceMVK = APPLE_FP(vkCreateMacOSSurfaceMVK);
357 #endif  // VK_USE_PLATFORM_MACOS_MVK
358 #ifdef VK_USE_PLATFORM_METAL_EXT
359     PFN_vkCreateMetalSurfaceEXT fp_vkCreateMetalSurfaceEXT = APPLE_FP(vkCreateMetalSurfaceEXT);
360 #endif  // VK_USE_PLATFORM_METAL_EXT
361     void InitializeDispatchPointers() {
362         Load(fp_vkCreateInstance, "vkCreateInstance");
363         Load(fp_vkDestroyInstance, "vkDestroyInstance");
364         Load(fp_vkEnumeratePhysicalDevices, "vkEnumeratePhysicalDevices");
365         Load(fp_vkGetPhysicalDeviceFeatures, "vkGetPhysicalDeviceFeatures");
366         Load(fp_vkGetPhysicalDeviceFormatProperties, "vkGetPhysicalDeviceFormatProperties");
367         Load(fp_vkGetPhysicalDeviceImageFormatProperties, "vkGetPhysicalDeviceImageFormatProperties");
368         Load(fp_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties");
369         Load(fp_vkGetPhysicalDeviceQueueFamilyProperties, "vkGetPhysicalDeviceQueueFamilyProperties");
370         Load(fp_vkGetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties");
371         Load(fp_vkGetInstanceProcAddr, "vkGetInstanceProcAddr");
372         Load(fp_vkGetDeviceProcAddr, "vkGetDeviceProcAddr");
373         Load(fp_vkCreateDevice, "vkCreateDevice");
374         Load(fp_vkDestroyDevice, "vkDestroyDevice");
375         Load(fp_vkEnumerateInstanceExtensionProperties, "vkEnumerateInstanceExtensionProperties");
376         Load(fp_vkEnumerateDeviceExtensionProperties, "vkEnumerateDeviceExtensionProperties");
377         Load(fp_vkEnumerateInstanceLayerProperties, "vkEnumerateInstanceLayerProperties");
378         Load(fp_vkGetDeviceQueue, "vkGetDeviceQueue");
379         Load(fp_vkCreateImage, "vkCreateImage");
380         Load(fp_vkDestroyImage, "vkDestroyImage");
381         Load(fp_vkGetBufferMemoryRequirements, "vkGetBufferMemoryRequirements");
382         Load(fp_vkGetImageMemoryRequirements, "vkGetImageMemoryRequirements");
383         Load(fp_vkGetImageSparseMemoryRequirements, "vkGetImageSparseMemoryRequirements");
384         Load(fp_vkEnumerateInstanceVersion, "vkEnumerateInstanceVersion");
385         Load(fp_vkEnumeratePhysicalDeviceGroups, "vkEnumeratePhysicalDeviceGroups");
386         Load(fp_vkGetPhysicalDeviceFeatures2, "vkGetPhysicalDeviceFeatures2");
387         Load(fp_vkGetPhysicalDeviceProperties2, "vkGetPhysicalDeviceProperties2");
388         Load(fp_vkGetPhysicalDeviceFormatProperties2, "vkGetPhysicalDeviceFormatProperties2");
389         Load(fp_vkGetPhysicalDeviceQueueFamilyProperties2, "vkGetPhysicalDeviceQueueFamilyProperties2");
390         Load(fp_vkGetPhysicalDeviceMemoryProperties2, "vkGetPhysicalDeviceMemoryProperties2");
391         Load(fp_vkDestroySurfaceKHR, "vkDestroySurfaceKHR");
392
393 #ifdef VK_USE_PLATFORM_XLIB_KHR
394         Load(fp_vkCreateXlibSurfaceKHR, "vkCreateXlibSurfaceKHR");
395 #endif  // VK_USE_PLATFORM_XLIB_KHR
396 #ifdef VK_USE_PLATFORM_XLIB_KHR
397         Load(fp_vkGetPhysicalDeviceXlibPresentationSupportKHR, "vkGetPhysicalDeviceXlibPresentationSupportKHR");
398 #endif  // VK_USE_PLATFORM_XLIB_KHR
399 #ifdef VK_USE_PLATFORM_XCB_KHR
400         Load(fp_vkCreateXcbSurfaceKHR, "vkCreateXcbSurfaceKHR");
401 #endif  // VK_USE_PLATFORM_XCB_KHR
402 #ifdef VK_USE_PLATFORM_XCB_KHR
403         Load(fp_vkGetPhysicalDeviceXcbPresentationSupportKHR, "vkGetPhysicalDeviceXcbPresentationSupportKHR");
404 #endif  // VK_USE_PLATFORM_XCB_KHR
405 #ifdef VK_USE_PLATFORM_WAYLAND_KHR
406         Load(fp_vkCreateWaylandSurfaceKHR, "vkCreateWaylandSurfaceKHR");
407 #endif  // VK_USE_PLATFORM_WAYLAND_KHR
408 #ifdef VK_USE_PLATFORM_WAYLAND_KHR
409         Load(fp_vkGetPhysicalDeviceWaylandPresentationSupportKHR, "vkGetPhysicalDeviceWaylandPresentationSupportKHR");
410 #endif  // VK_USE_PLATFORM_WAYLAND_KHR
411 #ifdef VK_USE_PLATFORM_ANDROID_KHR
412         Load(fp_vkCreateAndroidSurfaceKHR, "vkCreateAndroidSurfaceKHR");
413 #endif  // VK_USE_PLATFORM_ANDROID_KHR
414 #ifdef VK_USE_PLATFORM_WIN32_KHR
415         Load(fp_vkCreateWin32SurfaceKHR, "vkCreateWin32SurfaceKHR");
416 #endif  // VK_USE_PLATFORM_WIN32_KHR
417 #ifdef VK_USE_PLATFORM_WIN32_KHR
418         Load(fp_vkGetPhysicalDeviceWin32PresentationSupportKHR, "vkGetPhysicalDeviceWin32PresentationSupportKHR");
419 #endif  // VK_USE_PLATFORM_WIN32_KHR
420 #ifdef VK_USE_PLATFORM_MACOS_MVK
421         Load(fp_vkCreateMacOSSurfaceMVK, "vkCreateMacOSSurfaceMVK");
422 #endif  // VK_USE_PLATFORM_MACOS_MVK
423 #ifdef VK_USE_PLATFORM_METAL_EXT
424         Load(fp_vkCreateMetalSurfaceEXT, "vkCreateMetalSurfaceEXT");
425 #endif  // VK_USE_PLATFORM_METAL_EXT
426     }
427
428   private:
429     template <typename T>
430     void Load(T &func_dest, const char *func_name) {
431 #if defined(__linux__)
432         func_dest = reinterpret_cast<T>(dlsym(library, func_name));
433 #elif defined(_WIN32)
434         func_dest = reinterpret_cast<T>(GetProcAddress(library, func_name));
435 #endif
436     }
437 #if defined(__linux__)
438     void *library;
439 #elif defined(_WIN32)
440     HMODULE library;
441 #endif
442 };
443
444 struct ExtensionFunctions {
445     // Extension pointers, loaded after instance is created
446     PFN_vkGetPhysicalDeviceSurfaceSupportKHR vkGetPhysicalDeviceSurfaceSupportKHR;
447     PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR;
448     PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR;
449     PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR;
450     PFN_vkGetDeviceGroupPresentCapabilitiesKHR vkGetDeviceGroupPresentCapabilitiesKHR;
451     PFN_vkGetPhysicalDeviceSurfaceFormats2KHR vkGetPhysicalDeviceSurfaceFormats2KHR;
452     PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR;
453     PFN_vkGetPhysicalDeviceFormatProperties2KHR vkGetPhysicalDeviceFormatProperties2KHR;
454     PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR vkGetPhysicalDeviceQueueFamilyProperties2KHR;
455     PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR;
456     PFN_vkGetPhysicalDeviceMemoryProperties2KHR vkGetPhysicalDeviceMemoryProperties2KHR;
457     PFN_vkGetPhysicalDeviceSurfaceCapabilities2KHR vkGetPhysicalDeviceSurfaceCapabilities2KHR;
458     PFN_vkGetPhysicalDeviceSurfaceCapabilities2EXT vkGetPhysicalDeviceSurfaceCapabilities2EXT;
459     PFN_vkEnumeratePhysicalDeviceGroupsKHR vkEnumeratePhysicalDeviceGroupsKHR;
460     PFN_vkGetPhysicalDeviceToolPropertiesEXT vkGetPhysicalDeviceToolPropertiesEXT;
461
462     void LoadInstanceExtensionDispatchPointers(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr, VkInstance instance) {
463         this->instance = instance;
464         this->vkGetInstanceProcAddr = vkGetInstanceProcAddr;
465         Load(vkGetPhysicalDeviceSurfaceSupportKHR, "vkGetPhysicalDeviceSurfaceSupportKHR");
466         Load(vkGetPhysicalDeviceSurfaceCapabilitiesKHR, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR");
467         Load(vkGetPhysicalDeviceSurfaceFormatsKHR, "vkGetPhysicalDeviceSurfaceFormatsKHR");
468         Load(vkGetPhysicalDeviceSurfaceFormats2KHR, "vkGetPhysicalDeviceSurfaceFormats2KHR");
469         Load(vkGetPhysicalDeviceSurfacePresentModesKHR, "vkGetPhysicalDeviceSurfacePresentModesKHR");
470         Load(vkGetDeviceGroupPresentCapabilitiesKHR, "vkGetDeviceGroupPresentCapabilitiesKHR");
471         Load(vkGetPhysicalDeviceProperties2KHR, "vkGetPhysicalDeviceProperties2KHR");
472         Load(vkGetPhysicalDeviceFormatProperties2KHR, "vkGetPhysicalDeviceFormatProperties2KHR");
473         Load(vkGetPhysicalDeviceQueueFamilyProperties2KHR, "vkGetPhysicalDeviceQueueFamilyProperties2KHR");
474         Load(vkGetPhysicalDeviceFeatures2KHR, "vkGetPhysicalDeviceFeatures2KHR");
475         Load(vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR");
476         Load(vkGetPhysicalDeviceSurfaceCapabilities2KHR, "vkGetPhysicalDeviceSurfaceCapabilities2KHR");
477         Load(vkGetPhysicalDeviceSurfaceCapabilities2EXT, "vkGetPhysicalDeviceSurfaceCapabilities2EXT");
478         Load(vkEnumeratePhysicalDeviceGroupsKHR, "vkEnumeratePhysicalDeviceGroupsKHR");
479         Load(vkGetPhysicalDeviceToolPropertiesEXT, "vkGetPhysicalDeviceToolPropertiesEXT");
480     }
481
482   private:
483     PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
484     VkInstance instance;
485     template <typename T>
486     void Load(T &dest, const char *name) {
487         dest = reinterpret_cast<T>(vkGetInstanceProcAddr(instance, name));
488     }
489 };
490 struct VkStructureHeader {
491     VkStructureType sType;
492     VkStructureHeader *pNext;
493 };
494
495 struct pNextChainBuildingBlockInfo {
496     VkStructureType sType;
497     uint32_t mem_size;
498 };
499
500 void buildpNextChain(VkStructureHeader *first, const std::vector<pNextChainBuildingBlockInfo> &chain_info) {
501     VkStructureHeader *place = first;
502
503     for (uint32_t i = 0; i < chain_info.size(); i++) {
504         place->pNext = static_cast<VkStructureHeader *>(malloc(chain_info[i].mem_size));
505         if (!place->pNext) {
506             THROW_ERR("buildpNextChain's malloc failed to allocate");
507         }
508         std::memset(place->pNext, 0, chain_info[i].mem_size);
509         place = place->pNext;
510         place->sType = chain_info[i].sType;
511     }
512
513     place->pNext = nullptr;
514 }
515
516 void freepNextChain(VkStructureHeader *first) {
517     VkStructureHeader *place = first;
518     VkStructureHeader *next = nullptr;
519
520     while (place) {
521         next = place->pNext;
522         free(place);
523         place = next;
524     }
525 }
526
527 struct LayerExtensionList {
528     VkLayerProperties layer_properties{};
529     std::vector<VkExtensionProperties> extension_properties;
530 };
531
532 struct AppInstance;
533
534 struct SurfaceExtension {
535     std::string name;
536     void (*create_window)(AppInstance &) = nullptr;
537     VkSurfaceKHR (*create_surface)(AppInstance &) = nullptr;
538     void (*destroy_window)(AppInstance &) = nullptr;
539     VkSurfaceKHR surface = VK_NULL_HANDLE;
540     VkBool32 supports_present = 0;
541
542     bool operator==(const SurfaceExtension &other) {
543         return name == other.name && surface == other.surface && supports_present == other.supports_present;
544     }
545 };
546
547 struct VulkanVersion {
548     uint32_t major;
549     uint32_t minor;
550     uint32_t patch;
551 };
552
553 struct AppInstance {
554     VkDll dll;
555
556     VkInstance instance;
557     uint32_t instance_version;
558     VulkanVersion vk_version;
559
560     ExtensionFunctions ext_funcs;
561
562     std::vector<LayerExtensionList> global_layers;
563
564     std::vector<VkExtensionProperties> global_extensions;  // Instance Extensions
565
566     std::vector<std::string> inst_extensions;
567
568     std::vector<SurfaceExtension> surface_extensions;
569
570     int width = 256, height = 256;
571
572     VkSurfaceCapabilitiesKHR surface_capabilities;
573
574 #ifdef VK_USE_PLATFORM_WIN32_KHR
575     HINSTANCE h_instance;  // Windows Instance
576     HWND h_wnd;            // window handle
577 #endif
578 #ifdef VK_USE_PLATFORM_XCB_KHR
579     xcb_connection_t *xcb_connection;
580     xcb_screen_t *xcb_screen;
581     xcb_window_t xcb_window;
582 #endif
583 #ifdef VK_USE_PLATFORM_XLIB_KHR
584     Display *xlib_display;
585     Window xlib_window;
586 #endif
587 #ifdef VK_USE_PLATFORM_MACOS_MVK
588     void *macos_window;
589 #endif
590 #ifdef VK_USE_PLATFORM_METAL_EXT
591     void *metal_window;
592 #endif
593 #ifdef VK_USE_PLATFORM_WAYLAND_KHR
594     wl_display *wayland_display;
595     wl_surface *wayland_surface;
596 #endif
597 #ifdef VK_USE_PLATFORM_ANDROID_KHR  // TODO
598     ANativeWindow *window;
599 #endif
600     AppInstance() {
601         VkResult dllErr = dll.Initialize();
602         if (dllErr != VK_SUCCESS) {
603             THROW_ERR("Failed to initialize: Vulkan loader is not installed, not found, or failed to load.");
604         }
605         dll.InitializeDispatchPointers();
606
607         if (!dll.fp_vkEnumerateInstanceVersion) {
608             instance_version = VK_API_VERSION_1_0;
609         } else {
610             const VkResult err = dll.fp_vkEnumerateInstanceVersion(&instance_version);
611             if (err) THROW_VK_ERR("vkEnumerateInstanceVersion", err);
612         }
613
614         // fallback to baked header version if loader returns 0 for the patch version
615         uint32_t patch_version = VK_VERSION_PATCH(instance_version);
616         if (patch_version == 0) patch_version = VK_VERSION_PATCH(VK_HEADER_VERSION);
617         vk_version = {VK_VERSION_MAJOR(instance_version), VK_VERSION_MINOR(instance_version), patch_version};
618
619         AppGetInstanceExtensions();
620
621         const VkDebugReportCallbackCreateInfoEXT dbg_info = {VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT, nullptr,
622                                                              VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT,
623                                                              DbgCallback};
624
625         const VkApplicationInfo app_info = {
626             VK_STRUCTURE_TYPE_APPLICATION_INFO, nullptr, app_short_name, 1, nullptr, 0, instance_version};
627
628         AppCompileInstanceExtensionsToEnable();
629
630         std::vector<const char *> inst_exts;
631         for (auto &ext : inst_extensions) inst_exts.push_back(ext.c_str());
632
633         const VkInstanceCreateInfo inst_info = {VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,  &dbg_info,       0, &app_info, 0, nullptr,
634                                                 static_cast<uint32_t>(inst_exts.size()), inst_exts.data()};
635
636         VkResult err = dll.fp_vkCreateInstance(&inst_info, nullptr, &instance);
637         if (err == VK_ERROR_INCOMPATIBLE_DRIVER) {
638             std::cerr << "Cannot create Vulkan instance.\n";
639             std::cerr << "This problem is often caused by a faulty installation of the Vulkan driver or attempting to use a GPU "
640                          "that does not support Vulkan.\n";
641             THROW_VK_ERR("vkCreateInstance", err);
642         } else if (err) {
643             THROW_VK_ERR("vkCreateInstance", err);
644         }
645         ext_funcs.LoadInstanceExtensionDispatchPointers(dll.fp_vkGetInstanceProcAddr, instance);
646     }
647
648     ~AppInstance() {
649         if (dll.fp_vkDestroyInstance) dll.fp_vkDestroyInstance(instance, nullptr);
650         dll.Close();
651     }
652
653     AppInstance(const AppInstance &) = delete;
654     const AppInstance &operator=(const AppInstance &) = delete;
655
656     bool CheckExtensionEnabled(std::string extension_to_check) {
657         for (auto &extension : inst_extensions) {
658             if (extension_to_check == extension) {
659                 return true;
660             }
661         }
662         return false;
663     }
664
665     /* Gets a list of layer and instance extensions */
666     void AppGetInstanceExtensions() {
667         /* Scan layers */
668         auto global_layer_properties =
669             GetVector<VkLayerProperties>("vkEnumerateInstanceLayerProperties", dll.fp_vkEnumerateInstanceLayerProperties);
670         global_layers.resize(global_layer_properties.size());
671
672         for (size_t i = 0; i < global_layer_properties.size(); i++) {
673             global_layers[i].layer_properties = global_layer_properties[i];
674
675             global_layers[i].extension_properties = AppGetGlobalLayerExtensions(global_layer_properties[i].layerName);
676         }
677
678         // Collect global extensions
679         // Gets instance extensions, if no layer was specified in the first paramteter
680         global_extensions = AppGetGlobalLayerExtensions(nullptr);
681     }
682     void AppCompileInstanceExtensionsToEnable() {
683         // Get all supported Instance extensions (excl. layer-provided ones)
684         for (auto &ext : global_extensions) {
685             inst_extensions.push_back(ext.extensionName);
686         }
687     }
688
689     void AddSurfaceExtension(SurfaceExtension ext) { surface_extensions.push_back(ext); }
690
691     std::vector<VkExtensionProperties> AppGetGlobalLayerExtensions(char *layer_name) {
692         return GetVector<VkExtensionProperties>("vkEnumerateInstanceExtensionProperties",
693                                                 dll.fp_vkEnumerateInstanceExtensionProperties, layer_name);
694     }
695
696     std::vector<VkPhysicalDevice> FindPhysicalDevices() {
697         return GetVector<VkPhysicalDevice>("vkEnumerateInstanceExtensionProperties", dll.fp_vkEnumeratePhysicalDevices, instance);
698     }
699 };
700
701 // --------- Platform Specific Presentation Calls --------- //
702
703 //---------------------------Win32---------------------------
704 #ifdef VK_USE_PLATFORM_WIN32_KHR
705
706 // MS-Windows event handling function:
707 LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
708     return (CALL_PFN(DefWindowProcA)(hWnd, uMsg, wParam, lParam));
709 }
710
711 static void AppCreateWin32Window(AppInstance &inst) {
712     inst.h_instance = GetModuleHandle(nullptr);
713
714     WNDCLASSEX win_class;
715
716     // Initialize the window class structure:
717     win_class.cbSize = sizeof(WNDCLASSEX);
718     win_class.style = CS_HREDRAW | CS_VREDRAW;
719     win_class.lpfnWndProc = WndProc;
720     win_class.cbClsExtra = 0;
721     win_class.cbWndExtra = 0;
722     win_class.hInstance = inst.h_instance;
723     win_class.hIcon = CALL_PFN(LoadIconA)(nullptr, IDI_APPLICATION);
724     win_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
725     win_class.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
726     win_class.lpszMenuName = nullptr;
727     win_class.lpszClassName = app_short_name;
728     win_class.hInstance = inst.h_instance;
729     win_class.hIconSm = CALL_PFN(LoadIconA)(nullptr, IDI_WINLOGO);
730     // Register window class:
731     if (!CALL_PFN(RegisterClassExA)(&win_class)) {
732         // It didn't work, so try to give a useful error:
733         THROW_ERR("Failed to register the window class!");
734     }
735     // Create window with the registered class:
736     RECT wr = {0, 0, inst.width, inst.height};
737     CALL_PFN(AdjustWindowRect)(&wr, WS_OVERLAPPEDWINDOW, FALSE);
738     inst.h_wnd = CALL_PFN(CreateWindowExA)(0,
739                                            app_short_name,  // class name
740                                            app_short_name,  // app name
741                                            // WS_VISIBLE | WS_SYSMENU |
742                                            WS_OVERLAPPEDWINDOW,  // window style
743                                            100, 100,             // x/y coords
744                                            wr.right - wr.left,   // width
745                                            wr.bottom - wr.top,   // height
746                                            nullptr,              // handle to parent
747                                            nullptr,              // handle to menu
748                                            inst.h_instance,      // hInstance
749                                            nullptr);             // no extra parameters
750     if (!inst.h_wnd) {
751         // It didn't work, so try to give a useful error:
752         THROW_ERR("Failed to create a window!");
753     }
754 }
755
756 static VkSurfaceKHR AppCreateWin32Surface(AppInstance &inst) {
757     VkWin32SurfaceCreateInfoKHR createInfo;
758     createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
759     createInfo.pNext = nullptr;
760     createInfo.flags = 0;
761     createInfo.hinstance = inst.h_instance;
762     createInfo.hwnd = inst.h_wnd;
763
764     VkSurfaceKHR surface;
765     VkResult err = inst.dll.fp_vkCreateWin32SurfaceKHR(inst.instance, &createInfo, nullptr, &surface);
766     if (err) THROW_VK_ERR("vkCreateWin32SurfaceKHR", err);
767     return surface;
768 }
769
770 static void AppDestroyWin32Window(AppInstance &inst) { CALL_PFN(DestroyWindow)(inst.h_wnd); }
771 #endif  // VK_USE_PLATFORM_WIN32_KHR
772 //-----------------------------------------------------------
773
774 #if defined(VK_USE_PLATFORM_XCB_KHR) || defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_WIN32_KHR) ||      \
775     defined(VK_USE_PLATFORM_MACOS_MVK) || defined(VK_USE_PLATFORM_METAL_EXT) || defined(VK_USE_PLATFORM_WAYLAND_KHR) || \
776     defined(VK_USE_PLATFORM_ANDROID_KHR)
777 static void AppDestroySurface(AppInstance &inst, VkSurfaceKHR surface) {  // same for all platforms
778     inst.dll.fp_vkDestroySurfaceKHR(inst.instance, surface, nullptr);
779 }
780 #endif
781
782 //----------------------------XCB----------------------------
783
784 #ifdef VK_USE_PLATFORM_XCB_KHR
785 static void AppCreateXcbWindow(AppInstance &inst) {
786     //--Init Connection--
787     const xcb_setup_t *setup;
788     xcb_screen_iterator_t iter;
789     int scr;
790
791     // API guarantees non-null xcb_connection
792     inst.xcb_connection = xcb_connect(nullptr, &scr);
793     int conn_error = xcb_connection_has_error(inst.xcb_connection);
794     if (conn_error) {
795         fprintf(stderr, "XCB failed to connect to the X server due to error:%d.\n", conn_error);
796         fflush(stderr);
797         xcb_disconnect(inst.xcb_connection);
798         inst.xcb_connection = nullptr;
799         return;
800     }
801
802     setup = xcb_get_setup(inst.xcb_connection);
803     iter = xcb_setup_roots_iterator(setup);
804     while (scr-- > 0) {
805         xcb_screen_next(&iter);
806     }
807
808     inst.xcb_screen = iter.data;
809     //-------------------
810
811     inst.xcb_window = xcb_generate_id(inst.xcb_connection);
812     xcb_create_window(inst.xcb_connection, XCB_COPY_FROM_PARENT, inst.xcb_window, inst.xcb_screen->root, 0, 0, inst.width,
813                       inst.height, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, inst.xcb_screen->root_visual, 0, nullptr);
814
815     xcb_intern_atom_cookie_t cookie = xcb_intern_atom(inst.xcb_connection, 1, 12, "WM_PROTOCOLS");
816     xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(inst.xcb_connection, cookie, 0);
817     free(reply);
818 }
819
820 static VkSurfaceKHR AppCreateXcbSurface(AppInstance &inst) {
821     if (!inst.xcb_connection) {
822         THROW_ERR("AppCreateXcbSurface failed to establish connection");
823     }
824
825     VkXcbSurfaceCreateInfoKHR xcb_createInfo;
826     xcb_createInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
827     xcb_createInfo.pNext = nullptr;
828     xcb_createInfo.flags = 0;
829     xcb_createInfo.connection = inst.xcb_connection;
830     xcb_createInfo.window = inst.xcb_window;
831
832     VkSurfaceKHR surface;
833     VkResult err = inst.dll.fp_vkCreateXcbSurfaceKHR(inst.instance, &xcb_createInfo, nullptr, &surface);
834     if (err) THROW_VK_ERR("vkCreateXcbSurfaceKHR", err);
835     return surface;
836 }
837
838 static void AppDestroyXcbWindow(AppInstance &inst) {
839     if (!inst.xcb_connection) {
840         return;  // Nothing to destroy
841     }
842
843     xcb_destroy_window(inst.xcb_connection, inst.xcb_window);
844     xcb_disconnect(inst.xcb_connection);
845 }
846 #endif  // VK_USE_PLATFORM_XCB_KHR
847 //-----------------------------------------------------------
848
849 //----------------------------XLib---------------------------
850 #ifdef VK_USE_PLATFORM_XLIB_KHR
851 static void AppCreateXlibWindow(AppInstance &inst) {
852     long visualMask = VisualScreenMask;
853     int numberOfVisuals;
854
855     inst.xlib_display = XOpenDisplay(nullptr);
856     if (inst.xlib_display == nullptr) {
857         THROW_ERR("XLib failed to connect to the X server.\nExiting...");
858     }
859
860     XVisualInfo vInfoTemplate = {};
861     vInfoTemplate.screen = DefaultScreen(inst.xlib_display);
862     XVisualInfo *visualInfo = XGetVisualInfo(inst.xlib_display, visualMask, &vInfoTemplate, &numberOfVisuals);
863     inst.xlib_window = XCreateWindow(inst.xlib_display, RootWindow(inst.xlib_display, vInfoTemplate.screen), 0, 0, inst.width,
864                                      inst.height, 0, visualInfo->depth, InputOutput, visualInfo->visual, 0, nullptr);
865
866     XSync(inst.xlib_display, false);
867     XFree(visualInfo);
868 }
869
870 static VkSurfaceKHR AppCreateXlibSurface(AppInstance &inst) {
871     VkXlibSurfaceCreateInfoKHR createInfo;
872     createInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
873     createInfo.pNext = nullptr;
874     createInfo.flags = 0;
875     createInfo.dpy = inst.xlib_display;
876     createInfo.window = inst.xlib_window;
877
878     VkSurfaceKHR surface;
879     VkResult err = inst.dll.fp_vkCreateXlibSurfaceKHR(inst.instance, &createInfo, nullptr, &surface);
880     if (err) THROW_VK_ERR("vkCreateXlibSurfaceKHR", err);
881     return surface;
882 }
883
884 static void AppDestroyXlibWindow(AppInstance &inst) {
885     XDestroyWindow(inst.xlib_display, inst.xlib_window);
886     XCloseDisplay(inst.xlib_display);
887 }
888 #endif  // VK_USE_PLATFORM_XLIB_KHR
889 //-----------------------------------------------------------
890
891 //------------------------MACOS_MVK--------------------------
892 #ifdef VK_USE_PLATFORM_MACOS_MVK
893 static void AppCreateMacOSWindow(AppInstance &inst) {
894     inst.macos_window = CreateMetalView(inst.width, inst.height);
895     if (inst.macos_window == nullptr) {
896         THROW_ERR("Could not create a native Metal view.\nExiting...");
897     }
898 }
899
900 static VkSurfaceKHR AppCreateMacOSSurface(AppInstance &inst) {
901     VkMacOSSurfaceCreateInfoMVK createInfo;
902     createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
903     createInfo.pNext = nullptr;
904     createInfo.flags = 0;
905     createInfo.pView = inst.macos_window;
906
907     VkSurfaceKHR surface;
908     VkResult err = inst.dll.fp_vkCreateMacOSSurfaceMVK(inst.instance, &createInfo, nullptr, &surface);
909     if (err) THROW_VK_ERR("vkCreateMacOSSurfaceMVK", err);
910     return surface;
911 }
912
913 static void AppDestroyMacOSWindow(AppInstance &inst) { DestroyMetalView(inst.macos_window); }
914 #endif  // VK_USE_PLATFORM_MACOS_MVK
915 //-----------------------------------------------------------
916
917 //------------------------METAL_EXT--------------------------
918 #ifdef VK_USE_PLATFORM_METAL_EXT
919 static void AppCreateMetalWindow(AppInstance &inst) {
920     inst.metal_window = CreateMetalView(inst.width, inst.height);
921     if (inst.metal_window == nullptr) {
922         THROW_ERR("Could not create a native Metal view.\nExiting...");
923     }
924 }
925
926 static VkSurfaceKHR AppCreateMetalSurface(AppInstance &inst) {
927     VkMetalSurfaceCreateInfoEXT createInfo;
928     createInfo.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT;
929     createInfo.pNext = nullptr;
930     createInfo.flags = 0;
931     createInfo.pLayer = static_cast<CAMetalLayer *>(GetCAMetalLayerFromMetalView(inst.metal_window));
932
933     VkSurfaceKHR surface;
934     VkResult err = inst.dll.fp_vkCreateMetalSurfaceEXT(inst.instance, &createInfo, nullptr, &surface);
935     if (err) THROW_VK_ERR("vkCreateMetalSurfaceEXT", err);
936     return surface;
937 }
938
939 static void AppDestroyMetalWindow(AppInstance &inst) { DestroyMetalView(inst.metal_window); }
940 #endif  // VK_USE_PLATFORM_METAL_EXT
941 //-----------------------------------------------------------
942
943 //-------------------------WAYLAND---------------------------
944 #ifdef VK_USE_PLATFORM_WAYLAND_KHR
945 static void wayland_registry_global(void *data, struct wl_registry *registry, uint32_t id, const char *interface,
946                                     uint32_t version) {
947     AppInstance &inst = *static_cast<AppInstance *>(data);
948     if (strcmp(interface, "wl_compositor") == 0) {
949         struct wl_compositor *compositor = (struct wl_compositor *)wl_registry_bind(registry, id, &wl_compositor_interface, 1);
950         inst.wayland_surface = wl_compositor_create_surface(compositor);
951     }
952 }
953 static void wayland_registry_global_remove(void *data, struct wl_registry *registry, uint32_t id) {}
954 static const struct wl_registry_listener wayland_registry_listener = {wayland_registry_global, wayland_registry_global_remove};
955
956 static void AppCreateWaylandWindow(AppInstance &inst) {
957     inst.wayland_display = wl_display_connect(nullptr);
958     struct wl_registry *registry = wl_display_get_registry(inst.wayland_display);
959     wl_registry_add_listener(wl_display_get_registry(inst.wayland_display), &wayland_registry_listener, static_cast<void *>(&inst));
960     wl_display_roundtrip(inst.wayland_display);
961     wl_registry_destroy(registry);
962 }
963
964 static VkSurfaceKHR AppCreateWaylandSurface(AppInstance &inst) {
965     VkWaylandSurfaceCreateInfoKHR createInfo;
966     createInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
967     createInfo.pNext = nullptr;
968     createInfo.flags = 0;
969     createInfo.display = inst.wayland_display;
970     createInfo.surface = inst.wayland_surface;
971
972     VkSurfaceKHR surface;
973     VkResult err = inst.dll.fp_vkCreateWaylandSurfaceKHR(inst.instance, &createInfo, nullptr, &surface);
974     if (err) THROW_VK_ERR("vkCreateWaylandSurfaceKHR", err);
975     return surface;
976 }
977
978 static void AppDestroyWaylandWindow(AppInstance &inst) { wl_display_disconnect(inst.wayland_display); }
979 #endif  // VK_USE_PLATFORM_WAYLAND_KHR
980 //-----------------------------------------------------------
981
982 //-------------------------ANDROID---------------------------
983 #ifdef VK_USE_PLATFORM_ANDROID_KHR
984 static void AppCreateAndroidWindow(AppInstance &inst) {}
985 static VkSurfaceKHR AppCreateAndroidSurface(AppInstance &inst) {
986     VkAndroidSurfaceCreateInfoKHR createInfo;
987     createInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
988     createInfo.pNext = NULL;
989     createInfo.flags = 0;
990     createInfo.window = (struct ANativeWindow *)(inst.window);
991
992     err = inst.dll.fp_vkCreateAndroidSurfaceKHR(inst.inst, &createInfo, NULL, &inst.surface);
993     THROW_VK_ERR("vkCreateAndroidSurfaceKHR", err);
994 }
995 static VkSurfaceKHR AppDestroyAndroidSurface(AppInstance &inst) {}
996 #endif
997 //-----------------------------------------------------------
998
999 // ------------ Setup Windows ------------- //
1000
1001 void SetupWindowExtensions(AppInstance &inst) {
1002 #if defined(VK_USE_PLATFORM_XCB_KHR) || defined(VK_USE_PLATFORM_XLIB_KHR)
1003     bool has_display = true;
1004     const char *display_var = getenv("DISPLAY");
1005     if (display_var == nullptr || strlen(display_var) == 0) {
1006         has_display = false;
1007         std::cerr << "'DISPLAY' environment variable not set... skipping surface info\n";
1008     }
1009 #endif
1010
1011 #ifdef VK_USE_PLATFORM_WAYLAND_KHR
1012     wl_display *wayland_display = wl_display_connect(nullptr);
1013     bool has_wayland_display = false;
1014     if (wayland_display != nullptr) {
1015         wl_display_disconnect(wayland_display);
1016         has_wayland_display = true;
1017     }
1018 #endif
1019
1020 //--WIN32--
1021 #ifdef VK_USE_PLATFORM_WIN32_KHR
1022     SurfaceExtension surface_ext_win32;
1023     if (inst.CheckExtensionEnabled(VK_KHR_WIN32_SURFACE_EXTENSION_NAME)) {
1024         surface_ext_win32.name = VK_KHR_WIN32_SURFACE_EXTENSION_NAME;
1025         surface_ext_win32.create_window = AppCreateWin32Window;
1026         surface_ext_win32.create_surface = AppCreateWin32Surface;
1027         surface_ext_win32.destroy_window = AppDestroyWin32Window;
1028
1029         inst.AddSurfaceExtension(surface_ext_win32);
1030     }
1031 #endif
1032 //--XCB--
1033 #ifdef VK_USE_PLATFORM_XCB_KHR
1034     SurfaceExtension surface_ext_xcb;
1035     if (inst.CheckExtensionEnabled(VK_KHR_XCB_SURFACE_EXTENSION_NAME)) {
1036         surface_ext_xcb.name = VK_KHR_XCB_SURFACE_EXTENSION_NAME;
1037         surface_ext_xcb.create_window = AppCreateXcbWindow;
1038         surface_ext_xcb.create_surface = AppCreateXcbSurface;
1039         surface_ext_xcb.destroy_window = AppDestroyXcbWindow;
1040         if (has_display) {
1041             inst.AddSurfaceExtension(surface_ext_xcb);
1042         }
1043     }
1044 #endif
1045 //--XLIB--
1046 #ifdef VK_USE_PLATFORM_XLIB_KHR
1047     SurfaceExtension surface_ext_xlib;
1048     if (inst.CheckExtensionEnabled(VK_KHR_XLIB_SURFACE_EXTENSION_NAME)) {
1049         surface_ext_xlib.name = VK_KHR_XLIB_SURFACE_EXTENSION_NAME;
1050         surface_ext_xlib.create_window = AppCreateXlibWindow;
1051         surface_ext_xlib.create_surface = AppCreateXlibSurface;
1052         surface_ext_xlib.destroy_window = AppDestroyXlibWindow;
1053         if (has_display) {
1054             inst.AddSurfaceExtension(surface_ext_xlib);
1055         }
1056     }
1057 #endif
1058 //--MACOS--
1059 #ifdef VK_USE_PLATFORM_MACOS_MVK
1060     SurfaceExtension surface_ext_macos;
1061     if (inst.CheckExtensionEnabled(VK_MVK_MACOS_SURFACE_EXTENSION_NAME)) {
1062         surface_ext_macos.name = VK_MVK_MACOS_SURFACE_EXTENSION_NAME;
1063         surface_ext_macos.create_window = AppCreateMacOSWindow;
1064         surface_ext_macos.create_surface = AppCreateMacOSSurface;
1065         surface_ext_macos.destroy_window = AppDestroyMacOSWindow;
1066
1067         inst.AddSurfaceExtension(surface_ext_macos);
1068     }
1069 #endif
1070
1071 #ifdef VK_USE_PLATFORM_METAL_EXT
1072     SurfaceExtension surface_ext_metal;
1073     if (inst.CheckExtensionEnabled(VK_EXT_METAL_SURFACE_EXTENSION_NAME)) {
1074         surface_ext_metal.name = VK_EXT_METAL_SURFACE_EXTENSION_NAME;
1075         surface_ext_metal.create_window = AppCreateMetalWindow;
1076         surface_ext_metal.create_surface = AppCreateMetalSurface;
1077         surface_ext_metal.destroy_window = AppDestroyMetalWindow;
1078
1079         inst.AddSurfaceExtension(surface_ext_metal);
1080     }
1081 #endif
1082 //--WAYLAND--
1083 #ifdef VK_USE_PLATFORM_WAYLAND_KHR
1084     SurfaceExtension surface_ext_wayland;
1085     if (inst.CheckExtensionEnabled(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME)) {
1086         surface_ext_wayland.name = VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME;
1087         surface_ext_wayland.create_window = AppCreateWaylandWindow;
1088         surface_ext_wayland.create_surface = AppCreateWaylandSurface;
1089         surface_ext_wayland.destroy_window = AppDestroyWaylandWindow;
1090         if (has_wayland_display) {
1091             inst.AddSurfaceExtension(surface_ext_wayland);
1092         }
1093     }
1094 #endif
1095 //--ANDROID--
1096 #ifdef VK_USE_PLATFORM_ANDROID_KHR
1097     SurfaceExtension surface_ext_android;
1098     if (inst.CheckExtensionEnabled(VK_ANDROID_SURFACE_EXTENSION_NAME)) {
1099         surface_ext_android.name = VK_ANDROID_SURFACE_EXTENSION_NAME;
1100         surface_ext_android.create_window = AppCreateAndroidWindow;
1101         surface_ext_android.create_surface = AppCreateAndroidSurface;
1102         surface_ext_android.destroy_window = AppDestroyAndroidWindow;
1103
1104         inst.AddSurfaceExtension(surface_ext_android);
1105     }
1106 #endif
1107 }
1108
1109 // ---------- Surfaces -------------- //
1110
1111 class AppSurface {
1112   public:
1113     AppInstance &inst;
1114     VkPhysicalDevice phys_device;
1115     SurfaceExtension surface_extension;
1116
1117     std::vector<VkPresentModeKHR> surf_present_modes;
1118
1119     std::vector<VkSurfaceFormatKHR> surf_formats;
1120     std::vector<VkSurfaceFormat2KHR> surf_formats2;
1121
1122     VkSurfaceCapabilitiesKHR surface_capabilities{};
1123     VkSurfaceCapabilities2KHR surface_capabilities2_khr{};
1124     VkSurfaceCapabilities2EXT surface_capabilities2_ext{};
1125
1126     AppSurface(AppInstance &inst, VkPhysicalDevice phys_device, SurfaceExtension surface_extension,
1127                std::vector<pNextChainBuildingBlockInfo> &sur_extension_pNextChain)
1128         : inst(inst),
1129           phys_device(phys_device),
1130           surface_extension(surface_extension),
1131           surf_present_modes(GetVector<VkPresentModeKHR>("vkGetPhysicalDeviceSurfacePresentModesKHR",
1132                                                          inst.ext_funcs.vkGetPhysicalDeviceSurfacePresentModesKHR, phys_device,
1133                                                          surface_extension.surface)) {
1134         const VkPhysicalDeviceSurfaceInfo2KHR surface_info2 = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR, nullptr,
1135                                                                surface_extension.surface};
1136
1137         if (inst.CheckExtensionEnabled(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME)) {
1138             VkSurfaceFormat2KHR init;
1139             init.sType = VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR;
1140             init.pNext = nullptr;
1141             surf_formats2 = GetVectorInit<VkSurfaceFormat2KHR>("vkGetPhysicalDeviceSurfaceFormats2KHR",
1142                                                                inst.ext_funcs.vkGetPhysicalDeviceSurfaceFormats2KHR, init,
1143                                                                phys_device, &surface_info2);
1144         } else {
1145             surf_formats = GetVector<VkSurfaceFormatKHR>("vkGetPhysicalDeviceSurfaceFormatsKHR",
1146                                                          inst.ext_funcs.vkGetPhysicalDeviceSurfaceFormatsKHR, phys_device,
1147                                                          surface_extension.surface);
1148         }
1149
1150         if (inst.CheckExtensionEnabled(VK_KHR_SURFACE_EXTENSION_NAME)) {
1151             VkResult err = inst.ext_funcs.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(phys_device, surface_extension.surface,
1152                                                                                     &surface_capabilities);
1153             if (err) THROW_VK_ERR("vkGetPhysicalDeviceSurfaceCapabilitiesKHR", err);
1154         }
1155
1156         if (inst.CheckExtensionEnabled(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME)) {
1157             surface_capabilities2_khr.sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR;
1158             buildpNextChain((VkStructureHeader *)&surface_capabilities2_khr, sur_extension_pNextChain);
1159
1160             VkPhysicalDeviceSurfaceInfo2KHR surface_info;
1161             surface_info.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR;
1162             surface_info.pNext = nullptr;
1163             surface_info.surface = surface_extension.surface;
1164
1165             VkResult err =
1166                 inst.ext_funcs.vkGetPhysicalDeviceSurfaceCapabilities2KHR(phys_device, &surface_info, &surface_capabilities2_khr);
1167             if (err) THROW_VK_ERR("vkGetPhysicalDeviceSurfaceCapabilities2KHR", err);
1168         }
1169
1170         if (inst.CheckExtensionEnabled(VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME)) {
1171             surface_capabilities2_ext.sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_EXT;
1172             surface_capabilities2_ext.pNext = nullptr;
1173             VkResult err = inst.ext_funcs.vkGetPhysicalDeviceSurfaceCapabilities2EXT(phys_device, surface_extension.surface,
1174                                                                                      &surface_capabilities2_ext);
1175             if (err) THROW_VK_ERR("vkGetPhysicalDeviceSurfaceCapabilities2EXT", err);
1176         }
1177     }
1178
1179     ~AppSurface() {
1180         if (inst.CheckExtensionEnabled(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME)) {
1181             freepNextChain(static_cast<VkStructureHeader *>(surface_capabilities2_khr.pNext));
1182         }
1183     }
1184
1185     AppSurface(const AppSurface &) = delete;
1186     const AppSurface &operator=(const AppSurface &) = delete;
1187 };
1188
1189 // -------------------- Device Groups ------------------------//
1190
1191 std::vector<VkPhysicalDeviceGroupProperties> GetGroups(AppInstance &inst) {
1192     if (inst.CheckExtensionEnabled(VK_KHR_DEVICE_GROUP_CREATION_EXTENSION_NAME)) {
1193         return GetVector<VkPhysicalDeviceGroupProperties>("vkEnumeratePhysicalDeviceGroupsKHR",
1194                                                           inst.ext_funcs.vkEnumeratePhysicalDeviceGroupsKHR, inst.instance);
1195     }
1196     return {};
1197 }
1198
1199 std::vector<VkPhysicalDeviceProperties> GetGroupProps(AppInstance &inst, VkPhysicalDeviceGroupProperties group) {
1200     std::vector<VkPhysicalDeviceProperties> props(group.physicalDeviceCount);
1201
1202     for (uint32_t i = 0; i < group.physicalDeviceCount; ++i) {
1203         inst.dll.fp_vkGetPhysicalDeviceProperties(group.physicalDevices[i], &props[i]);
1204     }
1205
1206     return props;
1207 }
1208
1209 // The bool of the pair returns true if the extension VK_KHR_device_group is present
1210 std::pair<bool, VkDeviceGroupPresentCapabilitiesKHR> GetGroupCapabilities(AppInstance &inst,
1211                                                                           VkPhysicalDeviceGroupProperties group) {
1212     // Build create info for logical device made from all physical devices in this group.
1213     std::vector<std::string> extensions_list = {VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_KHR_DEVICE_GROUP_EXTENSION_NAME};
1214     VkDeviceGroupDeviceCreateInfoKHR dg_ci = {VK_STRUCTURE_TYPE_DEVICE_GROUP_DEVICE_CREATE_INFO_KHR, nullptr,
1215                                               group.physicalDeviceCount, group.physicalDevices};
1216
1217     float queue_priority = 1.0f;
1218
1219     auto ext_list = get_c_str_array(extensions_list);
1220
1221     VkDeviceQueueCreateInfo q_ci = {VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, nullptr, 0, 0, 1, &queue_priority};
1222     VkDeviceCreateInfo device_ci = {VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,   &dg_ci,         0, 1, &q_ci, 0, nullptr,
1223                                     static_cast<uint32_t>(ext_list.size()), ext_list.data()};
1224
1225     VkDevice logical_device = VK_NULL_HANDLE;
1226
1227     VkResult err = inst.dll.fp_vkCreateDevice(group.physicalDevices[0], &device_ci, nullptr, &logical_device);
1228     if (err != VK_SUCCESS && err != VK_ERROR_EXTENSION_NOT_PRESENT) THROW_VK_ERR("vkCreateDevice", err);
1229
1230     if (err == VK_ERROR_EXTENSION_NOT_PRESENT) {
1231         VkDeviceGroupPresentCapabilitiesKHR group_capabilities = {VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_CAPABILITIES_KHR, nullptr};
1232         inst.dll.fp_vkDestroyDevice(logical_device, nullptr);
1233         return std::pair<bool, VkDeviceGroupPresentCapabilitiesKHR>(false, group_capabilities);
1234     }
1235
1236     VkDeviceGroupPresentCapabilitiesKHR group_capabilities = {VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_CAPABILITIES_KHR, nullptr};
1237
1238     // If the KHR_device_group extension is present, write the capabilities of the logical device into a struct for later
1239     // output to user.
1240     err = inst.ext_funcs.vkGetDeviceGroupPresentCapabilitiesKHR(logical_device, &group_capabilities);
1241     if (err) THROW_VK_ERR("vkGetDeviceGroupPresentCapabilitiesKHR", err);
1242
1243     inst.dll.fp_vkDestroyDevice(logical_device, nullptr);
1244
1245     return std::pair<bool, VkDeviceGroupPresentCapabilitiesKHR>(true, group_capabilities);
1246 }
1247
1248 // -------------------- Device Setup ------------------- //
1249
1250 const VkFormat color_format = VK_FORMAT_R8G8B8A8_UNORM;
1251
1252 struct ImageTypeSupport {
1253     enum class Type { regular, sparse, transient } type = Type::regular;
1254     bool supported = false;
1255     uint32_t memoryTypeBits = 0;
1256
1257     bool Compatible(uint32_t memtype_bit) { return supported && (memoryTypeBits & memtype_bit); }
1258 };
1259
1260 struct ImageTypeFormatInfo {
1261     VkFormat format;
1262     std::vector<ImageTypeSupport> type_support;
1263 };
1264
1265 struct ImageTypeInfos {
1266     VkImageTiling tiling;
1267     std::vector<ImageTypeFormatInfo> formats;
1268 };
1269
1270 VkImageCreateInfo GetImageCreateInfo(VkFormat format, VkImageTiling tiling, VkImageCreateFlags flags, VkImageUsageFlags usages) {
1271     return {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
1272             nullptr,
1273             flags,
1274             VK_IMAGE_TYPE_2D,
1275             format,
1276             {8, 8, 1},
1277             1,
1278             1,
1279             VK_SAMPLE_COUNT_1_BIT,
1280             tiling,
1281             usages,
1282             VK_SHARING_MODE_EXCLUSIVE,
1283             0,
1284             nullptr,
1285             VK_IMAGE_LAYOUT_UNDEFINED};
1286 }
1287
1288 ImageTypeSupport FillImageTypeSupport(AppInstance &inst, VkPhysicalDevice phys_device, VkDevice device,
1289                                       ImageTypeSupport::Type img_type, VkImageCreateInfo image_ci) {
1290     VkImageFormatProperties img_props;
1291     VkResult res = inst.dll.fp_vkGetPhysicalDeviceImageFormatProperties(
1292         phys_device, image_ci.format, image_ci.imageType, image_ci.tiling, image_ci.usage, image_ci.flags, &img_props);
1293
1294     if (res == VK_SUCCESS) {
1295         ImageTypeSupport img_type_support{};
1296         img_type_support.type = img_type;
1297         img_type_support.supported = true;
1298
1299         VkImage dummy_img;
1300         res = inst.dll.fp_vkCreateImage(device, &image_ci, nullptr, &dummy_img);
1301         if (res) THROW_VK_ERR("vkCreateImage", res);
1302
1303         VkMemoryRequirements mem_req;
1304         inst.dll.fp_vkGetImageMemoryRequirements(device, dummy_img, &mem_req);
1305         img_type_support.memoryTypeBits = mem_req.memoryTypeBits;
1306
1307         inst.dll.fp_vkDestroyImage(device, dummy_img, nullptr);
1308         return img_type_support;
1309     } else if (res == VK_ERROR_FORMAT_NOT_SUPPORTED) {
1310         return {};  // default initialization has supported being false
1311     }
1312     THROW_VK_ERR("vkGetPhysicalDeviceImageFormatProperties", res);
1313     return {};
1314 }
1315
1316 struct pNextChainInfos {
1317     std::vector<pNextChainBuildingBlockInfo> phys_device_props2;
1318     std::vector<pNextChainBuildingBlockInfo> phys_device_mem_props2;
1319     std::vector<pNextChainBuildingBlockInfo> phys_device_features2;
1320     std::vector<pNextChainBuildingBlockInfo> surface_capabilities2;
1321     std::vector<pNextChainBuildingBlockInfo> format_properties2;
1322 };
1323
1324 struct FormatRange {
1325     // the Vulkan standard version that supports this format range, or 0 if non-standard
1326     uint32_t minimum_instance_version;
1327
1328     // The name of the extension that supports this format range, or NULL if the range
1329     // is only part of the standard
1330     const char *extension_name;
1331
1332     // The first and last supported formats within this range.
1333     VkFormat first_format;
1334     VkFormat last_format;
1335 };
1336
1337 struct AppGpu {
1338     AppInstance &inst;
1339     uint32_t id;
1340     VkPhysicalDevice phys_device;
1341     VulkanVersion api_version;
1342
1343     VkPhysicalDeviceProperties props;
1344     VkPhysicalDeviceProperties2KHR props2;
1345
1346     uint32_t queue_count;
1347     std::vector<VkQueueFamilyProperties> queue_props;
1348     std::vector<VkQueueFamilyProperties2KHR> queue_props2;
1349
1350     VkPhysicalDeviceMemoryProperties memory_props;
1351     VkPhysicalDeviceMemoryProperties2KHR memory_props2;
1352
1353     std::vector<ImageTypeInfos> memory_image_support_types;
1354
1355     VkPhysicalDeviceFeatures features;
1356     VkPhysicalDeviceFeatures2KHR features2;
1357     VkPhysicalDevice limits;
1358
1359     std::vector<VkExtensionProperties> device_extensions;
1360
1361     VkDevice dev;
1362     VkPhysicalDeviceFeatures enabled_features;
1363
1364     std::array<VkDeviceSize, VK_MAX_MEMORY_HEAPS> heapBudget;
1365     std::array<VkDeviceSize, VK_MAX_MEMORY_HEAPS> heapUsage;
1366
1367     std::vector<FormatRange> supported_format_ranges;
1368
1369     AppGpu(AppInstance &inst, uint32_t id, VkPhysicalDevice phys_device, pNextChainInfos chainInfos)
1370         : inst(inst), id(id), phys_device(phys_device) {
1371         inst.dll.fp_vkGetPhysicalDeviceProperties(phys_device, &props);
1372
1373         // needs to find the minimum of the instance and device version, and use that to print the device info
1374         uint32_t gpu_version = props.apiVersion < inst.instance_version ? props.apiVersion : inst.instance_version;
1375         api_version = {VK_VERSION_MAJOR(gpu_version), VK_VERSION_MINOR(gpu_version), VK_VERSION_PATCH(gpu_version)};
1376
1377         if (inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
1378             props2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR;
1379             buildpNextChain((VkStructureHeader *)&props2, chainInfos.phys_device_props2);
1380
1381             inst.ext_funcs.vkGetPhysicalDeviceProperties2KHR(phys_device, &props2);
1382         }
1383         /* get queue count */
1384         inst.dll.fp_vkGetPhysicalDeviceQueueFamilyProperties(phys_device, &queue_count, nullptr);
1385
1386         queue_props.resize(queue_count);
1387
1388         inst.dll.fp_vkGetPhysicalDeviceQueueFamilyProperties(phys_device, &queue_count, queue_props.data());
1389
1390         if (inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
1391             queue_props2.resize(queue_count);
1392
1393             for (size_t i = 0; i < queue_count; ++i) {
1394                 queue_props2[i].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2_KHR;
1395                 queue_props2[i].pNext = nullptr;
1396             }
1397
1398             inst.ext_funcs.vkGetPhysicalDeviceQueueFamilyProperties2KHR(phys_device, &queue_count, queue_props2.data());
1399         }
1400
1401         inst.dll.fp_vkGetPhysicalDeviceMemoryProperties(phys_device, &memory_props);
1402
1403         inst.dll.fp_vkGetPhysicalDeviceFeatures(phys_device, &features);
1404
1405         if (inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
1406             memory_props2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR;
1407             buildpNextChain((VkStructureHeader *)&memory_props2, chainInfos.phys_device_mem_props2);
1408
1409             inst.ext_funcs.vkGetPhysicalDeviceMemoryProperties2KHR(phys_device, &memory_props2);
1410
1411             features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR;
1412             buildpNextChain((VkStructureHeader *)&features2, chainInfos.phys_device_features2);
1413
1414             inst.ext_funcs.vkGetPhysicalDeviceFeatures2KHR(phys_device, &features2);
1415         }
1416
1417         device_extensions = AppGetPhysicalDeviceLayerExtensions(nullptr);
1418
1419         const float queue_priority = 1.0f;
1420         const VkDeviceQueueCreateInfo q_ci = {VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
1421                                               nullptr,
1422                                               0,
1423                                               0,  // just pick the first one and hope for the best
1424                                               1,
1425                                               &queue_priority};
1426         enabled_features = VkPhysicalDeviceFeatures{0};
1427         const VkDeviceCreateInfo device_ci = {
1428             VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, nullptr, 0, 1, &q_ci, 0, nullptr, 0, nullptr, &enabled_features};
1429
1430         VkResult err = inst.dll.fp_vkCreateDevice(phys_device, &device_ci, nullptr, &dev);
1431         if (err) THROW_VK_ERR("vkCreateDevice", err);
1432
1433         const std::vector<VkImageTiling> tilings = {VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_TILING_LINEAR};
1434         const std::vector<VkFormat> formats = {
1435             color_format,      VK_FORMAT_D16_UNORM,         VK_FORMAT_X8_D24_UNORM_PACK32, VK_FORMAT_D32_SFLOAT,
1436             VK_FORMAT_S8_UINT, VK_FORMAT_D16_UNORM_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT,   VK_FORMAT_D32_SFLOAT_S8_UINT};
1437
1438         for (VkImageTiling tiling : tilings) {
1439             ImageTypeInfos image_type_infos;
1440             image_type_infos.tiling = tiling;
1441
1442             for (VkFormat format : formats) {
1443                 ImageTypeFormatInfo image_type_format_info;
1444                 image_type_format_info.format = format;
1445
1446                 VkFormatProperties fmt_props;
1447                 inst.dll.fp_vkGetPhysicalDeviceFormatProperties(phys_device, format, &fmt_props);
1448                 if ((tiling == VK_IMAGE_TILING_OPTIMAL && fmt_props.optimalTilingFeatures == 0) ||
1449                     (tiling == VK_IMAGE_TILING_LINEAR && fmt_props.linearTilingFeatures == 0)) {
1450                     continue;
1451                 }
1452
1453                 VkImageCreateInfo image_ci_regular = GetImageCreateInfo(format, tiling, 0, 0);
1454                 VkImageCreateInfo image_ci_transient =
1455                     GetImageCreateInfo(format, tiling, VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT, 0);
1456                 VkImageCreateInfo image_ci_sparse = GetImageCreateInfo(format, tiling, 0, VK_IMAGE_CREATE_SPARSE_BINDING_BIT);
1457
1458                 if (tiling == VK_IMAGE_TILING_LINEAR) {
1459                     if (format == color_format) {
1460                         image_ci_regular.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
1461                         image_ci_transient.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
1462                     } else {
1463                         // linear tiling is only applicable to color image types
1464                         continue;
1465                     }
1466                 } else {
1467                     if (format == color_format) {
1468                         image_ci_regular.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
1469                         image_ci_transient.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
1470
1471                     } else {
1472                         image_ci_regular.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
1473                         image_ci_transient.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
1474                     }
1475                 }
1476
1477                 auto image_ts_regular =
1478                     FillImageTypeSupport(inst, phys_device, dev, ImageTypeSupport::Type::regular, image_ci_regular);
1479                 if (image_ts_regular.supported) {
1480                     image_type_format_info.type_support.push_back(image_ts_regular);
1481                 }
1482                 auto image_ts_transient =
1483                     FillImageTypeSupport(inst, phys_device, dev, ImageTypeSupport::Type::transient, image_ci_transient);
1484                 if (image_ts_transient.supported) {
1485                     image_type_format_info.type_support.push_back(image_ts_transient);
1486                 }
1487
1488                 if (enabled_features.sparseBinding) {
1489                     auto image_ts_sparse =
1490                         FillImageTypeSupport(inst, phys_device, dev, ImageTypeSupport::Type::sparse, image_ci_sparse);
1491                     if (image_ts_sparse.supported) {
1492                         image_type_format_info.type_support.push_back(image_ts_sparse);
1493                     }
1494                 }
1495                 image_type_infos.formats.push_back(image_type_format_info);
1496             }
1497             memory_image_support_types.push_back(image_type_infos);
1498         }
1499
1500         // Memory //
1501
1502         struct VkStructureHeader *structure = NULL;
1503         if (inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
1504             structure = (struct VkStructureHeader *)memory_props2.pNext;
1505
1506             while (structure) {
1507                 if (structure->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT &&
1508                     CheckPhysicalDeviceExtensionIncluded(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME)) {
1509                     VkPhysicalDeviceMemoryBudgetPropertiesEXT *mem_budget_props =
1510                         (VkPhysicalDeviceMemoryBudgetPropertiesEXT *)structure;
1511                     for (uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; i++) {
1512                         heapBudget[i] = mem_budget_props->heapBudget[i];
1513                         heapUsage[i] = mem_budget_props->heapUsage[i];
1514                     }
1515                 }
1516
1517                 structure = (struct VkStructureHeader *)structure->pNext;
1518             }
1519         }
1520         // TODO buffer - memory type compatibility
1521
1522         supported_format_ranges = {
1523             {
1524                 // Standard formats in Vulkan 1.0
1525                 VK_MAKE_VERSION(1, 0, 0), NULL,
1526                 static_cast<VkFormat>(0),   // first core VkFormat
1527                 static_cast<VkFormat>(184)  // last core VkFormat
1528             },
1529             {
1530                 // YCBCR extension, standard in Vulkan 1.1
1531                 VK_MAKE_VERSION(1, 1, 0),
1532                 VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME,
1533                 VK_FORMAT_G8B8G8R8_422_UNORM,
1534                 VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM,
1535             },
1536             {
1537                 // PVRTC extension, not standardized
1538                 0,
1539                 VK_IMG_FORMAT_PVRTC_EXTENSION_NAME,
1540                 VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG,
1541                 VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG,
1542             },
1543             {
1544                 // ASTC extension, not standardized
1545                 0,
1546                 VK_EXT_TEXTURE_COMPRESSION_ASTC_HDR_EXTENSION_NAME,
1547                 VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT,
1548                 VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT,
1549             },
1550         };
1551     }
1552     ~AppGpu() {
1553         inst.dll.fp_vkDestroyDevice(dev, nullptr);
1554
1555         if (inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
1556             freepNextChain(static_cast<VkStructureHeader *>(features2.pNext));
1557             freepNextChain(static_cast<VkStructureHeader *>(props2.pNext));
1558             freepNextChain(static_cast<VkStructureHeader *>(memory_props2.pNext));
1559         }
1560     }
1561
1562     AppGpu(const AppGpu &) = delete;
1563     const AppGpu &operator=(const AppGpu &) = delete;
1564
1565     bool CheckPhysicalDeviceExtensionIncluded(std::string extension_to_check) {
1566         for (auto &extension : device_extensions) {
1567             if (extension_to_check == std::string(extension.extensionName)) {
1568                 return true;
1569             }
1570         }
1571         return false;
1572     }
1573
1574     std::vector<VkExtensionProperties> AppGetPhysicalDeviceLayerExtensions(char *layer_name) {
1575         return GetVector<VkExtensionProperties>("vkEnumerateDeviceExtensionProperties",
1576                                                 inst.dll.fp_vkEnumerateDeviceExtensionProperties, phys_device, layer_name);
1577     }
1578
1579     // Helper function to determine whether a format range is currently supported.
1580     bool FormatRangeSupported(FormatRange &format_range) {
1581         // True if standard and supported by both this instance and this GPU
1582         if (format_range.minimum_instance_version > 0 && inst.instance_version >= format_range.minimum_instance_version &&
1583             props.apiVersion >= format_range.minimum_instance_version) {
1584             return true;
1585         }
1586
1587         // True if this extension is present
1588         if (format_range.extension_name != nullptr) {
1589             return inst.CheckExtensionEnabled(format_range.extension_name);
1590         }
1591
1592         // Otherwise, not supported.
1593         return false;
1594     }
1595
1596     VkPhysicalDeviceProperties GetDeviceProperties() {
1597         if (inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
1598             return props2.properties;
1599         } else {
1600             return props;
1601         }
1602     }
1603 };
1604 struct AppQueueFamilyProperties {
1605     VkQueueFamilyProperties props;
1606     uint32_t queue_index;
1607     bool is_present_platform_agnostic = true;
1608     VkBool32 platforms_support_present = VK_FALSE;
1609
1610     AppQueueFamilyProperties(AppGpu &gpu, uint32_t queue_index) : queue_index(queue_index) {
1611         if (gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
1612             props = gpu.queue_props2[queue_index].queueFamilyProperties;
1613         } else {
1614             props = gpu.queue_props[queue_index];
1615         }
1616
1617         for (auto &surface_ext : gpu.inst.surface_extensions) {
1618             VkResult err = gpu.inst.ext_funcs.vkGetPhysicalDeviceSurfaceSupportKHR(
1619                 gpu.phys_device, queue_index, surface_ext.surface, &surface_ext.supports_present);
1620             if (err) THROW_VK_ERR("vkGetPhysicalDeviceSurfaceSupportKHR", err);
1621
1622             const bool first = (surface_ext == gpu.inst.surface_extensions.at(0));
1623             if (!first && platforms_support_present != surface_ext.supports_present) {
1624                 is_present_platform_agnostic = false;
1625             }
1626             platforms_support_present = surface_ext.supports_present;
1627         }
1628     }
1629 };
1630
1631 std::vector<VkPhysicalDeviceToolPropertiesEXT> GetToolingInfo(AppGpu &gpu) {
1632     if (gpu.inst.ext_funcs.vkGetPhysicalDeviceToolPropertiesEXT == nullptr) return {};
1633     return GetVector<VkPhysicalDeviceToolPropertiesEXT>("vkGetPhysicalDeviceToolPropertiesEXT",
1634                                                         gpu.inst.ext_funcs.vkGetPhysicalDeviceToolPropertiesEXT, gpu.phys_device);
1635 }
1636
1637 // --------- Format Properties ----------//
1638
1639 struct PropFlags {
1640     uint32_t linear;
1641     uint32_t optimal;
1642     uint32_t buffer;
1643
1644     bool operator==(const PropFlags &other) const {
1645         return (linear == other.linear && optimal == other.optimal && buffer == other.buffer);
1646     }
1647 };
1648
1649 namespace std {
1650 template <>
1651 struct hash<PropFlags> {
1652     std::size_t operator()(const PropFlags &k) const {
1653         return ((std::hash<uint32_t>()(k.linear) ^ (std::hash<uint32_t>()(k.optimal) << 1)) >> 1) ^
1654                (std::hash<uint32_t>()(k.buffer) << 1);
1655     }
1656 };
1657 }  // namespace std
1658
1659 // Used to sort the formats into buckets by their properties.
1660 std::unordered_map<PropFlags, std::vector<VkFormat>> FormatPropMap(AppGpu &gpu) {
1661     std::unordered_map<PropFlags, std::vector<VkFormat>> map;
1662     for (auto fmtRange : gpu.supported_format_ranges) {
1663         for (int32_t fmt = fmtRange.first_format; fmt <= fmtRange.last_format; ++fmt) {
1664             VkFormatProperties props;
1665             gpu.inst.dll.fp_vkGetPhysicalDeviceFormatProperties(gpu.phys_device, static_cast<VkFormat>(fmt), &props);
1666
1667             PropFlags pf = {props.linearTilingFeatures, props.optimalTilingFeatures, props.bufferFeatures};
1668
1669             map[pf].push_back(static_cast<VkFormat>(fmt));
1670         }
1671     }
1672     return map;
1673 }
1674
1675 VkFormatProperties2 GetFormatProperties2(AppGpu &gpu, VkFormat format, pNextChainInfos &chainInfos) {
1676     VkFormatProperties2 props;
1677     props.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2;
1678     buildpNextChain((VkStructureHeader *)&props, chainInfos.format_properties2);
1679     gpu.inst.ext_funcs.vkGetPhysicalDeviceFormatProperties2KHR(gpu.phys_device, format, &props);
1680     return props;
1681 }