| /* |
| * Copyright (c) 2015-2019 The Khronos Group Inc. |
| * Copyright (c) 2015-2019 Valve Corporation |
| * Copyright (c) 2015-2019 LunarG, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| * Author: Courtney Goeltzenleuchter <courtney@LunarG.com> |
| * Author: David Pinedo <david@lunarg.com> |
| * Author: Mark Lobodzinski <mark@lunarg.com> |
| * Author: Rene Lindsay <rene@lunarg.com> |
| * Author: Jeremy Kniager <jeremyk@lunarg.com> |
| * Author: Shannon McPherson <shannon@lunarg.com> |
| * Author: Bob Ellison <bob@lunarg.com> |
| * Author: Charles Giessen <charles@lunarg.com> |
| * |
| */ |
| |
| #include <algorithm> |
| #include <array> |
| #include <iostream> |
| #include <fstream> |
| #include <memory> |
| #include <ostream> |
| #include <string> |
| #include <unordered_map> |
| #include <vector> |
| #include <utility> |
| |
| #include <assert.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <cstring> |
| |
| #ifdef __GNUC__ |
| #ifndef _POSIX_C_SOURCE |
| #define _POSIX_C_SOURCE 200809L |
| #endif |
| #else |
| #define strndup(p, n) strdup(p) |
| #endif |
| |
| #ifdef _WIN32 |
| #include <fcntl.h> |
| #include <io.h> |
| #endif // _WIN32 |
| |
| #if defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_XCB_KHR) |
| #include <X11/Xutil.h> |
| #endif |
| |
| #if defined(VK_USE_PLATFORM_MACOS_MVK) || defined(VK_USE_PLATFORM_METAL_EXT) |
| #include "metal_view.h" |
| #endif |
| |
| #include <vulkan/vulkan.h> |
| |
| #define ERR(err) std::cerr << __FILE__ << ":" << __LINE__ << ": failed with " << VkResultString(err) << "\n"; |
| |
| // global configuration |
| bool human_readable_output = true; |
| bool html_output = false; |
| bool json_output = false; |
| |
| #ifdef _WIN32 |
| |
| #define strdup _strdup |
| |
| // Returns nonzero if the console is used only for this process. Will return |
| // zero if another process (such as cmd.exe) is also attached. |
| static int ConsoleIsExclusive(void) { |
| DWORD pids[2]; |
| DWORD num_pids = GetConsoleProcessList(pids, ARRAYSIZE(pids)); |
| return num_pids <= 1; |
| } |
| |
| #define WAIT_FOR_CONSOLE_DESTROY \ |
| do { \ |
| if (ConsoleIsExclusive() && human_readable_output) Sleep(INFINITE); \ |
| } while (0) |
| #else |
| #define WAIT_FOR_CONSOLE_DESTROY |
| #endif |
| |
| #define ERR_EXIT(err) \ |
| do { \ |
| ERR(err); \ |
| fflush(stdout); \ |
| fflush(stderr); \ |
| WAIT_FOR_CONSOLE_DESTROY; \ |
| exit(-1); \ |
| } while (0) |
| |
| #ifdef _WIN32 |
| |
| #define _CALL_PFN(pfn, ...) (pfn) |
| #define CALL_PFN(fncName) _CALL_PFN(User32Handles::pfn##fncName) |
| |
| #define _CHECK_PFN(pfn, fncName) \ |
| do { \ |
| if (pfn == nullptr) { \ |
| fprintf(stderr, "Failed to get %s function address!\n", fncName); \ |
| WAIT_FOR_CONSOLE_DESTROY; \ |
| exit(1); \ |
| } \ |
| } while (false) |
| |
| #define _SET_PFN(dllHandle, pfnType, pfn, fncName) \ |
| do { \ |
| pfn = reinterpret_cast<pfnType>(GetProcAddress(dllHandle, fncName)); \ |
| _CHECK_PFN(pfn, fncName); \ |
| } while (false) |
| |
| #define SET_PFN(dllHandle, fncName) _SET_PFN(User32Handles::dllHandle, PFN_##fncName, User32Handles::pfn##fncName, #fncName) |
| |
| // User32 function declarations |
| typedef WINUSERAPI BOOL(WINAPI *PFN_AdjustWindowRect)(_Inout_ LPRECT, _In_ DWORD, _In_ BOOL); |
| typedef WINUSERAPI HWND(WINAPI *PFN_CreateWindowExA)(_In_ DWORD, _In_opt_ LPCSTR, _In_opt_ LPCSTR, _In_ DWORD, _In_ int, _In_ int, |
| _In_ int, _In_ int, _In_opt_ HWND, _In_opt_ HMENU, _In_opt_ HINSTANCE, |
| _In_opt_ LPVOID); |
| typedef WINUSERAPI LRESULT(WINAPI *PFN_DefWindowProcA)(_In_ HWND, _In_ UINT, _In_ WPARAM, _In_ LPARAM); |
| typedef WINUSERAPI BOOL(WINAPI *PFN_DestroyWindow)(_In_ HWND); |
| typedef WINUSERAPI HICON(WINAPI *PFN_LoadIconA)(_In_opt_ HINSTANCE, _In_ LPCSTR); |
| typedef WINUSERAPI ATOM(WINAPI *PFN_RegisterClassExA)(_In_ CONST WNDCLASSEXA *); |
| |
| struct User32Handles { |
| // User32 function pointers |
| static PFN_AdjustWindowRect pfnAdjustWindowRect; |
| static PFN_CreateWindowExA pfnCreateWindowExA; |
| static PFN_DefWindowProcA pfnDefWindowProcA; |
| static PFN_DestroyWindow pfnDestroyWindow; |
| static PFN_LoadIconA pfnLoadIconA; |
| static PFN_RegisterClassExA pfnRegisterClassExA; |
| |
| // User32 dll handle |
| static HMODULE user32DllHandle; |
| }; |
| |
| bool LoadUser32Dll() { |
| User32Handles::user32DllHandle = LoadLibraryExA("user32.dll", nullptr, 0); |
| if (User32Handles::user32DllHandle != NULL) { |
| SET_PFN(user32DllHandle, AdjustWindowRect); |
| SET_PFN(user32DllHandle, CreateWindowExA); |
| SET_PFN(user32DllHandle, DefWindowProcA); |
| SET_PFN(user32DllHandle, DestroyWindow); |
| SET_PFN(user32DllHandle, LoadIconA); |
| SET_PFN(user32DllHandle, RegisterClassExA); |
| return true; |
| } |
| return false; |
| } |
| |
| void FreeUser32Dll() { |
| if (User32Handles::user32DllHandle != nullptr) { |
| FreeLibrary(User32Handles::user32DllHandle); |
| User32Handles::user32DllHandle = nullptr; |
| } |
| } |
| #endif // _WIN32 |
| |
| static const char *VkResultString(VkResult err); |
| |
| const char *app_short_name = "vulkaninfo"; |
| |
| std::vector<const char *> get_c_str_array(std::vector<std::string> const &vec) { |
| std::vector<const char *> ret; |
| for (auto &str : vec) ret.push_back(str.c_str()); |
| return ret; |
| } |
| |
| static const char *VkDebugReportFlagsEXTString(const VkDebugReportFlagsEXT flags) { |
| switch (flags) { |
| case VK_DEBUG_REPORT_ERROR_BIT_EXT: |
| return "ERROR"; |
| case VK_DEBUG_REPORT_WARNING_BIT_EXT: |
| return "WARNING"; |
| case VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT: |
| return "PERF"; |
| case VK_DEBUG_REPORT_INFORMATION_BIT_EXT: |
| return "INFO"; |
| case VK_DEBUG_REPORT_DEBUG_BIT_EXT: |
| return "DEBUG"; |
| default: |
| return "UNKNOWN"; |
| } |
| } |
| static VKAPI_ATTR VkBool32 VKAPI_CALL DbgCallback(VkDebugReportFlagsEXT msgFlags, VkDebugReportObjectTypeEXT objType, |
| uint64_t srcObject, size_t location, int32_t msgCode, const char *pLayerPrefix, |
| const char *pMsg, void *pUserData) { |
| std::cerr << VkDebugReportFlagsEXTString(msgFlags) << ": [" << pLayerPrefix << "] Code " << msgCode << " : " << pMsg << "\n"; |
| |
| // True is reserved for layer developers, and MAY mean calls are not distributed down the layer chain after validation error. |
| // False SHOULD always be returned by apps: |
| return VK_FALSE; |
| } |
| |
| // ----------- Instance Setup ------- // |
| |
| struct VkStructureHeader { |
| VkStructureType sType; |
| VkStructureHeader *pNext; |
| }; |
| |
| struct pNextChainBuildingBlockInfo { |
| VkStructureType sType; |
| uint32_t mem_size; |
| }; |
| |
| void buildpNextChain(VkStructureHeader *first, const std::vector<pNextChainBuildingBlockInfo> &chain_info) { |
| VkStructureHeader *place = first; |
| |
| for (uint32_t i = 0; i < chain_info.size(); i++) { |
| place->pNext = static_cast<VkStructureHeader *>(malloc(chain_info[i].mem_size)); |
| if (!place->pNext) { |
| ERR_EXIT(VK_ERROR_OUT_OF_HOST_MEMORY); |
| } |
| std::memset(place->pNext, 0, chain_info[i].mem_size); |
| place = place->pNext; |
| place->sType = chain_info[i].sType; |
| } |
| |
| place->pNext = nullptr; |
| } |
| |
| void freepNextChain(VkStructureHeader *first) { |
| VkStructureHeader *place = first; |
| VkStructureHeader *next = nullptr; |
| |
| while (place) { |
| next = place->pNext; |
| free(place); |
| place = next; |
| } |
| } |
| |
| struct LayerExtensionList { |
| VkLayerProperties layer_properties{}; |
| std::vector<VkExtensionProperties> extension_properties; |
| }; |
| |
| struct AppInstance; |
| |
| struct SurfaceExtension { |
| std::string name; |
| void (*create_window)(AppInstance &) = nullptr; |
| VkSurfaceKHR (*create_surface)(AppInstance &) = nullptr; |
| void (*destroy_window)(AppInstance &) = nullptr; |
| VkSurfaceKHR surface = VK_NULL_HANDLE; |
| VkBool32 supports_present = 0; |
| |
| bool operator==(const SurfaceExtension &other) { |
| return name == other.name && surface == other.surface && supports_present == other.supports_present; |
| } |
| }; |
| |
| struct VulkanVersion { |
| uint32_t major; |
| uint32_t minor; |
| uint32_t patch; |
| }; |
| |
| struct AppInstance { |
| VkInstance instance; |
| uint32_t instance_version; |
| VulkanVersion vk_version; |
| |
| std::vector<LayerExtensionList> global_layers; |
| |
| std::vector<VkExtensionProperties> global_extensions; // Instance Extensions |
| |
| std::vector<std::string> inst_extensions; |
| |
| // Functions from vkGetInstanceProcAddress |
| PFN_vkGetPhysicalDeviceSurfaceSupportKHR vkGetPhysicalDeviceSurfaceSupportKHR; |
| PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR; |
| PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR; |
| PFN_vkGetPhysicalDeviceSurfaceFormats2KHR vkGetPhysicalDeviceSurfaceFormats2KHR; |
| PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR; |
| PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR; |
| PFN_vkGetPhysicalDeviceFormatProperties2KHR vkGetPhysicalDeviceFormatProperties2KHR; |
| PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR vkGetPhysicalDeviceQueueFamilyProperties2KHR; |
| PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR; |
| PFN_vkGetPhysicalDeviceMemoryProperties2KHR vkGetPhysicalDeviceMemoryProperties2KHR; |
| PFN_vkGetPhysicalDeviceSurfaceCapabilities2KHR vkGetPhysicalDeviceSurfaceCapabilities2KHR; |
| PFN_vkGetPhysicalDeviceSurfaceCapabilities2EXT vkGetPhysicalDeviceSurfaceCapabilities2EXT; |
| |
| std::vector<SurfaceExtension> surface_extensions; |
| |
| int width = 256, height = 256; |
| |
| VkSurfaceCapabilitiesKHR surface_capabilities; |
| |
| #ifdef VK_USE_PLATFORM_WIN32_KHR |
| HINSTANCE h_instance; // Windows Instance |
| HWND h_wnd; // window handle |
| #endif |
| #ifdef VK_USE_PLATFORM_XCB_KHR |
| xcb_connection_t *xcb_connection; |
| xcb_screen_t *xcb_screen; |
| xcb_window_t xcb_window; |
| #endif |
| #ifdef VK_USE_PLATFORM_XLIB_KHR |
| Display *xlib_display; |
| Window xlib_window; |
| #endif |
| #ifdef VK_USE_PLATFORM_MACOS_MVK |
| void *macos_window; |
| #endif |
| #ifdef VK_USE_PLATFORM_METAL_EXT |
| void *metal_window; |
| #endif |
| #ifdef VK_USE_PLATFORM_WAYLAND_KHR |
| wl_display *wayland_display; |
| wl_surface *wayland_surface; |
| #endif |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR // TODO |
| ANativeWindow *window; |
| #endif |
| AppInstance() { |
| PFN_vkEnumerateInstanceVersion enumerate_instance_version = |
| reinterpret_cast<PFN_vkEnumerateInstanceVersion>(vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceVersion")); |
| |
| if (!enumerate_instance_version) { |
| instance_version = VK_API_VERSION_1_0; |
| } else { |
| const VkResult err = enumerate_instance_version(&instance_version); |
| if (err) ERR_EXIT(err); |
| } |
| |
| vk_version = {VK_VERSION_MAJOR(instance_version), VK_VERSION_MINOR(instance_version), VK_VERSION_PATCH(VK_HEADER_VERSION)}; |
| |
| AppGetInstanceExtensions(); |
| |
| const VkDebugReportCallbackCreateInfoEXT dbg_info = {VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT, nullptr, |
| VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT, |
| DbgCallback}; |
| |
| const VkApplicationInfo app_info = { |
| VK_STRUCTURE_TYPE_APPLICATION_INFO, nullptr, app_short_name, 1, nullptr, 0, VK_API_VERSION_1_0}; |
| |
| AppCompileInstanceExtensionsToEnable(); |
| |
| std::vector<const char *> inst_exts; |
| for (auto &ext : inst_extensions) inst_exts.push_back(ext.c_str()); |
| |
| const VkInstanceCreateInfo inst_info = {VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, &dbg_info, 0, &app_info, 0, nullptr, |
| static_cast<uint32_t>(inst_exts.size()), inst_exts.data()}; |
| |
| VkResult err = vkCreateInstance(&inst_info, nullptr, &instance); |
| if (err == VK_ERROR_INCOMPATIBLE_DRIVER) { |
| std::cerr << "Cannot create Vulkan instance.\n"; |
| std::cerr << "This problem is often caused by a faulty installation of the Vulkan driver or attempting to use a GPU " |
| "that does not support Vulkan.\n"; |
| ERR_EXIT(err); |
| } else if (err) { |
| ERR_EXIT(err); |
| } |
| |
| AppLoadInstanceCommands(); |
| } |
| |
| ~AppInstance() { vkDestroyInstance(instance, nullptr); } |
| |
| AppInstance(const AppInstance &) = delete; |
| const AppInstance &operator=(const AppInstance &) = delete; |
| |
| bool CheckExtensionEnabled(std::string extension_to_check) { |
| for (auto &extension : inst_extensions) { |
| if (extension_to_check == extension) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /* Gets a list of layer and instance extensions */ |
| void AppGetInstanceExtensions() { |
| /* Scan layers */ |
| std::vector<VkLayerProperties> global_layer_properties; |
| |
| VkResult err; |
| uint32_t count = 0; |
| do { |
| err = vkEnumerateInstanceLayerProperties(&count, nullptr); |
| if (err) ERR_EXIT(err); |
| |
| global_layer_properties.resize(count); |
| |
| err = vkEnumerateInstanceLayerProperties(&count, global_layer_properties.data()); |
| } while (err == VK_INCOMPLETE); |
| if (err) ERR_EXIT(err); |
| |
| global_layers.resize(count); |
| assert(global_layer_properties.size() == global_layers.size()); |
| |
| for (size_t i = 0; i < global_layer_properties.size(); i++) { |
| global_layers[i].layer_properties = global_layer_properties[i]; |
| |
| global_layers[i].extension_properties = AppGetGlobalLayerExtensions(global_layer_properties[i].layerName); |
| } |
| |
| // Collect global extensions |
| // Gets instance extensions, if no layer was specified in the first paramteter |
| global_extensions = AppGetGlobalLayerExtensions(nullptr); |
| } |
| void AppCompileInstanceExtensionsToEnable() { |
| // Get all supported Instance extensions (excl. layer-provided ones) |
| for (auto &ext : global_extensions) { |
| inst_extensions.push_back(ext.extensionName); |
| } |
| } |
| void AppLoadInstanceCommands() { |
| #define LOAD_INSTANCE_VK_CMD(cmd) cmd = reinterpret_cast<PFN_##cmd>(vkGetInstanceProcAddr(instance, #cmd)) |
| |
| LOAD_INSTANCE_VK_CMD(vkGetPhysicalDeviceSurfaceSupportKHR); |
| LOAD_INSTANCE_VK_CMD(vkGetPhysicalDeviceSurfaceCapabilitiesKHR); |
| LOAD_INSTANCE_VK_CMD(vkGetPhysicalDeviceSurfaceFormatsKHR); |
| LOAD_INSTANCE_VK_CMD(vkGetPhysicalDeviceSurfaceFormats2KHR); |
| LOAD_INSTANCE_VK_CMD(vkGetPhysicalDeviceSurfacePresentModesKHR); |
| LOAD_INSTANCE_VK_CMD(vkGetPhysicalDeviceProperties2KHR); |
| LOAD_INSTANCE_VK_CMD(vkGetPhysicalDeviceFormatProperties2KHR); |
| LOAD_INSTANCE_VK_CMD(vkGetPhysicalDeviceQueueFamilyProperties2KHR); |
| LOAD_INSTANCE_VK_CMD(vkGetPhysicalDeviceFeatures2KHR); |
| LOAD_INSTANCE_VK_CMD(vkGetPhysicalDeviceMemoryProperties2KHR); |
| LOAD_INSTANCE_VK_CMD(vkGetPhysicalDeviceSurfaceCapabilities2KHR); |
| LOAD_INSTANCE_VK_CMD(vkGetPhysicalDeviceSurfaceCapabilities2EXT); |
| |
| #undef LOAD_INSTANCE_VK_CMD |
| } |
| |
| void AddSurfaceExtension(SurfaceExtension ext) { surface_extensions.push_back(ext); } |
| |
| static std::vector<VkExtensionProperties> AppGetGlobalLayerExtensions(char *layer_name) { |
| std::vector<VkExtensionProperties> ext_props; |
| VkResult err; |
| uint32_t ext_count = 0; |
| do { |
| // gets the extension count if the last parameter is nullptr |
| err = vkEnumerateInstanceExtensionProperties(layer_name, &ext_count, nullptr); |
| if (err) ERR_EXIT(err); |
| |
| ext_props.resize(ext_count); |
| // gets the extension properties if the last parameter is not nullptr |
| err = vkEnumerateInstanceExtensionProperties(layer_name, &ext_count, ext_props.data()); |
| } while (err == VK_INCOMPLETE); |
| if (err) ERR_EXIT(err); |
| return ext_props; |
| } |
| |
| std::vector<VkPhysicalDevice> FindPhysicalDevices() { |
| std::vector<VkPhysicalDevice> phys_devices; |
| |
| VkResult err; |
| uint32_t gpu_count = 0; |
| |
| /* repeat get until VK_INCOMPLETE goes away */ |
| do { |
| err = vkEnumeratePhysicalDevices(instance, &gpu_count, nullptr); |
| if (err) ERR_EXIT(err); |
| |
| phys_devices.resize(gpu_count); |
| err = vkEnumeratePhysicalDevices(instance, &gpu_count, phys_devices.data()); |
| if (err) ERR_EXIT(err); |
| phys_devices.resize(gpu_count); |
| } while (err == VK_INCOMPLETE); |
| |
| return phys_devices; |
| } |
| }; |
| |
| // --------- Platform Specific Presentation Calls --------- // |
| |
| //---------------------------Win32--------------------------- |
| #ifdef VK_USE_PLATFORM_WIN32_KHR |
| |
| // MS-Windows event handling function: |
| LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { |
| return (CALL_PFN(DefWindowProcA)(hWnd, uMsg, wParam, lParam)); |
| } |
| |
| static void AppCreateWin32Window(AppInstance &inst) { |
| inst.h_instance = GetModuleHandle(nullptr); |
| |
| WNDCLASSEX win_class; |
| |
| // Initialize the window class structure: |
| win_class.cbSize = sizeof(WNDCLASSEX); |
| win_class.style = CS_HREDRAW | CS_VREDRAW; |
| win_class.lpfnWndProc = WndProc; |
| win_class.cbClsExtra = 0; |
| win_class.cbWndExtra = 0; |
| win_class.hInstance = inst.h_instance; |
| win_class.hIcon = CALL_PFN(LoadIconA)(nullptr, IDI_APPLICATION); |
| win_class.hCursor = LoadCursor(nullptr, IDC_ARROW); |
| win_class.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); |
| win_class.lpszMenuName = nullptr; |
| win_class.lpszClassName = app_short_name; |
| win_class.hInstance = inst.h_instance; |
| win_class.hIconSm = CALL_PFN(LoadIconA)(nullptr, IDI_WINLOGO); |
| // Register window class: |
| if (!CALL_PFN(RegisterClassExA)(&win_class)) { |
| // It didn't work, so try to give a useful error: |
| fprintf(stderr, "Failed to register the window class!\n"); |
| exit(1); |
| } |
| // Create window with the registered class: |
| RECT wr = {0, 0, inst.width, inst.height}; |
| CALL_PFN(AdjustWindowRect)(&wr, WS_OVERLAPPEDWINDOW, FALSE); |
| inst.h_wnd = CALL_PFN(CreateWindowExA)(0, |
| app_short_name, // class name |
| app_short_name, // app name |
| // WS_VISIBLE | WS_SYSMENU | |
| WS_OVERLAPPEDWINDOW, // window style |
| 100, 100, // x/y coords |
| wr.right - wr.left, // width |
| wr.bottom - wr.top, // height |
| nullptr, // handle to parent |
| nullptr, // handle to menu |
| inst.h_instance, // hInstance |
| nullptr); // no extra parameters |
| if (!inst.h_wnd) { |
| // It didn't work, so try to give a useful error: |
| fprintf(stderr, "Failed to create a window!\n"); |
| exit(1); |
| } |
| } |
| |
| static VkSurfaceKHR AppCreateWin32Surface(AppInstance &inst) { |
| VkWin32SurfaceCreateInfoKHR createInfo; |
| createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; |
| createInfo.pNext = nullptr; |
| createInfo.flags = 0; |
| createInfo.hinstance = inst.h_instance; |
| createInfo.hwnd = inst.h_wnd; |
| |
| VkSurfaceKHR surface; |
| VkResult err = vkCreateWin32SurfaceKHR(inst.instance, &createInfo, nullptr, &surface); |
| if (err) ERR_EXIT(err); |
| return surface; |
| } |
| |
| static void AppDestroyWin32Window(AppInstance &inst) { CALL_PFN(DestroyWindow)(inst.h_wnd); } |
| #endif // VK_USE_PLATFORM_WIN32_KHR |
| //----------------------------------------------------------- |
| |
| #if defined(VK_USE_PLATFORM_XCB_KHR) || defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_WIN32_KHR) || \ |
| defined(VK_USE_PLATFORM_MACOS_MVK) || defined(VK_USE_PLATFORM_METAL_EXT) || defined(VK_USE_PLATFORM_WAYLAND_KHR) || \ |
| defined(VK_USE_PLATFORM_ANDROID_KHR) |
| static void AppDestroySurface(AppInstance &inst, VkSurfaceKHR surface) { // same for all platforms |
| vkDestroySurfaceKHR(inst.instance, surface, nullptr); |
| } |
| #endif |
| |
| //----------------------------XCB---------------------------- |
| |
| #ifdef VK_USE_PLATFORM_XCB_KHR |
| static void AppCreateXcbWindow(AppInstance &inst) { |
| //--Init Connection-- |
| const xcb_setup_t *setup; |
| xcb_screen_iterator_t iter; |
| int scr; |
| |
| // API guarantees non-null xcb_connection |
| inst.xcb_connection = xcb_connect(nullptr, &scr); |
| int conn_error = xcb_connection_has_error(inst.xcb_connection); |
| if (conn_error) { |
| fprintf(stderr, "XCB failed to connect to the X server due to error:%d.\n", conn_error); |
| fflush(stderr); |
| xcb_disconnect(inst.xcb_connection); |
| inst.xcb_connection = nullptr; |
| return; |
| } |
| |
| setup = xcb_get_setup(inst.xcb_connection); |
| iter = xcb_setup_roots_iterator(setup); |
| while (scr-- > 0) { |
| xcb_screen_next(&iter); |
| } |
| |
| inst.xcb_screen = iter.data; |
| //------------------- |
| |
| inst.xcb_window = xcb_generate_id(inst.xcb_connection); |
| xcb_create_window(inst.xcb_connection, XCB_COPY_FROM_PARENT, inst.xcb_window, inst.xcb_screen->root, 0, 0, inst.width, |
| inst.height, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, inst.xcb_screen->root_visual, 0, nullptr); |
| |
| xcb_intern_atom_cookie_t cookie = xcb_intern_atom(inst.xcb_connection, 1, 12, "WM_PROTOCOLS"); |
| xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(inst.xcb_connection, cookie, 0); |
| free(reply); |
| } |
| |
| static VkSurfaceKHR AppCreateXcbSurface(AppInstance &inst) { |
| if (!inst.xcb_connection) { |
| ERR_EXIT(VK_ERROR_INITIALIZATION_FAILED); |
| } |
| |
| VkXcbSurfaceCreateInfoKHR xcb_createInfo; |
| xcb_createInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; |
| xcb_createInfo.pNext = nullptr; |
| xcb_createInfo.flags = 0; |
| xcb_createInfo.connection = inst.xcb_connection; |
| xcb_createInfo.window = inst.xcb_window; |
| |
| VkSurfaceKHR surface; |
| VkResult err = vkCreateXcbSurfaceKHR(inst.instance, &xcb_createInfo, nullptr, &surface); |
| if (err) ERR_EXIT(err); |
| return surface; |
| } |
| |
| static void AppDestroyXcbWindow(AppInstance &inst) { |
| if (!inst.xcb_connection) { |
| return; // Nothing to destroy |
| } |
| |
| xcb_destroy_window(inst.xcb_connection, inst.xcb_window); |
| xcb_disconnect(inst.xcb_connection); |
| } |
| #endif // VK_USE_PLATFORM_XCB_KHR |
| //----------------------------------------------------------- |
| |
| //----------------------------XLib--------------------------- |
| #ifdef VK_USE_PLATFORM_XLIB_KHR |
| static void AppCreateXlibWindow(AppInstance &inst) { |
| long visualMask = VisualScreenMask; |
| int numberOfVisuals; |
| |
| inst.xlib_display = XOpenDisplay(nullptr); |
| if (inst.xlib_display == nullptr) { |
| fprintf(stderr, "XLib failed to connect to the X server.\nExiting ...\n"); |
| exit(1); |
| } |
| |
| XVisualInfo vInfoTemplate = {}; |
| vInfoTemplate.screen = DefaultScreen(inst.xlib_display); |
| XVisualInfo *visualInfo = XGetVisualInfo(inst.xlib_display, visualMask, &vInfoTemplate, &numberOfVisuals); |
| inst.xlib_window = XCreateWindow(inst.xlib_display, RootWindow(inst.xlib_display, vInfoTemplate.screen), 0, 0, inst.width, |
| inst.height, 0, visualInfo->depth, InputOutput, visualInfo->visual, 0, nullptr); |
| |
| XSync(inst.xlib_display, false); |
| XFree(visualInfo); |
| } |
| |
| static VkSurfaceKHR AppCreateXlibSurface(AppInstance &inst) { |
| VkXlibSurfaceCreateInfoKHR createInfo; |
| createInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; |
| createInfo.pNext = nullptr; |
| createInfo.flags = 0; |
| createInfo.dpy = inst.xlib_display; |
| createInfo.window = inst.xlib_window; |
| |
| VkSurfaceKHR surface; |
| VkResult err = vkCreateXlibSurfaceKHR(inst.instance, &createInfo, nullptr, &surface); |
| if (err) ERR_EXIT(err); |
| return surface; |
| } |
| |
| static void AppDestroyXlibWindow(AppInstance &inst) { |
| XDestroyWindow(inst.xlib_display, inst.xlib_window); |
| XCloseDisplay(inst.xlib_display); |
| } |
| #endif // VK_USE_PLATFORM_XLIB_KHR |
| //----------------------------------------------------------- |
| |
| //------------------------MACOS_MVK-------------------------- |
| #ifdef VK_USE_PLATFORM_MACOS_MVK |
| static void AppCreateMacOSWindow(AppInstance &inst) { |
| inst.macos_window = CreateMetalView(inst.width, inst.height); |
| if (inst.macos_window == nullptr) { |
| fprintf(stderr, "Could not create a native Metal view.\nExiting...\n"); |
| exit(1); |
| } |
| } |
| |
| static VkSurfaceKHR AppCreateMacOSSurface(AppInstance &inst) { |
| VkMacOSSurfaceCreateInfoMVK createInfo; |
| createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; |
| createInfo.pNext = nullptr; |
| createInfo.flags = 0; |
| createInfo.pView = inst.macos_window; |
| |
| VkSurfaceKHR surface; |
| VkResult err = vkCreateMacOSSurfaceMVK(inst.instance, &createInfo, nullptr, &surface); |
| if (err) ERR_EXIT(err); |
| return surface; |
| } |
| |
| static void AppDestroyMacOSWindow(AppInstance &inst) { DestroyMetalView(inst.macos_window); } |
| #endif // VK_USE_PLATFORM_MACOS_MVK |
| //----------------------------------------------------------- |
| |
| //------------------------METAL_EXT-------------------------- |
| #ifdef VK_USE_PLATFORM_METAL_EXT |
| static void AppCreateMetalWindow(AppInstance &inst) { |
| inst.metal_window = CreateMetalView(inst.width, inst.height); |
| if (inst.metal_window == nullptr) { |
| fprintf(stderr, "Could not create a native Metal view.\nExiting...\n"); |
| exit(1); |
| } |
| } |
| |
| static VkSurfaceKHR AppCreateMetalSurface(AppInstance &inst) { |
| VkMetalSurfaceCreateInfoEXT createInfo; |
| createInfo.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT; |
| createInfo.pNext = nullptr; |
| createInfo.flags = 0; |
| createInfo.pLayer = static_cast<CAMetalLayer *>(GetCAMetalLayerFromMetalView(inst.metal_window)); |
| |
| VkSurfaceKHR surface; |
| VkResult err = vkCreateMetalSurfaceEXT(inst.instance, &createInfo, nullptr, &surface); |
| if (err) ERR_EXIT(err); |
| return surface; |
| } |
| |
| static void AppDestroyMetalWindow(AppInstance &inst) { DestroyMetalView(inst.metal_window); } |
| #endif // VK_USE_PLATFORM_METAL_EXT |
| //----------------------------------------------------------- |
| |
| //-------------------------WAYLAND--------------------------- |
| #ifdef VK_USE_PLATFORM_WAYLAND_KHR |
| static void wayland_registry_global(void *data, struct wl_registry *registry, uint32_t id, const char *interface, |
| uint32_t version) { |
| AppInstance &inst = *static_cast<AppInstance *>(data); |
| if (strcmp(interface, "wl_compositor") == 0) { |
| struct wl_compositor *compositor = (struct wl_compositor *)wl_registry_bind(registry, id, &wl_compositor_interface, 1); |
| inst.wayland_surface = wl_compositor_create_surface(compositor); |
| } |
| } |
| static void wayland_registry_global_remove(void *data, struct wl_registry *registry, uint32_t id) {} |
| static const struct wl_registry_listener wayland_registry_listener = {wayland_registry_global, wayland_registry_global_remove}; |
| |
| static void AppCreateWaylandWindow(AppInstance &inst) { |
| inst.wayland_display = wl_display_connect(nullptr); |
| struct wl_registry *registry = wl_display_get_registry(inst.wayland_display); |
| wl_registry_add_listener(wl_display_get_registry(inst.wayland_display), &wayland_registry_listener, static_cast<void *>(&inst)); |
| wl_display_roundtrip(inst.wayland_display); |
| wl_registry_destroy(registry); |
| } |
| |
| static VkSurfaceKHR AppCreateWaylandSurface(AppInstance &inst) { |
| VkWaylandSurfaceCreateInfoKHR createInfo; |
| createInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; |
| createInfo.pNext = nullptr; |
| createInfo.flags = 0; |
| createInfo.display = inst.wayland_display; |
| createInfo.surface = inst.wayland_surface; |
| |
| VkSurfaceKHR surface; |
| VkResult err = vkCreateWaylandSurfaceKHR(inst.instance, &createInfo, nullptr, &surface); |
| if (err) ERR_EXIT(err); |
| return surface; |
| } |
| |
| static void AppDestroyWaylandWindow(AppInstance &inst) { wl_display_disconnect(inst.wayland_display); } |
| #endif // VK_USE_PLATFORM_WAYLAND_KHR |
| //----------------------------------------------------------- |
| |
| //-------------------------ANDROID--------------------------- |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| static void AppCreateAndroidWindow(AppInstance &inst) {} |
| static VkSurfaceKHR AppCreateAndroidSurface(AppInstance &inst) { |
| VkAndroidSurfaceCreateInfoKHR createInfo; |
| createInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR; |
| createInfo.pNext = NULL; |
| createInfo.flags = 0; |
| createInfo.window = (struct ANativeWindow *)(inst.window); |
| |
| err = vkCreateAndroidSurfaceKHR(inst.inst, &createInfo, NULL, &inst.surface); |
| EXIT_ERR(err); |
| } |
| static VkSurfaceKHR AppDestroyAndroidSurface(AppInstance &inst) {} |
| #endif |
| //----------------------------------------------------------- |
| |
| // ------------ Setup Windows ------------- // |
| |
| void SetupWindowExtensions(AppInstance &inst) { |
| #if defined(VK_USE_PLATFORM_XCB_KHR) || defined(VK_USE_PLATFORM_XLIB_KHR) |
| bool has_display = true; |
| const char *display_var = getenv("DISPLAY"); |
| if (display_var == nullptr || strlen(display_var) == 0) { |
| fprintf(stderr, "'DISPLAY' environment variable not set... skipping surface info\n"); |
| fflush(stderr); |
| has_display = false; |
| } |
| #endif |
| |
| #ifdef VK_USE_PLATFORM_WAYLAND_KHR |
| wl_display *wayland_display = wl_display_connect(nullptr); |
| bool has_wayland_display = false; |
| if (wayland_display != nullptr) { |
| wl_display_disconnect(wayland_display); |
| has_wayland_display = true; |
| } |
| #endif |
| |
| //--WIN32-- |
| #ifdef VK_USE_PLATFORM_WIN32_KHR |
| SurfaceExtension surface_ext_win32; |
| if (inst.CheckExtensionEnabled(VK_KHR_WIN32_SURFACE_EXTENSION_NAME)) { |
| surface_ext_win32.name = VK_KHR_WIN32_SURFACE_EXTENSION_NAME; |
| surface_ext_win32.create_window = AppCreateWin32Window; |
| surface_ext_win32.create_surface = AppCreateWin32Surface; |
| surface_ext_win32.destroy_window = AppDestroyWin32Window; |
| |
| inst.AddSurfaceExtension(surface_ext_win32); |
| } |
| #endif |
| //--XCB-- |
| #ifdef VK_USE_PLATFORM_XCB_KHR |
| SurfaceExtension surface_ext_xcb; |
| if (inst.CheckExtensionEnabled(VK_KHR_XCB_SURFACE_EXTENSION_NAME)) { |
| surface_ext_xcb.name = VK_KHR_XCB_SURFACE_EXTENSION_NAME; |
| surface_ext_xcb.create_window = AppCreateXcbWindow; |
| surface_ext_xcb.create_surface = AppCreateXcbSurface; |
| surface_ext_xcb.destroy_window = AppDestroyXcbWindow; |
| if (has_display) { |
| inst.AddSurfaceExtension(surface_ext_xcb); |
| } |
| } |
| #endif |
| //--XLIB-- |
| #ifdef VK_USE_PLATFORM_XLIB_KHR |
| SurfaceExtension surface_ext_xlib; |
| if (inst.CheckExtensionEnabled(VK_KHR_XLIB_SURFACE_EXTENSION_NAME)) { |
| surface_ext_xlib.name = VK_KHR_XLIB_SURFACE_EXTENSION_NAME; |
| surface_ext_xlib.create_window = AppCreateXlibWindow; |
| surface_ext_xlib.create_surface = AppCreateXlibSurface; |
| surface_ext_xlib.destroy_window = AppDestroyXlibWindow; |
| if (has_display) { |
| inst.AddSurfaceExtension(surface_ext_xlib); |
| } |
| } |
| #endif |
| //--MACOS-- |
| #ifdef VK_USE_PLATFORM_MACOS_MVK |
| SurfaceExtension surface_ext_macos; |
| if (inst.CheckExtensionEnabled(VK_MVK_MACOS_SURFACE_EXTENSION_NAME)) { |
| surface_ext_macos.name = VK_MVK_MACOS_SURFACE_EXTENSION_NAME; |
| surface_ext_macos.create_window = AppCreateMacOSWindow; |
| surface_ext_macos.create_surface = AppCreateMacOSSurface; |
| surface_ext_macos.destroy_window = AppDestroyMacOSWindow; |
| |
| inst.AddSurfaceExtension(surface_ext_macos); |
| } |
| #endif |
| |
| #ifdef VK_USE_PLATFORM_METAL_EXT |
| SurfaceExtension surface_ext_metal; |
| if (inst.CheckExtensionEnabled(VK_EXT_METAL_SURFACE_EXTENSION_NAME)) { |
| surface_ext_metal.name = VK_EXT_METAL_SURFACE_EXTENSION_NAME; |
| surface_ext_metal.create_window = AppCreateMetalWindow; |
| surface_ext_metal.create_surface = AppCreateMetalSurface; |
| surface_ext_metal.destroy_window = AppDestroyMetalWindow; |
| |
| inst.AddSurfaceExtension(surface_ext_metal); |
| } |
| #endif |
| //--WAYLAND-- |
| #ifdef VK_USE_PLATFORM_WAYLAND_KHR |
| SurfaceExtension surface_ext_wayland; |
| if (inst.CheckExtensionEnabled(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME)) { |
| surface_ext_wayland.name = VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME; |
| surface_ext_wayland.create_window = AppCreateWaylandWindow; |
| surface_ext_wayland.create_surface = AppCreateWaylandSurface; |
| surface_ext_wayland.destroy_window = AppDestroyWaylandWindow; |
| if (has_wayland_display) { |
| inst.AddSurfaceExtension(surface_ext_wayland); |
| } |
| } |
| #endif |
| //--ANDROID-- |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| SurfaceExtension surface_ext_android; |
| if (inst.CheckExtensionEnabled(VK_ANDROID_SURFACE_EXTENSION_NAME)) { |
| surface_ext_android.name = VK_ANDROID_SURFACE_EXTENSION_NAME; |
| surface_ext_android.create_window = AppCreateAndroidWindow; |
| surface_ext_android.create_surface = AppCreateAndroidSurface; |
| surface_ext_android.destroy_window = AppDestroyAndroidWindow; |
| |
| inst.AddSurfaceExtension(surface_ext_android); |
| } |
| #endif |
| } |
| |
| // ---------- Surfaces -------------- // |
| |
| class AppSurface { |
| public: |
| AppInstance &inst; |
| SurfaceExtension surface_extension; |
| |
| std::vector<VkPresentModeKHR> surf_present_modes; |
| |
| std::vector<VkSurfaceFormatKHR> surf_formats; |
| std::vector<VkSurfaceFormat2KHR> surf_formats2; |
| |
| VkSurfaceCapabilitiesKHR surface_capabilities; |
| VkSurfaceCapabilities2KHR surface_capabilities2_khr; |
| VkSurfaceCapabilities2EXT surface_capabilities2_ext; |
| |
| AppSurface(AppInstance &inst, VkPhysicalDevice phys_device, SurfaceExtension surface_extension, |
| std::vector<pNextChainBuildingBlockInfo> &sur_extension_pNextChain) |
| : inst(inst), surface_extension(surface_extension) { |
| uint32_t present_mode_count = 0; |
| VkResult error = |
| inst.vkGetPhysicalDeviceSurfacePresentModesKHR(phys_device, surface_extension.surface, &present_mode_count, nullptr); |
| if (error) ERR_EXIT(error); |
| |
| surf_present_modes.resize(present_mode_count); |
| error = inst.vkGetPhysicalDeviceSurfacePresentModesKHR(phys_device, surface_extension.surface, &present_mode_count, |
| surf_present_modes.data()); |
| if (error) ERR_EXIT(error); |
| |
| const VkPhysicalDeviceSurfaceInfo2KHR surface_info2 = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR, nullptr, |
| surface_extension.surface}; |
| |
| uint32_t format_count = 0; |
| if (inst.CheckExtensionEnabled(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME)) { |
| VkResult err = inst.vkGetPhysicalDeviceSurfaceFormats2KHR(phys_device, &surface_info2, &format_count, nullptr); |
| if (err) ERR_EXIT(err); |
| surf_formats2.resize(format_count); |
| for (uint32_t i = 0; i < format_count; ++i) { |
| surf_formats2[i].sType = VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR; |
| surf_formats2[i].pNext = nullptr; |
| } |
| err = inst.vkGetPhysicalDeviceSurfaceFormats2KHR(phys_device, &surface_info2, &format_count, surf_formats2.data()); |
| if (err) ERR_EXIT(err); |
| } else { |
| VkResult err = |
| inst.vkGetPhysicalDeviceSurfaceFormatsKHR(phys_device, surface_extension.surface, &format_count, nullptr); |
| if (err) ERR_EXIT(err); |
| surf_formats.resize(format_count); |
| err = inst.vkGetPhysicalDeviceSurfaceFormatsKHR(phys_device, surface_extension.surface, &format_count, |
| surf_formats.data()); |
| if (err) ERR_EXIT(err); |
| } |
| |
| if (inst.CheckExtensionEnabled(VK_KHR_SURFACE_EXTENSION_NAME)) { |
| VkResult err = |
| inst.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(phys_device, surface_extension.surface, &surface_capabilities); |
| if (err) ERR_EXIT(err); |
| } |
| |
| if (inst.CheckExtensionEnabled(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME)) { |
| surface_capabilities2_khr.sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR; |
| buildpNextChain((VkStructureHeader *)&surface_capabilities2_khr, sur_extension_pNextChain); |
| |
| VkPhysicalDeviceSurfaceInfo2KHR surface_info; |
| surface_info.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR; |
| surface_info.pNext = nullptr; |
| surface_info.surface = surface_extension.surface; |
| |
| VkResult err = inst.vkGetPhysicalDeviceSurfaceCapabilities2KHR(phys_device, &surface_info, &surface_capabilities2_khr); |
| if (err) ERR_EXIT(err); |
| } |
| |
| if (inst.CheckExtensionEnabled(VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME)) { |
| surface_capabilities2_ext.sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_EXT; |
| surface_capabilities2_ext.pNext = nullptr; |
| VkResult err = |
| inst.vkGetPhysicalDeviceSurfaceCapabilities2EXT(phys_device, surface_extension.surface, &surface_capabilities2_ext); |
| if (err) ERR_EXIT(err); |
| } |
| } |
| |
| ~AppSurface() { |
| if (inst.CheckExtensionEnabled(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME)) { |
| freepNextChain(static_cast<VkStructureHeader *>(surface_capabilities2_khr.pNext)); |
| } |
| } |
| |
| AppSurface(const AppSurface &) = delete; |
| const AppSurface &operator=(const AppSurface &) = delete; |
| }; |
| |
| // -------------------- Device Groups ------------------------// |
| |
| std::vector<VkPhysicalDeviceGroupProperties> GetGroups(AppInstance &inst) { |
| if (inst.CheckExtensionEnabled(VK_KHR_DEVICE_GROUP_CREATION_EXTENSION_NAME)) { |
| PFN_vkEnumeratePhysicalDeviceGroupsKHR vkEnumeratePhysicalDeviceGroupsKHR = |
| reinterpret_cast<PFN_vkEnumeratePhysicalDeviceGroupsKHR>( |
| vkGetInstanceProcAddr(inst.instance, "vkEnumeratePhysicalDeviceGroupsKHR")); |
| |
| std::vector<VkPhysicalDeviceGroupProperties> groups; |
| uint32_t group_count; |
| VkResult err; |
| do { |
| err = vkEnumeratePhysicalDeviceGroupsKHR(inst.instance, &group_count, NULL); |
| if (err != VK_SUCCESS && err != VK_INCOMPLETE) ERR_EXIT(err); |
| groups.resize(group_count); |
| |
| err = vkEnumeratePhysicalDeviceGroupsKHR(inst.instance, &group_count, groups.data()); |
| if (err != VK_SUCCESS && err != VK_INCOMPLETE) ERR_EXIT(err); |
| } while (err == VK_INCOMPLETE); |
| return groups; |
| } |
| return {}; |
| } |
| |
| std::vector<VkPhysicalDeviceProperties> GetGroupProps(VkPhysicalDeviceGroupProperties group) { |
| std::vector<VkPhysicalDeviceProperties> props(group.physicalDeviceCount); |
| |
| for (uint32_t i = 0; i < group.physicalDeviceCount; ++i) { |
| vkGetPhysicalDeviceProperties(group.physicalDevices[i], &props[i]); |
| } |
| |
| return props; |
| } |
| |
| // The bool of the pair returns true if the extension VK_KHR_device_group is present |
| std::pair<bool, VkDeviceGroupPresentCapabilitiesKHR> GetGroupCapabilities(AppInstance &inst, |
| VkPhysicalDeviceGroupProperties group) { |
| // Build create info for logical device made from all physical devices in this group. |
| std::vector<std::string> extensions_list = {VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_KHR_DEVICE_GROUP_EXTENSION_NAME}; |
| VkDeviceGroupDeviceCreateInfoKHR dg_ci = {VK_STRUCTURE_TYPE_DEVICE_GROUP_DEVICE_CREATE_INFO_KHR, nullptr, |
| group.physicalDeviceCount, group.physicalDevices}; |
| |
| float queue_priority = 1.0f; |
| |
| auto ext_list = get_c_str_array(extensions_list); |
| |
| VkDeviceQueueCreateInfo q_ci = {VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, nullptr, 0, 0, 1, &queue_priority}; |
| VkDeviceCreateInfo device_ci = {VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, &dg_ci, 0, 1, &q_ci, 0, nullptr, |
| static_cast<uint32_t>(ext_list.size()), ext_list.data()}; |
| |
| VkDevice logical_device = VK_NULL_HANDLE; |
| |
| VkResult err = vkCreateDevice(group.physicalDevices[0], &device_ci, nullptr, &logical_device); |
| if (err != VK_SUCCESS && err != VK_ERROR_EXTENSION_NOT_PRESENT) ERR_EXIT(err); |
| |
| if (err == VK_ERROR_EXTENSION_NOT_PRESENT) { |
| VkDeviceGroupPresentCapabilitiesKHR group_capabilities = {VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_CAPABILITIES_KHR, nullptr}; |
| vkDestroyDevice(logical_device, nullptr); |
| return std::pair<bool, VkDeviceGroupPresentCapabilitiesKHR>(false, group_capabilities); |
| } |
| |
| VkDeviceGroupPresentCapabilitiesKHR group_capabilities = {VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_CAPABILITIES_KHR, nullptr}; |
| |
| // If the KHR_device_group extension is present, write the capabilities of the logical device into a struct for later |
| // output to user. |
| PFN_vkGetDeviceGroupPresentCapabilitiesKHR vkGetDeviceGroupPresentCapabilitiesKHR = |
| reinterpret_cast<PFN_vkGetDeviceGroupPresentCapabilitiesKHR>( |
| vkGetInstanceProcAddr(inst.instance, "vkGetDeviceGroupPresentCapabilitiesKHR")); |
| err = vkGetDeviceGroupPresentCapabilitiesKHR(logical_device, &group_capabilities); |
| if (err) ERR_EXIT(err); |
| |
| vkDestroyDevice(logical_device, nullptr); |
| |
| return std::pair<bool, VkDeviceGroupPresentCapabilitiesKHR>(true, group_capabilities); |
| } |
| |
| // -------------------- Device Setup ------------------- // |
| |
| struct MemImageSupport { |
| bool regular_supported, sparse_supported, transient_supported; |
| VkFormat format; |
| uint32_t regular_memtypes, sparse_memtypes, transient_memtypes; |
| }; |
| |
| struct MemResSupport { |
| std::array<std::array<MemImageSupport, 8>, 2> image; |
| // TODO: buffers |
| }; |
| |
| struct pNextChainInfos { |
| std::vector<pNextChainBuildingBlockInfo> phys_device_props2; |
| std::vector<pNextChainBuildingBlockInfo> phys_device_mem_props2; |
| std::vector<pNextChainBuildingBlockInfo> phys_device_features2; |
| std::vector<pNextChainBuildingBlockInfo> surface_capabilities2; |
| std::vector<pNextChainBuildingBlockInfo> format_properties2; |
| }; |
| |
| struct FormatRange { |
| // the Vulkan standard version that supports this format range, or 0 if non-standard |
| uint32_t minimum_instance_version; |
| |
| // The name of the extension that supports this format range, or NULL if the range |
| // is only part of the standard |
| const char *extension_name; |
| |
| // The first and last supported formats within this range. |
| VkFormat first_format; |
| VkFormat last_format; |
| }; |
| |
| struct AppGpu { |
| AppInstance &inst; |
| uint32_t id; |
| VkPhysicalDevice phys_device; |
| |
| VkPhysicalDeviceProperties props; |
| VkPhysicalDeviceProperties2KHR props2; |
| |
| uint32_t queue_count; |
| std::vector<VkQueueFamilyProperties> queue_props; |
| std::vector<VkQueueFamilyProperties2KHR> queue_props2; |
| |
| VkPhysicalDeviceMemoryProperties memory_props; |
| VkPhysicalDeviceMemoryProperties2KHR memory_props2; |
| |
| MemResSupport mem_type_res_support; |
| |
| VkPhysicalDeviceFeatures features; |
| VkPhysicalDeviceFeatures2KHR features2; |
| VkPhysicalDevice limits; |
| |
| std::vector<VkExtensionProperties> device_extensions; |
| |
| VkDevice dev; |
| VkPhysicalDeviceFeatures enabled_features; |
| |
| std::array<VkDeviceSize, VK_MAX_MEMORY_HEAPS> heapBudget; |
| std::array<VkDeviceSize, VK_MAX_MEMORY_HEAPS> heapUsage; |
| |
| std::vector<FormatRange> supported_format_ranges; |
| |
| AppGpu(AppInstance &inst, uint32_t id, VkPhysicalDevice phys_device, pNextChainInfos chainInfos) |
| : inst(inst), id(id), phys_device(phys_device) { |
| vkGetPhysicalDeviceProperties(phys_device, &props); |
| |
| if (inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) { |
| props2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR; |
| buildpNextChain((VkStructureHeader *)&props2, chainInfos.phys_device_props2); |
| |
| inst.vkGetPhysicalDeviceProperties2KHR(phys_device, &props2); |
| } |
| |
| /* get queue count */ |
| vkGetPhysicalDeviceQueueFamilyProperties(phys_device, &queue_count, nullptr); |
| |
| queue_props.resize(queue_count); |
| |
| vkGetPhysicalDeviceQueueFamilyProperties(phys_device, &queue_count, queue_props.data()); |
| |
| if (inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) { |
| queue_props2.resize(queue_count); |
| |
| for (size_t i = 0; i < queue_count; ++i) { |
| queue_props2[i].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2_KHR; |
| queue_props2[i].pNext = nullptr; |
| } |
| |
| inst.vkGetPhysicalDeviceQueueFamilyProperties2KHR(phys_device, &queue_count, queue_props2.data()); |
| } |
| |
| vkGetPhysicalDeviceMemoryProperties(phys_device, &memory_props); |
| |
| vkGetPhysicalDeviceFeatures(phys_device, &features); |
| |
| if (inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) { |
| memory_props2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR; |
| buildpNextChain((VkStructureHeader *)&memory_props2, chainInfos.phys_device_mem_props2); |
| |
| inst.vkGetPhysicalDeviceMemoryProperties2KHR(phys_device, &memory_props2); |
| |
| features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR; |
| buildpNextChain((VkStructureHeader *)&features2, chainInfos.phys_device_features2); |
| |
| inst.vkGetPhysicalDeviceFeatures2KHR(phys_device, &features2); |
| } |
| |
| device_extensions = AppGetPhysicalDeviceLayerExtensions(nullptr); |
| |
| const float queue_priority = 1.0f; |
| const VkDeviceQueueCreateInfo q_ci = {VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, |
| nullptr, |
| 0, |
| 0, // just pick the first one and hope for the best |
| 1, |
| &queue_priority}; |
| enabled_features = VkPhysicalDeviceFeatures{0}; |
| const VkDeviceCreateInfo device_ci = { |
| VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, nullptr, 0, 1, &q_ci, 0, nullptr, 0, nullptr, &enabled_features}; |
| |
| VkResult err = vkCreateDevice(phys_device, &device_ci, nullptr, &dev); |
| if (err) ERR_EXIT(err); |
| |
| const VkFormat color_format = VK_FORMAT_R8G8B8A8_UNORM; |
| const std::vector<VkFormat> formats = { |
| color_format, VK_FORMAT_D16_UNORM, VK_FORMAT_X8_D24_UNORM_PACK32, VK_FORMAT_D32_SFLOAT, |
| VK_FORMAT_S8_UINT, VK_FORMAT_D16_UNORM_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D32_SFLOAT_S8_UINT}; |
| assert(mem_type_res_support.image[0].size() == formats.size()); |
| const std::array<VkImageUsageFlags, 2> usages = {0, VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT}; |
| const std::array<VkImageCreateFlags, 2> flagss = {0, VK_IMAGE_CREATE_SPARSE_BINDING_BIT}; |
| |
| for (size_t fmt_i = 0; fmt_i < formats.size(); ++fmt_i) { |
| // only iterate over VK_IMAGE_TILING_OPTIMAL and VK_IMAGE_TILING_LINEAR (0 and 1) |
| for (size_t tiling = VK_IMAGE_TILING_OPTIMAL; tiling <= VK_IMAGE_TILING_LINEAR; ++tiling) { |
| mem_type_res_support.image[tiling][fmt_i].format = formats[fmt_i]; |
| mem_type_res_support.image[tiling][fmt_i].regular_supported = true; |
| mem_type_res_support.image[tiling][fmt_i].sparse_supported = true; |
| mem_type_res_support.image[tiling][fmt_i].transient_supported = true; |
| |
| VkFormatProperties fmt_props; |
| vkGetPhysicalDeviceFormatProperties(phys_device, formats[fmt_i], &fmt_props); |
| if ((tiling == VK_IMAGE_TILING_OPTIMAL && fmt_props.optimalTilingFeatures == 0) || |
| (tiling == VK_IMAGE_TILING_LINEAR && fmt_props.linearTilingFeatures == 0)) { |
| mem_type_res_support.image[tiling][fmt_i].regular_supported = false; |
| mem_type_res_support.image[tiling][fmt_i].sparse_supported = false; |
| mem_type_res_support.image[tiling][fmt_i].transient_supported = false; |
| continue; |
| } |
| |
| for (size_t u_i = 0; u_i < usages.size(); ++u_i) { |
| for (size_t flg_i = 0; flg_i < flagss.size(); ++flg_i) { |
| VkImageCreateInfo image_ci = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, |
| nullptr, |
| flagss[flg_i], |
| VK_IMAGE_TYPE_2D, |
| formats[fmt_i], |
| {8, 8, 1}, |
| 1, |
| 1, |
| VK_SAMPLE_COUNT_1_BIT, |
| static_cast<VkImageTiling>(tiling), |
| usages[u_i], |
| VK_SHARING_MODE_EXCLUSIVE, |
| 0, |
| nullptr, |
| VK_IMAGE_LAYOUT_UNDEFINED}; |
| |
| if ((image_ci.flags & VK_IMAGE_CREATE_SPARSE_BINDING_BIT) && |
| (image_ci.usage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT)) { |
| continue; |
| } |
| |
| if (image_ci.usage == 0 || (image_ci.usage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT)) { |
| if (image_ci.format == color_format) |
| image_ci.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; |
| else |
| image_ci.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; |
| } |
| |
| if (!enabled_features.sparseBinding && (image_ci.flags & VK_IMAGE_CREATE_SPARSE_BINDING_BIT)) { |
| mem_type_res_support.image[tiling][fmt_i].sparse_supported = false; |
| continue; |
| } |
| |
| VkImageFormatProperties img_props; |
| err = vkGetPhysicalDeviceImageFormatProperties(phys_device, image_ci.format, image_ci.imageType, |
| image_ci.tiling, image_ci.usage, image_ci.flags, &img_props); |
| |
| uint32_t *memtypes; |
| bool *support; |
| |
| if (image_ci.flags == 0 && !(image_ci.usage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT)) { |
| memtypes = &mem_type_res_support.image[tiling][fmt_i].regular_memtypes; |
| support = &mem_type_res_support.image[tiling][fmt_i].regular_supported; |
| } else if ((image_ci.flags & VK_IMAGE_CREATE_SPARSE_BINDING_BIT) && |
| !(image_ci.usage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT)) { |
| memtypes = &mem_type_res_support.image[tiling][fmt_i].sparse_memtypes; |
| support = &mem_type_res_support.image[tiling][fmt_i].sparse_supported; |
| } else if (image_ci.flags == 0 && (image_ci.usage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT)) { |
| memtypes = &mem_type_res_support.image[tiling][fmt_i].transient_memtypes; |
| support = &mem_type_res_support.image[tiling][fmt_i].transient_supported; |
| } else { |
| assert(false); |
| return; |
| } |
| |
| if (err == VK_ERROR_FORMAT_NOT_SUPPORTED) { |
| *support = false; |
| } else { |
| if (err) ERR_EXIT(err); |
| |
| VkImage dummy_img; |
| err = vkCreateImage(dev, &image_ci, nullptr, &dummy_img); |
| if (err) ERR_EXIT(err); |
| |
| VkMemoryRequirements mem_req; |
| vkGetImageMemoryRequirements(dev, dummy_img, &mem_req); |
| *memtypes = mem_req.memoryTypeBits; |
| |
| vkDestroyImage(dev, dummy_img, nullptr); |
| } |
| } |
| } |
| } |
| } |
| |
| // Memory // |
| |
| struct VkStructureHeader *structure = NULL; |
| if (inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) { |
| structure = (struct VkStructureHeader *)memory_props2.pNext; |
| |
| while (structure) { |
| if (structure->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT && |
| CheckPhysicalDeviceExtensionIncluded(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME)) { |
| VkPhysicalDeviceMemoryBudgetPropertiesEXT *mem_budget_props = |
| (VkPhysicalDeviceMemoryBudgetPropertiesEXT *)structure; |
| for (uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; i++) { |
| heapBudget[i] = mem_budget_props->heapBudget[i]; |
| heapUsage[i] = mem_budget_props->heapUsage[i]; |
| } |
| } |
| |
| structure = (struct VkStructureHeader *)structure->pNext; |
| } |
| } |
| // TODO buffer - memory type compatibility |
| |
| supported_format_ranges = { |
| { |
| // Standard formats in Vulkan 1.0 |
| VK_MAKE_VERSION(1, 0, 0), |
| NULL, |
| VK_FORMAT_BEGIN_RANGE, |
| VK_FORMAT_END_RANGE, |
| }, |
| { |
| // YCBCR extension, standard in Vulkan 1.1 |
| VK_MAKE_VERSION(1, 1, 0), |
| VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME, |
| VK_FORMAT_G8B8G8R8_422_UNORM, |
| VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM, |
| }, |
| { |
| // PVRTC extension, not standardized |
| 0, |
| VK_IMG_FORMAT_PVRTC_EXTENSION_NAME, |
| VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG, |
| VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG, |
| }, |
| }; |
| } |
| ~AppGpu() { |
| vkDestroyDevice(dev, nullptr); |
| |
| if (inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) { |
| freepNextChain(static_cast<VkStructureHeader *>(features2.pNext)); |
| freepNextChain(static_cast<VkStructureHeader *>(props2.pNext)); |
| freepNextChain(static_cast<VkStructureHeader *>(memory_props2.pNext)); |
| } |
| } |
| |
| AppGpu(const AppGpu &) = delete; |
| const AppGpu &operator=(const AppGpu &) = delete; |
| |
| bool CheckPhysicalDeviceExtensionIncluded(std::string extension_to_check) { |
| for (auto &extension : device_extensions) { |
| if (extension_to_check == std::string(extension.extensionName)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| std::vector<VkExtensionProperties> AppGetPhysicalDeviceLayerExtensions(char *layer_name) { |
| std::vector<VkExtensionProperties> extension_properties; |
| |
| VkResult err; |
| uint32_t ext_count = 0; |
| |
| /* repeat get until VK_INCOMPLETE goes away */ |
| do { |
| err = vkEnumerateDeviceExtensionProperties(phys_device, layer_name, &ext_count, nullptr); |
| if (err) ERR_EXIT(err); |
| |
| extension_properties.resize(ext_count); |
| err = vkEnumerateDeviceExtensionProperties(phys_device, layer_name, &ext_count, extension_properties.data()); |
| if (err) ERR_EXIT(err); |
| extension_properties.resize(ext_count); |
| } while (err == VK_INCOMPLETE); |
| |
| return extension_properties; |
| } |
| |
| // Helper function to determine whether a format range is currently supported. |
| bool FormatRangeSupported(FormatRange &format_range) { |
| // True if standard and supported by both this instance and this GPU |
| if (format_range.minimum_instance_version > 0 && inst.instance_version >= format_range.minimum_instance_version && |
| props.apiVersion >= format_range.minimum_instance_version) { |
| return true; |
| } |
| |
| // True if this extension is present |
| if (format_range.extension_name != nullptr) { |
| return inst.CheckExtensionEnabled(format_range.extension_name); |
| } |
| |
| // Otherwise, not supported. |
| return false; |
| } |
| |
| VkPhysicalDeviceProperties GetDeviceProperties() { |
| if (inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) { |
| return props2.properties; |
| } else { |
| return props; |
| } |
| } |
| }; |
| struct AppQueueFamilyProperties { |
| VkQueueFamilyProperties props; |
| uint32_t queue_index; |
| bool is_present_platform_agnostic = true; |
| VkBool32 platforms_support_present = VK_FALSE; |
| |
| AppQueueFamilyProperties(AppGpu &gpu, uint32_t queue_index) : queue_index(queue_index) { |
| if (gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) { |
| props = gpu.queue_props2[queue_index].queueFamilyProperties; |
| } else { |
| props = gpu.queue_props[queue_index]; |
| } |
| |
| for (auto &surface_ext : gpu.inst.surface_extensions) { |
| VkResult err = vkGetPhysicalDeviceSurfaceSupportKHR(gpu.phys_device, queue_index, surface_ext.surface, |
| &surface_ext.supports_present); |
| if (err) ERR_EXIT(err); |
| |
| const bool first = (surface_ext == gpu.inst.surface_extensions.at(0)); |
| if (!first && platforms_support_present != surface_ext.supports_present) { |
| is_present_platform_agnostic = false; |
| |
| platforms_support_present = surface_ext.supports_present; |
| } |
| } |
| } |
| }; |
| |
| // --------- Format Properties ----------// |
| |
| struct PropFlags { |
| uint32_t linear; |
| uint32_t optimal; |
| uint32_t buffer; |
| |
| bool operator==(const PropFlags &other) const { |
| return (linear == other.linear && optimal == other.optimal && buffer == other.buffer); |
| } |
| }; |
| |
| namespace std { |
| template <> |
| struct hash<PropFlags> { |
| std::size_t operator()(const PropFlags &k) const { |
| return ((std::hash<uint32_t>()(k.linear) ^ (std::hash<uint32_t>()(k.optimal) << 1)) >> 1) ^ |
| (std::hash<uint32_t>()(k.buffer) << 1); |
| } |
| }; |
| } // namespace std |
| |
| // Used to sort the formats into buckets by their properties. |
| std::unordered_map<PropFlags, std::vector<VkFormat>> FormatPropMap(AppGpu &gpu) { |
| std::unordered_map<PropFlags, std::vector<VkFormat>> map; |
| for (auto fmtRange : gpu.supported_format_ranges) { |
| for (int32_t fmt = fmtRange.first_format; fmt <= fmtRange.last_format; ++fmt) { |
| VkFormatProperties props; |
| vkGetPhysicalDeviceFormatProperties(gpu.phys_device, static_cast<VkFormat>(fmt), &props); |
| |
| PropFlags pf = {props.linearTilingFeatures, props.optimalTilingFeatures, props.bufferFeatures}; |
| |
| map[pf].push_back(static_cast<VkFormat>(fmt)); |
| } |
| } |
| return map; |
| } |
| |
| VkFormatProperties2 GetFormatProperties2(AppGpu &gpu, VkFormat format, pNextChainInfos &chainInfos) { |
| VkFormatProperties2 props; |
| props.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; |
| buildpNextChain((VkStructureHeader *)&props, chainInfos.format_properties2); |
| |
| gpu.inst.vkGetPhysicalDeviceFormatProperties2KHR(gpu.phys_device, format, &props); |
| return props; |
| } |