dolphin/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1118 lines
42 KiB
C++
Raw Normal View History

2016-08-13 12:57:50 +00:00
// Copyright 2016 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
2016-08-13 12:57:50 +00:00
2021-12-10 02:22:16 +00:00
#include "VideoBackends/Vulkan/VulkanContext.h"
2016-08-13 12:57:50 +00:00
#include <algorithm>
#include <array>
#include <cstring>
2016-08-13 12:57:50 +00:00
#include "Common/Assert.h"
#include "Common/CommonFuncs.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#include "Common/StringUtil.h"
#include "VideoCommon/DriverDetails.h"
#include "VideoCommon/VideoCommon.h"
2016-08-13 12:57:50 +00:00
namespace Vulkan
{
static constexpr const char* VALIDATION_LAYER_NAME = "VK_LAYER_KHRONOS_validation";
2016-08-13 12:57:50 +00:00
std::unique_ptr<VulkanContext> g_vulkan_context;
template <typename Chain, typename Element>
static void InsertIntoChain(Chain* chain, Element* element)
{
element->pNext = chain->pNext;
chain->pNext = element;
}
VulkanContext::PhysicalDeviceInfo::PhysicalDeviceInfo(VkPhysicalDevice device)
{
VkPhysicalDeviceFeatures features;
VkPhysicalDeviceProperties2 properties2;
VkPhysicalDeviceProperties& properties = properties2.properties;
vkGetPhysicalDeviceProperties(device, &properties);
vkGetPhysicalDeviceFeatures(device, &features);
apiVersion = vkGetPhysicalDeviceProperties2 ? properties.apiVersion : VK_API_VERSION_1_0;
if (apiVersion >= VK_API_VERSION_1_1)
{
VkPhysicalDeviceSubgroupProperties properties_subgroup = {};
VkPhysicalDeviceVulkan12Properties properties_vk12 = {};
properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
properties2.pNext = nullptr;
properties_subgroup.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES;
InsertIntoChain(&properties2, &properties_subgroup);
if (apiVersion >= VK_API_VERSION_1_2)
{
properties_vk12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES;
InsertIntoChain(&properties2, &properties_vk12);
}
vkGetPhysicalDeviceProperties2(device, &properties2);
if (apiVersion >= VK_API_VERSION_1_2)
{
driverID = properties_vk12.driverID;
}
subgroupSize = properties_subgroup.subgroupSize;
// We require basic ops (for gl_SubgroupInvocationID), ballot (for subgroupBallot,
// subgroupBallotFindLSB), and arithmetic (for subgroupMin/subgroupMax).
// Shuffle is enabled as a workaround until SPIR-V >= 1.5 is enabled with broadcast(uniform)
// support.
constexpr VkSubgroupFeatureFlags required_operations =
VK_SUBGROUP_FEATURE_BASIC_BIT | VK_SUBGROUP_FEATURE_ARITHMETIC_BIT |
VK_SUBGROUP_FEATURE_BALLOT_BIT | VK_SUBGROUP_FEATURE_SHUFFLE_BIT;
shaderSubgroupOperations =
(properties_subgroup.supportedOperations & required_operations) == required_operations &&
properties_subgroup.supportedStages & VK_SHADER_STAGE_FRAGMENT_BIT;
}
memcpy(deviceName, properties.deviceName, sizeof(deviceName));
memcpy(pipelineCacheUUID, properties.pipelineCacheUUID, sizeof(pipelineCacheUUID));
vendorID = properties.vendorID;
deviceID = properties.deviceID;
minUniformBufferOffsetAlignment =
std::max<VkDeviceSize>(properties.limits.minUniformBufferOffsetAlignment, 1);
bufferImageGranularity = std::max<VkDeviceSize>(properties.limits.bufferImageGranularity, 1);
maxTexelBufferElements = properties.limits.maxTexelBufferElements;
maxImageDimension2D = properties.limits.maxImageDimension2D;
framebufferColorSampleCounts = properties.limits.framebufferColorSampleCounts;
framebufferDepthSampleCounts = properties.limits.framebufferDepthSampleCounts;
memcpy(pointSizeRange, properties.limits.pointSizeRange, sizeof(pointSizeRange));
maxSamplerAnisotropy = properties.limits.maxSamplerAnisotropy;
dualSrcBlend = features.dualSrcBlend != VK_FALSE;
geometryShader = features.geometryShader != VK_FALSE;
samplerAnisotropy = features.samplerAnisotropy != VK_FALSE;
logicOp = features.logicOp != VK_FALSE;
fragmentStoresAndAtomics = features.fragmentStoresAndAtomics != VK_FALSE;
sampleRateShading = features.sampleRateShading != VK_FALSE;
largePoints = features.largePoints != VK_FALSE;
shaderStorageImageMultisample = features.shaderStorageImageMultisample != VK_FALSE;
shaderTessellationAndGeometryPointSize =
features.shaderTessellationAndGeometryPointSize != VK_FALSE;
occlusionQueryPrecise = features.occlusionQueryPrecise != VK_FALSE;
shaderClipDistance = features.shaderClipDistance != VK_FALSE;
depthClamp = features.depthClamp != VK_FALSE;
textureCompressionBC = features.textureCompressionBC != VK_FALSE;
}
VkPhysicalDeviceFeatures VulkanContext::PhysicalDeviceInfo::features() const
{
VkPhysicalDeviceFeatures features;
memset(&features, 0, sizeof(features));
features.dualSrcBlend = dualSrcBlend ? VK_TRUE : VK_FALSE;
features.geometryShader = geometryShader ? VK_TRUE : VK_FALSE;
features.samplerAnisotropy = samplerAnisotropy ? VK_TRUE : VK_FALSE;
features.logicOp = logicOp ? VK_TRUE : VK_FALSE;
features.fragmentStoresAndAtomics = fragmentStoresAndAtomics ? VK_TRUE : VK_FALSE;
features.sampleRateShading = sampleRateShading ? VK_TRUE : VK_FALSE;
features.largePoints = largePoints ? VK_TRUE : VK_FALSE;
features.shaderStorageImageMultisample = shaderStorageImageMultisample ? VK_TRUE : VK_FALSE;
features.shaderTessellationAndGeometryPointSize =
shaderTessellationAndGeometryPointSize ? VK_TRUE : VK_FALSE;
features.occlusionQueryPrecise = occlusionQueryPrecise ? VK_TRUE : VK_FALSE;
features.shaderClipDistance = shaderClipDistance ? VK_TRUE : VK_FALSE;
features.depthClamp = depthClamp ? VK_TRUE : VK_FALSE;
features.textureCompressionBC = textureCompressionBC ? VK_TRUE : VK_FALSE;
return features;
}
2016-08-13 12:57:50 +00:00
VulkanContext::VulkanContext(VkInstance instance, VkPhysicalDevice physical_device)
: m_instance(instance), m_physical_device(physical_device), m_device_info(physical_device)
2016-08-13 12:57:50 +00:00
{
}
VulkanContext::~VulkanContext()
{
if (m_allocator != VK_NULL_HANDLE)
vmaDestroyAllocator(m_allocator);
2016-08-13 12:57:50 +00:00
if (m_device != VK_NULL_HANDLE)
vkDestroyDevice(m_device, nullptr);
if (m_debug_utils_messenger != VK_NULL_HANDLE)
DisableDebugUtils();
2016-08-13 12:57:50 +00:00
vkDestroyInstance(m_instance, nullptr);
}
bool VulkanContext::CheckValidationLayerAvailablility()
{
u32 extension_count = 0;
VkResult res = vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkEnumerateInstanceExtensionProperties failed: ");
return false;
}
std::vector<VkExtensionProperties> extension_list(extension_count);
res = vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, extension_list.data());
ASSERT(res == VK_SUCCESS);
2016-08-13 12:57:50 +00:00
u32 layer_count = 0;
res = vkEnumerateInstanceLayerProperties(&layer_count, nullptr);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkEnumerateInstanceExtensionProperties failed: ");
return false;
}
std::vector<VkLayerProperties> layer_list(layer_count);
res = vkEnumerateInstanceLayerProperties(&layer_count, layer_list.data());
ASSERT(res == VK_SUCCESS);
2016-08-13 12:57:50 +00:00
bool supports_validation_layers =
std::find_if(layer_list.begin(), layer_list.end(), [](const auto& it) {
return strcmp(it.layerName, VALIDATION_LAYER_NAME) == 0;
}) != layer_list.end();
bool supports_debug_utils =
std::find_if(extension_list.begin(), extension_list.end(), [](const auto& it) {
return strcmp(it.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == 0;
}) != extension_list.end();
if (!supports_debug_utils && supports_validation_layers)
{
// If the instance doesn't support debug utils but we're using validation layers,
// try to use the implementation of the extension provided by the validation layers.
extension_count = 0;
res = vkEnumerateInstanceExtensionProperties(VALIDATION_LAYER_NAME, &extension_count, nullptr);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkEnumerateInstanceExtensionProperties failed: ");
return false;
}
extension_list.resize(extension_count);
res = vkEnumerateInstanceExtensionProperties(VALIDATION_LAYER_NAME, &extension_count,
extension_list.data());
ASSERT(res == VK_SUCCESS);
supports_debug_utils =
std::find_if(extension_list.begin(), extension_list.end(), [](const auto& it) {
return strcmp(it.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == 0;
}) != extension_list.end();
}
// Check for both VK_EXT_debug_utils and VK_LAYER_KHRONOS_validation
return supports_debug_utils && supports_validation_layers;
2016-08-13 12:57:50 +00:00
}
static u32 getAPIVersion()
{
u32 supported_version;
u32 used_version = VK_API_VERSION_1_0;
if (vkEnumerateInstanceVersion && vkEnumerateInstanceVersion(&supported_version) == VK_SUCCESS)
{
// The device itself may not support 1.1, so we check that before using any 1.1 functionality.
if (supported_version >= VK_API_VERSION_1_2)
used_version = VK_API_VERSION_1_2;
else if (supported_version >= VK_API_VERSION_1_1)
used_version = VK_API_VERSION_1_1;
WARN_LOG_FMT(HOST_GPU, "Using Vulkan 1.{}, supported: {}.{}", VK_VERSION_MINOR(used_version),
VK_VERSION_MAJOR(supported_version), VK_VERSION_MINOR(supported_version));
}
else
{
WARN_LOG_FMT(HOST_GPU, "Using Vulkan 1.0");
}
return used_version;
}
VkInstance VulkanContext::CreateVulkanInstance(WindowSystemType wstype, bool enable_debug_utils,
bool enable_validation_layer,
u32* out_vk_api_version)
2016-08-13 12:57:50 +00:00
{
std::vector<const char*> enabled_extensions;
if (!SelectInstanceExtensions(&enabled_extensions, wstype, enable_debug_utils,
enable_validation_layer))
2016-08-13 12:57:50 +00:00
return VK_NULL_HANDLE;
VkApplicationInfo app_info = {};
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
app_info.pNext = nullptr;
app_info.pApplicationName = "Dolphin Emulator";
app_info.applicationVersion = VK_MAKE_VERSION(5, 0, 0);
app_info.pEngineName = "Dolphin Emulator";
app_info.engineVersion = VK_MAKE_VERSION(5, 0, 0);
app_info.apiVersion = getAPIVersion();
*out_vk_api_version = app_info.apiVersion;
2016-08-13 12:57:50 +00:00
VkInstanceCreateInfo instance_create_info = {};
instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instance_create_info.pNext = nullptr;
instance_create_info.flags = 0;
instance_create_info.pApplicationInfo = &app_info;
instance_create_info.enabledExtensionCount = static_cast<uint32_t>(enabled_extensions.size());
instance_create_info.ppEnabledExtensionNames = enabled_extensions.data();
instance_create_info.enabledLayerCount = 0;
instance_create_info.ppEnabledLayerNames = nullptr;
// Enable validation layer if the user enabled them in the settings
2016-08-13 12:57:50 +00:00
if (enable_validation_layer)
{
instance_create_info.enabledLayerCount = 1;
instance_create_info.ppEnabledLayerNames = &VALIDATION_LAYER_NAME;
2016-08-13 12:57:50 +00:00
}
VkInstance instance;
VkResult res = vkCreateInstance(&instance_create_info, nullptr, &instance);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreateInstance failed: ");
return nullptr;
}
return instance;
}
bool VulkanContext::SelectInstanceExtensions(std::vector<const char*>* extension_list,
WindowSystemType wstype, bool enable_debug_utils,
bool validation_layer_enabled)
2016-08-13 12:57:50 +00:00
{
u32 extension_count = 0;
VkResult res = vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkEnumerateInstanceExtensionProperties failed: ");
return false;
}
if (extension_count == 0)
{
ERROR_LOG_FMT(VIDEO, "Vulkan: No extensions supported by instance.");
2016-08-13 12:57:50 +00:00
return false;
}
std::vector<VkExtensionProperties> available_extension_list(extension_count);
res = vkEnumerateInstanceExtensionProperties(nullptr, &extension_count,
available_extension_list.data());
ASSERT(res == VK_SUCCESS);
2016-08-13 12:57:50 +00:00
u32 validation_layer_extension_count = 0;
std::vector<VkExtensionProperties> validation_layer_extension_list;
if (validation_layer_enabled)
{
res = vkEnumerateInstanceExtensionProperties(VALIDATION_LAYER_NAME,
&validation_layer_extension_count, nullptr);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res,
"vkEnumerateInstanceExtensionProperties failed for validation layers: ");
}
else
{
validation_layer_extension_list.resize(validation_layer_extension_count);
res = vkEnumerateInstanceExtensionProperties(VALIDATION_LAYER_NAME,
&validation_layer_extension_count,
validation_layer_extension_list.data());
ASSERT(res == VK_SUCCESS);
}
}
2016-08-13 12:57:50 +00:00
for (const auto& extension_properties : available_extension_list)
INFO_LOG_FMT(VIDEO, "Available extension: {}", extension_properties.extensionName);
2016-08-13 12:57:50 +00:00
for (const auto& extension_properties : validation_layer_extension_list)
{
INFO_LOG_FMT(VIDEO, "Available extension in validation layer: {}",
extension_properties.extensionName);
}
auto AddExtension = [&](const char* name, bool required) {
bool extension_supported =
std::find_if(available_extension_list.begin(), available_extension_list.end(),
2016-08-13 12:57:50 +00:00
[&](const VkExtensionProperties& properties) {
return !strcmp(name, properties.extensionName);
}) != available_extension_list.end();
extension_supported =
extension_supported ||
std::find_if(validation_layer_extension_list.begin(), validation_layer_extension_list.end(),
[&](const VkExtensionProperties& properties) {
return !strcmp(name, properties.extensionName);
}) != validation_layer_extension_list.end();
if (extension_supported)
2016-08-13 12:57:50 +00:00
{
INFO_LOG_FMT(VIDEO, "Enabling extension: {}", name);
2016-08-13 12:57:50 +00:00
extension_list->push_back(name);
return true;
}
if (required)
ERROR_LOG_FMT(VIDEO, "Vulkan: Missing required extension {}.", name);
2016-08-13 12:57:50 +00:00
return false;
2016-08-13 12:57:50 +00:00
};
// Common extensions
if (wstype != WindowSystemType::Headless && !AddExtension(VK_KHR_SURFACE_EXTENSION_NAME, true))
{
2016-08-13 12:57:50 +00:00
return false;
}
2016-08-13 12:57:50 +00:00
#if defined(VK_USE_PLATFORM_WIN32_KHR)
if (wstype == WindowSystemType::Windows &&
!AddExtension(VK_KHR_WIN32_SURFACE_EXTENSION_NAME, true))
{
2016-08-13 12:57:50 +00:00
return false;
}
#endif
#if defined(VK_USE_PLATFORM_XLIB_KHR)
if (wstype == WindowSystemType::X11 && !AddExtension(VK_KHR_XLIB_SURFACE_EXTENSION_NAME, true))
{
2016-08-13 12:57:50 +00:00
return false;
}
#endif
#if defined(VK_USE_PLATFORM_ANDROID_KHR)
if (wstype == WindowSystemType::Android &&
!AddExtension(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, true))
{
2016-08-13 12:57:50 +00:00
return false;
}
#endif
#if defined(VK_USE_PLATFORM_METAL_EXT)
if (wstype == WindowSystemType::MacOS && !AddExtension(VK_EXT_METAL_SURFACE_EXTENSION_NAME, true))
{
return false;
}
2016-08-13 12:57:50 +00:00
#endif
AddExtension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, false);
if (wstype != WindowSystemType::Headless)
{
AddExtension(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, false);
}
// VK_EXT_debug_utils
if (AddExtension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, false))
{
g_Config.backend_info.bSupportsSettingObjectNames = true;
}
else if (enable_debug_utils)
{
WARN_LOG_FMT(VIDEO, "Vulkan: Debug utils requested, but extension is not available.");
}
2016-08-13 12:57:50 +00:00
return true;
}
VulkanContext::GPUList VulkanContext::EnumerateGPUs(VkInstance instance)
{
u32 gpu_count = 0;
VkResult res = vkEnumeratePhysicalDevices(instance, &gpu_count, nullptr);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkEnumeratePhysicalDevices failed: ");
return {};
}
GPUList gpus;
gpus.resize(gpu_count);
res = vkEnumeratePhysicalDevices(instance, &gpu_count, gpus.data());
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkEnumeratePhysicalDevices failed: ");
return {};
}
return gpus;
}
void VulkanContext::PopulateBackendInfo(VideoConfig* config)
{
config->backend_info.api_type = APIType::Vulkan;
config->backend_info.bSupports3DVision = false; // D3D-exclusive.
config->backend_info.bSupportsEarlyZ = true; // Assumed support.
config->backend_info.bSupportsPrimitiveRestart = true; // Assumed support.
config->backend_info.bSupportsBindingLayout = false; // Assumed support.
config->backend_info.bSupportsPaletteConversion = true; // Assumed support.
config->backend_info.bSupportsClipControl = true; // Assumed support.
config->backend_info.bSupportsMultithreading = true; // Assumed support.
config->backend_info.bSupportsComputeShaders = true; // Assumed support.
config->backend_info.bSupportsGPUTextureDecoding = true; // Assumed support.
config->backend_info.bSupportsBitfield = true; // Assumed support.
config->backend_info.bSupportsPartialDepthCopies = true; // Assumed support.
config->backend_info.bSupportsShaderBinaries = true; // Assumed support.
config->backend_info.bSupportsPipelineCacheData = false; // Handled via pipeline caches.
config->backend_info.bSupportsDynamicSamplerIndexing = true; // Assumed support.
config->backend_info.bSupportsPostProcessing = true; // Assumed support.
config->backend_info.bSupportsBackgroundCompiling = true; // Assumed support.
config->backend_info.bSupportsCopyToVram = true; // Assumed support.
config->backend_info.bSupportsReversedDepthRange = true; // Assumed support.
config->backend_info.bSupportsExclusiveFullscreen = false; // Dependent on OS and features.
config->backend_info.bSupportsDualSourceBlend = false; // Dependent on features.
config->backend_info.bSupportsGeometryShaders = false; // Dependent on features.
config->backend_info.bSupportsGSInstancing = false; // Dependent on features.
config->backend_info.bSupportsBBox = false; // Dependent on features.
config->backend_info.bSupportsFragmentStoresAndAtomics = false; // Dependent on features.
config->backend_info.bSupportsSSAA = false; // Dependent on features.
config->backend_info.bSupportsDepthClamp = false; // Dependent on features.
config->backend_info.bSupportsST3CTextures = false; // Dependent on features.
config->backend_info.bSupportsBPTCTextures = false; // Dependent on features.
config->backend_info.bSupportsLogicOp = false; // Dependent on features.
config->backend_info.bSupportsLargePoints = false; // Dependent on features.
config->backend_info.bSupportsFramebufferFetch = false; // Dependent on OS and features.
config->backend_info.bSupportsCoarseDerivatives = true; // Assumed support.
config->backend_info.bSupportsTextureQueryLevels = true; // Assumed support.
config->backend_info.bSupportsLodBiasInSampler = false; // Dependent on OS.
config->backend_info.bSupportsSettingObjectNames = false; // Dependent on features.
2022-06-21 07:07:35 +00:00
config->backend_info.bSupportsPartialMultisampleResolve = true; // Assumed support.
config->backend_info.bSupportsDynamicVertexLoader = true; // Assumed support.
config->backend_info.bSupportsVSLinePointExpand = true; // Assumed support.
Video: implement color correction to match the NTSC and PAL color spaces (and gamma) that GC and Wii targeted. To further increase the accuracy of the post process phase, I've added (scRGB) HDR support, which is necessary to fully display the PAL and NTSC-J color spaces, and also to improve the quality of post process texture samplings and do them in linear space instead of gamma space (which is very important when playing at low resolutions). For SDR, the quality is also slightly increased, at least if any post process runs, as the buffer is now R10G10B10A2 (on Vulkan, DX11 and DX12) if supported; previously it was R8G8B8A8 but the alpha bits were wasted. Gamma correction is arguably the most important thing as Dolphin on Windows outputted in "sRGB" (implicitly) as that's what Windows expects by default, though sRGB gamma is very different from the gamma commonly used by video standards dating to the pre HDR era (roughly gamma 2.35). Additionally, the addition of HDR support (which is pretty straight forward and minimal), added support for our own custom AutoHDR shaders, which would allow us to achieve decent looking HDR in Dolphin games without having to use SpecialK or Windows 11 AutoHDR. Both of which don't necessarily play nice with older games with strongly different and simpler lighting. HDR should also be supported in Linux. Development of my own AutoHDR shader is almost complete and will come next. This has been carefully tested and there should be no regression in any of the different features that Dolphin offers, like multisampling, stereo rendering, other post processes, etc etc. Fixes: https://bugs.dolphin-emu.org/issues/8941 Co-authored-by: EndlesslyFlowering <EndlesslyFlowering@protonmail.com> Co-authored-by: Dogway <lin_ares@hotmail.com>
2023-06-10 08:48:05 +00:00
config->backend_info.bSupportsHDROutput = true; // Assumed support.
2016-08-13 12:57:50 +00:00
}
void VulkanContext::PopulateBackendInfoAdapters(VideoConfig* config, const GPUList& gpu_list)
{
config->backend_info.Adapters.clear();
for (VkPhysicalDevice physical_device : gpu_list)
{
VkPhysicalDeviceProperties properties;
vkGetPhysicalDeviceProperties(physical_device, &properties);
config->backend_info.Adapters.push_back(properties.deviceName);
}
}
void VulkanContext::PopulateBackendInfoFeatures(VideoConfig* config, VkPhysicalDevice gpu,
const PhysicalDeviceInfo& info)
2016-08-13 12:57:50 +00:00
{
config->backend_info.MaxTextureSize = info.maxImageDimension2D;
config->backend_info.bUsesLowerLeftOrigin = false;
config->backend_info.bSupportsDualSourceBlend = info.dualSrcBlend;
config->backend_info.bSupportsGeometryShaders = info.geometryShader;
config->backend_info.bSupportsGSInstancing = info.geometryShader;
config->backend_info.bSupportsBBox = config->backend_info.bSupportsFragmentStoresAndAtomics =
info.fragmentStoresAndAtomics;
config->backend_info.bSupportsSSAA = info.sampleRateShading;
config->backend_info.bSupportsLogicOp = info.logicOp;
2016-08-13 12:57:50 +00:00
// Metal doesn't support this.
config->backend_info.bSupportsLodBiasInSampler = info.driverID != VK_DRIVER_ID_MOLTENVK;
2016-08-13 12:57:50 +00:00
// Disable geometry shader when shaderTessellationAndGeometryPointSize is not supported.
// Seems this is needed for gl_Layer.
if (!info.shaderTessellationAndGeometryPointSize)
2016-12-04 10:53:49 +00:00
{
2016-08-13 12:57:50 +00:00
config->backend_info.bSupportsGeometryShaders = VK_FALSE;
2016-12-04 10:53:49 +00:00
config->backend_info.bSupportsGSInstancing = VK_FALSE;
}
2016-08-13 12:57:50 +00:00
// Depth clamping implies shaderClipDistance and depthClamp
config->backend_info.bSupportsDepthClamp = info.depthClamp && info.shaderClipDistance;
2017-02-18 04:13:32 +00:00
// textureCompressionBC implies BC1 through BC7, which is a superset of DXT1/3/5, which we need.
config->backend_info.bSupportsST3CTextures = info.textureCompressionBC;
config->backend_info.bSupportsBPTCTextures = info.textureCompressionBC;
// Some devices don't support point sizes >1 (e.g. Adreno).
// If we can't use a point size above our maximum IR, use triangles instead for EFB pokes.
// This means a 6x increase in the size of the vertices, though.
config->backend_info.bSupportsLargePoints =
info.largePoints && info.pointSizeRange[0] <= 1.0f && info.pointSizeRange[1] >= 16;
std::string device_name = info.deviceName;
u32 vendor_id = info.vendorID;
bool is_moltenvk = info.driverID == VK_DRIVER_ID_MOLTENVK;
// Only Apple family GPUs support framebuffer fetch.
// We currently use a hacked MoltenVK to implement this, so don't attempt outside of MVK
if (is_moltenvk && (vendor_id == 0x106B || device_name.find("Apple") != std::string::npos))
{
config->backend_info.bSupportsFramebufferFetch = true;
}
2017-02-18 04:13:32 +00:00
// Our usage of primitive restart appears to be broken on AMD's binary drivers.
// Seems to be fine on GCN Gen 1-2, unconfirmed on GCN Gen 3, causes driver resets on GCN Gen 4.
if (DriverDetails::HasBug(DriverDetails::BUG_PRIMITIVE_RESTART))
config->backend_info.bSupportsPrimitiveRestart = false;
// Reversed depth range is broken on some drivers, or is broken when used in combination
// with depth clamping. Fall back to inverted depth range for these.
if (DriverDetails::HasBug(DriverDetails::BUG_BROKEN_REVERSED_DEPTH_RANGE))
config->backend_info.bSupportsReversedDepthRange = false;
// Dynamic sampler indexing locks up Intel GPUs on MoltenVK/Metal
if (DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DYNAMIC_SAMPLER_INDEXING))
config->backend_info.bSupportsDynamicSamplerIndexing = false;
2016-08-13 12:57:50 +00:00
}
void VulkanContext::PopulateBackendInfoMultisampleModes(VideoConfig* config, VkPhysicalDevice gpu,
const PhysicalDeviceInfo& info)
2016-08-13 12:57:50 +00:00
{
// Query image support for the EFB texture formats.
VkImageFormatProperties efb_color_properties = {};
vkGetPhysicalDeviceImageFormatProperties(
gpu, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
2016-08-13 12:57:50 +00:00
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 0, &efb_color_properties);
VkImageFormatProperties efb_depth_properties = {};
vkGetPhysicalDeviceImageFormatProperties(
gpu, VK_FORMAT_D32_SFLOAT, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
2016-08-13 12:57:50 +00:00
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, 0, &efb_depth_properties);
// We can only support MSAA if it's supported on our render target formats.
VkSampleCountFlags supported_sample_counts =
info.framebufferColorSampleCounts & info.framebufferDepthSampleCounts &
efb_color_properties.sampleCounts & efb_depth_properties.sampleCounts;
2016-08-13 12:57:50 +00:00
// No AA
config->backend_info.AAModes.clear();
config->backend_info.AAModes.emplace_back(1);
// 2xMSAA/SSAA
if (supported_sample_counts & VK_SAMPLE_COUNT_2_BIT)
config->backend_info.AAModes.emplace_back(2);
// 4xMSAA/SSAA
if (supported_sample_counts & VK_SAMPLE_COUNT_4_BIT)
config->backend_info.AAModes.emplace_back(4);
// 8xMSAA/SSAA
if (supported_sample_counts & VK_SAMPLE_COUNT_8_BIT)
config->backend_info.AAModes.emplace_back(8);
// 16xMSAA/SSAA
if (supported_sample_counts & VK_SAMPLE_COUNT_16_BIT)
config->backend_info.AAModes.emplace_back(16);
// 32xMSAA/SSAA
if (supported_sample_counts & VK_SAMPLE_COUNT_32_BIT)
config->backend_info.AAModes.emplace_back(32);
// 64xMSAA/SSAA
if (supported_sample_counts & VK_SAMPLE_COUNT_64_BIT)
config->backend_info.AAModes.emplace_back(64);
}
std::unique_ptr<VulkanContext> VulkanContext::Create(VkInstance instance, VkPhysicalDevice gpu,
VkSurfaceKHR surface, bool enable_debug_utils,
bool enable_validation_layer,
u32 vk_api_version)
2016-08-13 12:57:50 +00:00
{
std::unique_ptr<VulkanContext> context = std::make_unique<VulkanContext>(instance, gpu);
// Initialize DriverDetails so that we can check for bugs to disable features if needed.
context->InitDriverDetails();
2016-08-13 12:57:50 +00:00
// Enable debug messages if the "Host GPU" log category is enabled.
if (enable_debug_utils)
context->EnableDebugUtils();
2016-08-13 12:57:50 +00:00
// Attempt to create the device.
if (!context->CreateDevice(surface, enable_validation_layer) ||
!context->CreateAllocator(vk_api_version))
2016-08-13 12:57:50 +00:00
{
// Since we are destroying the instance, we're also responsible for destroying the surface.
if (surface != VK_NULL_HANDLE)
vkDestroySurfaceKHR(instance, surface, nullptr);
return nullptr;
}
return context;
}
bool VulkanContext::SelectDeviceExtensions(bool enable_surface)
2016-08-13 12:57:50 +00:00
{
u32 extension_count = 0;
VkResult res =
vkEnumerateDeviceExtensionProperties(m_physical_device, nullptr, &extension_count, nullptr);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkEnumerateDeviceExtensionProperties failed: ");
return false;
}
if (extension_count == 0)
{
ERROR_LOG_FMT(VIDEO, "Vulkan: No extensions supported by device.");
2016-08-13 12:57:50 +00:00
return false;
}
std::vector<VkExtensionProperties> available_extension_list(extension_count);
res = vkEnumerateDeviceExtensionProperties(m_physical_device, nullptr, &extension_count,
available_extension_list.data());
ASSERT(res == VK_SUCCESS);
2016-08-13 12:57:50 +00:00
for (const auto& extension_properties : available_extension_list)
INFO_LOG_FMT(VIDEO, "Available extension: {}", extension_properties.extensionName);
2016-08-13 12:57:50 +00:00
auto AddExtension = [&](const char* name, bool required) {
2016-08-13 12:57:50 +00:00
if (std::find_if(available_extension_list.begin(), available_extension_list.end(),
[&](const VkExtensionProperties& properties) {
return !strcmp(name, properties.extensionName);
}) != available_extension_list.end())
{
INFO_LOG_FMT(VIDEO, "Enabling extension: {}", name);
m_device_extensions.push_back(name);
2016-08-13 12:57:50 +00:00
return true;
}
if (required)
ERROR_LOG_FMT(VIDEO, "Vulkan: Missing required extension {}.", name);
2016-08-13 12:57:50 +00:00
return false;
2016-08-13 12:57:50 +00:00
};
if (enable_surface && !AddExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME, true))
2016-08-13 12:57:50 +00:00
return false;
#ifdef SUPPORTS_VULKAN_EXCLUSIVE_FULLSCREEN
// VK_EXT_full_screen_exclusive
if (AddExtension(VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME, true))
INFO_LOG_FMT(VIDEO, "Using VK_EXT_full_screen_exclusive for exclusive fullscreen.");
#endif
AddExtension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, false);
AddExtension(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME, false);
2016-08-13 12:57:50 +00:00
return true;
}
void VulkanContext::WarnMissingDeviceFeatures()
2016-08-13 12:57:50 +00:00
{
if (!m_device_info.largePoints)
WARN_LOG_FMT(VIDEO, "Vulkan: Missing large points feature. CPU EFB writes will be slower.");
if (!m_device_info.occlusionQueryPrecise)
{
WARN_LOG_FMT(VIDEO,
"Vulkan: Missing precise occlusion queries. Perf queries will be inaccurate.");
}
2016-08-13 12:57:50 +00:00
}
bool VulkanContext::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer)
{
u32 queue_family_count;
vkGetPhysicalDeviceQueueFamilyProperties(m_physical_device, &queue_family_count, nullptr);
if (queue_family_count == 0)
{
ERROR_LOG_FMT(VIDEO, "No queue families found on specified vulkan physical device.");
2016-08-13 12:57:50 +00:00
return false;
}
std::vector<VkQueueFamilyProperties> queue_family_properties(queue_family_count);
vkGetPhysicalDeviceQueueFamilyProperties(m_physical_device, &queue_family_count,
queue_family_properties.data());
INFO_LOG_FMT(VIDEO, "{} vulkan queue families", queue_family_count);
2016-08-13 12:57:50 +00:00
// Find graphics and present queues.
2016-08-13 12:57:50 +00:00
m_graphics_queue_family_index = queue_family_count;
m_present_queue_family_index = queue_family_count;
2016-08-13 12:57:50 +00:00
for (uint32_t i = 0; i < queue_family_count; i++)
{
VkBool32 graphics_supported = queue_family_properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT;
if (graphics_supported)
2016-08-13 12:57:50 +00:00
{
m_graphics_queue_family_index = i;
// Quit now, no need for a present queue.
if (!surface)
2016-08-13 12:57:50 +00:00
{
break;
}
}
if (surface)
{
VkBool32 present_supported;
VkResult res =
vkGetPhysicalDeviceSurfaceSupportKHR(m_physical_device, i, surface, &present_supported);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceSurfaceSupportKHR failed: ");
return false;
}
if (present_supported)
{
m_present_queue_family_index = i;
2016-08-13 12:57:50 +00:00
}
// Prefer one queue family index that does both graphics and present.
if (graphics_supported && present_supported)
2016-08-13 12:57:50 +00:00
{
break;
}
}
}
if (m_graphics_queue_family_index == queue_family_count)
{
ERROR_LOG_FMT(VIDEO, "Vulkan: Failed to find an acceptable graphics queue.");
2016-08-13 12:57:50 +00:00
return false;
}
if (surface && m_present_queue_family_index == queue_family_count)
{
ERROR_LOG_FMT(VIDEO, "Vulkan: Failed to find an acceptable present queue.");
return false;
}
2016-08-13 12:57:50 +00:00
VkDeviceCreateInfo device_info = {};
device_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
device_info.pNext = nullptr;
device_info.flags = 0;
static constexpr float queue_priorities[] = {1.0f};
VkDeviceQueueCreateInfo graphics_queue_info = {};
graphics_queue_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
graphics_queue_info.pNext = nullptr;
graphics_queue_info.flags = 0;
graphics_queue_info.queueFamilyIndex = m_graphics_queue_family_index;
graphics_queue_info.queueCount = 1;
graphics_queue_info.pQueuePriorities = queue_priorities;
VkDeviceQueueCreateInfo present_queue_info = {};
present_queue_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
present_queue_info.pNext = nullptr;
present_queue_info.flags = 0;
present_queue_info.queueFamilyIndex = m_present_queue_family_index;
present_queue_info.queueCount = 1;
present_queue_info.pQueuePriorities = queue_priorities;
std::array<VkDeviceQueueCreateInfo, 2> queue_infos = {{
graphics_queue_info,
present_queue_info,
}};
2016-08-13 12:57:50 +00:00
device_info.queueCreateInfoCount = 1;
if (m_graphics_queue_family_index != m_present_queue_family_index &&
m_present_queue_family_index != queue_family_count)
{
device_info.queueCreateInfoCount = 2;
}
device_info.pQueueCreateInfos = queue_infos.data();
2016-08-13 12:57:50 +00:00
if (!SelectDeviceExtensions(surface != VK_NULL_HANDLE))
2016-08-13 12:57:50 +00:00
return false;
// convert std::string list to a char pointer list which we can feed in
std::vector<const char*> extension_name_pointers;
for (const std::string& name : m_device_extensions)
extension_name_pointers.push_back(name.c_str());
2016-08-13 12:57:50 +00:00
device_info.enabledLayerCount = 0;
device_info.ppEnabledLayerNames = nullptr;
device_info.enabledExtensionCount = static_cast<uint32_t>(extension_name_pointers.size());
device_info.ppEnabledExtensionNames = extension_name_pointers.data();
2016-08-13 12:57:50 +00:00
WarnMissingDeviceFeatures();
2016-08-13 12:57:50 +00:00
VkPhysicalDeviceFeatures device_features = m_device_info.features();
device_info.pEnabledFeatures = &device_features;
2016-08-13 12:57:50 +00:00
// Enable debug layer on debug builds
if (enable_validation_layer)
{
device_info.enabledLayerCount = 1;
device_info.ppEnabledLayerNames = &VALIDATION_LAYER_NAME;
2016-08-13 12:57:50 +00:00
}
VkResult res = vkCreateDevice(m_physical_device, &device_info, nullptr, &m_device);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreateDevice failed: ");
return false;
}
// With the device created, we can fill the remaining entry points.
if (!LoadVulkanDeviceFunctions(m_device))
return false;
// Grab the graphics and present queues.
2016-08-13 12:57:50 +00:00
vkGetDeviceQueue(m_device, m_graphics_queue_family_index, 0, &m_graphics_queue);
if (surface)
{
vkGetDeviceQueue(m_device, m_present_queue_family_index, 0, &m_present_queue);
}
2016-08-13 12:57:50 +00:00
return true;
}
bool VulkanContext::CreateAllocator(u32 vk_api_version)
{
VmaAllocatorCreateInfo allocator_info = {};
allocator_info.flags = VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT;
allocator_info.physicalDevice = m_physical_device;
allocator_info.device = m_device;
allocator_info.preferredLargeHeapBlockSize = 64 << 20;
allocator_info.pAllocationCallbacks = nullptr;
allocator_info.pDeviceMemoryCallbacks = nullptr;
allocator_info.pHeapSizeLimit = nullptr;
allocator_info.pVulkanFunctions = nullptr;
allocator_info.instance = m_instance;
allocator_info.vulkanApiVersion = vk_api_version;
allocator_info.pTypeExternalMemoryHandleTypes = nullptr;
if (SupportsDeviceExtension(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME))
allocator_info.flags |= VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT;
VkResult res = vmaCreateAllocator(&allocator_info, &m_allocator);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vmaCreateAllocator failed: ");
return false;
}
return true;
}
static VKAPI_ATTR VkBool32 VKAPI_CALL
DebugUtilsCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageTypes,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData)
2016-08-13 12:57:50 +00:00
{
const std::string log_message = fmt::format("Vulkan debug message: {}", pCallbackData->pMessage);
if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)
ERROR_LOG_FMT(HOST_GPU, "{}", log_message);
else if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT)
WARN_LOG_FMT(HOST_GPU, "{}", log_message);
else if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT)
INFO_LOG_FMT(HOST_GPU, "{}", log_message);
2016-08-13 12:57:50 +00:00
else
DEBUG_LOG_FMT(HOST_GPU, "{}", log_message);
2016-08-13 12:57:50 +00:00
return VK_FALSE;
}
bool VulkanContext::EnableDebugUtils()
2016-08-13 12:57:50 +00:00
{
// Already enabled?
if (m_debug_utils_messenger != VK_NULL_HANDLE)
2016-08-13 12:57:50 +00:00
return true;
// Check for presence of the functions before calling
if (!vkCreateDebugUtilsMessengerEXT || !vkDestroyDebugUtilsMessengerEXT ||
!vkSubmitDebugUtilsMessageEXT)
2016-08-13 12:57:50 +00:00
{
return false;
}
VkDebugUtilsMessengerCreateInfoEXT messenger_info = {
VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
nullptr,
0,
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT,
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
DebugUtilsCallback,
nullptr};
VkResult res = vkCreateDebugUtilsMessengerEXT(m_instance, &messenger_info, nullptr,
&m_debug_utils_messenger);
2016-08-13 12:57:50 +00:00
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreateDebugUtilsMessengerEXT failed: ");
2016-08-13 12:57:50 +00:00
return false;
}
return true;
}
void VulkanContext::DisableDebugUtils()
2016-08-13 12:57:50 +00:00
{
if (m_debug_utils_messenger != VK_NULL_HANDLE)
2016-08-13 12:57:50 +00:00
{
vkDestroyDebugUtilsMessengerEXT(m_instance, m_debug_utils_messenger, nullptr);
m_debug_utils_messenger = VK_NULL_HANDLE;
2016-08-13 12:57:50 +00:00
}
}
bool VulkanContext::SupportsDeviceExtension(const char* name) const
{
return std::any_of(m_device_extensions.begin(), m_device_extensions.end(),
[name](const std::string& extension) { return extension == name; });
}
static bool DriverIsMesa(VkDriverId driver_id)
{
switch (driver_id)
{
case VK_DRIVER_ID_MESA_RADV:
case VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA:
case VK_DRIVER_ID_MESA_LLVMPIPE:
case VK_DRIVER_ID_MESA_TURNIP:
case VK_DRIVER_ID_MESA_V3DV:
case VK_DRIVER_ID_MESA_PANVK:
case VK_DRIVER_ID_MESA_VENUS:
case VK_DRIVER_ID_MESA_DOZEN:
case VK_DRIVER_ID_MESA_NVK:
case VK_DRIVER_ID_IMAGINATION_OPEN_SOURCE_MESA:
case VK_DRIVER_ID_MESA_HONEYKRISP:
return true;
default:
return false;
}
}
static DriverDetails::Driver GetMesaDriver(VkDriverId driver_id)
{
switch (driver_id)
{
// clang-format off
case VK_DRIVER_ID_MESA_RADV: return DriverDetails::DRIVER_R600;
case VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA: return DriverDetails::DRIVER_I965;
case VK_DRIVER_ID_MESA_NVK: return DriverDetails::DRIVER_NOUVEAU;
case VK_DRIVER_ID_MESA_TURNIP: return DriverDetails::DRIVER_FREEDRENO;
default: return DriverDetails::DRIVER_UNKNOWN;
// clang-format on
}
}
void VulkanContext::InitDriverDetails()
{
DriverDetails::Vendor vendor;
DriverDetails::Driver driver;
// String comparisons aren't ideal, but there doesn't seem to be any other way to tell
// which vendor a driver is for. These names are based on the reports submitted to
// vulkan.gpuinfo.org, as of 19/09/2017.
std::string device_name = m_device_info.deviceName;
u32 vendor_id = m_device_info.vendorID;
// Note: driver_id may be 0 on vulkan < 1.2
VkDriverId driver_id = m_device_info.driverID;
if (DriverIsMesa(driver_id))
{
vendor = DriverDetails::VENDOR_MESA;
driver = GetMesaDriver(driver_id);
}
else if (vendor_id == 0x10DE)
{
// Currently, there is only the official NV binary driver.
// "NVIDIA" does not appear in the device name.
vendor = DriverDetails::VENDOR_NVIDIA;
driver = DriverDetails::DRIVER_NVIDIA;
}
else if (vendor_id == 0x1002 || vendor_id == 0x1022 ||
device_name.find("AMD") != std::string::npos)
{
// RADV always advertises its name in the device string.
// If not RADV, assume the AMD binary driver.
if (device_name.find("RADV") != std::string::npos)
{
vendor = DriverDetails::VENDOR_MESA;
driver = DriverDetails::DRIVER_R600;
}
else
{
vendor = DriverDetails::VENDOR_ATI;
driver = DriverDetails::DRIVER_ATI;
}
}
else if (vendor_id == 0x8086 || vendor_id == 0x8087 ||
device_name.find("Intel") != std::string::npos)
{
// Apart from the driver version, Intel does not appear to provide a way to
// differentiate between anv and the binary driver (Skylake+). Assume to be
// using anv if we're not running on Windows or macOS.
#if defined(WIN32) || defined(__APPLE__)
vendor = DriverDetails::VENDOR_INTEL;
driver = DriverDetails::DRIVER_INTEL;
#else
vendor = DriverDetails::VENDOR_MESA;
driver = DriverDetails::DRIVER_I965;
#endif
}
else if (vendor_id == 0x5143 || device_name.find("Adreno") != std::string::npos)
{
// Currently only the Qualcomm binary driver exists for Adreno.
vendor = DriverDetails::VENDOR_QUALCOMM;
driver = DriverDetails::DRIVER_QUALCOMM;
}
else if (vendor_id == 0x13B6 || device_name.find("Mali") != std::string::npos)
{
// Currently only the ARM binary driver exists for Mali.
vendor = DriverDetails::VENDOR_ARM;
driver = DriverDetails::DRIVER_ARM;
}
else if (vendor_id == 0x1010 || device_name.find("PowerVR") != std::string::npos)
{
// Currently only the binary driver exists for PowerVR.
vendor = DriverDetails::VENDOR_IMGTEC;
driver = DriverDetails::DRIVER_IMGTEC;
}
else if (device_name.find("Apple") != std::string::npos)
{
vendor = DriverDetails::VENDOR_APPLE;
driver = DriverDetails::DRIVER_PORTABILITY;
}
else
{
WARN_LOG_FMT(VIDEO, "Unknown Vulkan driver vendor, please report it to us.");
WARN_LOG_FMT(VIDEO, "Vendor ID: {:#X}, Device Name: {}", vendor_id, device_name);
vendor = DriverDetails::VENDOR_UNKNOWN;
driver = DriverDetails::DRIVER_UNKNOWN;
}
// Vulkan on macOS goes through Metal, and is not susceptible to the same bugs
// as the vendor's native Vulkan drivers. We use a different driver fields to
// differentiate MoltenVK.
if (driver_id == VK_DRIVER_ID_MOLTENVK)
driver = DriverDetails::DRIVER_PORTABILITY;
DriverDetails::Init(DriverDetails::API_VULKAN, vendor, driver,
static_cast<double>(m_device_info.driverVersion),
DriverDetails::Family::UNKNOWN, std::move(device_name));
}
bool VulkanContext::SupportsExclusiveFullscreen(const WindowSystemInfo& wsi, VkSurfaceKHR surface)
{
#ifdef SUPPORTS_VULKAN_EXCLUSIVE_FULLSCREEN
if (!surface || !vkGetPhysicalDeviceSurfaceCapabilities2KHR ||
!SupportsDeviceExtension(VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME))
{
return false;
}
VkPhysicalDeviceSurfaceInfo2KHR si = {};
si.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR;
si.surface = surface;
auto platform_info = GetPlatformExclusiveFullscreenInfo(wsi);
si.pNext = &platform_info;
VkSurfaceCapabilities2KHR caps = {};
caps.sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR;
VkSurfaceCapabilitiesFullScreenExclusiveEXT fullscreen_caps = {};
fullscreen_caps.sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_FULL_SCREEN_EXCLUSIVE_EXT;
fullscreen_caps.fullScreenExclusiveSupported = VK_TRUE;
caps.pNext = &fullscreen_caps;
VkResult res = vkGetPhysicalDeviceSurfaceCapabilities2KHR(m_physical_device, &si, &caps);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceSurfaceCapabilities2KHR failed:");
return false;
}
return fullscreen_caps.fullScreenExclusiveSupported;
#else
return false;
#endif
}
#ifdef WIN32
VkSurfaceFullScreenExclusiveWin32InfoEXT
VulkanContext::GetPlatformExclusiveFullscreenInfo(const WindowSystemInfo& wsi)
{
VkSurfaceFullScreenExclusiveWin32InfoEXT info = {};
info.sType = VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_WIN32_INFO_EXT;
info.hmonitor =
MonitorFromWindow(static_cast<HWND>(wsi.render_surface), MONITOR_DEFAULTTOPRIMARY);
return info;
}
#endif
} // namespace Vulkan