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