diff options
| author | Lizzy Fleckenstein <lizzy@vlhl.dev> | 2026-03-31 01:30:36 +0200 |
|---|---|---|
| committer | Lizzy Fleckenstein <lizzy@vlhl.dev> | 2026-03-31 01:30:36 +0200 |
| commit | 8e2ff15dbd3fe70fe2b52397b1eaba3fe2d7a5e8 (patch) | |
| tree | 925fa596210d1a1f01e00e0743a643f4552e7a7a /tools/Vulkan-Tools/vulkaninfo/vulkaninfo.h | |
| parent | 1f17b4df127bd280e50d93a46ae93df704adc2b0 (diff) | |
| parent | 90bf5bc4fd8bea0d300f6564af256a51a34124b8 (diff) | |
| download | usermoji-8e2ff15dbd3fe70fe2b52397b1eaba3fe2d7a5e8.tar.xz | |
add tools/Vulkan-Tools
Diffstat (limited to 'tools/Vulkan-Tools/vulkaninfo/vulkaninfo.h')
| -rw-r--r-- | tools/Vulkan-Tools/vulkaninfo/vulkaninfo.h | 2035 |
1 files changed, 2035 insertions, 0 deletions
diff --git a/tools/Vulkan-Tools/vulkaninfo/vulkaninfo.h b/tools/Vulkan-Tools/vulkaninfo/vulkaninfo.h new file mode 100644 index 00000000..e15583e6 --- /dev/null +++ b/tools/Vulkan-Tools/vulkaninfo/vulkaninfo.h @@ -0,0 +1,2035 @@ +/* + * Copyright (c) 2015-2026 The Khronos Group Inc. + * Copyright (c) 2015-2026 Valve Corporation + * Copyright (c) 2015-2026 LunarG, Inc. + * Copyright (c) 2023-2024 RasterGrid Kft. + * + * 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> + * + */ +#pragma once + +#include <algorithm> +#include <array> +#include <cstdint> +#include <exception> +#include <iostream> +#include <fstream> +#include <map> +#include <memory> +#include <ostream> +#include <set> +#include <string> +#include <unordered_map> +#include <set> +#include <vector> +#include <utility> +#include <functional> + +#include <assert.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <cstring> + +#ifdef __GNUC__ +#ifndef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200809L +#endif +#else +#define strndup(p, n) strdup(p) +#endif + +#if defined(_WIN32) +#include <fcntl.h> +#include <io.h> +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include <windows.h> +#if _MSC_VER == 1900 +#pragma warning(disable : 4800) +#endif +#endif // _WIN32 + +#if defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_XCB_KHR) +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#endif + +#if defined(VK_USE_PLATFORM_MACOS_MVK) || defined(VK_USE_PLATFORM_METAL_EXT) +#include "metal_view.h" +#endif + +#if defined(VK_USE_PLATFORM_WAYLAND_KHR) +#include <wayland-client.h> +#endif + +#include "vulkaninfo_functions.h" + +#include <vulkan/vulkan.h> + +static std::string VkResultString(VkResult err); + +// General error: Get file + line and a short message +struct FileLineException : std::runtime_error { + FileLineException(const std::string &arg, const char *file, int line) : runtime_error(arg) { + msg = std::string(file) + ":" + std::to_string(line) + ": " + arg; + } + ~FileLineException() throw() {} + const char *what() const throw() { return msg.c_str(); } + + private: + std::string msg; +}; +#define THROW_ERR(arg) throw FileLineException(arg, __FILE__, __LINE__); + +// Vulkan function error: Get name of function, file, line, and the error code returned by the function +struct VulkanException : std::runtime_error { + VulkanException(const std::string &function, const char *file, int line, VkResult err) : runtime_error(function) { + msg = std::string(file) + ":" + std::to_string(line) + ":" + function + " failed with " + VkResultString(err); + } + ~VulkanException() throw() {} + const char *what() const throw() { return msg.c_str(); } + + private: + std::string msg; +}; +#define THROW_VK_ERR(func_name, err) throw VulkanException(func_name, __FILE__, __LINE__, err); + +#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; +} +void wait_for_console_destroy() { + if (ConsoleIsExclusive()) Sleep(INFINITE); +} + +// User32 function declarations +using PFN_AdjustWindowRect = WINUSERAPI BOOL(WINAPI *)(_Inout_ LPRECT, _In_ DWORD, _In_ BOOL); +using PFN_CreateWindowExA = WINUSERAPI HWND(WINAPI *)(_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); +using PFN_DefWindowProcA = WINUSERAPI LRESULT(WINAPI *)(_In_ HWND, _In_ UINT, _In_ WPARAM, _In_ LPARAM); +using PFN_DestroyWindow = WINUSERAPI BOOL(WINAPI *)(_In_ HWND); +using PFN_LoadIconA = WINUSERAPI HICON(WINAPI *)(_In_opt_ HINSTANCE, _In_ LPCSTR); +using PFN_RegisterClassExA = WINUSERAPI ATOM(WINAPI *)(_In_ CONST WNDCLASSEXA *); + +struct User32Handles { + // User32 dll handle + HMODULE user32DllHandle = nullptr; + + // User32 function pointers + PFN_AdjustWindowRect pfnAdjustWindowRect = nullptr; + PFN_CreateWindowExA pfnCreateWindowExA = nullptr; + PFN_DefWindowProcA pfnDefWindowProcA = nullptr; + PFN_DestroyWindow pfnDestroyWindow = nullptr; + PFN_LoadIconA pfnLoadIconA = nullptr; + PFN_RegisterClassExA pfnRegisterClassExA = nullptr; + + User32Handles() noexcept {} + ~User32Handles() noexcept { + if (user32DllHandle != nullptr) { + FreeLibrary(user32DllHandle); + } + } + // Don't allow moving of this class + User32Handles(User32Handles const &) = delete; + User32Handles &operator=(User32Handles const &) = delete; + User32Handles(User32Handles &&) = delete; + User32Handles &operator=(User32Handles &&) = delete; + + bool load() { + user32DllHandle = LoadLibraryExA("user32.dll", nullptr, 0); + if (user32DllHandle == nullptr) return false; + if (!load_function(pfnAdjustWindowRect, "AdjustWindowRect")) return false; + if (!load_function(pfnCreateWindowExA, "CreateWindowExA")) return false; + if (!load_function(pfnDefWindowProcA, "DefWindowProcA")) return false; + if (!load_function(pfnDestroyWindow, "DestroyWindow")) return false; + if (!load_function(pfnLoadIconA, "LoadIconA")) return false; + if (!load_function(pfnRegisterClassExA, "RegisterClassExA")) return false; + return true; + } + + private: + template <typename T> + bool load_function(T &function_pointer, const char *function_name) { + function_pointer = reinterpret_cast<T>(GetProcAddress(user32DllHandle, function_name)); + if (function_pointer == nullptr) { + fprintf(stderr, "Failed to load function: %s\n", function_name); + return false; + } + return true; + } +}; + +// Global user handles function used in windows callback and code +User32Handles *user32_handles; +#endif // _WIN32 + +#define APP_SHORT_NAME "vulkaninfo" +#define APP_UPPER_CASE_NAME "VULKANINFO" +#define API_NAME "Vulkan" + +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; +} + +// Helper for robustly executing the two-call pattern +template <typename T, typename F, typename... Ts> +auto GetVectorInit(const char *func_name, F &&f, T init, Ts &&...ts) -> std::vector<T> { + uint32_t count = 32; // Preallocate enough so that most calls only happen once + std::vector<T> results; + VkResult err; + uint32_t iteration_count = 0; + uint32_t max_iterations = 5; + do { + count *= 2; + results.resize(count, init); + err = f(ts..., &count, results.data()); + results.resize(count); + iteration_count++; + } while (err == VK_INCOMPLETE && iteration_count < max_iterations); + if (err) THROW_VK_ERR(func_name, err); + return results; +} + +template <typename T, typename F, typename... Ts> +auto GetVector(const char *func_name, F &&f, Ts &&...ts) -> std::vector<T> { + return GetVectorInit(func_name, f, T(), ts...); +} + +// Forward declarations for pNext chains +struct phys_device_props2_chain; +struct phys_device_mem_props2_chain; +struct phys_device_features2_chain; +struct surface_capabilities2_chain; +struct format_properties2_chain; +struct queue_properties2_chain; +struct video_profile_info_chain; +struct video_capabilities_chain; +struct video_format_properties_chain; +struct AppInstance; +struct AppGpu; +struct AppVideoProfile; +struct AppDisplay; +struct AppDisplayMode; +struct AppDisplayPlane; + +void setup_phys_device_props2_chain(VkPhysicalDeviceProperties2 &start, std::unique_ptr<phys_device_props2_chain> &chain, + AppInstance &inst, AppGpu &gpu, bool show_promoted_structs); +void setup_phys_device_mem_props2_chain(VkPhysicalDeviceMemoryProperties2 &start, + std::unique_ptr<phys_device_mem_props2_chain> &chain, AppGpu &gpu); +void setup_phys_device_features2_chain(VkPhysicalDeviceFeatures2 &start, std::unique_ptr<phys_device_features2_chain> &chain, + AppGpu &gpu, bool show_promoted_structs); +void setup_surface_capabilities2_chain(VkSurfaceCapabilities2KHR &start, std::unique_ptr<surface_capabilities2_chain> &chain, + AppInstance &inst, AppGpu &gpu); +void setup_format_properties2_chain(VkFormatProperties2 &start, std::unique_ptr<format_properties2_chain> &chain, AppGpu &gpu); +void setup_queue_properties2_chain(VkQueueFamilyProperties2 &start, std::unique_ptr<queue_properties2_chain> &chain, AppGpu &gpu); + +bool prepare_phys_device_props2_twocall_chain_vectors(std::unique_ptr<phys_device_props2_chain> &chain); + +bool is_video_format_same(const VkVideoFormatPropertiesKHR &format_a, const VkVideoFormatPropertiesKHR &format_b); +std::vector<std::unique_ptr<AppVideoProfile>> enumerate_supported_video_profiles(AppGpu &gpu); + +std::vector<AppDisplayPlane> enumerate_display_planes(AppGpu &gpu); +std::vector<AppDisplay> enumerate_displays(AppGpu &gpu, const std::vector<AppDisplayPlane> &all_planes); + +/* An ptional contains either a value or nothing. The optional asserts if a value is trying to be gotten but none exist. + * The interface is taken from C++17's <optional> with many aspects removed. + * This class assumes the template type is 'trivial' + */ +namespace util { +template <typename T> +struct vulkaninfo_optional { + using value_type = T; + + bool _contains_value = false; + value_type _value; + + vulkaninfo_optional() noexcept : _contains_value(false), _value({}) {} + vulkaninfo_optional(T value) noexcept : _contains_value(true), _value(value) {} + + explicit operator bool() const noexcept { return _contains_value; } + bool has_value() const noexcept { return _contains_value; } + + value_type value() const noexcept { + assert(_contains_value); + return _value; + } + // clang-format off + const value_type* operator->() const { assert(_contains_value); return _value;} + value_type* operator->() { assert(_contains_value); return &_value;} + const value_type& operator*() const& { assert(_contains_value); return _value;} + value_type& operator*() & { assert(_contains_value); return _value;} + const value_type&& operator*() const&& { assert(_contains_value); return _value;} + value_type&& operator*() && { assert(_contains_value); return _value;} + // clang-format on +}; // namespace util +} // namespace util +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; + + bool operator==(const SurfaceExtension &other) { return name == other.name && surface == other.surface; } +}; + +struct AppVideoProfile { + bool supported; + + std::string name; + + VkVideoProfileInfoKHR profile_info; + std::unique_ptr<video_profile_info_chain> profile_info_chain; + + VkVideoCapabilitiesKHR capabilities; + std::unique_ptr<video_capabilities_chain> capabilities_chain; + + struct Format { + VkVideoFormatPropertiesKHR properties; + std::unique_ptr<video_format_properties_chain> properties_chain; + }; + std::vector<Format> formats; + std::unordered_map<std::string, std::vector<VkVideoFormatPropertiesKHR>> formats_by_category; + + using CreateProfileInfoChainCb = std::function<std::unique_ptr<video_profile_info_chain>(const void **)>; + using CreateCapabilitiesChainCb = std::function<std::unique_ptr<video_capabilities_chain>(void **)>; + struct CreateFormatPropertiesChainCb { + std::string format_name; + VkImageUsageFlags image_usage_flags; + std::function<bool(const VkVideoCapabilitiesKHR &capabilities)> check_required_caps; + std::function<std::unique_ptr<video_format_properties_chain>(void **)> callback; + }; + using CreateFormatPropertiesChainCbList = std::vector<CreateFormatPropertiesChainCb>; + using InitProfileCb = std::function<void(AppVideoProfile &)>; + + AppVideoProfile(AppGpu &gpu, VkPhysicalDevice phys_device, const std::string &in_name, + const VkVideoProfileInfoKHR &in_profile_info, CreateProfileInfoChainCb create_profile_info_chain, + CreateCapabilitiesChainCb create_capabilities_chain, + const CreateFormatPropertiesChainCbList &create_format_properties_chain_list, InitProfileCb init_profile) + : supported(true), name(in_name), profile_info(in_profile_info), capabilities({}) { + profile_info_chain = create_profile_info_chain(&profile_info.pNext); + if (profile_info_chain == nullptr) { + supported = false; + return; + } + + capabilities.sType = VK_STRUCTURE_TYPE_VIDEO_CAPABILITIES_KHR; + capabilities.pNext = nullptr; + capabilities_chain = create_capabilities_chain(&capabilities.pNext); + if (capabilities_chain == nullptr) { + supported = false; + return; + } + + init_profile(*this); + + VkResult result = vkGetPhysicalDeviceVideoCapabilitiesKHR(phys_device, &profile_info, &capabilities); + if (result != VK_SUCCESS) { + supported = false; + return; + } + + VkVideoProfileListInfoKHR profile_list = {VK_STRUCTURE_TYPE_VIDEO_PROFILE_LIST_INFO_KHR, nullptr, 1, &profile_info}; + + std::vector<VkVideoFormatPropertiesKHR> video_format_props{}; + std::vector<std::unique_ptr<video_format_properties_chain>> video_format_props_chains{}; + for (const auto &create_format_properties_chain_info : create_format_properties_chain_list) { + if (!create_format_properties_chain_info.check_required_caps(capabilities)) { + continue; + } + + VkPhysicalDeviceVideoFormatInfoKHR video_format_info = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_FORMAT_INFO_KHR, + &profile_list, + create_format_properties_chain_info.image_usage_flags}; + + uint32_t video_format_property_count = 0; + result = + vkGetPhysicalDeviceVideoFormatPropertiesKHR(phys_device, &video_format_info, &video_format_property_count, nullptr); + if (result != VK_SUCCESS) { + continue; + } + + video_format_props.resize(video_format_property_count); + video_format_props_chains.resize(video_format_property_count); + + for (uint32_t i = 0; i < video_format_property_count; ++i) { + video_format_props[i].sType = VK_STRUCTURE_TYPE_VIDEO_FORMAT_PROPERTIES_KHR; + video_format_props_chains[i] = create_format_properties_chain_info.callback(&video_format_props[i].pNext); + } + + result = vkGetPhysicalDeviceVideoFormatPropertiesKHR(phys_device, &video_format_info, &video_format_property_count, + video_format_props.data()); + if (result == VK_SUCCESS) { + for (uint32_t i = 0; i < video_format_property_count; ++i) { + const VkVideoFormatPropertiesKHR *existing_format = nullptr; + for (const auto &format : formats) { + if (is_video_format_same(format.properties, video_format_props[i])) { + existing_format = &format.properties; + break; + } + } + if (existing_format == nullptr) { + formats.push_back(Format{video_format_props[i], std::move(video_format_props_chains[i])}); + formats_by_category[create_format_properties_chain_info.format_name].push_back(video_format_props[i]); + } else { + formats_by_category[create_format_properties_chain_info.format_name].push_back(*existing_format); + } + } + } + + video_format_props.clear(); + video_format_props_chains.clear(); + } + } +}; + +struct AppDisplayPlane { + uint32_t global_index; + std::string name; + VkDisplayPlanePropertiesKHR properties; + std::vector<VkDisplayKHR> supported_displays; + + AppDisplayPlane(AppGpu &gpu, uint32_t index, const VkDisplayPlanePropertiesKHR &in_prop); +}; + +struct AppDisplayMode { + VkDisplayModePropertiesKHR properties; + + // key is a AppDisplayPlane::global_index value + std::map<uint32_t, VkDisplayPlaneCapabilitiesKHR> capabilities; + + AppDisplayMode(AppGpu &gpu, const VkDisplayModePropertiesKHR &in_prop, const std::set<uint32_t> &supported_planes); +}; + +struct AppDisplay { + uint32_t global_index; + + std::string name; + + VkDisplayPropertiesKHR properties; + std::vector<AppDisplayMode> modes; + + AppDisplay(AppGpu &gpu, uint32_t index, const VkDisplayPropertiesKHR &in_properties, + const std::vector<AppDisplayPlane> &all_planes); +}; + +class APIVersion { + public: + APIVersion() : api_version_(VK_API_VERSION_1_0) {} + APIVersion(uint32_t api_version) : api_version_(api_version) {} + void SetPatch(uint32_t patch) { api_version_ = api_version_ - Patch() + VK_API_VERSION_PATCH(patch); } + uint32_t Major() const { return VK_API_VERSION_MAJOR(api_version_); } + uint32_t Minor() const { return VK_API_VERSION_MINOR(api_version_); } + uint32_t Patch() const { return VK_API_VERSION_PATCH(api_version_); } + bool operator<(APIVersion api_version) const { return api_version_ < api_version.api_version_; } + bool operator<=(APIVersion api_version) const { return api_version_ <= api_version.api_version_; } + bool operator>(APIVersion api_version) const { return api_version_ > api_version.api_version_; } + bool operator>=(APIVersion api_version) const { return api_version_ >= api_version.api_version_; } + bool operator==(APIVersion api_version) const { return api_version_ == api_version.api_version_; } + bool operator!=(APIVersion api_version) const { return api_version_ != api_version.api_version_; } + std::string str() { return std::to_string(Major()) + "." + std::to_string(Minor()) + "." + std::to_string(Patch()); } + operator std::string() { return str(); } + + private: + uint32_t api_version_; +}; + +std::ostream &operator<<(std::ostream &out, const APIVersion &v) { + return out << v.Major() << "." << v.Minor() << "." << v.Patch(); +} + +struct AppInstance { + VkInstance instance; + APIVersion api_version; + + VkDebugReportCallbackEXT debug_callback = VK_NULL_HANDLE; + + std::vector<LayerExtensionList> global_layers; + + std::vector<VkExtensionProperties> global_extensions; // Instance Extensions + + std::vector<std::string> inst_extensions; + + 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_DIRECTFB_EXT + IDirectFB *dfb; + IDirectFBSurface *directfb_surface; +#endif +#ifdef VK_USE_PLATFORM_ANDROID_KHR // TODO + ANativeWindow *window; +#endif +#ifdef VK_USE_PLATFORM_SCREEN_QNX + struct _screen_context *context; + struct _screen_window *window; +#endif + AppInstance() { + VkResult dllErr = load_vulkan_library(); + + if (dllErr != VK_SUCCESS) { + THROW_ERR("Failed to initialize: " API_NAME " loader is not installed, not found, or failed to load."); + } + + uint32_t instance_version = VK_API_VERSION_1_0; + if (vkEnumerateInstanceVersion) { + const VkResult err = vkEnumerateInstanceVersion(&instance_version); + if (err) THROW_VK_ERR("vkEnumerateInstanceVersion", err); + } + + api_version = APIVersion(instance_version); + // fallback to baked header version if loader returns 0 for the patch version + if (api_version.Patch() == 0) api_version.SetPatch(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, instance_version}; + + AppCompileInstanceExtensionsToEnable(); + + std::vector<const char *> inst_exts; + for (const auto &ext : inst_extensions) { + inst_exts.push_back(ext.c_str()); + } + + const VkInstanceCreateInfo inst_info = { + VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + &dbg_info, + (CheckExtensionEnabled(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME) + ? static_cast<VkInstanceCreateFlags>(VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR) + : 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 " API_NAME " instance.\n"; + std::cerr << "This problem is often caused by a faulty installation of the " API_NAME + " driver or attempting to use a GPU " + "that does not support " API_NAME ".\n"; + THROW_VK_ERR("vkCreateInstance", err); + } else if (err) { + THROW_VK_ERR("vkCreateInstance", err); + } + + load_vulkan_instance_functions(instance); + + err = vkCreateDebugReportCallbackEXT(instance, &dbg_info, nullptr, &debug_callback); + if (err != VK_SUCCESS) { + THROW_VK_ERR("vkCreateDebugReportCallbackEXT", err); + } + } + + ~AppInstance() { + if (debug_callback) vkDestroyDebugReportCallbackEXT(instance, debug_callback, nullptr); + if (vkDestroyInstance) vkDestroyInstance(instance, nullptr); + unload_vulkan_library(); + } + + AppInstance(const AppInstance &) = delete; + const AppInstance &operator=(const AppInstance &) = delete; + + bool CheckExtensionEnabled(std::string extension_to_check) const { + return std::any_of(inst_extensions.begin(), inst_extensions.end(), + [extension_to_check](std::string str) { return str == extension_to_check; }); + } + + /* Gets a list of layer and instance extensions */ + void AppGetInstanceExtensions() { + /* Scan layers */ + auto global_layer_properties = + GetVector<VkLayerProperties>("vkEnumerateInstanceLayerProperties", vkEnumerateInstanceLayerProperties); + + for (const auto &layer : global_layer_properties) { + global_layers.push_back(LayerExtensionList{layer, AppGetGlobalLayerExtensions(layer.layerName)}); + } + + // Collect global extensions + // Gets instance extensions, if no layer was specified in the first paramteter + global_extensions = AppGetGlobalLayerExtensions(nullptr); + } + void AppCompileInstanceExtensionsToEnable() { +#if defined(VK_USE_PLATFORM_MACOS_MVK) || defined(VK_USE_PLATFORM_IOS_MVK) + bool metal_surface_available = false; + for (const auto &ext : global_extensions) { + if (strcmp("VK_EXT_metal_surface", ext.extensionName) == 0) { + metal_surface_available = true; + } + } +#endif + + for (const auto &ext : global_extensions) { + if (strcmp(VK_EXT_DEBUG_REPORT_EXTENSION_NAME, ext.extensionName) == 0) { + inst_extensions.push_back(ext.extensionName); + } + if (strcmp(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, ext.extensionName) == 0) { + inst_extensions.push_back(ext.extensionName); + } + if (strcmp(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME, ext.extensionName) == 0) { + inst_extensions.push_back(ext.extensionName); + } + if (strcmp(VK_KHR_SURFACE_EXTENSION_NAME, ext.extensionName) == 0) { + inst_extensions.push_back(ext.extensionName); + } +#ifdef VK_USE_PLATFORM_ANDROID_KHR + if (strcmp(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, ext.extensionName) == 0) { + inst_extensions.push_back(ext.extensionName); + } +#endif +#ifdef VK_USE_PLATFORM_FUCHSIA + if (strcmp(VK_FUCHSIA_IMAGEPIPE_SURFACE_EXTENSION_NAME, ext.extensionName) == 0) { + inst_extensions.push_back(ext.extensionName); + } +#endif +#ifdef VK_USE_PLATFORM_IOS_MVK + if (strcmp(VK_MVK_IOS_SURFACE_EXTENSION_NAME, ext.extensionName) == 0 && !metal_surface_available) { + inst_extensions.push_back(ext.extensionName); + } +#endif +#ifdef VK_USE_PLATFORM_MACOS_MVK + if (strcmp(VK_MVK_MACOS_SURFACE_EXTENSION_NAME, ext.extensionName) == 0 && !metal_surface_available) { + inst_extensions.push_back(ext.extensionName); + } +#endif +#ifdef VK_USE_PLATFORM_METAL_EXT + if (strcmp(VK_EXT_METAL_SURFACE_EXTENSION_NAME, ext.extensionName) == 0) { + inst_extensions.push_back(ext.extensionName); + } +#endif +#ifdef VK_USE_PLATFORM_VI_NN + if (strcmp(VK_NN_VI_SURFACE_EXTENSION_NAME, ext.extensionName) == 0) { + inst_extensions.push_back(ext.extensionName); + } +#endif +#ifdef VK_USE_PLATFORM_WAYLAND_KHR + if (strcmp(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, ext.extensionName) == 0) { + inst_extensions.push_back(ext.extensionName); + } +#endif +#ifdef VK_USE_PLATFORM_WIN32_KHR + if (strcmp(VK_KHR_WIN32_SURFACE_EXTENSION_NAME, ext.extensionName) == 0) { + inst_extensions.push_back(ext.extensionName); + } +#endif +#ifdef VK_USE_PLATFORM_XCB_KHR + if (strcmp(VK_KHR_XCB_SURFACE_EXTENSION_NAME, ext.extensionName) == 0) { + inst_extensions.push_back(ext.extensionName); + } +#endif +#ifdef VK_USE_PLATFORM_XLIB_KHR + if (strcmp(VK_KHR_XLIB_SURFACE_EXTENSION_NAME, ext.extensionName) == 0) { + inst_extensions.push_back(ext.extensionName); + } +#endif +#ifdef VK_USE_PLATFORM_DIRECTFB_EXT + if (strcmp(VK_EXT_DIRECTFB_SURFACE_EXTENSION_NAME, ext.extensionName) == 0) { + inst_extensions.push_back(ext.extensionName); + } +#endif +#ifdef VK_USE_PLATFORM_GGP + if (strcmp(VK_GGP_STREAM_DESCRIPTOR_SURFACE_EXTENSION_NAME, ext.extensionName) == 0) { + inst_extensions.push_back(ext.extensionName); + } +#endif +#ifdef VK_USE_PLATFORM_SCREEN_QNX + if (strcmp(VK_QNX_SCREEN_SURFACE_EXTENSION_NAME, ext.extensionName) == 0) { + inst_extensions.push_back(ext.extensionName); + } +#endif +#ifdef VK_USE_PLATFORM_DISPLAY + if (strcmp(VK_KHR_DISPLAY_EXTENSION_NAME, ext.extensionName) == 0) { + inst_extensions.push_back(ext.extensionName); + } +#endif + if (strcmp(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, ext.extensionName) == 0) { + inst_extensions.push_back(ext.extensionName); + } + if (strcmp(VK_KHR_SURFACE_PROTECTED_CAPABILITIES_EXTENSION_NAME, ext.extensionName) == 0) { + inst_extensions.push_back(ext.extensionName); + } + if (strcmp(VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME, ext.extensionName) == 0) { + inst_extensions.push_back(ext.extensionName); + } + if (strcmp(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME, ext.extensionName) == 0) { + inst_extensions.push_back(ext.extensionName); + } + } + } + + void AddSurfaceExtension(SurfaceExtension ext) { surface_extensions.push_back(ext); } + + std::vector<VkExtensionProperties> AppGetGlobalLayerExtensions(const char *layer_name) { + return GetVector<VkExtensionProperties>("vkEnumerateInstanceExtensionProperties", vkEnumerateInstanceExtensionProperties, + layer_name); + } + + std::vector<VkPhysicalDevice> FindPhysicalDevices() { + return GetVector<VkPhysicalDevice>("vkEnumeratePhysicalDevices", vkEnumeratePhysicalDevices, instance); + } + + std::vector<VkExtensionProperties> AppGetPhysicalDeviceLayerExtensions(VkPhysicalDevice phys_device, const char *layer_name) { + return GetVector<VkExtensionProperties>("vkEnumerateDeviceExtensionProperties", vkEnumerateDeviceExtensionProperties, + phys_device, layer_name); + } +}; + +// --------- Platform Specific Presentation Calls --------- // + +#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_DIRECTFB_EXT) || defined(VK_USE_PLATFORM_GGP) || defined(VK_USE_PLATFORM_SCREEN_QNX) || \ + defined(VK_USE_PLATFORM_DISPLAY) + +#define VULKANINFO_WSI_ENABLED +#endif + +//----------------------------------------------------------- +#if defined(VULKANINFO_WSI_ENABLED) +static void AppDestroySurface(AppInstance &inst, VkSurfaceKHR surface) { // same for all platforms + vkDestroySurfaceKHR(inst.instance, surface, nullptr); +} +#endif // defined(VULKANINFO_WSI_ENABLED) +//----------------------------------------------------------- + +//---------------------------Win32--------------------------- +#ifdef VK_USE_PLATFORM_WIN32_KHR + +// MS-Windows event handling function: +LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + return user32_handles->pfnDefWindowProcA(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 = user32_handles->pfnLoadIconA(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 = user32_handles->pfnLoadIconA(nullptr, IDI_WINLOGO); + // Register window class: + if (!user32_handles->pfnRegisterClassExA(&win_class)) { + // It didn't work, so try to give a useful error: + THROW_ERR("Failed to register the window class!"); + } + // Create window with the registered class: + RECT wr = {0, 0, inst.width, inst.height}; + user32_handles->pfnAdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE); + inst.h_wnd = user32_handles->pfnCreateWindowExA(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: + THROW_ERR("Failed to create a window!"); + } +} + +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) THROW_VK_ERR("vkCreateWin32SurfaceKHR", err); + return surface; +} + +static void AppDestroyWin32Window(AppInstance &inst) { user32_handles->pfnDestroyWindow(inst.h_wnd); } +#endif // VK_USE_PLATFORM_WIN32_KHR +//----------------------------------------------------------- + +//----------------------------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) { + THROW_ERR("AppCreateXcbSurface failed to establish connection"); + } + + 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) THROW_VK_ERR("vkCreateXcbSurfaceKHR", 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) { + THROW_ERR("XLib failed to connect to the X server.\nExiting..."); + } + + XVisualInfo vInfoTemplate = {}; + vInfoTemplate.screen = DefaultScreen(inst.xlib_display); + XVisualInfo *visualInfoBegin = XGetVisualInfo(inst.xlib_display, visualMask, &vInfoTemplate, &numberOfVisuals); + XVisualInfo *visualInfoEnd = visualInfoBegin + numberOfVisuals; + const Visual *rootVisual = DefaultVisual(inst.xlib_display, vInfoTemplate.screen); + const XVisualInfo *foundVisualInfo = + std::find_if(visualInfoBegin, visualInfoEnd, [rootVisual](const XVisualInfo &vi) { return vi.visual == rootVisual; }); + const XVisualInfo *visualInfo = foundVisualInfo == visualInfoEnd ? visualInfoBegin : foundVisualInfo; + 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(visualInfoBegin); +} + +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) THROW_VK_ERR("vkCreateXlibSurfaceKHR", 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) { + THROW_ERR("Could not create a native Metal view.\nExiting..."); + } +} + +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) THROW_VK_ERR("vkCreateMacOSSurfaceMVK", 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) { + THROW_ERR("Could not create a native Metal view.\nExiting..."); + } +} + +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) THROW_VK_ERR("vkCreateMetalSurfaceEXT", 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) THROW_VK_ERR("vkCreateWaylandSurfaceKHR", err); + return surface; +} + +static void AppDestroyWaylandWindow(AppInstance &inst) { wl_display_disconnect(inst.wayland_display); } +#endif // VK_USE_PLATFORM_WAYLAND_KHR +//----------------------------------------------------------- + +//-------------------------DIRECTFB-------------------------- +#ifdef VK_USE_PLATFORM_DIRECTFB_EXT +static void AppCreateDirectFBWindow(AppInstance &inst) { + DFBResult ret; + + ret = DirectFBInit(NULL, NULL); + if (ret) { + THROW_ERR("DirectFBInit failed to initialize DirectFB.\nExiting..."); + } + + ret = DirectFBCreate(&inst.dfb); + if (ret) { + THROW_ERR("DirectFBCreate failed to create main interface of DirectFB.\nExiting..."); + } + + DFBSurfaceDescription desc; + desc.flags = (DFBSurfaceDescriptionFlags)(DSDESC_CAPS | DSDESC_WIDTH | DSDESC_HEIGHT); + desc.caps = DSCAPS_PRIMARY; + desc.width = inst.width; + desc.height = inst.height; + ret = inst.dfb->CreateSurface(inst.dfb, &desc, &inst.directfb_surface); + if (ret) { + THROW_ERR("CreateSurface failed to create DirectFB surface interface.\nExiting..."); + } +} + +static VkSurfaceKHR AppCreateDirectFBSurface(AppInstance &inst) { + VkDirectFBSurfaceCreateInfoEXT createInfo; + createInfo.sType = VK_STRUCTURE_TYPE_DIRECTFB_SURFACE_CREATE_INFO_EXT; + createInfo.pNext = nullptr; + createInfo.flags = 0; + createInfo.dfb = inst.dfb; + createInfo.surface = inst.directfb_surface; + + VkSurfaceKHR surface; + VkResult err = vkCreateDirectFBSurfaceEXT(inst.instance, &createInfo, nullptr, &surface); + if (err) THROW_VK_ERR("vkCreateDirectFBSurfaceEXT", err); + return surface; +} + +static void AppDestroyDirectFBWindow(AppInstance &inst) { + inst.directfb_surface->Release(inst.directfb_surface); + inst.dfb->Release(inst.dfb); +} +#endif // VK_USE_PLATFORM_DIRECTFB_EXT +//----------------------------------------------------------- + +//-------------------------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); + + VkSurfaceKHR surface; + VkResult err = vkCreateAndroidSurfaceKHR(inst.instance, &createInfo, NULL, &surface); + if (err) THROW_VK_ERR("vkCreateAndroidSurfaceKHR", err); + return surface; +} +static void AppDestroyAndroidWindow(AppInstance &inst) {} +#endif +//----------------------------------------------------------- +//---------------------------GGP----------------------------- +#ifdef VK_USE_PLATFORM_GGP +static void AppCreateGgpWindow(AppInstance &inst) {} +static VkSurfaceKHR AppCreateGgpSurface(AppInstance &inst) { + VkStreamDescriptorSurfaceCreateInfoGGP createInfo; + createInfo.sType = VK_STRUCTURE_TYPE_STREAM_DESCRIPTOR_SURFACE_CREATE_INFO_GGP; + createInfo.pNext = NULL; + createInfo.flags = 0; + createInfo.streamDescriptor = 1; + + VkSurfaceKHR surface; + VkResult err = vkCreateStreamDescriptorSurfaceGGP(inst.instance, &createInfo, NULL, &surface); + if (err) THROW_VK_ERR("vkCreateStreamDescriptorSurfaceGGP", err); + return surface; +} +static void AppDestroyGgpWindow(AppInstance &inst) {} +#endif +//----------------------------------------------------------- +//----------------------QNX SCREEN--------------------------- +#ifdef VK_USE_PLATFORM_SCREEN_QNX +static void AppCreateScreenWindow(AppInstance &inst) { + int usage = SCREEN_USAGE_VULKAN; + int rc; + + rc = screen_create_context(&inst.context, 0); + if (rc) { + THROW_ERR("Could not create a QNX Screen context.\nExiting..."); + } + rc = screen_create_window(&inst.window, inst.context); + if (rc) { + THROW_ERR("Could not create a QNX Screen window.\nExiting..."); + } + rc = screen_set_window_property_iv(inst.window, SCREEN_PROPERTY_USAGE, &usage); + if (rc) { + THROW_ERR("Could not set SCREEN_USAGE_VULKAN flag for QNX Screen window!\nExiting..."); + } +} + +static VkSurfaceKHR AppCreateScreenSurface(AppInstance &inst) { + VkScreenSurfaceCreateInfoQNX createInfo; + createInfo.sType = VK_STRUCTURE_TYPE_SCREEN_SURFACE_CREATE_INFO_QNX; + createInfo.pNext = nullptr; + createInfo.flags = 0; + createInfo.context = inst.context; + createInfo.window = inst.window; + + VkSurfaceKHR surface; + VkResult err = vkCreateScreenSurfaceQNX(inst.instance, &createInfo, nullptr, &surface); + if (err) THROW_VK_ERR("vkCreateScreenSurfaceQNX", err); + return surface; +} + +static void AppDestroyScreenWindow(AppInstance &inst) { + screen_destroy_window(inst.window); + screen_destroy_context(inst.context); +} +#endif // VK_USE_PLATFORM_SCREEN_QNX +//----------------------------------------------------------- +// ------------ 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) { + has_display = false; + std::cerr << "'DISPLAY' environment variable not set... skipping surface info\n"; + } +#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) && !inst.CheckExtensionEnabled("VK_EXT_metal_surface")) { + 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 +//--DIRECTFB-- +#ifdef VK_USE_PLATFORM_DIRECTFB_EXT + SurfaceExtension surface_ext_directfb; + if (inst.CheckExtensionEnabled(VK_EXT_DIRECTFB_SURFACE_EXTENSION_NAME)) { + surface_ext_directfb.name = VK_EXT_DIRECTFB_SURFACE_EXTENSION_NAME; + surface_ext_directfb.create_window = AppCreateDirectFBWindow; + surface_ext_directfb.create_surface = AppCreateDirectFBSurface; + surface_ext_directfb.destroy_window = AppDestroyDirectFBWindow; + + inst.AddSurfaceExtension(surface_ext_directfb); + } +#endif +//--ANDROID-- +#ifdef VK_USE_PLATFORM_ANDROID_KHR + SurfaceExtension surface_ext_android; + if (inst.CheckExtensionEnabled(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME)) { + surface_ext_android.name = VK_KHR_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 +//--GGP-- +#ifdef VK_USE_PLATFORM_GGP + SurfaceExtension surface_ext_ggp; + if (inst.CheckExtensionEnabled(VK_GGP_STREAM_DESCRIPTOR_SURFACE_EXTENSION_NAME)) { + surface_ext_ggp.name = VK_GGP_STREAM_DESCRIPTOR_SURFACE_EXTENSION_NAME; + surface_ext_ggp.create_window = AppCreateGgpWindow; + surface_ext_ggp.create_surface = AppCreateGgpSurface; + surface_ext_ggp.destroy_window = AppDestroyGgpWindow; + + inst.AddSurfaceExtension(surface_ext_ggp); + } +#endif +//--QNX_SCREEN-- +#ifdef VK_USE_PLATFORM_SCREEN_QNX + SurfaceExtension surface_ext_qnx_screen; + if (inst.CheckExtensionEnabled(VK_QNX_SCREEN_SURFACE_EXTENSION_NAME)) { + surface_ext_qnx_screen.name = VK_QNX_SCREEN_SURFACE_EXTENSION_NAME; + surface_ext_qnx_screen.create_window = AppCreateScreenWindow; + surface_ext_qnx_screen.create_surface = AppCreateScreenSurface; + surface_ext_qnx_screen.destroy_window = AppDestroyScreenWindow; + + inst.AddSurfaceExtension(surface_ext_qnx_screen); + } +#endif +// TODO: add support for VK_KHR_display surfaces +} + +// ---------- Surfaces -------------- // + +class AppSurface { + public: + AppInstance &inst; + VkPhysicalDevice phys_device; + 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{}; + + std::unique_ptr<surface_capabilities2_chain> chain_for_surface_capabilities2; + + AppSurface(AppInstance &inst, AppGpu &gpu, VkPhysicalDevice phys_device, SurfaceExtension surface_extension) + : inst(inst), phys_device(phys_device), surface_extension(surface_extension) { + surf_present_modes = + GetVector<VkPresentModeKHR>("vkGetPhysicalDeviceSurfacePresentModesKHR", vkGetPhysicalDeviceSurfacePresentModesKHR, + phys_device, surface_extension.surface); + + if (inst.CheckExtensionEnabled(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME)) { + const VkPhysicalDeviceSurfaceInfo2KHR surface_info2 = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR, nullptr, + surface_extension.surface}; + VkSurfaceFormat2KHR init{}; + init.sType = VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR; + surf_formats2 = GetVectorInit<VkSurfaceFormat2KHR>( + "vkGetPhysicalDeviceSurfaceFormats2KHR", vkGetPhysicalDeviceSurfaceFormats2KHR, init, phys_device, &surface_info2); + } else { + surf_formats = + GetVector<VkSurfaceFormatKHR>("vkGetPhysicalDeviceSurfaceFormatsKHR", vkGetPhysicalDeviceSurfaceFormatsKHR, + phys_device, surface_extension.surface); + } + + if (inst.CheckExtensionEnabled(VK_KHR_SURFACE_EXTENSION_NAME)) { + VkResult err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(phys_device, surface_extension.surface, &surface_capabilities); + if (err) THROW_VK_ERR("vkGetPhysicalDeviceSurfaceCapabilitiesKHR", err); + } + + if (inst.CheckExtensionEnabled(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME)) { + surface_capabilities2_khr.sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR; + setup_surface_capabilities2_chain(surface_capabilities2_khr, chain_for_surface_capabilities2, inst, gpu); + + VkPhysicalDeviceSurfaceInfo2KHR surface_info{}; + surface_info.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR; + surface_info.surface = surface_extension.surface; +#if defined(WIN32) + VkSurfaceFullScreenExclusiveWin32InfoEXT win32_fullscreen_exclusive_info{}; + win32_fullscreen_exclusive_info.sType = VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_WIN32_INFO_EXT; + win32_fullscreen_exclusive_info.hmonitor = MonitorFromWindow(inst.h_wnd, MONITOR_DEFAULTTOPRIMARY); + + surface_info.pNext = static_cast<void *>(&win32_fullscreen_exclusive_info); +#endif // defined(WIN32) + VkResult err = vkGetPhysicalDeviceSurfaceCapabilities2KHR(phys_device, &surface_info, &surface_capabilities2_khr); + if (err) THROW_VK_ERR("vkGetPhysicalDeviceSurfaceCapabilities2KHR", 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 = + vkGetPhysicalDeviceSurfaceCapabilities2EXT(phys_device, surface_extension.surface, &surface_capabilities2_ext); + if (err) THROW_VK_ERR("vkGetPhysicalDeviceSurfaceCapabilities2EXT", err); + } + } + + 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)) { + VkPhysicalDeviceGroupProperties group_props{}; + group_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES; + return GetVectorInit<VkPhysicalDeviceGroupProperties>("vkEnumeratePhysicalDeviceGroupsKHR", + vkEnumeratePhysicalDeviceGroupsKHR, group_props, inst.instance); + } + return {}; +} + +std::vector<VkPhysicalDeviceProperties> GetGroupProps(AppInstance &inst, 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; +} + +util::vulkaninfo_optional<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}; + +#ifdef VK_ENABLE_BETA_EXTENSIONS + for (const auto &extension : inst.AppGetPhysicalDeviceLayerExtensions(group.physicalDevices[0], nullptr)) { + if (std::string(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME) == extension.extensionName) { + extensions_list.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); + } + } +#endif + + 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) THROW_VK_ERR("vkCreateDevice", err); + + if (err == VK_ERROR_EXTENSION_NOT_PRESENT) { + vkDestroyDevice(logical_device, nullptr); + return {}; + } + + 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. + err = vkGetDeviceGroupPresentCapabilitiesKHR(logical_device, &group_capabilities); + if (err) THROW_VK_ERR("vkGetDeviceGroupPresentCapabilitiesKHR", err); + + vkDestroyDevice(logical_device, nullptr); + + return {group_capabilities}; +} + +// -------------------- Device Setup ------------------- // + +const VkFormat color_format = VK_FORMAT_R8G8B8A8_UNORM; + +struct ImageTypeSupport { + enum class Type { regular, sparse, transient } type; + uint32_t memoryTypeBits; + + bool Compatible(uint32_t memtype_bit) { return memoryTypeBits & memtype_bit; } +}; + +struct ImageTypeFormatInfo { + VkFormat format; + std::vector<ImageTypeSupport> type_support; +}; + +struct ImageTypeInfos { + VkImageTiling tiling; + std::vector<ImageTypeFormatInfo> formats; +}; + +VkImageCreateInfo GetImageCreateInfo(VkImageCreateFlags flags, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usages) { + return {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + nullptr, + flags, + VK_IMAGE_TYPE_2D, + format, + {8, 8, 1}, + 1, + 1, + VK_SAMPLE_COUNT_1_BIT, + tiling, + usages, + VK_SHARING_MODE_EXCLUSIVE, + 0, + nullptr, + VK_IMAGE_LAYOUT_UNDEFINED}; +} + +util::vulkaninfo_optional<ImageTypeSupport> FillImageTypeSupport(AppInstance &inst, VkPhysicalDevice phys_device, VkDevice device, + ImageTypeSupport::Type img_type, VkImageCreateInfo image_ci) { + VkImageFormatProperties img_props; + VkResult res = vkGetPhysicalDeviceImageFormatProperties(phys_device, image_ci.format, image_ci.imageType, image_ci.tiling, + image_ci.usage, image_ci.flags, &img_props); + + if (res == VK_SUCCESS) { + ImageTypeSupport img_type_support{}; + img_type_support.type = img_type; + + VkImage dummy_img; + res = vkCreateImage(device, &image_ci, nullptr, &dummy_img); + if (res) THROW_VK_ERR("vkCreateImage", res); + + VkMemoryRequirements mem_req; + vkGetImageMemoryRequirements(device, dummy_img, &mem_req); + img_type_support.memoryTypeBits = mem_req.memoryTypeBits; + + vkDestroyImage(device, dummy_img, nullptr); + return img_type_support; + } else if (res == VK_ERROR_FORMAT_NOT_SUPPORTED) { + return {}; // return empty util::vulkaninfo_optional + } + THROW_VK_ERR("vkGetPhysicalDeviceImageFormatProperties", res); + return {}; +} + +struct FormatRange { + // the Vulkan standard version that supports this format range, or 0 if non-standard + APIVersion 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 AppQueueFamilyProperties { + VkQueueFamilyProperties props; + uint32_t queue_index; + void *pNext = nullptr; // assumes the lifetime of the pNext chain outlives this object, eg parent object must keep both alive + bool can_present = false; + bool can_always_present = true; + std::vector<std::pair<std::string, VkBool32>> present_support; + AppQueueFamilyProperties(AppInstance &inst, VkPhysicalDevice physical_device, VkQueueFamilyProperties family_properties, + uint32_t queue_index, void *pNext = nullptr) + : props(family_properties), queue_index(queue_index), pNext(pNext) { + for (const auto &surface_ext : inst.surface_extensions) { + present_support.push_back({surface_ext.name, VK_FALSE}); + VkResult err = vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, queue_index, surface_ext.surface, + &present_support.back().second); + if (err) THROW_VK_ERR("vkGetPhysicalDeviceSurfaceSupportKHR", err); + if (present_support.back().second) { + can_present = true; + } else { + can_always_present = false; + } + } + } +}; + +struct AppGpu { + AppInstance &inst; + uint32_t id{}; + VkPhysicalDevice phys_device = VK_NULL_HANDLE; + APIVersion api_version; + + VkPhysicalDeviceProperties props{}; + VkPhysicalDeviceProperties2KHR props2{}; + + // VkPhysicalDeviceDriverProperties + VkDriverId driverID; + char driverName[VK_MAX_DRIVER_NAME_SIZE]; + char driverInfo[VK_MAX_DRIVER_INFO_SIZE]; + VkConformanceVersion conformanceVersion; + // VkPhysicalDeviceIDProperties + uint8_t deviceUUID[VK_UUID_SIZE]; + uint8_t driverUUID[VK_UUID_SIZE]; + uint8_t deviceLUID[VK_LUID_SIZE]; + uint32_t deviceNodeMask; + VkBool32 deviceLUIDValid; + + bool found_driver_props = false; + bool found_device_id_props = false; + + std::vector<VkQueueFamilyProperties> queue_props; + std::vector<VkQueueFamilyProperties2KHR> queue_props2; + std::vector<AppQueueFamilyProperties> extended_queue_props; + + VkPhysicalDeviceMemoryProperties memory_props{}; + VkPhysicalDeviceMemoryProperties2KHR memory_props2{}; + + std::vector<ImageTypeInfos> memory_image_support_types; + + VkPhysicalDeviceFeatures features{}; + VkPhysicalDeviceFeatures2KHR features2{}; + + std::vector<VkExtensionProperties> device_extensions; + + VkDevice dev = VK_NULL_HANDLE; + VkPhysicalDeviceFeatures enabled_features{}; + + std::array<VkDeviceSize, VK_MAX_MEMORY_HEAPS> heapBudget; + std::array<VkDeviceSize, VK_MAX_MEMORY_HEAPS> heapUsage; + + std::unique_ptr<phys_device_props2_chain> chain_for_phys_device_props2; + std::unique_ptr<phys_device_mem_props2_chain> chain_for_phys_device_mem_props2; + std::unique_ptr<phys_device_features2_chain> chain_for_phys_device_features2; + std::vector<std::unique_ptr<queue_properties2_chain>> chain_for_queue_props2; + + std::vector<std::unique_ptr<AppVideoProfile>> video_profiles; + + std::vector<AppDisplay> displays; + std::vector<AppDisplayPlane> display_planes; + + AppGpu(AppInstance &inst, uint32_t id, VkPhysicalDevice phys_device, bool show_promoted_structs) + : inst(inst), id(id), phys_device(phys_device) { + vkGetPhysicalDeviceProperties(phys_device, &props); + + // needs to find the minimum of the instance and device version, and use that to print the device info + api_version = APIVersion(props.apiVersion); + + device_extensions = inst.AppGetPhysicalDeviceLayerExtensions(phys_device, nullptr); + + vkGetPhysicalDeviceMemoryProperties(phys_device, &memory_props); + + vkGetPhysicalDeviceFeatures(phys_device, &features); + + uint32_t queue_count = 0; + 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)) { + // VkPhysicalDeviceProperties2 + props2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR; + setup_phys_device_props2_chain(props2, chain_for_phys_device_props2, inst, *this, show_promoted_structs); + + vkGetPhysicalDeviceProperties2KHR(phys_device, &props2); + prepare_phys_device_props2_twocall_chain_vectors(chain_for_phys_device_props2); + vkGetPhysicalDeviceProperties2KHR(phys_device, &props2); + + // VkPhysicalDeviceMemoryProperties2 + memory_props2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR; + setup_phys_device_mem_props2_chain(memory_props2, chain_for_phys_device_mem_props2, *this); + + vkGetPhysicalDeviceMemoryProperties2KHR(phys_device, &memory_props2); + + // VkPhysicalDeviceFeatures2 + features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR; + setup_phys_device_features2_chain(features2, chain_for_phys_device_features2, *this, show_promoted_structs); + + vkGetPhysicalDeviceFeatures2KHR(phys_device, &features2); + + // std::vector<VkPhysicalDeviceQueueFamilyProperties2> + uint32_t queue_prop2_count = 0; + vkGetPhysicalDeviceQueueFamilyProperties2KHR(phys_device, &queue_prop2_count, nullptr); + queue_props2.resize(queue_prop2_count); + chain_for_queue_props2.resize(queue_prop2_count); + for (size_t i = 0; i < queue_props2.size(); i++) { + queue_props2[i].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2_KHR; + setup_queue_properties2_chain(queue_props2[i], chain_for_queue_props2[i], *this); + } + vkGetPhysicalDeviceQueueFamilyProperties2KHR(phys_device, &queue_prop2_count, queue_props2.data()); + + if (CheckPhysicalDeviceExtensionIncluded(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME) || + api_version >= VK_API_VERSION_1_2) { + void *place = props2.pNext; + while (place) { + VkBaseOutStructure *structure = static_cast<VkBaseOutStructure *>(place); + if (structure->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES) { + VkPhysicalDeviceDriverProperties *driver_driver_properties = + reinterpret_cast<VkPhysicalDeviceDriverProperties *>(structure); + driverID = driver_driver_properties->driverID; + memcpy(driverName, driver_driver_properties->driverName, VK_MAX_DRIVER_NAME_SIZE); + memcpy(driverInfo, driver_driver_properties->driverInfo, VK_MAX_DRIVER_INFO_SIZE); + conformanceVersion = driver_driver_properties->conformanceVersion; + found_driver_props = true; + } else if (structure->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES) { + VkPhysicalDeviceIDProperties *device_id_props = reinterpret_cast<VkPhysicalDeviceIDProperties *>(structure); + memcpy(deviceUUID, device_id_props->deviceUUID, VK_UUID_SIZE); + memcpy(driverUUID, device_id_props->driverUUID, VK_UUID_SIZE); + memcpy(deviceLUID, device_id_props->deviceLUID, VK_LUID_SIZE); + deviceNodeMask = device_id_props->deviceNodeMask; + deviceLUIDValid = device_id_props->deviceLUIDValid; + found_device_id_props = true; + } else if (structure->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES) { + VkPhysicalDeviceVulkan11Properties *vulkan_11_props = + reinterpret_cast<VkPhysicalDeviceVulkan11Properties *>(structure); + memcpy(deviceUUID, vulkan_11_props->deviceUUID, VK_UUID_SIZE); + memcpy(driverUUID, vulkan_11_props->driverUUID, VK_UUID_SIZE); + memcpy(deviceLUID, vulkan_11_props->deviceLUID, VK_LUID_SIZE); + deviceNodeMask = vulkan_11_props->deviceNodeMask; + deviceLUIDValid = vulkan_11_props->deviceLUIDValid; + found_device_id_props = true; + } else if (structure->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES) { + VkPhysicalDeviceVulkan12Properties *vulkan_12_props = + reinterpret_cast<VkPhysicalDeviceVulkan12Properties *>(structure); + driverID = vulkan_12_props->driverID; + memcpy(driverName, vulkan_12_props->driverName, VK_MAX_DRIVER_NAME_SIZE); + memcpy(driverInfo, vulkan_12_props->driverInfo, VK_MAX_DRIVER_INFO_SIZE); + conformanceVersion = vulkan_12_props->conformanceVersion; + found_driver_props = true; + } + place = structure->pNext; + } + } + } + + // Use the queue_props2 if they exist, else fallback on vulkan 1.0 queue_props + int queue_index = 0; + if (queue_props2.size() > 0) { + for (auto &queue_prop : queue_props2) { + extended_queue_props.push_back( + AppQueueFamilyProperties(inst, phys_device, queue_prop.queueFamilyProperties, queue_index++, queue_prop.pNext)); + } + } else { + for (auto &queue_prop : queue_props) { + extended_queue_props.push_back(AppQueueFamilyProperties(inst, phys_device, queue_prop, queue_index++, nullptr)); + } + } + + if (features.sparseBinding) { + enabled_features.sparseBinding = VK_TRUE; + } + + std::vector<const char *> extensions_to_enable; +#ifdef VK_ENABLE_BETA_EXTENSIONS + for (const auto &extension : device_extensions) { + if (std::string(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME) == extension.extensionName) { + extensions_to_enable.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); + } + } +#endif + + const float queue_priority = 1.0f; + // pick the first queue index and hope for the best + const VkDeviceQueueCreateInfo q_ci = {VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, nullptr, 0, 0, 1, &queue_priority}; + VkDeviceCreateInfo device_ci{}; + device_ci.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + device_ci.queueCreateInfoCount = 1; + device_ci.pQueueCreateInfos = &q_ci; + device_ci.enabledExtensionCount = static_cast<uint32_t>(extensions_to_enable.size()); + device_ci.ppEnabledExtensionNames = extensions_to_enable.data(); + device_ci.pEnabledFeatures = &enabled_features; + + VkResult err = vkCreateDevice(phys_device, &device_ci, nullptr, &dev); + if (err) THROW_VK_ERR("vkCreateDevice", err); + + const std::array<VkImageTiling, 2> tilings = {VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_TILING_LINEAR}; + const std::array<VkFormat, 8> 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}; + + for (const VkImageTiling tiling : tilings) { + ImageTypeInfos image_type_infos; + image_type_infos.tiling = tiling; + + for (const VkFormat format : formats) { + ImageTypeFormatInfo image_type_format_info; + image_type_format_info.format = format; + + VkFormatProperties fmt_props; + vkGetPhysicalDeviceFormatProperties(phys_device, format, &fmt_props); + if ((tiling == VK_IMAGE_TILING_OPTIMAL && fmt_props.optimalTilingFeatures == 0) || + (tiling == VK_IMAGE_TILING_LINEAR && fmt_props.linearTilingFeatures == 0)) { + continue; + } + + VkImageCreateInfo image_ci_regular = GetImageCreateInfo(0, format, tiling, 0); + VkImageCreateInfo image_ci_transient = + GetImageCreateInfo(0, format, tiling, VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT); + VkImageCreateInfo image_ci_sparse = + GetImageCreateInfo(VK_IMAGE_CREATE_SPARSE_BINDING_BIT, format, tiling, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); + + if (tiling == VK_IMAGE_TILING_LINEAR) { + if (format == color_format) { + image_ci_regular.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + image_ci_transient.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + } else { + // linear tiling is only applicable to color image types + continue; + } + } else { + if (format == color_format) { + image_ci_regular.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + image_ci_transient.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + + } else { + image_ci_regular.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + image_ci_transient.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + } + } + + auto image_ts_regular_ret = + FillImageTypeSupport(inst, phys_device, dev, ImageTypeSupport::Type::regular, image_ci_regular); + if (image_ts_regular_ret) { + image_type_format_info.type_support.push_back(image_ts_regular_ret.value()); + } + auto image_ts_transient_ret = + FillImageTypeSupport(inst, phys_device, dev, ImageTypeSupport::Type::transient, image_ci_transient); + if (image_ts_transient_ret) { + image_type_format_info.type_support.push_back(image_ts_transient_ret.value()); + } + + if (enabled_features.sparseBinding) { + auto image_ts_sparse_ret = + FillImageTypeSupport(inst, phys_device, dev, ImageTypeSupport::Type::sparse, image_ci_sparse); + if (image_ts_sparse_ret) { + image_type_format_info.type_support.push_back(image_ts_sparse_ret.value()); + } + } + image_type_infos.formats.push_back(image_type_format_info); + } + memory_image_support_types.push_back(image_type_infos); + } + + // Memory // + + struct VkBaseOutStructure *structure = NULL; + if (inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) { + structure = (struct VkBaseOutStructure *)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 = + reinterpret_cast<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 = structure->pNext; + } + } + // TODO buffer - memory type compatibility + + // Video // + video_profiles = enumerate_supported_video_profiles(*this); + + // Display // + display_planes = enumerate_display_planes(*this); + displays = enumerate_displays(*this, display_planes); + + vkDestroyDevice(dev, nullptr); + dev = VK_NULL_HANDLE; + } + ~AppGpu() { vkDestroyDevice(dev, nullptr); } + + AppGpu(const AppGpu &) = delete; + const AppGpu &operator=(const AppGpu &) = delete; + + bool CheckPhysicalDeviceExtensionIncluded(std::string extension_to_check) const { + return std::any_of( + device_extensions.begin(), device_extensions.end(), + [extension_to_check](const VkExtensionProperties &prop) { return prop.extensionName == extension_to_check; }); + } + + // Helper function to determine whether a format range is currently supported. + bool FormatRangeSupported(const FormatRange &format_range) const { + // Formats from base vulkan spec + if (format_range.minimum_instance_version == 0 && format_range.extension_name == nullptr) { + return true; + } + + // True if this extension is present + if (format_range.extension_name != nullptr) { + return CheckPhysicalDeviceExtensionIncluded(format_range.extension_name); + } + + // True if standard and supported by both this instance and this GPU + if (inst.api_version >= format_range.minimum_instance_version && + APIVersion(props.apiVersion) >= format_range.minimum_instance_version) { + return true; + } + + // 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; + } + } + + // Vendor specific driverVersion mapping scheme + // If one isn't present, fall back to the standard Vulkan scheme + std::string GetDriverVersionString() { + uint32_t v = props.driverVersion; + if ((found_driver_props && driverID == VK_DRIVER_ID_NVIDIA_PROPRIETARY) || + (!found_driver_props && props.deviceID == 4318)) { + return std::to_string((v >> 22) & 0x3ff) + "." + std::to_string((v >> 14) & 0x0ff) + "." + + std::to_string((v >> 6) & 0x0ff) + "." + std::to_string(v & 0x003f); + } else if ((found_driver_props && driverID == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) +#if defined(WIN32) + || (!found_driver_props && props.deviceID == 0x8086) // only do the fallback check if running in windows +#endif + ) { + return std::to_string(v >> 14) + "." + std::to_string(v & 0x3fff); + } else { + // AMD uses the standard vulkan scheme + return APIVersion(v).str(); + } + } + const AppDisplay *FindDisplay(VkDisplayKHR handle) { + for (const auto &disp : displays) { + if (disp.properties.display == handle) { + return &disp; + } + } + return NULL; + } +}; + +std::vector<VkPhysicalDeviceToolPropertiesEXT> GetToolingInfo(AppGpu &gpu) { + if (vkGetPhysicalDeviceToolPropertiesEXT == nullptr) return {}; + return GetVector<VkPhysicalDeviceToolPropertiesEXT>("vkGetPhysicalDeviceToolPropertiesEXT", + vkGetPhysicalDeviceToolPropertiesEXT, gpu.phys_device); +} + +std::vector<VkCooperativeMatrixPropertiesKHR> GetCooperativeMatrixInfo(AppGpu &gpu) { + if (vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR == nullptr) return {}; + return GetVector<VkCooperativeMatrixPropertiesKHR>("vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR", + vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR, gpu.phys_device); +} +std::vector<VkTimeDomainKHR> GetTimeDomainInfo(AppGpu &gpu) { + if (vkGetPhysicalDeviceCalibrateableTimeDomainsKHR == nullptr) return {}; + return GetVector<VkTimeDomainKHR>("vkGetPhysicalDeviceCalibrateableTimeDomainsKHR", + vkGetPhysicalDeviceCalibrateableTimeDomainsKHR, gpu.phys_device); +} +std::vector<VkPhysicalDeviceFragmentShadingRateKHR> GetFragmentShadingRateInfo(AppGpu &gpu) { + if (vkGetPhysicalDeviceFragmentShadingRatesKHR == nullptr) return {}; + return GetVector<VkPhysicalDeviceFragmentShadingRateKHR>("vkGetPhysicalDeviceFragmentShadingRatesKHR", + vkGetPhysicalDeviceFragmentShadingRatesKHR, gpu.phys_device); +} +// Returns vector where each index maps to VkSampleCountFlagBits +std::vector<VkMultisamplePropertiesEXT> GetSampleLocationInfo(AppGpu &gpu) { + if (vkGetPhysicalDeviceMultisamplePropertiesEXT == nullptr) return {}; + std::vector<VkMultisamplePropertiesEXT> result; + // 7 covers VK_SAMPLE_COUNT_1_BIT to 64_BIT + for (uint32_t i = 0; i < 7; i++) { + const VkSampleCountFlagBits sample_count = (VkSampleCountFlagBits)(1 << i); + VkMultisamplePropertiesEXT props = {VK_STRUCTURE_TYPE_MULTISAMPLE_PROPERTIES_EXT, nullptr}; + vkGetPhysicalDeviceMultisamplePropertiesEXT(gpu.phys_device, sample_count, &props); + result.emplace_back(props); + } + return result; +} + +// --------- Format Properties ----------// +// can't use autogen because that is put in a header that we can't include because that header depends on stuff defined here +bool operator==(const VkFormatProperties &a, const VkFormatProperties b) { + return a.linearTilingFeatures == b.linearTilingFeatures && a.optimalTilingFeatures == b.optimalTilingFeatures && + a.bufferFeatures == b.bufferFeatures; +} +bool operator==(const VkFormatProperties3 &a, const VkFormatProperties3 b) { + return a.linearTilingFeatures == b.linearTilingFeatures && a.optimalTilingFeatures == b.optimalTilingFeatures && + a.bufferFeatures == b.bufferFeatures; +} + +struct PropFlags { + VkFormatProperties props; + VkFormatProperties3 props3; + + bool operator==(const PropFlags &other) const { return props == other.props && props3 == other.props3; } +}; + +PropFlags get_format_properties(const AppGpu &gpu, VkFormat fmt) { + VkFormatProperties props; + vkGetPhysicalDeviceFormatProperties(gpu.phys_device, fmt, &props); + + VkFormatProperties3 props3{}; + props3.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3; + + if (gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME) && + gpu.CheckPhysicalDeviceExtensionIncluded(VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME)) { + VkFormatProperties2 props2{}; + props2.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + props2.formatProperties = props; + props2.pNext = static_cast<void *>(&props3); + vkGetPhysicalDeviceFormatProperties2KHR(gpu.phys_device, fmt, &props2); + } + return {props, props3}; +} + +namespace std { +template <> +struct hash<VkFormatProperties> { + std::size_t operator()(const VkFormatProperties &k) const { + return ((std::hash<uint32_t>()(k.linearTilingFeatures) ^ (std::hash<uint32_t>()(k.optimalTilingFeatures) << 1)) >> 1) ^ + (std::hash<uint32_t>()(k.bufferFeatures) << 1); + } +}; +template <> +struct hash<VkFormatProperties3> { + std::size_t operator()(const VkFormatProperties3 &k) const { + return ((std::hash<uint64_t>()(k.linearTilingFeatures) ^ (std::hash<uint64_t>()(k.optimalTilingFeatures) << 1)) >> 1) ^ + (std::hash<uint64_t>()(k.bufferFeatures) << 1); + } +}; +template <> +struct hash<PropFlags> { + std::size_t operator()(const PropFlags &k) const { + return (std::hash<VkFormatProperties>()(k.props) ^ std::hash<VkFormatProperties3>()(k.props3)) >> 1; + } +}; +} // namespace std |
