CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Common/GPU/Vulkan/VulkanContext.cpp
Views: 1401
#define __STDC_LIMIT_MACROS12#include <cstdlib>3#include <cstdint>4#include <cstring>5#include <iostream>67#include "Core/Config.h"8#include "Common/System/System.h"9#include "Common/System/Display.h"10#include "Common/Log.h"11#include "Common/GPU/Shader.h"12#include "Common/GPU/Vulkan/VulkanContext.h"13#include "Common/GPU/Vulkan/VulkanDebug.h"14#include "Common/StringUtils.h"1516#ifdef USE_CRT_DBG17#undef new18#endif1920#include "ext/vma/vk_mem_alloc.h"212223// Change this to 1, 2, and 3 to fake failures in a few places, so that24// we can test our fallback-to-GL code.25#define SIMULATE_VULKAN_FAILURE 02627#include "ext/glslang/SPIRV/GlslangToSpv.h"2829#ifdef USE_CRT_DBG30#define new DBG_NEW31#endif3233using namespace PPSSPP_VK;3435VulkanLogOptions g_LogOptions;3637static const char * const validationLayers[] = {38"VK_LAYER_KHRONOS_validation",39/*40// For layers included in the Android NDK.41"VK_LAYER_GOOGLE_threading",42"VK_LAYER_LUNARG_parameter_validation",43"VK_LAYER_LUNARG_core_validation",44"VK_LAYER_LUNARG_image",45"VK_LAYER_LUNARG_object_tracker",46"VK_LAYER_LUNARG_swapchain",47"VK_LAYER_GOOGLE_unique_objects",48*/49};5051std::string VulkanVendorString(uint32_t vendorId) {52switch (vendorId) {53case VULKAN_VENDOR_INTEL: return "Intel";54case VULKAN_VENDOR_NVIDIA: return "NVIDIA";55case VULKAN_VENDOR_AMD: return "AMD";56case VULKAN_VENDOR_ARM: return "ARM";57case VULKAN_VENDOR_QUALCOMM: return "Qualcomm";58case VULKAN_VENDOR_IMGTEC: return "Imagination";59case VULKAN_VENDOR_APPLE: return "Apple";60case VULKAN_VENDOR_MESA: return "Mesa";61default:62return StringFromFormat("%08x", vendorId);63}64}6566const char *VulkanPresentModeToString(VkPresentModeKHR presentMode) {67switch (presentMode) {68case VK_PRESENT_MODE_IMMEDIATE_KHR: return "IMMEDIATE";69case VK_PRESENT_MODE_MAILBOX_KHR: return "MAILBOX";70case VK_PRESENT_MODE_FIFO_KHR: return "FIFO";71case VK_PRESENT_MODE_FIFO_RELAXED_KHR: return "FIFO_RELAXED";72case VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR: return "SHARED_DEMAND_REFRESH_KHR";73case VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR: return "SHARED_CONTINUOUS_REFRESH_KHR";74default: return "UNKNOWN";75}76}7778const char *VulkanImageLayoutToString(VkImageLayout imageLayout) {79switch (imageLayout) {80case VK_IMAGE_LAYOUT_UNDEFINED: return "UNDEFINED";81case VK_IMAGE_LAYOUT_GENERAL: return "GENERAL";82case VK_IMAGE_LAYOUT_PREINITIALIZED: return "PREINITIALIZED";83case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: return "TRANSFER_SRC_OPTIMAL";84case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: return "TRANSFER_DST_OPTIMAL";85case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: return "SHADER_READ_ONLY_OPTIMAL";86case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: return "COLOR_ATTACHMENT_OPTIMAL";87case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: return "DEPTH_STENCIL_ATTACHMENT_OPTIMAL";88default: return "OTHER";89}90}9192VulkanContext::VulkanContext() {93// Do nothing here.94}9596VkResult VulkanContext::CreateInstance(const CreateInfo &info) {97if (!vkCreateInstance) {98init_error_ = "Vulkan not loaded - can't create instance";99return VK_ERROR_INITIALIZATION_FAILED;100}101102// Check which Vulkan version we should request.103// Our code is fine with any version from 1.0 to 1.2, we don't know about higher versions.104vulkanInstanceApiVersion_ = VK_API_VERSION_1_0;105if (vkEnumerateInstanceVersion) {106vkEnumerateInstanceVersion(&vulkanInstanceApiVersion_);107vulkanInstanceApiVersion_ &= 0xFFFFF000; // Remove patch version.108vulkanInstanceApiVersion_ = std::min(VK_API_VERSION_1_3, vulkanInstanceApiVersion_);109std::string versionString = FormatAPIVersion(vulkanInstanceApiVersion_);110INFO_LOG(Log::G3D, "Detected Vulkan API version: %s", versionString.c_str());111}112113instance_layer_names_.clear();114device_layer_names_.clear();115116// We can get the list of layers and extensions without an instance so we can use this information117// to enable the extensions we need that are available.118GetInstanceLayerProperties();119GetInstanceLayerExtensionList(nullptr, instance_extension_properties_);120121if (!IsInstanceExtensionAvailable(VK_KHR_SURFACE_EXTENSION_NAME)) {122// Cannot create a Vulkan display without VK_KHR_SURFACE_EXTENSION.123init_error_ = "Vulkan not loaded - no surface extension";124return VK_ERROR_INITIALIZATION_FAILED;125}126flags_ = info.flags;127128// List extensions to try to enable.129instance_extensions_enabled_.push_back(VK_KHR_SURFACE_EXTENSION_NAME);130#ifdef _WIN32131instance_extensions_enabled_.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);132#elif defined(__ANDROID__)133instance_extensions_enabled_.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);134#else135#if defined(VK_USE_PLATFORM_XLIB_KHR)136if (IsInstanceExtensionAvailable(VK_KHR_XLIB_SURFACE_EXTENSION_NAME)) {137instance_extensions_enabled_.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);138}139#endif140//#if defined(VK_USE_PLATFORM_XCB_KHR)141// instance_extensions_enabled_.push_back(VK_KHR_XCB_SURFACE_EXTENSION_NAME);142//#endif143#if defined(VK_USE_PLATFORM_WAYLAND_KHR)144if (IsInstanceExtensionAvailable(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME)) {145instance_extensions_enabled_.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);146}147#endif148#if defined(VK_USE_PLATFORM_DISPLAY_KHR)149if (IsInstanceExtensionAvailable(VK_KHR_DISPLAY_EXTENSION_NAME)) {150instance_extensions_enabled_.push_back(VK_KHR_DISPLAY_EXTENSION_NAME);151}152#endif153#if defined(VK_USE_PLATFORM_METAL_EXT)154if (IsInstanceExtensionAvailable(VK_EXT_METAL_SURFACE_EXTENSION_NAME)) {155instance_extensions_enabled_.push_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME);156}157#endif158#endif159160if ((flags_ & VULKAN_FLAG_VALIDATE) && g_Config.sCustomDriver.empty()) {161if (IsInstanceExtensionAvailable(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) {162// Enable the validation layers163for (size_t i = 0; i < ARRAY_SIZE(validationLayers); i++) {164instance_layer_names_.push_back(validationLayers[i]);165device_layer_names_.push_back(validationLayers[i]);166}167instance_extensions_enabled_.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);168extensionsLookup_.EXT_debug_utils = true;169INFO_LOG(Log::G3D, "Vulkan debug_utils validation enabled.");170} else {171ERROR_LOG(Log::G3D, "Validation layer extension not available - not enabling Vulkan validation.");172flags_ &= ~VULKAN_FLAG_VALIDATE;173}174}175176// Temporary hack for libretro. For some reason, when we try to load the functions from this extension,177// we get null pointers when running libretro. Quite strange.178#if !defined(__LIBRETRO__)179if (EnableInstanceExtension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, VK_API_VERSION_1_1)) {180extensionsLookup_.KHR_get_physical_device_properties2 = true;181}182#endif183184if (EnableInstanceExtension(VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME, 0)) {185extensionsLookup_.EXT_swapchain_colorspace = true;186}187#if PPSSPP_PLATFORM(IOS_APP_STORE)188if (EnableInstanceExtension(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME, 0)) {189190}191#endif192193// Validate that all the instance extensions we ask for are actually available.194for (auto ext : instance_extensions_enabled_) {195if (!IsInstanceExtensionAvailable(ext))196WARN_LOG(Log::G3D, "WARNING: Does not seem that instance extension '%s' is available. Trying to proceed anyway.", ext);197}198199VkApplicationInfo app_info{ VK_STRUCTURE_TYPE_APPLICATION_INFO };200app_info.pApplicationName = info.app_name;201app_info.applicationVersion = info.app_ver;202app_info.pEngineName = info.app_name;203// Let's increment this when we make major engine/context changes.204app_info.engineVersion = 2;205app_info.apiVersion = vulkanInstanceApiVersion_;206207VkInstanceCreateInfo inst_info{ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };208inst_info.flags = 0;209inst_info.pApplicationInfo = &app_info;210inst_info.enabledLayerCount = (uint32_t)instance_layer_names_.size();211inst_info.ppEnabledLayerNames = instance_layer_names_.size() ? instance_layer_names_.data() : nullptr;212inst_info.enabledExtensionCount = (uint32_t)instance_extensions_enabled_.size();213inst_info.ppEnabledExtensionNames = instance_extensions_enabled_.size() ? instance_extensions_enabled_.data() : nullptr;214215#if PPSSPP_PLATFORM(IOS_APP_STORE)216inst_info.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;217#endif218219#if SIMULATE_VULKAN_FAILURE == 2220VkResult res = VK_ERROR_INCOMPATIBLE_DRIVER;221#else222VkResult res = vkCreateInstance(&inst_info, nullptr, &instance_);223#endif224if (res != VK_SUCCESS) {225if (res == VK_ERROR_LAYER_NOT_PRESENT) {226WARN_LOG(Log::G3D, "Validation on but instance layer not available - dropping layers");227// Drop the validation layers and try again.228instance_layer_names_.clear();229device_layer_names_.clear();230inst_info.enabledLayerCount = 0;231inst_info.ppEnabledLayerNames = nullptr;232res = vkCreateInstance(&inst_info, nullptr, &instance_);233if (res != VK_SUCCESS)234ERROR_LOG(Log::G3D, "Failed to create instance even without validation: %d", res);235} else {236ERROR_LOG(Log::G3D, "Failed to create instance : %d", res);237}238}239if (res != VK_SUCCESS) {240init_error_ = "Failed to create Vulkan instance";241return res;242}243244VulkanLoadInstanceFunctions(instance_, extensionsLookup_, vulkanInstanceApiVersion_);245if (!CheckLayers(instance_layer_properties_, instance_layer_names_)) {246WARN_LOG(Log::G3D, "CheckLayers for instance failed");247// init_error_ = "Failed to validate instance layers";248// return;249}250251uint32_t gpu_count = 1;252#if SIMULATE_VULKAN_FAILURE == 3253gpu_count = 0;254#else255res = vkEnumeratePhysicalDevices(instance_, &gpu_count, nullptr);256#endif257if (gpu_count <= 0) {258ERROR_LOG(Log::G3D, "Vulkan driver found but no supported GPU is available");259init_error_ = "No Vulkan physical devices found";260vkDestroyInstance(instance_, nullptr);261instance_ = nullptr;262return VK_ERROR_INITIALIZATION_FAILED;263}264265_dbg_assert_(gpu_count > 0);266physical_devices_.resize(gpu_count);267physicalDeviceProperties_.resize(gpu_count);268res = vkEnumeratePhysicalDevices(instance_, &gpu_count, physical_devices_.data());269if (res != VK_SUCCESS) {270init_error_ = "Failed to enumerate physical devices";271vkDestroyInstance(instance_, nullptr);272instance_ = nullptr;273return res;274}275276if (extensionsLookup_.KHR_get_physical_device_properties2 && vkGetPhysicalDeviceProperties2) {277for (uint32_t i = 0; i < gpu_count; i++) {278VkPhysicalDeviceProperties2 props2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2};279VkPhysicalDevicePushDescriptorPropertiesKHR pushProps{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR};280VkPhysicalDeviceExternalMemoryHostPropertiesEXT extHostMemProps{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_HOST_PROPERTIES_EXT};281VkPhysicalDeviceDepthStencilResolveProperties depthStencilResolveProps{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES};282ChainStruct(props2, &pushProps);283ChainStruct(props2, &extHostMemProps);284ChainStruct(props2, &depthStencilResolveProps);285vkGetPhysicalDeviceProperties2(physical_devices_[i], &props2);286287// Don't want bad pointers sitting around. Probably not really necessary.288props2.pNext = nullptr;289pushProps.pNext = nullptr;290extHostMemProps.pNext = nullptr;291depthStencilResolveProps.pNext = nullptr;292physicalDeviceProperties_[i].properties = props2.properties;293physicalDeviceProperties_[i].pushDescriptorProperties = pushProps;294physicalDeviceProperties_[i].externalMemoryHostProperties = extHostMemProps;295physicalDeviceProperties_[i].depthStencilResolve = depthStencilResolveProps;296}297} else {298for (uint32_t i = 0; i < gpu_count; i++) {299vkGetPhysicalDeviceProperties(physical_devices_[i], &physicalDeviceProperties_[i].properties);300}301}302303if (extensionsLookup_.EXT_debug_utils) {304_assert_(vkCreateDebugUtilsMessengerEXT != nullptr);305InitDebugUtilsCallback();306}307308return VK_SUCCESS;309}310311VulkanContext::~VulkanContext() {312_dbg_assert_(instance_ == VK_NULL_HANDLE);313}314315void VulkanContext::DestroyInstance() {316if (extensionsLookup_.EXT_debug_utils) {317while (utils_callbacks.size() > 0) {318vkDestroyDebugUtilsMessengerEXT(instance_, utils_callbacks.back(), nullptr);319utils_callbacks.pop_back();320}321}322323vkDestroyInstance(instance_, nullptr);324VulkanFree();325instance_ = VK_NULL_HANDLE;326}327328void VulkanContext::BeginFrame(VkCommandBuffer firstCommandBuffer) {329FrameData *frame = &frame_[curFrame_];330// Process pending deletes.331frame->deleteList.PerformDeletes(this, allocator_);332// VK_NULL_HANDLE when profiler is disabled.333if (firstCommandBuffer) {334frame->profiler.BeginFrame(this, firstCommandBuffer);335}336}337338void VulkanContext::EndFrame() {339frame_[curFrame_].deleteList.Take(globalDeleteList_);340curFrame_++;341if (curFrame_ >= inflightFrames_) {342curFrame_ = 0;343}344}345346void VulkanContext::UpdateInflightFrames(int n) {347_dbg_assert_(n >= 1 && n <= MAX_INFLIGHT_FRAMES);348inflightFrames_ = n;349if (curFrame_ >= inflightFrames_) {350curFrame_ = 0;351}352}353354void VulkanContext::WaitUntilQueueIdle() {355// Should almost never be used356vkQueueWaitIdle(gfx_queue_);357}358359bool VulkanContext::MemoryTypeFromProperties(uint32_t typeBits, VkFlags requirements_mask, uint32_t *typeIndex) {360// Search memtypes to find first index with those properties361for (uint32_t i = 0; i < 32; i++) {362if ((typeBits & 1) == 1) {363// Type is available, does it match user properties?364if ((memory_properties_.memoryTypes[i].propertyFlags & requirements_mask) == requirements_mask) {365*typeIndex = i;366return true;367}368}369typeBits >>= 1;370}371// No memory types matched, return failure372return false;373}374375void VulkanContext::DestroySwapchain() {376if (swapchain_ != VK_NULL_HANDLE) {377vkDestroySwapchainKHR(device_, swapchain_, nullptr);378swapchain_ = VK_NULL_HANDLE;379}380}381382void VulkanContext::DestroySurface() {383if (surface_ != VK_NULL_HANDLE) {384vkDestroySurfaceKHR(instance_, surface_, nullptr);385surface_ = VK_NULL_HANDLE;386}387}388389VkResult VulkanContext::GetInstanceLayerExtensionList(const char *layerName, std::vector<VkExtensionProperties> &extensions) {390VkResult res;391do {392uint32_t instance_extension_count;393res = vkEnumerateInstanceExtensionProperties(layerName, &instance_extension_count, nullptr);394if (res != VK_SUCCESS)395return res;396if (instance_extension_count == 0)397return VK_SUCCESS;398extensions.resize(instance_extension_count);399res = vkEnumerateInstanceExtensionProperties(layerName, &instance_extension_count, extensions.data());400} while (res == VK_INCOMPLETE);401return res;402}403404VkResult VulkanContext::GetInstanceLayerProperties() {405/*406* It's possible, though very rare, that the number of407* instance layers could change. For example, installing something408* could include new layers that the loader would pick up409* between the initial query for the count and the410* request for VkLayerProperties. The loader indicates that411* by returning a VK_INCOMPLETE status and will update the412* the count parameter.413* The count parameter will be updated with the number of414* entries loaded into the data pointer - in case the number415* of layers went down or is smaller than the size given.416*/417uint32_t instance_layer_count;418std::vector<VkLayerProperties> vk_props;419VkResult res;420do {421res = vkEnumerateInstanceLayerProperties(&instance_layer_count, nullptr);422if (res != VK_SUCCESS)423return res;424if (!instance_layer_count)425return VK_SUCCESS;426vk_props.resize(instance_layer_count);427res = vkEnumerateInstanceLayerProperties(&instance_layer_count, vk_props.data());428} while (res == VK_INCOMPLETE);429430// Now gather the extension list for each instance layer.431for (uint32_t i = 0; i < instance_layer_count; i++) {432LayerProperties layer_props;433layer_props.properties = vk_props[i];434res = GetInstanceLayerExtensionList(layer_props.properties.layerName, layer_props.extensions);435if (res != VK_SUCCESS)436return res;437instance_layer_properties_.push_back(layer_props);438}439return res;440}441442// Pass layerName == nullptr to get the extension list for the device.443VkResult VulkanContext::GetDeviceLayerExtensionList(const char *layerName, std::vector<VkExtensionProperties> &extensions) {444VkResult res;445do {446uint32_t device_extension_count;447res = vkEnumerateDeviceExtensionProperties(physical_devices_[physical_device_], layerName, &device_extension_count, nullptr);448if (res != VK_SUCCESS)449return res;450if (!device_extension_count)451return VK_SUCCESS;452extensions.resize(device_extension_count);453res = vkEnumerateDeviceExtensionProperties(physical_devices_[physical_device_], layerName, &device_extension_count, extensions.data());454} while (res == VK_INCOMPLETE);455return res;456}457458VkResult VulkanContext::GetDeviceLayerProperties() {459/*460* It's possible, though very rare, that the number of461* instance layers could change. For example, installing something462* could include new layers that the loader would pick up463* between the initial query for the count and the464* request for VkLayerProperties. The loader indicates that465* by returning a VK_INCOMPLETE status and will update the466* the count parameter.467* The count parameter will be updated with the number of468* entries loaded into the data pointer - in case the number469* of layers went down or is smaller than the size given.470*/471uint32_t device_layer_count;472std::vector<VkLayerProperties> vk_props;473VkResult res;474do {475res = vkEnumerateDeviceLayerProperties(physical_devices_[physical_device_], &device_layer_count, nullptr);476if (res != VK_SUCCESS)477return res;478if (device_layer_count == 0)479return VK_SUCCESS;480vk_props.resize(device_layer_count);481res = vkEnumerateDeviceLayerProperties(physical_devices_[physical_device_], &device_layer_count, vk_props.data());482} while (res == VK_INCOMPLETE);483484// Gather the list of extensions for each device layer.485for (uint32_t i = 0; i < device_layer_count; i++) {486LayerProperties layer_props;487layer_props.properties = vk_props[i];488res = GetDeviceLayerExtensionList(layer_props.properties.layerName, layer_props.extensions);489if (res != VK_SUCCESS)490return res;491device_layer_properties_.push_back(layer_props);492}493return res;494}495496// Returns true if all layer names specified in check_names can be found in given layer properties.497bool VulkanContext::CheckLayers(const std::vector<LayerProperties> &layer_props, const std::vector<const char *> &layer_names) const {498uint32_t check_count = (uint32_t)layer_names.size();499uint32_t layer_count = (uint32_t)layer_props.size();500for (uint32_t i = 0; i < check_count; i++) {501bool found = false;502for (uint32_t j = 0; j < layer_count; j++) {503if (!strcmp(layer_names[i], layer_props[j].properties.layerName)) {504found = true;505}506}507if (!found) {508std::cout << "Cannot find layer: " << layer_names[i] << std::endl;509return false;510}511}512return true;513}514515int VulkanContext::GetPhysicalDeviceByName(const std::string &name) {516for (size_t i = 0; i < physical_devices_.size(); i++) {517if (physicalDeviceProperties_[i].properties.deviceName == name)518return (int)i;519}520return -1;521}522523int VulkanContext::GetBestPhysicalDevice() {524// Rules: Prefer discrete over embedded.525// Prefer nVidia over Intel.526527int maxScore = -1;528int best = -1;529530for (size_t i = 0; i < physical_devices_.size(); i++) {531int score = 0;532VkPhysicalDeviceProperties props;533vkGetPhysicalDeviceProperties(physical_devices_[i], &props);534switch (props.deviceType) {535case VK_PHYSICAL_DEVICE_TYPE_CPU:536score += 1;537break;538case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:539score += 2;540break;541case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:542score += 20;543break;544case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:545score += 10;546break;547default:548break;549}550if (props.vendorID == VULKAN_VENDOR_AMD) {551score += 5;552} else if (props.vendorID == VULKAN_VENDOR_NVIDIA) {553score += 5;554}555if (score > maxScore) {556best = (int)i;557maxScore = score;558}559}560return best;561}562563bool VulkanContext::EnableDeviceExtension(const char *extension, uint32_t coreVersion) {564if (coreVersion != 0 && vulkanDeviceApiVersion_ >= coreVersion) {565return true;566}567for (auto &iter : device_extension_properties_) {568if (!strcmp(iter.extensionName, extension)) {569device_extensions_enabled_.push_back(extension);570return true;571}572}573return false;574}575576bool VulkanContext::EnableInstanceExtension(const char *extension, uint32_t coreVersion) {577if (coreVersion != 0 && vulkanInstanceApiVersion_ >= coreVersion) {578return true;579}580for (auto &iter : instance_extension_properties_) {581if (!strcmp(iter.extensionName, extension)) {582instance_extensions_enabled_.push_back(extension);583return true;584}585}586return false;587}588589VkResult VulkanContext::CreateDevice(int physical_device) {590physical_device_ = physical_device;591INFO_LOG(Log::G3D, "Chose physical device %d: %s", physical_device, physicalDeviceProperties_[physical_device].properties.deviceName);592593vulkanDeviceApiVersion_ = physicalDeviceProperties_[physical_device].properties.apiVersion;594595GetDeviceLayerProperties();596if (!CheckLayers(device_layer_properties_, device_layer_names_)) {597WARN_LOG(Log::G3D, "CheckLayers for device %d failed", physical_device);598}599600vkGetPhysicalDeviceQueueFamilyProperties(physical_devices_[physical_device_], &queue_count, nullptr);601_dbg_assert_(queue_count >= 1);602603queueFamilyProperties_.resize(queue_count);604vkGetPhysicalDeviceQueueFamilyProperties(physical_devices_[physical_device_], &queue_count, queueFamilyProperties_.data());605_dbg_assert_(queue_count >= 1);606607// Detect preferred depth/stencil formats, in this order. All supported devices will support at least one of these.608static const VkFormat depthStencilFormats[] = {609VK_FORMAT_D24_UNORM_S8_UINT,610VK_FORMAT_D32_SFLOAT_S8_UINT,611VK_FORMAT_D16_UNORM_S8_UINT,612};613614deviceInfo_.preferredDepthStencilFormat = VK_FORMAT_UNDEFINED;615for (size_t i = 0; i < ARRAY_SIZE(depthStencilFormats); i++) {616VkFormatProperties props;617vkGetPhysicalDeviceFormatProperties(physical_devices_[physical_device_], depthStencilFormats[i], &props);618if (props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) {619deviceInfo_.preferredDepthStencilFormat = depthStencilFormats[i];620break;621}622}623624_assert_msg_(deviceInfo_.preferredDepthStencilFormat != VK_FORMAT_UNDEFINED, "Could not find a usable depth stencil format.");625VkFormatProperties preferredProps;626vkGetPhysicalDeviceFormatProperties(physical_devices_[physical_device_], deviceInfo_.preferredDepthStencilFormat, &preferredProps);627if ((preferredProps.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) &&628(preferredProps.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT)) {629deviceInfo_.canBlitToPreferredDepthStencilFormat = true;630}631632// This is as good a place as any to do this. Though, we don't use this much anymore after we added633// support for VMA.634vkGetPhysicalDeviceMemoryProperties(physical_devices_[physical_device_], &memory_properties_);635INFO_LOG(Log::G3D, "Memory Types (%d):", memory_properties_.memoryTypeCount);636for (int i = 0; i < (int)memory_properties_.memoryTypeCount; i++) {637// Don't bother printing dummy memory types.638if (!memory_properties_.memoryTypes[i].propertyFlags)639continue;640INFO_LOG(Log::G3D, " %d: Heap %d; Flags: %s%s%s%s ", i, memory_properties_.memoryTypes[i].heapIndex,641(memory_properties_.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) ? "DEVICE_LOCAL " : "",642(memory_properties_.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) ? "HOST_VISIBLE " : "",643(memory_properties_.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) ? "HOST_CACHED " : "",644(memory_properties_.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) ? "HOST_COHERENT " : "");645}646647GetDeviceLayerExtensionList(nullptr, device_extension_properties_);648649device_extensions_enabled_.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);650651if (!init_error_.empty() || physical_device_ < 0) {652ERROR_LOG(Log::G3D, "Vulkan init failed: %s", init_error_.c_str());653return VK_ERROR_INITIALIZATION_FAILED;654}655656VkDeviceQueueCreateInfo queue_info{ VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO };657float queue_priorities[1] = { 1.0f };658queue_info.queueCount = 1;659queue_info.pQueuePriorities = queue_priorities;660bool found = false;661for (int i = 0; i < (int)queue_count; i++) {662if (queueFamilyProperties_[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {663queue_info.queueFamilyIndex = i;664found = true;665break;666}667}668_dbg_assert_(found);669670// TODO: A lot of these are on by default in later Vulkan versions, should check for that, technically.671extensionsLookup_.KHR_maintenance1 = EnableDeviceExtension(VK_KHR_MAINTENANCE1_EXTENSION_NAME, VK_API_VERSION_1_1);672extensionsLookup_.KHR_maintenance2 = EnableDeviceExtension(VK_KHR_MAINTENANCE2_EXTENSION_NAME, VK_API_VERSION_1_1);673extensionsLookup_.KHR_maintenance3 = EnableDeviceExtension(VK_KHR_MAINTENANCE3_EXTENSION_NAME, VK_API_VERSION_1_1);674extensionsLookup_.KHR_maintenance4 = EnableDeviceExtension("VK_KHR_maintenance4", VK_API_VERSION_1_3);675extensionsLookup_.KHR_multiview = EnableDeviceExtension(VK_KHR_MULTIVIEW_EXTENSION_NAME, VK_API_VERSION_1_1);676677if (EnableDeviceExtension(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, VK_API_VERSION_1_1)) {678extensionsLookup_.KHR_get_memory_requirements2 = true;679extensionsLookup_.KHR_dedicated_allocation = EnableDeviceExtension(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME, VK_API_VERSION_1_1);680}681if (EnableDeviceExtension(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME, VK_API_VERSION_1_2)) {682extensionsLookup_.KHR_create_renderpass2 = true;683extensionsLookup_.KHR_depth_stencil_resolve = EnableDeviceExtension(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME, VK_API_VERSION_1_2);684}685686extensionsLookup_.EXT_shader_stencil_export = EnableDeviceExtension(VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME, 0);687extensionsLookup_.EXT_fragment_shader_interlock = EnableDeviceExtension(VK_EXT_FRAGMENT_SHADER_INTERLOCK_EXTENSION_NAME, 0);688extensionsLookup_.ARM_rasterization_order_attachment_access = EnableDeviceExtension(VK_ARM_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME, 0);689690#if !PPSSPP_PLATFORM(MAC) && !PPSSPP_PLATFORM(IOS)691extensionsLookup_.GOOGLE_display_timing = EnableDeviceExtension(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME, 0);692#endif693if (!extensionsLookup_.GOOGLE_display_timing) {694extensionsLookup_.KHR_present_id = EnableDeviceExtension(VK_KHR_PRESENT_ID_EXTENSION_NAME, 0);695extensionsLookup_.KHR_present_wait = EnableDeviceExtension(VK_KHR_PRESENT_WAIT_EXTENSION_NAME, 0);696}697698extensionsLookup_.EXT_provoking_vertex = EnableDeviceExtension(VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME, 0);699700// Optional features701if (extensionsLookup_.KHR_get_physical_device_properties2 && vkGetPhysicalDeviceFeatures2) {702VkPhysicalDeviceFeatures2 features2{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR };703// Add to chain even if not supported, GetPhysicalDeviceFeatures is supposed to ignore unknown structs.704VkPhysicalDeviceMultiviewFeatures multiViewFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES };705VkPhysicalDevicePresentWaitFeaturesKHR presentWaitFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR };706VkPhysicalDevicePresentIdFeaturesKHR presentIdFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR };707VkPhysicalDeviceProvokingVertexFeaturesEXT provokingVertexFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT };708709ChainStruct(features2, &multiViewFeatures);710if (extensionsLookup_.KHR_present_wait) {711ChainStruct(features2, &presentWaitFeatures);712}713if (extensionsLookup_.KHR_present_id) {714ChainStruct(features2, &presentIdFeatures);715}716if (extensionsLookup_.EXT_provoking_vertex) {717ChainStruct(features2, &provokingVertexFeatures);718}719vkGetPhysicalDeviceFeatures2(physical_devices_[physical_device_], &features2);720deviceFeatures_.available.standard = features2.features;721deviceFeatures_.available.multiview = multiViewFeatures;722if (extensionsLookup_.KHR_present_wait) {723deviceFeatures_.available.presentWait = presentWaitFeatures;724}725if (extensionsLookup_.KHR_present_id) {726deviceFeatures_.available.presentId = presentIdFeatures;727}728if (extensionsLookup_.EXT_provoking_vertex) {729deviceFeatures_.available.provokingVertex = provokingVertexFeatures;730}731} else {732vkGetPhysicalDeviceFeatures(physical_devices_[physical_device_], &deviceFeatures_.available.standard);733deviceFeatures_.available.multiview = {};734}735736deviceFeatures_.enabled = {};737// Enable a few safe ones if they are available.738deviceFeatures_.enabled.standard.dualSrcBlend = deviceFeatures_.available.standard.dualSrcBlend;739deviceFeatures_.enabled.standard.logicOp = deviceFeatures_.available.standard.logicOp;740deviceFeatures_.enabled.standard.depthClamp = deviceFeatures_.available.standard.depthClamp;741deviceFeatures_.enabled.standard.depthBounds = deviceFeatures_.available.standard.depthBounds;742deviceFeatures_.enabled.standard.samplerAnisotropy = deviceFeatures_.available.standard.samplerAnisotropy;743deviceFeatures_.enabled.standard.shaderClipDistance = deviceFeatures_.available.standard.shaderClipDistance;744deviceFeatures_.enabled.standard.shaderCullDistance = deviceFeatures_.available.standard.shaderCullDistance;745deviceFeatures_.enabled.standard.geometryShader = deviceFeatures_.available.standard.geometryShader;746deviceFeatures_.enabled.standard.sampleRateShading = deviceFeatures_.available.standard.sampleRateShading;747748#ifdef _DEBUG749// For debugging! Although, it might hide problems, so turning it off. Can be useful to rule out classes of issues.750// deviceFeatures_.enabled.standard.robustBufferAccess = deviceFeatures_.available.standard.robustBufferAccess;751#endif752753deviceFeatures_.enabled.multiview = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES };754if (extensionsLookup_.KHR_multiview) {755deviceFeatures_.enabled.multiview.multiview = deviceFeatures_.available.multiview.multiview;756}757// Strangely, on Intel, it reports these as available even though the extension isn't in the list.758deviceFeatures_.enabled.presentId = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR };759if (extensionsLookup_.KHR_present_id) {760deviceFeatures_.enabled.presentId.presentId = deviceFeatures_.available.presentId.presentId;761}762deviceFeatures_.enabled.presentWait = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR };763if (extensionsLookup_.KHR_present_wait) {764deviceFeatures_.enabled.presentWait.presentWait = deviceFeatures_.available.presentWait.presentWait;765}766deviceFeatures_.enabled.provokingVertex = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT };767if (extensionsLookup_.EXT_provoking_vertex) {768deviceFeatures_.enabled.provokingVertex.provokingVertexLast = true;769}770771// deviceFeatures_.enabled.multiview.multiviewGeometryShader = deviceFeatures_.available.multiview.multiviewGeometryShader;772773VkPhysicalDeviceFeatures2 features2{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 };774775VkDeviceCreateInfo device_info{ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };776device_info.queueCreateInfoCount = 1;777device_info.pQueueCreateInfos = &queue_info;778device_info.enabledLayerCount = (uint32_t)device_layer_names_.size();779device_info.ppEnabledLayerNames = device_info.enabledLayerCount ? device_layer_names_.data() : nullptr;780device_info.enabledExtensionCount = (uint32_t)device_extensions_enabled_.size();781device_info.ppEnabledExtensionNames = device_info.enabledExtensionCount ? device_extensions_enabled_.data() : nullptr;782783if (extensionsLookup_.KHR_get_physical_device_properties2) {784device_info.pNext = &features2;785features2.features = deviceFeatures_.enabled.standard;786ChainStruct(features2, &deviceFeatures_.enabled.multiview);787if (extensionsLookup_.KHR_present_wait) {788ChainStruct(features2, &deviceFeatures_.enabled.presentWait);789}790if (extensionsLookup_.KHR_present_id) {791ChainStruct(features2, &deviceFeatures_.enabled.presentId);792}793if (extensionsLookup_.EXT_provoking_vertex) {794ChainStruct(features2, &deviceFeatures_.enabled.provokingVertex);795}796} else {797device_info.pEnabledFeatures = &deviceFeatures_.enabled.standard;798}799800VkResult res = vkCreateDevice(physical_devices_[physical_device_], &device_info, nullptr, &device_);801if (res != VK_SUCCESS) {802init_error_ = "Unable to create Vulkan device";803ERROR_LOG(Log::G3D, "%s", init_error_.c_str());804} else {805VulkanLoadDeviceFunctions(device_, extensionsLookup_, vulkanDeviceApiVersion_);806}807INFO_LOG(Log::G3D, "Vulkan Device created: %s", physicalDeviceProperties_[physical_device_].properties.deviceName);808809// Since we successfully created a device (however we got here, might be interesting in debug), we force the choice to be visible in the menu.810VulkanSetAvailable(true);811812VmaAllocatorCreateInfo allocatorInfo = {};813allocatorInfo.vulkanApiVersion = std::min(vulkanDeviceApiVersion_, vulkanInstanceApiVersion_);814allocatorInfo.physicalDevice = physical_devices_[physical_device_];815allocatorInfo.device = device_;816allocatorInfo.instance = instance_;817VkResult result = vmaCreateAllocator(&allocatorInfo, &allocator_);818_assert_(result == VK_SUCCESS);819_assert_(allocator_ != VK_NULL_HANDLE);820821// Examine the physical device to figure out super rough performance grade.822// Basically all we want to do is to identify low performance mobile devices823// so we can make decisions on things like texture scaling strategy.824auto &props = physicalDeviceProperties_[physical_device_].properties;825switch (props.vendorID) {826case VULKAN_VENDOR_AMD:827case VULKAN_VENDOR_NVIDIA:828case VULKAN_VENDOR_INTEL:829devicePerfClass_ = PerfClass::FAST;830break;831832case VULKAN_VENDOR_ARM:833devicePerfClass_ = PerfClass::SLOW;834{835// Parse the device name as an ultra rough heuristic.836int maliG = 0;837if (sscanf(props.deviceName, "Mali-G%d", &maliG) == 1) {838if (maliG >= 72) {839devicePerfClass_ = PerfClass::FAST;840}841}842}843break;844845case VULKAN_VENDOR_QUALCOMM:846devicePerfClass_ = PerfClass::SLOW;847#if PPSSPP_PLATFORM(ANDROID)848if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 30) {849devicePerfClass_ = PerfClass::FAST;850}851#endif852break;853854case VULKAN_VENDOR_IMGTEC:855default:856devicePerfClass_ = PerfClass::SLOW;857break;858}859860return res;861}862863VkResult VulkanContext::InitDebugUtilsCallback() {864// We're intentionally skipping VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT and865// VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT, just too spammy.866int bits = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT867| VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT868| VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;869870VkDebugUtilsMessengerCreateInfoEXT callback1{VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT};871callback1.messageSeverity = bits;872callback1.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;873callback1.pfnUserCallback = &VulkanDebugUtilsCallback;874callback1.pUserData = (void *)&g_LogOptions;875VkDebugUtilsMessengerEXT messenger;876VkResult res = vkCreateDebugUtilsMessengerEXT(instance_, &callback1, nullptr, &messenger);877if (res != VK_SUCCESS) {878ERROR_LOG(Log::G3D, "Failed to register debug callback with vkCreateDebugUtilsMessengerEXT");879// Do error handling for VK_ERROR_OUT_OF_MEMORY880} else {881INFO_LOG(Log::G3D, "Debug callback registered with vkCreateDebugUtilsMessengerEXT.");882utils_callbacks.push_back(messenger);883}884return res;885}886887bool VulkanContext::CreateInstanceAndDevice(const CreateInfo &info) {888VkResult res = CreateInstance(info);889if (res != VK_SUCCESS) {890ERROR_LOG(Log::G3D, "Failed to create vulkan context: %s", InitError().c_str());891VulkanSetAvailable(false);892return false;893}894895int physicalDevice = GetBestPhysicalDevice();896if (physicalDevice < 0) {897ERROR_LOG(Log::G3D, "No usable Vulkan device found.");898DestroyInstance();899return false;900}901902INFO_LOG(Log::G3D, "Creating Vulkan device (flags: %08x)", info.flags);903if (CreateDevice(physicalDevice) != VK_SUCCESS) {904INFO_LOG(Log::G3D, "Failed to create vulkan device: %s", InitError().c_str());905DestroyInstance();906return false;907}908909return true;910}911912void VulkanContext::SetDebugNameImpl(uint64_t handle, VkObjectType type, const char *name) {913VkDebugUtilsObjectNameInfoEXT info{ VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT };914info.pObjectName = name;915info.objectHandle = handle;916info.objectType = type;917vkSetDebugUtilsObjectNameEXT(device_, &info);918}919920VkResult VulkanContext::InitSurface(WindowSystem winsys, void *data1, void *data2) {921winsys_ = winsys;922winsysData1_ = data1;923winsysData2_ = data2;924return ReinitSurface();925}926927VkResult VulkanContext::ReinitSurface() {928if (surface_ != VK_NULL_HANDLE) {929INFO_LOG(Log::G3D, "Destroying Vulkan surface (%d, %d)", swapChainExtent_.width, swapChainExtent_.height);930vkDestroySurfaceKHR(instance_, surface_, nullptr);931surface_ = VK_NULL_HANDLE;932}933934INFO_LOG(Log::G3D, "Creating Vulkan surface for window (data1=%p data2=%p)", winsysData1_, winsysData2_);935936VkResult retval = VK_SUCCESS;937938switch (winsys_) {939#ifdef _WIN32940case WINDOWSYSTEM_WIN32:941{942VkWin32SurfaceCreateInfoKHR win32{ VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR };943win32.flags = 0;944win32.hwnd = (HWND)winsysData2_;945win32.hinstance = (HINSTANCE)winsysData1_;946retval = vkCreateWin32SurfaceKHR(instance_, &win32, nullptr, &surface_);947break;948}949#endif950#if defined(__ANDROID__)951case WINDOWSYSTEM_ANDROID:952{953ANativeWindow *wnd = (ANativeWindow *)winsysData1_;954VkAndroidSurfaceCreateInfoKHR android{ VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR };955android.flags = 0;956android.window = wnd;957retval = vkCreateAndroidSurfaceKHR(instance_, &android, nullptr, &surface_);958break;959}960#endif961#if defined(VK_USE_PLATFORM_METAL_EXT)962case WINDOWSYSTEM_METAL_EXT:963{964VkMetalSurfaceCreateInfoEXT metal{ VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT };965metal.flags = 0;966metal.pLayer = winsysData1_;967metal.pNext = winsysData2_;968retval = vkCreateMetalSurfaceEXT(instance_, &metal, nullptr, &surface_);969break;970}971#endif972#if defined(VK_USE_PLATFORM_XLIB_KHR)973case WINDOWSYSTEM_XLIB:974{975VkXlibSurfaceCreateInfoKHR xlib{ VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR };976xlib.flags = 0;977xlib.dpy = (Display *)winsysData1_;978xlib.window = (Window)winsysData2_;979retval = vkCreateXlibSurfaceKHR(instance_, &xlib, nullptr, &surface_);980break;981}982#endif983#if defined(VK_USE_PLATFORM_XCB_KHR)984case WINDOWSYSTEM_XCB:985{986VkXCBSurfaceCreateInfoKHR xcb{ VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR };987xcb.flags = 0;988xcb.connection = (Connection *)winsysData1_;989xcb.window = (Window)(uintptr_t)winsysData2_;990retval = vkCreateXcbSurfaceKHR(instance_, &xcb, nullptr, &surface_);991break;992}993#endif994#if defined(VK_USE_PLATFORM_WAYLAND_KHR)995case WINDOWSYSTEM_WAYLAND:996{997VkWaylandSurfaceCreateInfoKHR wayland{ VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR };998wayland.flags = 0;999wayland.display = (wl_display *)winsysData1_;1000wayland.surface = (wl_surface *)winsysData2_;1001retval = vkCreateWaylandSurfaceKHR(instance_, &wayland, nullptr, &surface_);1002break;1003}1004#endif1005#if defined(VK_USE_PLATFORM_DISPLAY_KHR)1006case WINDOWSYSTEM_DISPLAY:1007{1008VkDisplaySurfaceCreateInfoKHR display{ VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR };1009#if !defined(__LIBRETRO__)1010/*1011And when not to use libretro need VkDisplaySurfaceCreateInfoKHR this extension,1012then you need to use dlopen to read vulkan loader in VulkanLoader.cpp.1013huangzihan China1014*/10151016if(!vkGetPhysicalDeviceDisplayPropertiesKHR ||1017!vkGetPhysicalDeviceDisplayPlanePropertiesKHR ||1018!vkGetDisplayModePropertiesKHR ||1019!vkGetDisplayPlaneSupportedDisplaysKHR ||1020!vkGetDisplayPlaneCapabilitiesKHR ) {1021_assert_msg_(false, "DISPLAY Vulkan cannot find any vulkan function symbols.");1022return VK_ERROR_INITIALIZATION_FAILED;1023}10241025//The following code is for reference:1026// https://github.com/vanfanel/ppsspp1027// When using the VK_KHR_display extension and not using LIBRETRO, a complete1028// VkDisplaySurfaceCreateInfoKHR is needed.10291030uint32_t display_count;1031uint32_t plane_count;10321033VkDisplayPropertiesKHR *display_props = NULL;1034VkDisplayPlanePropertiesKHR *plane_props = NULL;1035VkDisplayModePropertiesKHR* mode_props = NULL;10361037VkExtent2D image_size;1038// This is the chosen physical_device, it has been chosen elsewhere.1039VkPhysicalDevice phys_device = physical_devices_[physical_device_];1040VkDisplayModeKHR display_mode = VK_NULL_HANDLE;1041VkDisplayPlaneAlphaFlagBitsKHR alpha_mode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR;1042uint32_t plane = UINT32_MAX;10431044// For now, use the first available (connected) display.1045int display_index = 0;10461047VkResult result;1048bool ret = false;1049bool mode_found = false;10501051int i, j;10521053// 1 physical device can have N displays connected.1054// Vulkan only counts the connected displays.10551056// Get a list of displays on the physical device.1057display_count = 0;1058vkGetPhysicalDeviceDisplayPropertiesKHR(phys_device, &display_count, NULL);1059if (display_count == 0) {1060_assert_msg_(false, "DISPLAY Vulkan couldn't find any displays.");1061return VK_ERROR_INITIALIZATION_FAILED;1062}1063display_props = new VkDisplayPropertiesKHR[display_count];1064vkGetPhysicalDeviceDisplayPropertiesKHR(phys_device, &display_count, display_props);10651066// Get a list of display planes on the physical device.1067plane_count = 0;1068vkGetPhysicalDeviceDisplayPlanePropertiesKHR(phys_device, &plane_count, NULL);1069if (plane_count == 0) {1070_assert_msg_(false, "DISPLAY Vulkan couldn't find any planes on the physical device");1071return VK_ERROR_INITIALIZATION_FAILED;10721073}1074plane_props = new VkDisplayPlanePropertiesKHR[plane_count];1075vkGetPhysicalDeviceDisplayPlanePropertiesKHR(phys_device, &plane_count, plane_props);10761077// Get the Vulkan display we are going to use.1078VkDisplayKHR myDisplay = display_props[display_index].display;10791080// Get the list of display modes of the display1081uint32_t mode_count = 0;1082vkGetDisplayModePropertiesKHR(phys_device, myDisplay, &mode_count, NULL);1083if (mode_count == 0) {1084_assert_msg_(false, "DISPLAY Vulkan couldn't find any video modes on the display");1085return VK_ERROR_INITIALIZATION_FAILED;1086}1087mode_props = new VkDisplayModePropertiesKHR[mode_count];1088vkGetDisplayModePropertiesKHR(phys_device, myDisplay, &mode_count, mode_props);10891090// See if there's an appropiate mode available on the display1091display_mode = VK_NULL_HANDLE;1092for (i = 0; i < mode_count; ++i)1093{1094const VkDisplayModePropertiesKHR* mode = &mode_props[i];10951096if (mode->parameters.visibleRegion.width == g_display.pixel_xres &&1097mode->parameters.visibleRegion.height == g_display.pixel_yres)1098{1099display_mode = mode->displayMode;1100mode_found = true;1101break;1102}1103}11041105// Free the mode list now.1106delete [] mode_props;11071108// If there are no useable modes found on the display, error out1109if (display_mode == VK_NULL_HANDLE)1110{1111_assert_msg_(false, "DISPLAY Vulkan couldn't find any video modes on the display");1112return VK_ERROR_INITIALIZATION_FAILED;1113}11141115/* Iterate on the list of planes of the physical device1116to find a plane that matches these criteria:1117-It must be compatible with the chosen display + mode.1118-It isn't currently bound to another display.1119-It supports per-pixel alpha, if possible. */1120for (i = 0; i < plane_count; i++) {1121uint32_t supported_displays_count = 0;1122VkDisplayKHR* supported_displays;1123VkDisplayPlaneCapabilitiesKHR plane_caps;11241125/* See if the plane is compatible with the current display. */1126vkGetDisplayPlaneSupportedDisplaysKHR(phys_device, i, &supported_displays_count, NULL);1127if (supported_displays_count == 0) {1128/* This plane doesn't support any displays. Continue to the next plane. */1129continue;1130}11311132/* Get the list of displays supported by this plane. */1133supported_displays = new VkDisplayKHR[supported_displays_count];1134vkGetDisplayPlaneSupportedDisplaysKHR(phys_device, i,1135&supported_displays_count, supported_displays);11361137/* The plane must be bound to the chosen display, or not in use.1138If none of these is true, iterate to another plane. */1139if ( !( (plane_props[i].currentDisplay == myDisplay) ||1140(plane_props[i].currentDisplay == VK_NULL_HANDLE)))1141continue;11421143/* Iterate the list of displays supported by this plane1144in order to find out if the chosen display is among them. */1145bool plane_supports_display = false;1146for (j = 0; j < supported_displays_count; j++) {1147if (supported_displays[j] == myDisplay) {1148plane_supports_display = true;1149break;1150}1151}11521153/* Free the list of displays supported by this plane. */1154delete [] supported_displays;11551156/* If the display is not supported by this plane, iterate to the next plane. */1157if (!plane_supports_display)1158continue;11591160/* Want a plane that supports the alpha mode we have chosen. */1161vkGetDisplayPlaneCapabilitiesKHR(phys_device, display_mode, i, &plane_caps);1162if (plane_caps.supportedAlpha & alpha_mode) {1163/* Yep, this plane is alright. */1164plane = i;1165break;1166}1167}11681169/* If we couldn't find an appropiate plane, error out. */1170if (plane == UINT32_MAX) {1171_assert_msg_(false, "DISPLAY Vulkan couldn't find an appropiate plane");1172return VK_ERROR_INITIALIZATION_FAILED;1173}11741175// Finally, create the vulkan surface.1176image_size.width = g_display.pixel_xres;1177image_size.height = g_display.pixel_yres;11781179display.displayMode = display_mode;1180display.imageExtent = image_size;1181display.transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;1182display.alphaMode = alpha_mode;1183display.globalAlpha = 1.0f;1184display.planeIndex = plane;1185display.planeStackIndex = plane_props[plane].currentStackIndex;1186display.pNext = nullptr;1187delete [] display_props;1188delete [] plane_props;1189#endif1190display.flags = 0;1191retval = vkCreateDisplayPlaneSurfaceKHR(instance_, &display, nullptr, &surface_);1192break;1193}1194#endif11951196default:1197_assert_msg_(false, "Vulkan support for chosen window system not implemented");1198return VK_ERROR_INITIALIZATION_FAILED;1199}12001201if (retval != VK_SUCCESS) {1202return retval;1203}12041205if (!ChooseQueue()) {1206return VK_ERROR_INITIALIZATION_FAILED;1207}12081209for (int i = 0; i < ARRAY_SIZE(frame_); i++) {1210frame_[i].profiler.Init(this);1211}12121213return VK_SUCCESS;1214}12151216bool VulkanContext::ChooseQueue() {1217// Iterate over each queue to learn whether it supports presenting:1218VkBool32 *supportsPresent = new VkBool32[queue_count];1219for (uint32_t i = 0; i < queue_count; i++) {1220vkGetPhysicalDeviceSurfaceSupportKHR(physical_devices_[physical_device_], i, surface_, &supportsPresent[i]);1221}12221223// Search for a graphics queue and a present queue in the array of queue1224// families, try to find one that supports both1225uint32_t graphicsQueueNodeIndex = UINT32_MAX;1226uint32_t presentQueueNodeIndex = UINT32_MAX;1227for (uint32_t i = 0; i < queue_count; i++) {1228if ((queueFamilyProperties_[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) {1229if (graphicsQueueNodeIndex == UINT32_MAX) {1230graphicsQueueNodeIndex = i;1231}12321233if (supportsPresent[i] == VK_TRUE) {1234graphicsQueueNodeIndex = i;1235presentQueueNodeIndex = i;1236break;1237}1238}1239}1240if (presentQueueNodeIndex == UINT32_MAX) {1241// If didn't find a queue that supports both graphics and present, then1242// find a separate present queue. NOTE: We don't actually currently support this arrangement!1243for (uint32_t i = 0; i < queue_count; ++i) {1244if (supportsPresent[i] == VK_TRUE) {1245presentQueueNodeIndex = i;1246break;1247}1248}1249}1250delete[] supportsPresent;12511252// Generate error if could not find both a graphics and a present queue1253if (graphicsQueueNodeIndex == UINT32_MAX || presentQueueNodeIndex == UINT32_MAX) {1254ERROR_LOG(Log::G3D, "Could not find a graphics and a present queue");1255return false;1256}12571258graphics_queue_family_index_ = graphicsQueueNodeIndex;12591260// Get the list of VkFormats that are supported:1261uint32_t formatCount = 0;1262VkResult res = vkGetPhysicalDeviceSurfaceFormatsKHR(physical_devices_[physical_device_], surface_, &formatCount, nullptr);1263_assert_msg_(res == VK_SUCCESS, "Failed to get formats for device %d: %d", physical_device_, (int)res);1264if (res != VK_SUCCESS) {1265return false;1266}12671268surfFormats_.resize(formatCount);1269res = vkGetPhysicalDeviceSurfaceFormatsKHR(physical_devices_[physical_device_], surface_, &formatCount, surfFormats_.data());1270_dbg_assert_(res == VK_SUCCESS);1271if (res != VK_SUCCESS) {1272return false;1273}1274// If the format list includes just one entry of VK_FORMAT_UNDEFINED,1275// the surface has no preferred format. Otherwise, at least one1276// supported format will be returned.1277if (formatCount == 0 || (formatCount == 1 && surfFormats_[0].format == VK_FORMAT_UNDEFINED)) {1278INFO_LOG(Log::G3D, "swapchain_format: Falling back to B8G8R8A8_UNORM");1279swapchainFormat_ = VK_FORMAT_B8G8R8A8_UNORM;1280} else {1281swapchainFormat_ = VK_FORMAT_UNDEFINED;1282for (uint32_t i = 0; i < formatCount; ++i) {1283if (surfFormats_[i].colorSpace != VK_COLORSPACE_SRGB_NONLINEAR_KHR) {1284continue;1285}1286if (surfFormats_[i].format == VK_FORMAT_B8G8R8A8_UNORM || surfFormats_[i].format == VK_FORMAT_R8G8B8A8_UNORM) {1287swapchainFormat_ = surfFormats_[i].format;1288break;1289}1290}1291if (swapchainFormat_ == VK_FORMAT_UNDEFINED) {1292// Okay, take the first one then.1293swapchainFormat_ = surfFormats_[0].format;1294}1295INFO_LOG(Log::G3D, "swapchain_format: %d (/%d)", swapchainFormat_, formatCount);1296}12971298vkGetDeviceQueue(device_, graphics_queue_family_index_, 0, &gfx_queue_);1299return true;1300}13011302int clamp(int x, int a, int b) {1303if (x < a)1304return a;1305if (x > b)1306return b;1307return x;1308}13091310static std::string surface_transforms_to_string(VkSurfaceTransformFlagsKHR transformFlags) {1311std::string str;1312if (transformFlags & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) str += "IDENTITY ";1313if (transformFlags & VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR) str += "ROTATE_90 ";1314if (transformFlags & VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR) str += "ROTATE_180 ";1315if (transformFlags & VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) str += "ROTATE_270 ";1316if (transformFlags & VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR) str += "HMIRROR ";1317if (transformFlags & VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR) str += "HMIRROR_90 ";1318if (transformFlags & VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR) str += "HMIRROR_180 ";1319if (transformFlags & VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR) str += "HMIRROR_270 ";1320if (transformFlags & VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR) str += "INHERIT ";1321return str;1322}13231324bool VulkanContext::InitSwapchain() {1325_assert_(physical_device_ >= 0 && physical_device_ < physical_devices_.size());1326if (!surface_) {1327ERROR_LOG(Log::G3D, "VK: No surface, can't create swapchain");1328return false;1329}13301331VkResult res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_devices_[physical_device_], surface_, &surfCapabilities_);1332if (res == VK_ERROR_SURFACE_LOST_KHR) {1333// Not much to do.1334ERROR_LOG(Log::G3D, "VK: Surface lost in InitSwapchain");1335return false;1336}1337_dbg_assert_(res == VK_SUCCESS);1338uint32_t presentModeCount;1339res = vkGetPhysicalDeviceSurfacePresentModesKHR(physical_devices_[physical_device_], surface_, &presentModeCount, nullptr);1340_dbg_assert_(res == VK_SUCCESS);1341VkPresentModeKHR *presentModes = new VkPresentModeKHR[presentModeCount];1342_dbg_assert_(presentModes);1343res = vkGetPhysicalDeviceSurfacePresentModesKHR(physical_devices_[physical_device_], surface_, &presentModeCount, presentModes);1344_dbg_assert_(res == VK_SUCCESS);13451346VkExtent2D currentExtent { surfCapabilities_.currentExtent };1347// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkSurfaceCapabilitiesKHR.html1348// currentExtent is the current width and height of the surface, or the special value (0xFFFFFFFF, 0xFFFFFFFF) indicating that the surface size will be determined by the extent of a swapchain targeting the surface.1349if (currentExtent.width == 0xFFFFFFFFu || currentExtent.height == 0xFFFFFFFFu1350#if PPSSPP_PLATFORM(IOS)1351|| currentExtent.width == 0 || currentExtent.height == 01352#endif1353) {1354_dbg_assert_((bool)cbGetDrawSize_)1355if (cbGetDrawSize_) {1356currentExtent = cbGetDrawSize_();1357}1358}13591360swapChainExtent_.width = clamp(currentExtent.width, surfCapabilities_.minImageExtent.width, surfCapabilities_.maxImageExtent.width);1361swapChainExtent_.height = clamp(currentExtent.height, surfCapabilities_.minImageExtent.height, surfCapabilities_.maxImageExtent.height);13621363INFO_LOG(Log::G3D, "surfCapabilities_.current: %dx%d min: %dx%d max: %dx%d computed: %dx%d",1364currentExtent.width, currentExtent.height,1365surfCapabilities_.minImageExtent.width, surfCapabilities_.minImageExtent.height,1366surfCapabilities_.maxImageExtent.width, surfCapabilities_.maxImageExtent.height,1367swapChainExtent_.width, swapChainExtent_.height);13681369availablePresentModes_.clear();1370// TODO: Find a better way to specify the prioritized present mode while being able1371// to fall back in a sensible way.1372VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_MAX_ENUM_KHR;1373std::string modes = "";1374for (size_t i = 0; i < presentModeCount; i++) {1375modes += VulkanPresentModeToString(presentModes[i]);1376if (i != presentModeCount - 1) {1377modes += ", ";1378}1379availablePresentModes_.push_back(presentModes[i]);1380}13811382INFO_LOG(Log::G3D, "Supported present modes: %s", modes.c_str());1383for (size_t i = 0; i < presentModeCount; i++) {1384bool match = false;1385match = match || ((flags_ & VULKAN_FLAG_PRESENT_MAILBOX) && presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR);1386match = match || ((flags_ & VULKAN_FLAG_PRESENT_IMMEDIATE) && presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR);1387match = match || ((flags_ & VULKAN_FLAG_PRESENT_FIFO_RELAXED) && presentModes[i] == VK_PRESENT_MODE_FIFO_RELAXED_KHR);1388match = match || ((flags_ & VULKAN_FLAG_PRESENT_FIFO) && presentModes[i] == VK_PRESENT_MODE_FIFO_KHR);13891390// Default to the first present mode from the list.1391if (match || swapchainPresentMode == VK_PRESENT_MODE_MAX_ENUM_KHR) {1392swapchainPresentMode = presentModes[i];1393}1394if (match) {1395break;1396}1397}1398delete[] presentModes;1399// Determine the number of VkImage's to use in the swap chain (we desire to1400// own only 1 image at a time, besides the images being displayed and1401// queued for display):1402uint32_t desiredNumberOfSwapChainImages = surfCapabilities_.minImageCount + 1;1403if ((surfCapabilities_.maxImageCount > 0) &&1404(desiredNumberOfSwapChainImages > surfCapabilities_.maxImageCount))1405{1406// Application must settle for fewer images than desired:1407desiredNumberOfSwapChainImages = surfCapabilities_.maxImageCount;1408}14091410INFO_LOG(Log::G3D, "Chosen present mode: %d (%s). numSwapChainImages: %d/%d",1411swapchainPresentMode, VulkanPresentModeToString(swapchainPresentMode),1412desiredNumberOfSwapChainImages, surfCapabilities_.maxImageCount);14131414// We mostly follow the practices from1415// https://arm-software.github.io/vulkan_best_practice_for_mobile_developers/samples/surface_rotation/surface_rotation_tutorial.html1416//1417VkSurfaceTransformFlagBitsKHR preTransform;1418std::string supportedTransforms = surface_transforms_to_string(surfCapabilities_.supportedTransforms);1419std::string currentTransform = surface_transforms_to_string(surfCapabilities_.currentTransform);1420g_display.rotation = DisplayRotation::ROTATE_0;1421g_display.rot_matrix.setIdentity();14221423uint32_t allowedRotations = VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR;1424// Hack: Don't allow 270 degrees pretransform (inverse landscape), it creates bizarre issues on some devices (see #15773).1425allowedRotations &= ~VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR;14261427if (surfCapabilities_.currentTransform & (VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR | VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR)) {1428preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;1429} else if (surfCapabilities_.currentTransform & allowedRotations) {1430// Normal, sensible rotations. Let's handle it.1431preTransform = surfCapabilities_.currentTransform;1432g_display.rot_matrix.setIdentity();1433switch (surfCapabilities_.currentTransform) {1434case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:1435g_display.rotation = DisplayRotation::ROTATE_90;1436g_display.rot_matrix.setRotationZ90();1437std::swap(swapChainExtent_.width, swapChainExtent_.height);1438break;1439case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:1440g_display.rotation = DisplayRotation::ROTATE_180;1441g_display.rot_matrix.setRotationZ180();1442break;1443case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:1444g_display.rotation = DisplayRotation::ROTATE_270;1445g_display.rot_matrix.setRotationZ270();1446std::swap(swapChainExtent_.width, swapChainExtent_.height);1447break;1448default:1449_dbg_assert_(false);1450}1451} else {1452// Let the OS rotate the image (potentially slower on many Android devices)1453preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;1454}14551456std::string preTransformStr = surface_transforms_to_string(preTransform);1457INFO_LOG(Log::G3D, "Transform supported: %s current: %s chosen: %s", supportedTransforms.c_str(), currentTransform.c_str(), preTransformStr.c_str());14581459if (physicalDeviceProperties_[physical_device_].properties.vendorID == VULKAN_VENDOR_IMGTEC) {1460u32 driverVersion = physicalDeviceProperties_[physical_device_].properties.driverVersion;1461// Cutoff the hack at driver version 1.386.1368 (0x00582558, see issue #15773).1462if (driverVersion < 0x00582558) {1463INFO_LOG(Log::G3D, "Applying PowerVR hack (rounding off the width!) driverVersion=%08x", driverVersion);1464// Swap chain width hack to avoid issue #11743 (PowerVR driver bug).1465// To keep the size consistent even with pretransform, do this after the swap. Should be fine.1466// This is fixed in newer PowerVR drivers but I don't know the cutoff.1467swapChainExtent_.width &= ~31;14681469// TODO: Also modify display_xres/display_yres appropriately for scissors to match.1470// This will get a bit messy. Ideally we should remove that logic from app-android.cpp1471// and move it here, but the OpenGL code still needs it.1472} else {1473INFO_LOG(Log::G3D, "PowerVR driver version new enough (%08x), not applying swapchain width hack", driverVersion);1474}1475}14761477VkSwapchainCreateInfoKHR swap_chain_info{ VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR };1478swap_chain_info.surface = surface_;1479swap_chain_info.minImageCount = desiredNumberOfSwapChainImages;1480swap_chain_info.imageFormat = swapchainFormat_;1481swap_chain_info.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;1482swap_chain_info.imageExtent.width = swapChainExtent_.width;1483swap_chain_info.imageExtent.height = swapChainExtent_.height;1484swap_chain_info.preTransform = preTransform;1485swap_chain_info.imageArrayLayers = 1;1486swap_chain_info.presentMode = swapchainPresentMode;1487swap_chain_info.oldSwapchain = VK_NULL_HANDLE;1488swap_chain_info.clipped = true;1489swap_chain_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;14901491presentMode_ = swapchainPresentMode;14921493// Don't ask for TRANSFER_DST for the swapchain image, we don't use that.1494// if (surfCapabilities_.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT)1495// swap_chain_info.imageUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;14961497#ifndef ANDROID1498// We don't support screenshots on Android1499// Add more usage flags if they're supported.1500if (surfCapabilities_.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT)1501swap_chain_info.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;1502#endif15031504swap_chain_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;1505swap_chain_info.queueFamilyIndexCount = 0;1506swap_chain_info.pQueueFamilyIndices = NULL;1507// OPAQUE is not supported everywhere.1508if (surfCapabilities_.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR) {1509swap_chain_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;1510} else {1511// This should be supported anywhere, and is the only thing supported on the SHIELD TV, for example.1512swap_chain_info.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;1513}15141515res = vkCreateSwapchainKHR(device_, &swap_chain_info, NULL, &swapchain_);1516if (res != VK_SUCCESS) {1517ERROR_LOG(Log::G3D, "vkCreateSwapchainKHR failed!");1518return false;1519}1520INFO_LOG(Log::G3D, "Created swapchain: %dx%d", swap_chain_info.imageExtent.width, swap_chain_info.imageExtent.height);1521return true;1522}15231524void VulkanContext::SetCbGetDrawSize(std::function<VkExtent2D()> cb) {1525cbGetDrawSize_ = cb;1526}15271528VkFence VulkanContext::CreateFence(bool presignalled) {1529VkFence fence;1530VkFenceCreateInfo fenceInfo{ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };1531fenceInfo.flags = presignalled ? VK_FENCE_CREATE_SIGNALED_BIT : 0;1532vkCreateFence(device_, &fenceInfo, NULL, &fence);1533return fence;1534}15351536void VulkanContext::PerformPendingDeletes() {1537for (int i = 0; i < ARRAY_SIZE(frame_); i++) {1538frame_[i].deleteList.PerformDeletes(this, allocator_);1539}1540Delete().PerformDeletes(this, allocator_);1541}15421543void VulkanContext::DestroyDevice() {1544if (swapchain_) {1545ERROR_LOG(Log::G3D, "DestroyDevice: Swapchain should have been destroyed.");1546}1547if (surface_) {1548ERROR_LOG(Log::G3D, "DestroyDevice: Surface should have been destroyed.");1549}15501551for (int i = 0; i < ARRAY_SIZE(frame_); i++) {1552frame_[i].profiler.Shutdown();1553}15541555INFO_LOG(Log::G3D, "VulkanContext::DestroyDevice (performing deletes)");1556PerformPendingDeletes();15571558vmaDestroyAllocator(allocator_);1559allocator_ = VK_NULL_HANDLE;15601561vkDestroyDevice(device_, nullptr);1562device_ = nullptr;1563}15641565bool VulkanContext::CreateShaderModule(const std::vector<uint32_t> &spirv, VkShaderModule *shaderModule, const char *tag) {1566VkShaderModuleCreateInfo sm{ VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };1567sm.pCode = spirv.data();1568sm.codeSize = spirv.size() * sizeof(uint32_t);1569sm.flags = 0;1570VkResult result = vkCreateShaderModule(device_, &sm, nullptr, shaderModule);1571if (tag) {1572SetDebugName(*shaderModule, VK_OBJECT_TYPE_SHADER_MODULE, tag);1573}1574if (result != VK_SUCCESS) {1575return false;1576} else {1577return true;1578}1579}15801581EShLanguage FindLanguage(const VkShaderStageFlagBits shader_type) {1582switch (shader_type) {1583case VK_SHADER_STAGE_VERTEX_BIT:1584return EShLangVertex;15851586case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT:1587return EShLangTessControl;15881589case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT:1590return EShLangTessEvaluation;15911592case VK_SHADER_STAGE_GEOMETRY_BIT:1593return EShLangGeometry;15941595case VK_SHADER_STAGE_FRAGMENT_BIT:1596return EShLangFragment;15971598case VK_SHADER_STAGE_COMPUTE_BIT:1599return EShLangCompute;16001601default:1602return EShLangVertex;1603}1604}16051606// Compile a given string containing GLSL into SPV for use by VK1607// Return value of false means an error was encountered.1608bool GLSLtoSPV(const VkShaderStageFlagBits shader_type, const char *sourceCode, GLSLVariant variant,1609std::vector<unsigned int> &spirv, std::string *errorMessage) {16101611glslang::TProgram program;1612const char *shaderStrings[1];1613TBuiltInResource Resources{};1614InitShaderResources(Resources);16151616int defaultVersion = 0;1617EShMessages messages;1618EProfile profile;16191620switch (variant) {1621case GLSLVariant::VULKAN:1622// Enable SPIR-V and Vulkan rules when parsing GLSL1623messages = (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules);1624defaultVersion = 450;1625profile = ECoreProfile;1626break;1627case GLSLVariant::GL140:1628messages = (EShMessages)(EShMsgDefault);1629defaultVersion = 140;1630profile = ECompatibilityProfile;1631break;1632case GLSLVariant::GLES300:1633messages = (EShMessages)(EShMsgDefault);1634defaultVersion = 300;1635profile = EEsProfile;1636break;1637default:1638return false;1639}16401641EShLanguage stage = FindLanguage(shader_type);1642glslang::TShader shader(stage);16431644shaderStrings[0] = sourceCode;1645shader.setStrings(shaderStrings, 1);16461647if (!shader.parse(&Resources, defaultVersion, profile, false, true, messages)) {1648puts(shader.getInfoLog());1649puts(shader.getInfoDebugLog());1650if (errorMessage) {1651*errorMessage = shader.getInfoLog();1652(*errorMessage) += shader.getInfoDebugLog();1653}1654return false; // something didn't work1655}16561657// TODO: Propagate warnings into errorMessages even if we succeeded here.16581659// Note that program does not take ownership of &shader, so this is fine.1660program.addShader(&shader);16611662if (!program.link(messages)) {1663puts(shader.getInfoLog());1664puts(shader.getInfoDebugLog());1665if (errorMessage) {1666*errorMessage = shader.getInfoLog();1667(*errorMessage) += shader.getInfoDebugLog();1668}1669return false;1670}16711672// Can't fail, parsing worked, "linking" worked.1673glslang::SpvOptions options;1674options.disableOptimizer = false;1675options.optimizeSize = false;1676options.generateDebugInfo = false;1677glslang::GlslangToSpv(*program.getIntermediate(stage), spirv, &options);1678return true;1679}16801681void init_glslang() {1682glslang::InitializeProcess();1683}16841685void finalize_glslang() {1686glslang::FinalizeProcess();1687}16881689void VulkanDeleteList::Take(VulkanDeleteList &del) {1690_dbg_assert_(cmdPools_.empty());1691_dbg_assert_(descPools_.empty());1692_dbg_assert_(modules_.empty());1693_dbg_assert_(buffers_.empty());1694_dbg_assert_(bufferViews_.empty());1695_dbg_assert_(buffersWithAllocs_.empty());1696_dbg_assert_(imageViews_.empty());1697_dbg_assert_(imagesWithAllocs_.empty());1698_dbg_assert_(deviceMemory_.empty());1699_dbg_assert_(samplers_.empty());1700_dbg_assert_(pipelines_.empty());1701_dbg_assert_(pipelineCaches_.empty());1702_dbg_assert_(renderPasses_.empty());1703_dbg_assert_(framebuffers_.empty());1704_dbg_assert_(pipelineLayouts_.empty());1705_dbg_assert_(descSetLayouts_.empty());1706_dbg_assert_(callbacks_.empty());1707cmdPools_ = std::move(del.cmdPools_);1708descPools_ = std::move(del.descPools_);1709modules_ = std::move(del.modules_);1710buffers_ = std::move(del.buffers_);1711buffersWithAllocs_ = std::move(del.buffersWithAllocs_);1712bufferViews_ = std::move(del.bufferViews_);1713imageViews_ = std::move(del.imageViews_);1714imagesWithAllocs_ = std::move(del.imagesWithAllocs_);1715deviceMemory_ = std::move(del.deviceMemory_);1716samplers_ = std::move(del.samplers_);1717pipelines_ = std::move(del.pipelines_);1718pipelineCaches_ = std::move(del.pipelineCaches_);1719renderPasses_ = std::move(del.renderPasses_);1720framebuffers_ = std::move(del.framebuffers_);1721pipelineLayouts_ = std::move(del.pipelineLayouts_);1722descSetLayouts_ = std::move(del.descSetLayouts_);1723callbacks_ = std::move(del.callbacks_);1724del.cmdPools_.clear();1725del.descPools_.clear();1726del.modules_.clear();1727del.buffers_.clear();1728del.buffersWithAllocs_.clear();1729del.imageViews_.clear();1730del.imagesWithAllocs_.clear();1731del.deviceMemory_.clear();1732del.samplers_.clear();1733del.pipelines_.clear();1734del.pipelineCaches_.clear();1735del.renderPasses_.clear();1736del.framebuffers_.clear();1737del.pipelineLayouts_.clear();1738del.descSetLayouts_.clear();1739del.callbacks_.clear();1740}17411742void VulkanDeleteList::PerformDeletes(VulkanContext *vulkan, VmaAllocator allocator) {1743int deleteCount = 0;17441745for (auto &callback : callbacks_) {1746callback.func(vulkan, callback.userdata);1747deleteCount++;1748}1749callbacks_.clear();17501751VkDevice device = vulkan->GetDevice();1752for (auto &cmdPool : cmdPools_) {1753vkDestroyCommandPool(device, cmdPool, nullptr);1754deleteCount++;1755}1756cmdPools_.clear();1757for (auto &descPool : descPools_) {1758vkDestroyDescriptorPool(device, descPool, nullptr);1759deleteCount++;1760}1761descPools_.clear();1762for (auto &module : modules_) {1763vkDestroyShaderModule(device, module, nullptr);1764deleteCount++;1765}1766modules_.clear();1767for (auto &buf : buffers_) {1768vkDestroyBuffer(device, buf, nullptr);1769deleteCount++;1770}1771buffers_.clear();1772for (auto &buf : buffersWithAllocs_) {1773vmaDestroyBuffer(allocator, buf.buffer, buf.alloc);1774deleteCount++;1775}1776buffersWithAllocs_.clear();1777for (auto &bufView : bufferViews_) {1778vkDestroyBufferView(device, bufView, nullptr);1779deleteCount++;1780}1781bufferViews_.clear();1782for (auto &imageWithAlloc : imagesWithAllocs_) {1783vmaDestroyImage(allocator, imageWithAlloc.image, imageWithAlloc.alloc);1784deleteCount++;1785}1786imagesWithAllocs_.clear();1787for (auto &imageView : imageViews_) {1788vkDestroyImageView(device, imageView, nullptr);1789deleteCount++;1790}1791imageViews_.clear();1792for (auto &mem : deviceMemory_) {1793vkFreeMemory(device, mem, nullptr);1794deleteCount++;1795}1796deviceMemory_.clear();1797for (auto &sampler : samplers_) {1798vkDestroySampler(device, sampler, nullptr);1799deleteCount++;1800}1801samplers_.clear();1802for (auto &pipeline : pipelines_) {1803vkDestroyPipeline(device, pipeline, nullptr);1804deleteCount++;1805}1806pipelines_.clear();1807for (auto &pcache : pipelineCaches_) {1808vkDestroyPipelineCache(device, pcache, nullptr);1809deleteCount++;1810}1811pipelineCaches_.clear();1812for (auto &renderPass : renderPasses_) {1813vkDestroyRenderPass(device, renderPass, nullptr);1814deleteCount++;1815}1816renderPasses_.clear();1817for (auto &framebuffer : framebuffers_) {1818vkDestroyFramebuffer(device, framebuffer, nullptr);1819deleteCount++;1820}1821framebuffers_.clear();1822for (auto &pipeLayout : pipelineLayouts_) {1823vkDestroyPipelineLayout(device, pipeLayout, nullptr);1824deleteCount++;1825}1826pipelineLayouts_.clear();1827for (auto &descSetLayout : descSetLayouts_) {1828vkDestroyDescriptorSetLayout(device, descSetLayout, nullptr);1829deleteCount++;1830}1831descSetLayouts_.clear();1832for (auto &queryPool : queryPools_) {1833vkDestroyQueryPool(device, queryPool, nullptr);1834deleteCount++;1835}1836queryPools_.clear();1837deleteCount_ = deleteCount;1838}18391840void VulkanContext::GetImageMemoryRequirements(VkImage image, VkMemoryRequirements *mem_reqs, bool *dedicatedAllocation) {1841if (Extensions().KHR_dedicated_allocation) {1842VkImageMemoryRequirementsInfo2KHR memReqInfo2{VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR};1843memReqInfo2.image = image;18441845VkMemoryRequirements2KHR memReq2 = {VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR};1846VkMemoryDedicatedRequirementsKHR memDedicatedReq{VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR};1847ChainStruct(memReq2, &memDedicatedReq);18481849vkGetImageMemoryRequirements2(GetDevice(), &memReqInfo2, &memReq2);18501851*mem_reqs = memReq2.memoryRequirements;1852*dedicatedAllocation =1853(memDedicatedReq.requiresDedicatedAllocation != VK_FALSE) ||1854(memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);1855} else {1856vkGetImageMemoryRequirements(GetDevice(), image, mem_reqs);1857*dedicatedAllocation = false;1858}1859}18601861bool IsHashMaliDriverVersion(const VkPhysicalDeviceProperties &props) {1862// ARM used to put a hash in place of the driver version.1863// Now they only use major versions. We'll just make a bad heuristic.1864uint32_t major = VK_VERSION_MAJOR(props.driverVersion);1865uint32_t branch = VK_VERSION_PATCH(props.driverVersion);1866if (branch > 0)1867return true;1868if (branch > 100 || major > 100)1869return true;1870// Can (in theory) have false negatives!1871return false;1872}18731874// From Sascha's code1875std::string FormatDriverVersion(const VkPhysicalDeviceProperties &props) {1876if (props.vendorID == VULKAN_VENDOR_NVIDIA) {1877// For whatever reason, NVIDIA has their own scheme.1878// 10 bits = major version (up to r1023)1879// 8 bits = minor version (up to 255)1880// 8 bits = secondary branch version/build version (up to 255)1881// 6 bits = tertiary branch/build version (up to 63)1882uint32_t major = (props.driverVersion >> 22) & 0x3ff;1883uint32_t minor = (props.driverVersion >> 14) & 0x0ff;1884uint32_t secondaryBranch = (props.driverVersion >> 6) & 0x0ff;1885uint32_t tertiaryBranch = (props.driverVersion) & 0x003f;1886return StringFromFormat("%d.%d.%d.%d", major, minor, secondaryBranch, tertiaryBranch);1887} else if (props.vendorID == VULKAN_VENDOR_ARM) {1888// ARM used to just put a hash here. No point in splitting it up.1889if (IsHashMaliDriverVersion(props)) {1890return StringFromFormat("(hash) %08x", props.driverVersion);1891}1892}1893// Qualcomm has an inscrutable versioning scheme. Let's just display it as normal.1894// Standard scheme, use the standard macros.1895uint32_t major = VK_VERSION_MAJOR(props.driverVersion);1896uint32_t minor = VK_VERSION_MINOR(props.driverVersion);1897uint32_t branch = VK_VERSION_PATCH(props.driverVersion);1898return StringFromFormat("%d.%d.%d (%08x)", major, minor, branch, props.driverVersion);1899}19001901std::string FormatAPIVersion(u32 version) {1902return StringFromFormat("%d.%d.%d", VK_API_VERSION_MAJOR(version), VK_API_VERSION_MINOR(version), VK_API_VERSION_PATCH(version));1903}19041905// Mainly just the formats seen on gpuinfo.org for swapchains, as this function is only used for listing1906// those in the UI. Also depth buffers that we used in one place.1907// Might add more in the future if we find more uses for this.1908const char *VulkanFormatToString(VkFormat format) {1909switch (format) {1910case VK_FORMAT_A1R5G5B5_UNORM_PACK16: return "A1R5G5B5_UNORM_PACK16";1911case VK_FORMAT_A2B10G10R10_UNORM_PACK32: return "A2B10G10R10_UNORM_PACK32";1912case VK_FORMAT_A2R10G10B10_UNORM_PACK32: return "A2R10G10B10_UNORM_PACK32";1913case VK_FORMAT_A8B8G8R8_SNORM_PACK32: return "A8B8G8R8_SNORM_PACK32";1914case VK_FORMAT_A8B8G8R8_SRGB_PACK32: return "A8B8G8R8_SRGB_PACK32";1915case VK_FORMAT_A8B8G8R8_UNORM_PACK32: return "A8B8G8R8_UNORM_PACK32";1916case VK_FORMAT_B10G11R11_UFLOAT_PACK32: return "B10G11R11_UFLOAT_PACK32";1917case VK_FORMAT_B4G4R4A4_UNORM_PACK16: return "B4G4R4A4_UNORM_PACK16";1918case VK_FORMAT_B5G5R5A1_UNORM_PACK16: return "B5G5R5A1_UNORM_PACK16";1919case VK_FORMAT_B5G6R5_UNORM_PACK16: return "B5G6R5_UNORM_PACK16";1920case VK_FORMAT_B8G8R8A8_SNORM: return "B8G8R8A8_SNORM";1921case VK_FORMAT_B8G8R8A8_SRGB: return "B8G8R8A8_SRGB";1922case VK_FORMAT_B8G8R8A8_UNORM: return "B8G8R8A8_UNORM";1923case VK_FORMAT_R16G16B16A16_SFLOAT: return "R16G16B16A16_SFLOAT";1924case VK_FORMAT_R16G16B16A16_SNORM: return "R16G16B16A16_SNORM";1925case VK_FORMAT_R16G16B16A16_UNORM: return "R16G16B16A16_UNORM";1926case VK_FORMAT_R4G4B4A4_UNORM_PACK16: return "R4G4B4A4_UNORM_PACK16";1927case VK_FORMAT_R5G5B5A1_UNORM_PACK16: return "R5G5B5A1_UNORM_PACK16";1928case VK_FORMAT_R5G6B5_UNORM_PACK16: return "R5G6B5_UNORM_PACK16";1929case VK_FORMAT_R8G8B8A8_SNORM: return "R8G8B8A8_SNORM";1930case VK_FORMAT_R8G8B8A8_SRGB: return "R8G8B8A8_SRGB";1931case VK_FORMAT_R8G8B8A8_UNORM: return "R8G8B8A8_UNORM";19321933case VK_FORMAT_D24_UNORM_S8_UINT: return "D24S8";1934case VK_FORMAT_D16_UNORM: return "D16";1935case VK_FORMAT_D16_UNORM_S8_UINT: return "D16S8";1936case VK_FORMAT_D32_SFLOAT: return "D32f";1937case VK_FORMAT_D32_SFLOAT_S8_UINT: return "D32fS8";1938case VK_FORMAT_S8_UINT: return "S8";1939case VK_FORMAT_UNDEFINED: return "UNDEFINED (BAD!)";19401941default: return "(format not added to string list)";1942}1943}19441945// I miss Rust where this is automatic :(1946const char *VulkanColorSpaceToString(VkColorSpaceKHR colorSpace) {1947switch (colorSpace) {1948case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: return "SRGB_NONLINEAR";1949case VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT: return "DISPLAY_P3_NONLINEAR";1950case VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT: return "EXTENDED_SRGB_LINEAR";1951case VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT: return "DISPLAY_P3_LINEAR";1952case VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT: return "DCI_P3_NONLINEAR";1953case VK_COLOR_SPACE_BT709_LINEAR_EXT: return "BT709_LINEAR";1954case VK_COLOR_SPACE_BT709_NONLINEAR_EXT: return "BT709_NONLINEAR";1955case VK_COLOR_SPACE_BT2020_LINEAR_EXT: return "BT2020_LINEAR";1956case VK_COLOR_SPACE_HDR10_ST2084_EXT: return "HDR10_ST2084";1957case VK_COLOR_SPACE_DOLBYVISION_EXT: return "DOLBYVISION";1958case VK_COLOR_SPACE_HDR10_HLG_EXT: return "HDR10_HLG";1959case VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT: return "ADOBERGB_LINEAR";1960case VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT: return "ADOBERGB_NONLINEAR";1961case VK_COLOR_SPACE_PASS_THROUGH_EXT: return "PASS_THROUGH";1962case VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT: return "EXTENDED_SRGB_NONLINEAR";1963case VK_COLOR_SPACE_DISPLAY_NATIVE_AMD: return "DISPLAY_NATIVE_AMD";1964default: return "(unknown)";1965}1966}196719681969