ppsspp/Common/GPU/Vulkan/VulkanLoader.cpp
Henrik Rydgård b13c5c2d1b Add compat setting to force using maximum depth resolution
Fixes #17014

Even if our depth-testing-equal heuristic believes that the game needs
lower depth resolution.

This removes some depth-fighting artifacts (that are present on the real
PSP, but nice to avoid) in Outrun, Split/Second and Cars: Race-o-Rama -
essentially reverting these to the behavior we had before the heuristic.

(The heuristic is good though - it means less compat.ini hacks going in
the other direction).

In the case of Outrun, this relies on two passes that pass exactly the
same vertex coordinates twice resulting in the exact same final
geometry. This is actually guaranteed by the spec if the vertex math is
exactly the same and "invariant" is set on the position output, though
I guess you never know.. Haven't seen any issues at least.

Also sneak in disabling some validation messages from using extra Vulkan
validation layers other than the default.
2023-03-26 00:27:58 +01:00

787 lines
33 KiB
C++

// Copyright (c) 2016- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "ppsspp_config.h"
#include <vector>
#include <string>
#include <cstring>
#include "Common/GPU/Vulkan/VulkanLoader.h"
#include "Common/Log.h"
#include "Common/System/System.h"
#include "Common/VR/PPSSPPVR.h"
#if !PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(SWITCH)
#include <dlfcn.h>
#endif
namespace PPSSPP_VK {
PFN_vkCreateInstance vkCreateInstance;
PFN_vkDestroyInstance vkDestroyInstance;
PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices;
PFN_vkEnumerateInstanceVersion vkEnumerateInstanceVersion;
PFN_vkGetPhysicalDeviceFeatures vkGetPhysicalDeviceFeatures;
PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties;
PFN_vkGetPhysicalDeviceImageFormatProperties vkGetPhysicalDeviceImageFormatProperties;
PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties;
PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2;
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;
PFN_vkCreateDevice vkCreateDevice;
PFN_vkDestroyDevice vkDestroyDevice;
PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties;
PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties;
PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties;
PFN_vkEnumerateDeviceLayerProperties vkEnumerateDeviceLayerProperties;
PFN_vkGetDeviceQueue vkGetDeviceQueue;
PFN_vkQueueSubmit vkQueueSubmit;
PFN_vkQueueWaitIdle vkQueueWaitIdle;
PFN_vkDeviceWaitIdle vkDeviceWaitIdle;
PFN_vkAllocateMemory vkAllocateMemory;
PFN_vkFreeMemory vkFreeMemory;
PFN_vkMapMemory vkMapMemory;
PFN_vkUnmapMemory vkUnmapMemory;
PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges;
PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges;
PFN_vkGetDeviceMemoryCommitment vkGetDeviceMemoryCommitment;
PFN_vkBindBufferMemory vkBindBufferMemory;
PFN_vkBindBufferMemory2 vkBindBufferMemory2;
PFN_vkBindImageMemory vkBindImageMemory;
PFN_vkBindImageMemory2 vkBindImageMemory2;
PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2;
PFN_vkGetDeviceBufferMemoryRequirements vkGetDeviceBufferMemoryRequirements;
PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
PFN_vkGetImageMemoryRequirements2 vkGetImageMemoryRequirements2;
PFN_vkGetDeviceImageMemoryRequirements vkGetDeviceImageMemoryRequirements;
PFN_vkCreateFence vkCreateFence;
PFN_vkDestroyFence vkDestroyFence;
PFN_vkGetFenceStatus vkGetFenceStatus;
PFN_vkCreateSemaphore vkCreateSemaphore;
PFN_vkDestroySemaphore vkDestroySemaphore;
PFN_vkCreateEvent vkCreateEvent;
PFN_vkDestroyEvent vkDestroyEvent;
PFN_vkGetEventStatus vkGetEventStatus;
PFN_vkSetEvent vkSetEvent;
PFN_vkResetEvent vkResetEvent;
PFN_vkCreateQueryPool vkCreateQueryPool;
PFN_vkDestroyQueryPool vkDestroyQueryPool;
PFN_vkGetQueryPoolResults vkGetQueryPoolResults;
PFN_vkCreateBuffer vkCreateBuffer;
PFN_vkDestroyBuffer vkDestroyBuffer;
PFN_vkCreateBufferView vkCreateBufferView;
PFN_vkDestroyBufferView vkDestroyBufferView;
PFN_vkCreateImage vkCreateImage;
PFN_vkDestroyImage vkDestroyImage;
PFN_vkGetImageSubresourceLayout vkGetImageSubresourceLayout;
PFN_vkCreateImageView vkCreateImageView;
PFN_vkDestroyImageView vkDestroyImageView;
PFN_vkCreateShaderModule vkCreateShaderModule;
PFN_vkDestroyShaderModule vkDestroyShaderModule;
PFN_vkCreatePipelineCache vkCreatePipelineCache;
PFN_vkDestroyPipelineCache vkDestroyPipelineCache;
PFN_vkGetPipelineCacheData vkGetPipelineCacheData;
PFN_vkMergePipelineCaches vkMergePipelineCaches;
PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines;
PFN_vkCreateComputePipelines vkCreateComputePipelines;
PFN_vkDestroyPipeline vkDestroyPipeline;
PFN_vkCreatePipelineLayout vkCreatePipelineLayout;
PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout;
PFN_vkCreateSampler vkCreateSampler;
PFN_vkDestroySampler vkDestroySampler;
PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout;
PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout;
PFN_vkCreateDescriptorPool vkCreateDescriptorPool;
PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool;
PFN_vkResetDescriptorPool vkResetDescriptorPool;
PFN_vkAllocateDescriptorSets vkAllocateDescriptorSets;
PFN_vkFreeDescriptorSets vkFreeDescriptorSets;
PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets;
PFN_vkCreateFramebuffer vkCreateFramebuffer;
PFN_vkDestroyFramebuffer vkDestroyFramebuffer;
PFN_vkCreateRenderPass vkCreateRenderPass;
PFN_vkDestroyRenderPass vkDestroyRenderPass;
PFN_vkGetRenderAreaGranularity vkGetRenderAreaGranularity;
PFN_vkCreateCommandPool vkCreateCommandPool;
PFN_vkDestroyCommandPool vkDestroyCommandPool;
PFN_vkResetCommandPool vkResetCommandPool;
PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers;
PFN_vkFreeCommandBuffers vkFreeCommandBuffers;
// Used frequently together
PFN_vkCmdBindPipeline vkCmdBindPipeline;
PFN_vkCmdSetViewport vkCmdSetViewport;
PFN_vkCmdSetScissor vkCmdSetScissor;
PFN_vkCmdSetBlendConstants vkCmdSetBlendConstants;
PFN_vkCmdSetStencilCompareMask vkCmdSetStencilCompareMask;
PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask;
PFN_vkCmdSetStencilReference vkCmdSetStencilReference;
PFN_vkCmdBindDescriptorSets vkCmdBindDescriptorSets;
PFN_vkCmdBindIndexBuffer vkCmdBindIndexBuffer;
PFN_vkCmdBindVertexBuffers vkCmdBindVertexBuffers;
PFN_vkCmdDraw vkCmdDraw;
PFN_vkCmdDrawIndexed vkCmdDrawIndexed;
PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier;
PFN_vkCmdPushConstants vkCmdPushConstants;
// Every frame to a few times per frame
PFN_vkWaitForFences vkWaitForFences;
PFN_vkResetFences vkResetFences;
PFN_vkBeginCommandBuffer vkBeginCommandBuffer;
PFN_vkEndCommandBuffer vkEndCommandBuffer;
PFN_vkResetCommandBuffer vkResetCommandBuffer;
PFN_vkCmdClearAttachments vkCmdClearAttachments;
PFN_vkCmdSetEvent vkCmdSetEvent;
PFN_vkCmdResetEvent vkCmdResetEvent;
PFN_vkCmdWaitEvents vkCmdWaitEvents;
PFN_vkCmdBeginRenderPass vkCmdBeginRenderPass;
PFN_vkCmdEndRenderPass vkCmdEndRenderPass;
PFN_vkCmdCopyBuffer vkCmdCopyBuffer;
PFN_vkCmdCopyImage vkCmdCopyImage;
PFN_vkCmdBlitImage vkCmdBlitImage;
PFN_vkCmdCopyBufferToImage vkCmdCopyBufferToImage;
PFN_vkCmdCopyImageToBuffer vkCmdCopyImageToBuffer;
// Rare or not used
PFN_vkCmdSetDepthBounds vkCmdSetDepthBounds;
PFN_vkCmdSetLineWidth vkCmdSetLineWidth;
PFN_vkCmdSetDepthBias vkCmdSetDepthBias;
PFN_vkCmdDrawIndirect vkCmdDrawIndirect;
PFN_vkCmdDrawIndexedIndirect vkCmdDrawIndexedIndirect;
PFN_vkCmdDispatch vkCmdDispatch;
PFN_vkCmdDispatchIndirect vkCmdDispatchIndirect;
PFN_vkCmdUpdateBuffer vkCmdUpdateBuffer;
PFN_vkCmdFillBuffer vkCmdFillBuffer;
PFN_vkCmdClearColorImage vkCmdClearColorImage;
PFN_vkCmdClearDepthStencilImage vkCmdClearDepthStencilImage;
PFN_vkCmdResolveImage vkCmdResolveImage;
PFN_vkCmdBeginQuery vkCmdBeginQuery;
PFN_vkCmdEndQuery vkCmdEndQuery;
PFN_vkCmdResetQueryPool vkCmdResetQueryPool;
PFN_vkCmdWriteTimestamp vkCmdWriteTimestamp;
PFN_vkCmdCopyQueryPoolResults vkCmdCopyQueryPoolResults;
PFN_vkCmdNextSubpass vkCmdNextSubpass;
PFN_vkCmdExecuteCommands vkCmdExecuteCommands;
#ifdef __ANDROID__
PFN_vkCreateAndroidSurfaceKHR vkCreateAndroidSurfaceKHR;
#elif defined(_WIN32)
PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR;
#endif
#if defined(VK_USE_PLATFORM_METAL_EXT)
PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT;
#endif
#if defined(VK_USE_PLATFORM_XLIB_KHR)
PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR;
#endif
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR;
#endif
#if defined(VK_USE_PLATFORM_DISPLAY_KHR)
PFN_vkCreateDisplayPlaneSurfaceKHR vkCreateDisplayPlaneSurfaceKHR;
PFN_vkGetPhysicalDeviceDisplayPropertiesKHR vkGetPhysicalDeviceDisplayPropertiesKHR;
PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR vkGetPhysicalDeviceDisplayPlanePropertiesKHR;
PFN_vkGetDisplayModePropertiesKHR vkGetDisplayModePropertiesKHR;
PFN_vkGetDisplayPlaneSupportedDisplaysKHR vkGetDisplayPlaneSupportedDisplaysKHR;
PFN_vkGetDisplayPlaneCapabilitiesKHR vkGetDisplayPlaneCapabilitiesKHR;
#endif
PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR;
// WSI extension.
PFN_vkGetPhysicalDeviceSurfaceSupportKHR vkGetPhysicalDeviceSurfaceSupportKHR;
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR;
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR;
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR;
PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR;
PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR;
PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR;
PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR;
PFN_vkQueuePresentKHR vkQueuePresentKHR;
PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT;
PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT;
PFN_vkCmdBeginDebugUtilsLabelEXT vkCmdBeginDebugUtilsLabelEXT;
PFN_vkCmdEndDebugUtilsLabelEXT vkCmdEndDebugUtilsLabelEXT;
PFN_vkCmdInsertDebugUtilsLabelEXT vkCmdInsertDebugUtilsLabelEXT;
PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT;
PFN_vkSetDebugUtilsObjectTagEXT vkSetDebugUtilsObjectTagEXT;
PFN_vkGetBufferMemoryRequirements2KHR vkGetBufferMemoryRequirements2KHR;
PFN_vkGetImageMemoryRequirements2KHR vkGetImageMemoryRequirements2KHR;
PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR;
PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR;
PFN_vkCreateRenderPass2KHR vkCreateRenderPass2KHR;
} // namespace PPSSPP_VK
using namespace PPSSPP_VK;
#if PPSSPP_PLATFORM(SWITCH)
typedef void *VulkanLibraryHandle;
static VulkanLibraryHandle vulkanLibrary;
#define dlsym(x, y) nullptr
#elif PPSSPP_PLATFORM(WINDOWS)
typedef HINSTANCE VulkanLibraryHandle;
static VulkanLibraryHandle vulkanLibrary;
#define dlsym(x, y) GetProcAddress(x, y)
#else
typedef void *VulkanLibraryHandle;
static VulkanLibraryHandle vulkanLibrary;
#endif
bool g_vulkanAvailabilityChecked = false;
bool g_vulkanMayBeAvailable = false;
#define LOAD_INSTANCE_FUNC(instance, x) x = (PFN_ ## x)vkGetInstanceProcAddr(instance, #x); if (!x) {INFO_LOG(G3D, "Missing (instance): %s", #x);}
#define LOAD_DEVICE_FUNC(instance, x) x = (PFN_ ## x)vkGetDeviceProcAddr(instance, #x); if (!x) {INFO_LOG(G3D, "Missing (device): %s", #x);}
#define LOAD_GLOBAL_FUNC(x) x = (PFN_ ## x)dlsym(vulkanLibrary, #x); if (!x) {INFO_LOG(G3D,"Missing (global): %s", #x);}
#define LOAD_GLOBAL_FUNC_LOCAL(lib, x) (PFN_ ## x)dlsym(lib, #x);
static const char *device_name_blacklist[] = {
"NVIDIA:SHIELD Tablet K1",
"SDL:Horizon",
};
#ifndef _WIN32
static const char *so_names[] = {
#if PPSSPP_PLATFORM(IOS)
"@executable_path/Frameworks/libMoltenVK.dylib",
#elif PPSSPP_PLATFORM(MAC)
"@executable_path/../Frameworks/libMoltenVK.dylib",
#else
"libvulkan.so",
#if !defined(__ANDROID__)
"libvulkan.so.1",
#endif
#endif
};
#endif
static VulkanLibraryHandle VulkanLoadLibrary(const char *logname) {
#if PPSSPP_PLATFORM(SWITCH)
// Always unavailable, for now.
return nullptr;
#elif PPSSPP_PLATFORM(UWP)
return nullptr;
#elif PPSSPP_PLATFORM(WINDOWS)
return LoadLibrary(L"vulkan-1.dll");
#else
void *lib = nullptr;
for (int i = 0; i < ARRAY_SIZE(so_names); i++) {
lib = dlopen(so_names[i], RTLD_NOW | RTLD_LOCAL);
if (lib) {
INFO_LOG(G3D, "%s: Library loaded ('%s')", logname, so_names[i]);
break;
}
}
return lib;
#endif
}
static void VulkanFreeLibrary(VulkanLibraryHandle &h) {
if (h) {
#if PPSSPP_PLATFORM(SWITCH)
// Can't load, and can't free.
#elif PPSSPP_PLATFORM(WINDOWS)
FreeLibrary(h);
#else
dlclose(h);
#endif
h = nullptr;
}
}
void VulkanSetAvailable(bool available) {
INFO_LOG(G3D, "Forcing Vulkan availability to true");
g_vulkanAvailabilityChecked = true;
g_vulkanMayBeAvailable = available;
}
bool VulkanMayBeAvailable() {
// Unsupported in VR at the moment
if (IsVREnabled()) {
return false;
}
if (g_vulkanAvailabilityChecked) {
return g_vulkanMayBeAvailable;
}
std::string name = System_GetProperty(SYSPROP_NAME);
for (const char *blacklisted_name : device_name_blacklist) {
if (!strcmp(name.c_str(), blacklisted_name)) {
INFO_LOG(G3D, "VulkanMayBeAvailable: Device blacklisted ('%s')", name.c_str());
g_vulkanAvailabilityChecked = true;
g_vulkanMayBeAvailable = false;
return false;
}
}
INFO_LOG(G3D, "VulkanMayBeAvailable: Device allowed ('%s')", name.c_str());
VulkanLibraryHandle lib = VulkanLoadLibrary("VulkanMayBeAvailable");
if (!lib) {
INFO_LOG(G3D, "Vulkan loader: Library not available");
g_vulkanAvailabilityChecked = true;
g_vulkanMayBeAvailable = false;
return false;
}
// Do a hyper minimal initialization and teardown to figure out if there's any chance
// that any sort of Vulkan will be usable.
PFN_vkEnumerateInstanceExtensionProperties localEnumerateInstanceExtensionProperties = LOAD_GLOBAL_FUNC_LOCAL(lib, vkEnumerateInstanceExtensionProperties);
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<VkPhysicalDevice> devices;
bool anyGood = false;
const char *instanceExtensions[10]{};
VkInstance instance = VK_NULL_HANDLE;
VkResult res = VK_SUCCESS;
uint32_t physicalDeviceCount = 0;
uint32_t instanceExtCount = 0;
bool surfaceExtensionFound = false;
bool platformSurfaceExtensionFound = false;
std::vector<VkExtensionProperties> instanceExts;
ci.enabledExtensionCount = 0; // Should have been reset by struct initialization anyway, just paranoia.
#ifdef _WIN32
const char * const platformSurfaceExtension = VK_KHR_WIN32_SURFACE_EXTENSION_NAME;
#elif defined(__ANDROID__)
const char *platformSurfaceExtension = VK_KHR_ANDROID_SURFACE_EXTENSION_NAME;
#elif defined(VK_USE_PLATFORM_METAL_EXT)
const char * const platformSurfaceExtension = VK_EXT_METAL_SURFACE_EXTENSION_NAME;
#else
const char *platformSurfaceExtension = 0;
#endif
if (!localEnumerateInstanceExtensionProperties || !localCreateInstance || !localEnumerate || !localDestroyInstance || !localGetPhysicalDeviceProperties) {
WARN_LOG(G3D, "VulkanMayBeAvailable: Function pointer missing, bailing");
goto bail;
}
INFO_LOG(G3D, "VulkanMayBeAvailable: Enumerating instance extensions");
res = localEnumerateInstanceExtensionProperties(nullptr, &instanceExtCount, nullptr);
// Maximum paranoia.
if (res != VK_SUCCESS) {
ERROR_LOG(G3D, "Enumerating VK extensions failed (%s)", VulkanResultToString(res));
goto bail;
}
if (instanceExtCount == 0) {
ERROR_LOG(G3D, "No VK instance extensions - won't be able to present.");
goto bail;
}
INFO_LOG(G3D, "VulkanMayBeAvailable: Instance extension count: %d", instanceExtCount);
instanceExts.resize(instanceExtCount);
res = localEnumerateInstanceExtensionProperties(nullptr, &instanceExtCount, instanceExts.data());
if (res != VK_SUCCESS) {
ERROR_LOG(G3D, "Enumerating VK extensions failed (%s)", VulkanResultToString(res));
goto bail;
}
for (auto iter : instanceExts) {
INFO_LOG(G3D, "VulkanMaybeAvailable: Instance extension found: %s (%08x)", iter.extensionName, iter.specVersion);
if (platformSurfaceExtension && !strcmp(iter.extensionName, platformSurfaceExtension)) {
INFO_LOG(G3D, "VulkanMayBeAvailable: Found platform surface extension '%s'", platformSurfaceExtension);
instanceExtensions[ci.enabledExtensionCount++] = platformSurfaceExtension;
platformSurfaceExtensionFound = true;
break;
} else if (!strcmp(iter.extensionName, VK_KHR_SURFACE_EXTENSION_NAME)) {
instanceExtensions[ci.enabledExtensionCount++] = VK_KHR_SURFACE_EXTENSION_NAME;
surfaceExtensionFound = true;
}
}
if (platformSurfaceExtension) {
if (!platformSurfaceExtensionFound || !surfaceExtensionFound) {
ERROR_LOG(G3D, "Platform surface extension not found");
goto bail;
}
} else {
if (!surfaceExtensionFound) {
ERROR_LOG(G3D, "Surface extension not found");
goto bail;
}
}
// This can't happen unless the driver is double-reporting a surface extension.
if (ci.enabledExtensionCount > 2) {
ERROR_LOG(G3D, "Unexpected number of enabled instance extensions");
goto bail;
}
ci.ppEnabledExtensionNames = instanceExtensions;
ci.enabledLayerCount = 0;
info.apiVersion = VK_API_VERSION_1_0;
info.applicationVersion = 1;
info.engineVersion = 1;
info.pApplicationName = "VulkanChecker";
info.pEngineName = "VulkanCheckerEngine";
ci.pApplicationInfo = &info;
ci.flags = 0;
INFO_LOG(G3D, "VulkanMayBeAvailable: Calling vkCreateInstance");
res = localCreateInstance(&ci, nullptr, &instance);
if (res != VK_SUCCESS) {
instance = nullptr;
ERROR_LOG(G3D, "VulkanMayBeAvailable: Failed to create vulkan instance (%s)", VulkanResultToString(res));
goto bail;
}
INFO_LOG(G3D, "VulkanMayBeAvailable: Vulkan test instance created successfully.");
res = localEnumerate(instance, &physicalDeviceCount, nullptr);
if (res != VK_SUCCESS) {
ERROR_LOG(G3D, "VulkanMayBeAvailable: Failed to count physical devices (%s)", VulkanResultToString(res));
goto bail;
}
if (physicalDeviceCount == 0) {
ERROR_LOG(G3D, "VulkanMayBeAvailable: No physical Vulkan devices (count = 0).");
goto bail;
}
devices.resize(physicalDeviceCount);
res = localEnumerate(instance, &physicalDeviceCount, devices.data());
if (res != VK_SUCCESS) {
ERROR_LOG(G3D, "VulkanMayBeAvailable: Failed to enumerate physical devices (%s)", VulkanResultToString(res));
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;
INFO_LOG(G3D, "VulkanMayBeAvailable: Eligible device found: '%s'", props.deviceName);
break;
default:
INFO_LOG(G3D, "VulkanMayBeAvailable: Ineligible device found and ignored: '%s'", props.deviceName);
break;
}
// TODO: Should also check queuefamilyproperties for a GRAPHICS queue family? Oh well.
}
if (!anyGood) {
WARN_LOG(G3D, "VulkanMayBeAvailable: Found Vulkan API, but no good Vulkan device!");
g_vulkanMayBeAvailable = false;
} else {
INFO_LOG(G3D, "VulkanMayBeAvailable: Found working Vulkan API!");
g_vulkanMayBeAvailable = true;
}
bail:
g_vulkanAvailabilityChecked = true;
if (instance) {
INFO_LOG(G3D, "VulkanMayBeAvailable: Destroying instance");
localDestroyInstance(instance, nullptr);
}
if (lib) {
VulkanFreeLibrary(lib);
}
if (!g_vulkanMayBeAvailable) {
WARN_LOG(G3D, "Vulkan with working device not detected.");
}
return g_vulkanMayBeAvailable;
}
bool VulkanLoad() {
if (!vulkanLibrary) {
vulkanLibrary = VulkanLoadLibrary("VulkanLoad");
if (!vulkanLibrary) {
return false;
}
}
LOAD_GLOBAL_FUNC(vkCreateInstance);
LOAD_GLOBAL_FUNC(vkGetInstanceProcAddr);
LOAD_GLOBAL_FUNC(vkGetDeviceProcAddr);
LOAD_GLOBAL_FUNC(vkEnumerateInstanceVersion);
LOAD_GLOBAL_FUNC(vkEnumerateInstanceExtensionProperties);
LOAD_GLOBAL_FUNC(vkEnumerateInstanceLayerProperties);
if (vkCreateInstance && vkGetInstanceProcAddr && vkGetDeviceProcAddr && vkEnumerateInstanceExtensionProperties && vkEnumerateInstanceLayerProperties) {
INFO_LOG(G3D, "VulkanLoad: Base functions loaded.");
return true;
} else {
ERROR_LOG(G3D, "VulkanLoad: Failed to load Vulkan base functions.");
VulkanFreeLibrary(vulkanLibrary);
return false;
}
}
void VulkanLoadInstanceFunctions(VkInstance instance, const VulkanExtensions &enabledExtensions) {
// OK, let's use the above functions to get the rest.
LOAD_INSTANCE_FUNC(instance, vkDestroyInstance);
LOAD_INSTANCE_FUNC(instance, vkEnumeratePhysicalDevices);
LOAD_INSTANCE_FUNC(instance, vkGetPhysicalDeviceFeatures);
LOAD_INSTANCE_FUNC(instance, vkGetPhysicalDeviceFormatProperties);
LOAD_INSTANCE_FUNC(instance, vkGetPhysicalDeviceImageFormatProperties);
LOAD_INSTANCE_FUNC(instance, vkGetPhysicalDeviceProperties);
LOAD_INSTANCE_FUNC(instance, vkGetPhysicalDeviceQueueFamilyProperties);
LOAD_INSTANCE_FUNC(instance, vkGetPhysicalDeviceMemoryProperties);
LOAD_INSTANCE_FUNC(instance, vkCreateDevice);
LOAD_INSTANCE_FUNC(instance, vkDestroyDevice);
LOAD_INSTANCE_FUNC(instance, vkEnumerateDeviceExtensionProperties);
LOAD_INSTANCE_FUNC(instance, vkEnumerateDeviceLayerProperties);
LOAD_INSTANCE_FUNC(instance, vkGetDeviceQueue);
LOAD_INSTANCE_FUNC(instance, vkDeviceWaitIdle);
LOAD_INSTANCE_FUNC(instance, vkGetPhysicalDeviceSurfaceSupportKHR);
LOAD_INSTANCE_FUNC(instance, vkGetPhysicalDeviceSurfaceCapabilitiesKHR);
LOAD_INSTANCE_FUNC(instance, vkGetPhysicalDeviceSurfaceFormatsKHR);
LOAD_INSTANCE_FUNC(instance, vkGetPhysicalDeviceSurfacePresentModesKHR);
LOAD_INSTANCE_FUNC(instance, vkCreateSwapchainKHR);
LOAD_INSTANCE_FUNC(instance, vkDestroySwapchainKHR);
LOAD_INSTANCE_FUNC(instance, vkGetSwapchainImagesKHR);
LOAD_INSTANCE_FUNC(instance, vkAcquireNextImageKHR);
LOAD_INSTANCE_FUNC(instance, vkQueuePresentKHR);
#ifdef _WIN32
LOAD_INSTANCE_FUNC(instance, vkCreateWin32SurfaceKHR);
#elif defined(__ANDROID__)
LOAD_INSTANCE_FUNC(instance, vkCreateAndroidSurfaceKHR);
#elif defined(VK_USE_PLATFORM_METAL_EXT)
LOAD_INSTANCE_FUNC(instance, vkCreateMetalSurfaceEXT);
#endif
#if defined(VK_USE_PLATFORM_XLIB_KHR)
LOAD_INSTANCE_FUNC(instance, vkCreateXlibSurfaceKHR);
#endif
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
LOAD_INSTANCE_FUNC(instance, vkCreateWaylandSurfaceKHR);
#endif
#if defined(VK_USE_PLATFORM_DISPLAY_KHR)
LOAD_INSTANCE_FUNC(instance, vkCreateDisplayPlaneSurfaceKHR);
LOAD_INSTANCE_FUNC(instance, vkGetPhysicalDeviceDisplayPropertiesKHR);
LOAD_INSTANCE_FUNC(instance, vkGetPhysicalDeviceDisplayPlanePropertiesKHR);
LOAD_INSTANCE_FUNC(instance, vkGetDisplayModePropertiesKHR);
LOAD_INSTANCE_FUNC(instance, vkGetDisplayPlaneSupportedDisplaysKHR);
LOAD_INSTANCE_FUNC(instance, vkGetDisplayPlaneCapabilitiesKHR);
#endif
LOAD_INSTANCE_FUNC(instance, vkDestroySurfaceKHR);
if (enabledExtensions.KHR_get_physical_device_properties2) {
LOAD_INSTANCE_FUNC(instance, vkGetPhysicalDeviceProperties2KHR);
LOAD_INSTANCE_FUNC(instance, vkGetPhysicalDeviceFeatures2KHR);
}
if (enabledExtensions.EXT_debug_utils) {
LOAD_INSTANCE_FUNC(instance, vkCreateDebugUtilsMessengerEXT);
LOAD_INSTANCE_FUNC(instance, vkDestroyDebugUtilsMessengerEXT);
LOAD_INSTANCE_FUNC(instance, vkCmdBeginDebugUtilsLabelEXT);
LOAD_INSTANCE_FUNC(instance, vkCmdEndDebugUtilsLabelEXT);
LOAD_INSTANCE_FUNC(instance, vkCmdInsertDebugUtilsLabelEXT);
LOAD_INSTANCE_FUNC(instance, vkSetDebugUtilsObjectNameEXT);
LOAD_INSTANCE_FUNC(instance, vkSetDebugUtilsObjectTagEXT);
}
INFO_LOG(G3D, "Vulkan instance functions loaded.");
}
// On some implementations, loading functions (that have Device as their first parameter) via vkGetDeviceProcAddr may
// increase performance - but then these function pointers will only work on that specific device. Thus, this loader is not very
// good for multi-device - not likely we'll ever try that anyway though.
void VulkanLoadDeviceFunctions(VkDevice device, const VulkanExtensions &enabledExtensions) {
INFO_LOG(G3D, "Vulkan device functions loaded.");
LOAD_DEVICE_FUNC(device, vkQueueSubmit);
LOAD_DEVICE_FUNC(device, vkQueueWaitIdle);
LOAD_DEVICE_FUNC(device, vkAllocateMemory);
LOAD_DEVICE_FUNC(device, vkFreeMemory);
LOAD_DEVICE_FUNC(device, vkMapMemory);
LOAD_DEVICE_FUNC(device, vkUnmapMemory);
LOAD_DEVICE_FUNC(device, vkFlushMappedMemoryRanges);
LOAD_DEVICE_FUNC(device, vkInvalidateMappedMemoryRanges);
LOAD_DEVICE_FUNC(device, vkGetDeviceMemoryCommitment);
LOAD_DEVICE_FUNC(device, vkBindBufferMemory);
LOAD_DEVICE_FUNC(device, vkBindBufferMemory2);
LOAD_DEVICE_FUNC(device, vkBindImageMemory);
LOAD_DEVICE_FUNC(device, vkBindImageMemory2);
LOAD_DEVICE_FUNC(device, vkGetBufferMemoryRequirements);
LOAD_DEVICE_FUNC(device, vkGetBufferMemoryRequirements2);
LOAD_DEVICE_FUNC(device, vkGetDeviceBufferMemoryRequirements);
LOAD_DEVICE_FUNC(device, vkGetImageMemoryRequirements);
LOAD_DEVICE_FUNC(device, vkGetImageMemoryRequirements2);
LOAD_DEVICE_FUNC(device, vkGetDeviceImageMemoryRequirements);
LOAD_DEVICE_FUNC(device, vkCreateFence);
LOAD_DEVICE_FUNC(device, vkDestroyFence);
LOAD_DEVICE_FUNC(device, vkResetFences);
LOAD_DEVICE_FUNC(device, vkGetFenceStatus);
LOAD_DEVICE_FUNC(device, vkWaitForFences);
LOAD_DEVICE_FUNC(device, vkCreateSemaphore);
LOAD_DEVICE_FUNC(device, vkDestroySemaphore);
LOAD_DEVICE_FUNC(device, vkCreateEvent);
LOAD_DEVICE_FUNC(device, vkDestroyEvent);
LOAD_DEVICE_FUNC(device, vkGetEventStatus);
LOAD_DEVICE_FUNC(device, vkSetEvent);
LOAD_DEVICE_FUNC(device, vkResetEvent);
LOAD_DEVICE_FUNC(device, vkCreateQueryPool);
LOAD_DEVICE_FUNC(device, vkDestroyQueryPool);
LOAD_DEVICE_FUNC(device, vkGetQueryPoolResults);
LOAD_DEVICE_FUNC(device, vkCreateBuffer);
LOAD_DEVICE_FUNC(device, vkDestroyBuffer);
LOAD_DEVICE_FUNC(device, vkCreateBufferView);
LOAD_DEVICE_FUNC(device, vkDestroyBufferView);
LOAD_DEVICE_FUNC(device, vkCreateImage);
LOAD_DEVICE_FUNC(device, vkDestroyImage);
LOAD_DEVICE_FUNC(device, vkGetImageSubresourceLayout);
LOAD_DEVICE_FUNC(device, vkCreateImageView);
LOAD_DEVICE_FUNC(device, vkDestroyImageView);
LOAD_DEVICE_FUNC(device, vkCreateShaderModule);
LOAD_DEVICE_FUNC(device, vkDestroyShaderModule);
LOAD_DEVICE_FUNC(device, vkCreatePipelineCache);
LOAD_DEVICE_FUNC(device, vkDestroyPipelineCache);
LOAD_DEVICE_FUNC(device, vkGetPipelineCacheData);
LOAD_DEVICE_FUNC(device, vkMergePipelineCaches);
LOAD_DEVICE_FUNC(device, vkCreateGraphicsPipelines);
LOAD_DEVICE_FUNC(device, vkCreateComputePipelines);
LOAD_DEVICE_FUNC(device, vkDestroyPipeline);
LOAD_DEVICE_FUNC(device, vkCreatePipelineLayout);
LOAD_DEVICE_FUNC(device, vkDestroyPipelineLayout);
LOAD_DEVICE_FUNC(device, vkCreateSampler);
LOAD_DEVICE_FUNC(device, vkDestroySampler);
LOAD_DEVICE_FUNC(device, vkCreateDescriptorSetLayout);
LOAD_DEVICE_FUNC(device, vkDestroyDescriptorSetLayout);
LOAD_DEVICE_FUNC(device, vkCreateDescriptorPool);
LOAD_DEVICE_FUNC(device, vkDestroyDescriptorPool);
LOAD_DEVICE_FUNC(device, vkResetDescriptorPool);
LOAD_DEVICE_FUNC(device, vkAllocateDescriptorSets);
LOAD_DEVICE_FUNC(device, vkFreeDescriptorSets);
LOAD_DEVICE_FUNC(device, vkUpdateDescriptorSets);
LOAD_DEVICE_FUNC(device, vkCreateFramebuffer);
LOAD_DEVICE_FUNC(device, vkDestroyFramebuffer);
LOAD_DEVICE_FUNC(device, vkCreateRenderPass);
LOAD_DEVICE_FUNC(device, vkDestroyRenderPass);
LOAD_DEVICE_FUNC(device, vkGetRenderAreaGranularity);
LOAD_DEVICE_FUNC(device, vkCreateCommandPool);
LOAD_DEVICE_FUNC(device, vkDestroyCommandPool);
LOAD_DEVICE_FUNC(device, vkResetCommandPool);
LOAD_DEVICE_FUNC(device, vkAllocateCommandBuffers);
LOAD_DEVICE_FUNC(device, vkFreeCommandBuffers);
LOAD_DEVICE_FUNC(device, vkBeginCommandBuffer);
LOAD_DEVICE_FUNC(device, vkEndCommandBuffer);
LOAD_DEVICE_FUNC(device, vkResetCommandBuffer);
LOAD_DEVICE_FUNC(device, vkCmdBindPipeline);
LOAD_DEVICE_FUNC(device, vkCmdSetViewport);
LOAD_DEVICE_FUNC(device, vkCmdSetScissor);
LOAD_DEVICE_FUNC(device, vkCmdSetLineWidth);
LOAD_DEVICE_FUNC(device, vkCmdSetDepthBias);
LOAD_DEVICE_FUNC(device, vkCmdSetBlendConstants);
LOAD_DEVICE_FUNC(device, vkCmdSetDepthBounds);
LOAD_DEVICE_FUNC(device, vkCmdSetStencilCompareMask);
LOAD_DEVICE_FUNC(device, vkCmdSetStencilWriteMask);
LOAD_DEVICE_FUNC(device, vkCmdSetStencilReference);
LOAD_DEVICE_FUNC(device, vkCmdBindDescriptorSets);
LOAD_DEVICE_FUNC(device, vkCmdBindIndexBuffer);
LOAD_DEVICE_FUNC(device, vkCmdBindVertexBuffers);
LOAD_DEVICE_FUNC(device, vkCmdDraw);
LOAD_DEVICE_FUNC(device, vkCmdDrawIndexed);
LOAD_DEVICE_FUNC(device, vkCmdDrawIndirect);
LOAD_DEVICE_FUNC(device, vkCmdDrawIndexedIndirect);
LOAD_DEVICE_FUNC(device, vkCmdDispatch);
LOAD_DEVICE_FUNC(device, vkCmdDispatchIndirect);
LOAD_DEVICE_FUNC(device, vkCmdCopyBuffer);
LOAD_DEVICE_FUNC(device, vkCmdCopyImage);
LOAD_DEVICE_FUNC(device, vkCmdBlitImage);
LOAD_DEVICE_FUNC(device, vkCmdCopyBufferToImage);
LOAD_DEVICE_FUNC(device, vkCmdCopyImageToBuffer);
LOAD_DEVICE_FUNC(device, vkCmdUpdateBuffer);
LOAD_DEVICE_FUNC(device, vkCmdFillBuffer);
LOAD_DEVICE_FUNC(device, vkCmdClearColorImage);
LOAD_DEVICE_FUNC(device, vkCmdClearDepthStencilImage);
LOAD_DEVICE_FUNC(device, vkCmdClearAttachments);
LOAD_DEVICE_FUNC(device, vkCmdResolveImage);
LOAD_DEVICE_FUNC(device, vkCmdSetEvent);
LOAD_DEVICE_FUNC(device, vkCmdResetEvent);
LOAD_DEVICE_FUNC(device, vkCmdWaitEvents);
LOAD_DEVICE_FUNC(device, vkCmdPipelineBarrier);
LOAD_DEVICE_FUNC(device, vkCmdBeginQuery);
LOAD_DEVICE_FUNC(device, vkCmdEndQuery);
LOAD_DEVICE_FUNC(device, vkCmdResetQueryPool);
LOAD_DEVICE_FUNC(device, vkCmdWriteTimestamp);
LOAD_DEVICE_FUNC(device, vkCmdCopyQueryPoolResults);
LOAD_DEVICE_FUNC(device, vkCmdPushConstants);
LOAD_DEVICE_FUNC(device, vkCmdBeginRenderPass);
LOAD_DEVICE_FUNC(device, vkCmdNextSubpass);
LOAD_DEVICE_FUNC(device, vkCmdEndRenderPass);
LOAD_DEVICE_FUNC(device, vkCmdExecuteCommands);
if (enabledExtensions.KHR_dedicated_allocation) {
LOAD_DEVICE_FUNC(device, vkGetBufferMemoryRequirements2KHR);
LOAD_DEVICE_FUNC(device, vkGetImageMemoryRequirements2KHR);
}
if (enabledExtensions.KHR_create_renderpass2) {
LOAD_DEVICE_FUNC(device, vkCreateRenderPass2KHR);
}
}
void VulkanFree() {
VulkanFreeLibrary(vulkanLibrary);
}
const char *VulkanResultToString(VkResult res) {
static char temp[128]{};
switch (res) {
case VK_NOT_READY: return "VK_NOT_READY";
case VK_TIMEOUT: return "VK_TIMEOUT";
case VK_EVENT_SET: return "VK_EVENT_SET";
case VK_EVENT_RESET: return "VK_EVENT_RESET";
case VK_INCOMPLETE: return "VK_INCOMPLETE";
case VK_ERROR_OUT_OF_HOST_MEMORY: return "VK_ERROR_OUT_OF_HOST_MEMORY";
case VK_ERROR_OUT_OF_DEVICE_MEMORY: return "VK_ERROR_OUT_OF_DEVICE_MEMORY";
case VK_ERROR_INITIALIZATION_FAILED: return "VK_ERROR_INITIALIZATION_FAILED";
case VK_ERROR_DEVICE_LOST: return "VK_ERROR_DEVICE_LOST";
case VK_ERROR_MEMORY_MAP_FAILED: return "VK_ERROR_MEMORY_MAP_FAILED";
case VK_ERROR_LAYER_NOT_PRESENT: return "VK_ERROR_LAYER_NOT_PRESENT";
case VK_ERROR_EXTENSION_NOT_PRESENT: return "VK_ERROR_EXTENSION_NOT_PRESENT";
case VK_ERROR_FEATURE_NOT_PRESENT: return "VK_ERROR_FEATURE_NOT_PRESENT";
case VK_ERROR_INCOMPATIBLE_DRIVER: return "VK_ERROR_INCOMPATIBLE_DRIVER";
case VK_ERROR_TOO_MANY_OBJECTS: return "VK_ERROR_TOO_MANY_OBJECTS";
case VK_ERROR_FORMAT_NOT_SUPPORTED: return "VK_ERROR_FORMAT_NOT_SUPPORTED";
case VK_ERROR_SURFACE_LOST_KHR: return "VK_ERROR_SURFACE_LOST_KHR";
case VK_SUBOPTIMAL_KHR: return "VK_SUBOPTIMAL_KHR";
case VK_ERROR_OUT_OF_DATE_KHR: return "VK_ERROR_OUT_OF_DATE_KHR";
case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR: return "VK_ERROR_INCOMPATIBLE_DISPLAY_KHR";
case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR: return "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR";
case VK_ERROR_OUT_OF_POOL_MEMORY: return "VK_ERROR_OUT_OF_POOL_MEMORY_KHR";
case VK_ERROR_INVALID_EXTERNAL_HANDLE: return "VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR";
case VK_ERROR_FRAGMENTED_POOL: return "VK_ERROR_FRAGMENTED_POOL";
case VK_ERROR_UNKNOWN: return "VK_ERROR_UNKNOWN (-13)";
case VK_ERROR_FRAGMENTATION: return "VK_ERROR_FRAGMENTATION";
case VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS: return "VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS";
case VK_PIPELINE_COMPILE_REQUIRED: return "VK_PIPELINE_COMPILE_REQUIRED";
case VK_ERROR_VALIDATION_FAILED_EXT: return "VK_ERROR_VALIDATION_FAILED_EXT";
case VK_ERROR_INVALID_SHADER_NV: return "VK_ERROR_INVALID_SHADER_NV";
case VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT: return "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT";
case VK_ERROR_NOT_PERMITTED_KHR: return "VK_ERROR_NOT_PERMITTED_KHR";
case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT: return "VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT";
case VK_THREAD_IDLE_KHR: return "VK_THREAD_IDLE_KHR";
case VK_THREAD_DONE_KHR: return "VK_THREAD_DONE_KHR";
case VK_OPERATION_DEFERRED_KHR: return "VK_OPERATION_DEFERRED_KHR";
case VK_OPERATION_NOT_DEFERRED_KHR: return "VK_OPERATION_NOT_DEFERRED_KHR";
default:
// This isn't thread safe, but this should be rare, and at worst, we'll get a jumble of two messages.
snprintf(temp, sizeof(temp), "VK_ERROR_???: 0x%08x", (u32)res);
return temp;
}
}