blob: a3f356b1e927873792b327e96142ac99578b1dc4 [file] [log] [blame]
/*
* Copyright (c) 2015-2021 The Khronos Group Inc.
* Copyright (c) 2015-2021 Valve Corporation
* Copyright (c) 2015-2021 LunarG, Inc.
* Copyright (c) 2023-2023 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 <exception>
#include <iostream>
#include <fstream>
#include <memory>
#include <ostream>
#include <set>
#include <string>
#include <unordered_map>
#include <set>
#include <vector>
#include <utility>
#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/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 <vulkan/vulkan.h>
#define VOLK_IMPLEMENTATION
#include "volk.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 = 0;
std::vector<T> results;
VkResult err;
uint32_t iteration_count = 0;
uint32_t max_iterations = 3;
do {
err = f(ts..., &count, nullptr);
if (err) THROW_VK_ERR(func_name, err);
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 && iteration_count <= max_iterations) 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 AppInstance;
struct AppGpu;
void setup_phys_device_props2_chain(VkPhysicalDeviceProperties2 &start, std::unique_ptr<phys_device_props2_chain> &chain,
AppInstance &inst, AppGpu &gpu);
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);
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);
/* 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;
VkBool32 supports_present = 0;
bool operator==(const SurfaceExtension &other) {
return name == other.name && surface == other.surface && supports_present == other.supports_present;
}
};
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 = volkInitialize();
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);
}
volkLoadInstance(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);
volkFinalize();
}
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() {
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) {
inst_extensions.push_back(ext.extensionName);
}
#endif
#ifdef VK_USE_PLATFORM_MACOS_MVK
if (strcmp(VK_MVK_MACOS_SURFACE_EXTENSION_NAME, ext.extensionName) == 0) {
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
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)
#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 *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) 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)) {
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
}
// ---------- 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);
const VkPhysicalDeviceSurfaceInfo2KHR surface_info2 = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR, nullptr,
surface_extension.surface};
if (inst.CheckExtensionEnabled(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME)) {
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 is_present_platform_agnostic = true;
VkBool32 platforms_support_present = VK_FALSE;
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 (auto &surface_ext : inst.surface_extensions) {
VkResult err = vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, queue_index, surface_ext.surface,
&surface_ext.supports_present);
if (err) THROW_VK_ERR("vkGetPhysicalDeviceSurfaceSupportKHR", err);
const bool first = (surface_ext == 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;
}
}
};
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;
AppGpu(AppInstance &inst, uint32_t id, VkPhysicalDevice phys_device) : 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);
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);
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
}
~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 inst.CheckExtensionEnabled(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();
}
}
};
std::vector<VkPhysicalDeviceToolPropertiesEXT> GetToolingInfo(AppGpu &gpu) {
if (vkGetPhysicalDeviceToolPropertiesEXT == nullptr) return {};
return GetVector<VkPhysicalDeviceToolPropertiesEXT>("vkGetPhysicalDeviceToolPropertiesEXT",
vkGetPhysicalDeviceToolPropertiesEXT, gpu.phys_device);
}
// --------- 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