From 08c3e13517505fce8d4e359f3ca98337e75a3c4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 17 Mar 2018 11:03:24 +0100 Subject: [PATCH] Do more detailed checks for Vulkan availability. Should hopefully solve #10729. --- Common/Vulkan/VulkanContext.cpp | 1 + Common/Vulkan/VulkanLoader.cpp | 119 +++++++++++++++++++++++++-- Common/Vulkan/VulkanLoader.h | 1 + android/jni/AndroidVulkanContext.cpp | 4 +- android/jni/app-android.cpp | 2 +- 5 files changed, 117 insertions(+), 10 deletions(-) diff --git a/Common/Vulkan/VulkanContext.cpp b/Common/Vulkan/VulkanContext.cpp index c14d6a561e..77fa7ed392 100644 --- a/Common/Vulkan/VulkanContext.cpp +++ b/Common/Vulkan/VulkanContext.cpp @@ -579,6 +579,7 @@ VkResult VulkanContext::CreateDevice() { VulkanLoadDeviceFunctions(device_); } ILOG("Device created.\n"); + VulkanSetAvailable(true); return res; } diff --git a/Common/Vulkan/VulkanLoader.cpp b/Common/Vulkan/VulkanLoader.cpp index 907f2d61e8..01f1d1c178 100644 --- a/Common/Vulkan/VulkanLoader.cpp +++ b/Common/Vulkan/VulkanLoader.cpp @@ -16,6 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "Common/Vulkan/VulkanLoader.h" +#include #include "base/logging.h" #include "base/basictypes.h" @@ -203,37 +204,139 @@ static HINSTANCE vulkanLibrary; static void *vulkanLibrary; #endif +bool g_vulkanAvailabilityChecked = false; +bool g_vulkanMayBeAvailable = false; + #define LOAD_INSTANCE_FUNC(instance, x) x = (PFN_ ## x)vkGetInstanceProcAddr(instance, #x); if (!x) {ILOG("Missing (instance): %s", #x);} #define LOAD_DEVICE_FUNC(instance, x) x = (PFN_ ## x)vkGetDeviceProcAddr(instance, #x); if (!x) {ILOG("Missing (device): %s", #x);} #define LOAD_GLOBAL_FUNC(x) x = (PFN_ ## x)dlsym(vulkanLibrary, #x); if (!x) {ILOG("Missing (global): %s", #x);} +#define LOAD_GLOBAL_FUNC_LOCAL(lib, x) (PFN_ ## x)dlsym(lib, #x); + static const char *so_names[] = { "libvulkan.so", "libvulkan.so.1", }; +void VulkanSetAvailable(bool available) { + g_vulkanAvailabilityChecked = true; + g_vulkanMayBeAvailable = available; +} + bool VulkanMayBeAvailable() { - if (vulkanLibrary) - return true; + if (g_vulkanAvailabilityChecked) + return g_vulkanMayBeAvailable; bool available = false; #ifndef _WIN32 + void *lib = nullptr; for (int i = 0; i < ARRAY_SIZE(so_names); i++) { - void *lib = dlopen(so_names[i], RTLD_NOW | RTLD_LOCAL); + lib = dlopen(so_names[i], RTLD_NOW | RTLD_LOCAL); if (lib) { available = true; - dlclose(lib); break; } } #else // LoadLibrary etc HINSTANCE lib = LoadLibrary(L"vulkan-1.dll"); + available = lib != 0; - if (lib) { - FreeLibrary(lib); - } #endif - return available; + // Do a hyper minimal initialization and teardown to figure out if there's any chance + // that any sort of Vulkan will be usable. + PFN_vkCreateInstance localCreateInstance = LOAD_GLOBAL_FUNC_LOCAL(lib, vkCreateInstance); + PFN_vkEnumeratePhysicalDevices localEnumerate = LOAD_GLOBAL_FUNC_LOCAL(lib, vkEnumeratePhysicalDevices); + PFN_vkDestroyInstance localDestroyInstance = LOAD_GLOBAL_FUNC_LOCAL(lib, vkDestroyInstance); + PFN_vkGetPhysicalDeviceProperties localGetPhysicalDeviceProperties = LOAD_GLOBAL_FUNC_LOCAL(lib, vkGetPhysicalDeviceProperties); + + // Need to predeclare all this because of the gotos... + VkInstanceCreateInfo ci{ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO }; + VkApplicationInfo info{ VK_STRUCTURE_TYPE_APPLICATION_INFO }; + std::vector devices; + bool anyGood; + const char *instanceExtensions[1]; + int extCount = 0; + VkInstance instance = VK_NULL_HANDLE; + VkResult res; + uint32_t physicalDeviceCount = 0; + + if (!localCreateInstance || !localEnumerate || !localDestroyInstance || !localGetPhysicalDeviceProperties) + goto bail; + +#ifdef _WIN32 + instanceExtensions[0] = VK_KHR_WIN32_SURFACE_EXTENSION_NAME; + ci.enabledExtensionCount = 1; +#elif defined(__ANDROID__) + instanceExtensions[0] = VK_KHR_ANDROID_SURFACE_EXTENSION_NAME; + ci.enabledExtensionCount = 1; +#endif + ci.ppEnabledExtensionNames = instanceExtensions; + ci.enabledLayerCount = 0; + info.apiVersion = VK_API_VERSION_1_0; + info.applicationVersion = 1; + info.engineVersion = 1; + info.pApplicationName = "VulkanChecker"; + info.pEngineName = "VulkanChecker"; + ci.pApplicationInfo = &info; + ci.flags = 0; + res = localCreateInstance(&ci, nullptr, &instance); + if (res != VK_SUCCESS) { + ELOG("Failed to create vulkan instance."); + goto bail; + } + ILOG("Vulkan test instance created successfully."); + res = localEnumerate(instance, &physicalDeviceCount, nullptr); + if (res != VK_SUCCESS) { + ELOG("Failed to count physical devices."); + goto bail; + } + if (physicalDeviceCount == 0) { + ELOG("No physical Vulkan devices."); + goto bail; + } + devices.resize(physicalDeviceCount); + res = localEnumerate(instance, &physicalDeviceCount, devices.data()); + if (res != VK_SUCCESS) { + ELOG("Failed to enumerate physical devices."); + goto bail; + } + anyGood = false; + for (auto device : devices) { + VkPhysicalDeviceProperties props; + localGetPhysicalDeviceProperties(device, &props); + switch (props.deviceType) { + case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: + case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: + case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: + anyGood = true; + break; + } + // TODO: Should also check queuefamilyproperties for a GRAPHICS queue family? Oh well. + } + + if (!anyGood) { + WLOG("Found Vulkan API, but no good Vulkan device!"); + g_vulkanMayBeAvailable = false; + } else { + ILOG("Found working Vulkan API!"); + g_vulkanMayBeAvailable = true; + } + +bail: + g_vulkanAvailabilityChecked = true; + if (instance) { + localDestroyInstance(instance, nullptr); + } + if (lib) { +#ifndef _WIN32 + dlclose(lib); +#else + FreeLibrary(lib); +#endif + } else { + ELOG("Vulkan with working device not detected."); + } + return g_vulkanMayBeAvailable; } bool VulkanLoad() { diff --git a/Common/Vulkan/VulkanLoader.h b/Common/Vulkan/VulkanLoader.h index 636bff82c7..d51d833da5 100644 --- a/Common/Vulkan/VulkanLoader.h +++ b/Common/Vulkan/VulkanLoader.h @@ -201,6 +201,7 @@ extern PFN_vkDestroyDebugReportCallbackEXT dyn_vkDestroyDebugReportCallbackEXT; // Way to do a quick check before even attempting to load. bool VulkanMayBeAvailable(); +void VulkanSetAvailable(bool available); bool VulkanLoad(); void VulkanLoadInstanceFunctions(VkInstance instance); diff --git a/android/jni/AndroidVulkanContext.cpp b/android/jni/AndroidVulkanContext.cpp index d41c6af5ad..4240bde18c 100644 --- a/android/jni/AndroidVulkanContext.cpp +++ b/android/jni/AndroidVulkanContext.cpp @@ -98,9 +98,11 @@ bool AndroidVulkanContext::InitAPI() { info.app_name = "PPSSPP"; info.app_ver = gitVer.ToInteger(); info.flags = VULKAN_FLAG_PRESENT_MAILBOX | VULKAN_FLAG_PRESENT_FIFO_RELAXED; - if (VK_SUCCESS != g_Vulkan->CreateInstance(info)) { + VkResult res = g_Vulkan->CreateInstance(info); + if (res != VK_SUCCESS) { ELOG("Failed to create vulkan context: %s", g_Vulkan->InitError().c_str()); System_SendMessage("toast", "No Vulkan compatible device found. Using OpenGL instead."); + VulkanSetAvailable(false); delete g_Vulkan; g_Vulkan = nullptr; return false; diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 49d472ceba..335230f3ef 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -431,10 +431,10 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init NativeInit(2, argv, user_data_path.c_str(), externalDir.c_str(), cacheDir.c_str()); } - retry: // Now that we've loaded config, set javaGL. javaGL = NativeQueryConfig("androidJavaGL") == "true"; + switch (g_Config.iGPUBackend) { case (int)GPUBackend::OPENGL: useCPUThread = true;