From 8dc8fae16d0c203104aa8a5dfd364b6a7d0b7035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 25 Jan 2022 19:22:49 +0100 Subject: [PATCH] Vulkan: Add support for listing all the available surface formats in system info. Enabling the EXT_swapchain_colorspace extension lets the driver expose all the formats it really supports. Used this to discover that my Galaxy S21 supports Display-P3 which is a wider gamut than sRGB. Might be able to do some color boosting mode for fun. Or, use this stuff to play around with HDR? --- Common/GPU/Vulkan/VulkanContext.cpp | 94 ++++++++++++++++++++++++++--- Common/GPU/Vulkan/VulkanContext.h | 9 +++ Common/GPU/Vulkan/VulkanLoader.h | 1 + Common/GPU/Vulkan/thin3d_vulkan.cpp | 14 ----- UI/DevScreens.cpp | 14 +++++ 5 files changed, 108 insertions(+), 24 deletions(-) diff --git a/Common/GPU/Vulkan/VulkanContext.cpp b/Common/GPU/Vulkan/VulkanContext.cpp index 645e67898e..23a854dd8e 100644 --- a/Common/GPU/Vulkan/VulkanContext.cpp +++ b/Common/GPU/Vulkan/VulkanContext.cpp @@ -151,12 +151,15 @@ VkResult VulkanContext::CreateInstance(const CreateInfo &info) { // Temporary hack for libretro. For some reason, when we try to load the functions from this extension, // we get null pointers when running libretro. Quite strange. #if !defined(__LIBRETRO__) - if (IsInstanceExtensionAvailable(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) { - instance_extensions_enabled_.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + if (EnableInstanceExtension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) { extensionsLookup_.KHR_get_physical_device_properties2 = true; } #endif + if (EnableInstanceExtension(VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME)) { + extensionsLookup_.EXT_swapchain_colorspace = true; + } + // Validate that all the instance extensions we ask for are actually available. for (auto ext : instance_extensions_enabled_) { if (!IsInstanceExtensionAvailable(ext)) @@ -615,6 +618,16 @@ bool VulkanContext::EnableDeviceExtension(const char *extension) { return false; } +bool VulkanContext::EnableInstanceExtension(const char *extension) { + for (auto &iter : instance_extension_properties_) { + if (!strcmp(iter.extensionName, extension)) { + instance_extensions_enabled_.push_back(extension); + return true; + } + } + return false; +} + VkResult VulkanContext::CreateDevice() { if (!init_error_.empty() || physical_device_ < 0) { ERROR_LOG(G3D, "Vulkan init failed: %s", init_error_.c_str()); @@ -923,8 +936,8 @@ bool VulkanContext::ChooseQueue() { return false; } - std::vector surfFormats(formatCount); - res = vkGetPhysicalDeviceSurfaceFormatsKHR(physical_devices_[physical_device_], surface_, &formatCount, surfFormats.data()); + surfFormats_.resize(formatCount); + res = vkGetPhysicalDeviceSurfaceFormatsKHR(physical_devices_[physical_device_], surface_, &formatCount, surfFormats_.data()); _dbg_assert_(res == VK_SUCCESS); if (res != VK_SUCCESS) { return false; @@ -932,24 +945,23 @@ bool VulkanContext::ChooseQueue() { // If the format list includes just one entry of VK_FORMAT_UNDEFINED, // the surface has no preferred format. Otherwise, at least one // supported format will be returned. - if (formatCount == 0 || (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED)) { + if (formatCount == 0 || (formatCount == 1 && surfFormats_[0].format == VK_FORMAT_UNDEFINED)) { INFO_LOG(G3D, "swapchain_format: Falling back to B8G8R8A8_UNORM"); swapchainFormat_ = VK_FORMAT_B8G8R8A8_UNORM; } else { swapchainFormat_ = VK_FORMAT_UNDEFINED; for (uint32_t i = 0; i < formatCount; ++i) { - if (surfFormats[i].colorSpace != VK_COLORSPACE_SRGB_NONLINEAR_KHR) { + if (surfFormats_[i].colorSpace != VK_COLORSPACE_SRGB_NONLINEAR_KHR) { continue; } - - if (surfFormats[i].format == VK_FORMAT_B8G8R8A8_UNORM || surfFormats[i].format == VK_FORMAT_R8G8B8A8_UNORM) { - swapchainFormat_ = surfFormats[i].format; + if (surfFormats_[i].format == VK_FORMAT_B8G8R8A8_UNORM || surfFormats_[i].format == VK_FORMAT_R8G8B8A8_UNORM) { + swapchainFormat_ = surfFormats_[i].format; break; } } if (swapchainFormat_ == VK_FORMAT_UNDEFINED) { // Okay, take the first one then. - swapchainFormat_ = surfFormats[0].format; + swapchainFormat_ = surfFormats_[0].format; } INFO_LOG(G3D, "swapchain_format: %d (/%d)", swapchainFormat_, formatCount); } @@ -1537,3 +1549,65 @@ std::string FormatDriverVersion(const VkPhysicalDeviceProperties &props) { uint32_t branch = VK_VERSION_PATCH(props.driverVersion); return StringFromFormat("%d.%d.%d (%08x)", major, minor, branch, props.driverVersion); } + +// Mainly just the formats seen on gpuinfo.org for swapchains, as this function is only used for listing +// those in the UI. Also depth buffers that we used in one place. +// Might add more in the future if we find more uses for this. +const char *VulkanFormatToString(VkFormat format) { + switch (format) { + case VK_FORMAT_A1R5G5B5_UNORM_PACK16: return "A1R5G5B5_UNORM_PACK16"; + case VK_FORMAT_A2B10G10R10_UNORM_PACK32: return "A2B10G10R10_UNORM_PACK32"; + case VK_FORMAT_A8B8G8R8_SNORM_PACK32: return "A8B8G8R8_SNORM_PACK32"; + case VK_FORMAT_A8B8G8R8_SRGB_PACK32: return "A8B8G8R8_SRGB_PACK32"; + case VK_FORMAT_A8B8G8R8_UNORM_PACK32: return "A8B8G8R8_UNORM_PACK32"; + case VK_FORMAT_B10G11R11_UFLOAT_PACK32: return "B10G11R11_UFLOAT_PACK32"; + case VK_FORMAT_B4G4R4A4_UNORM_PACK16: return "B4G4R4A4_UNORM_PACK16"; + case VK_FORMAT_B5G5R5A1_UNORM_PACK16: return "B5G5R5A1_UNORM_PACK16"; + case VK_FORMAT_B5G6R5_UNORM_PACK16: return "B5G6R5_UNORM_PACK16"; + case VK_FORMAT_B8G8R8A8_SNORM: return "B8G8R8A8_SNORM"; + case VK_FORMAT_B8G8R8A8_SRGB: return "B8G8R8A8_SRGB"; + case VK_FORMAT_B8G8R8A8_UNORM: return "B8G8R8A8_UNORM"; + case VK_FORMAT_R16G16B16A16_SFLOAT: return "R16G16B16A16_SFLOAT"; + case VK_FORMAT_R16G16B16A16_SNORM: return "R16G16B16A16_SNORM"; + case VK_FORMAT_R16G16B16A16_UNORM: return "R16G16B16A16_UNORM"; + case VK_FORMAT_R4G4B4A4_UNORM_PACK16: return "R4G4B4A4_UNORM_PACK16"; + case VK_FORMAT_R5G5B5A1_UNORM_PACK16: return "R5G5B5A1_UNORM_PACK16"; + case VK_FORMAT_R5G6B5_UNORM_PACK16: return "R5G6B5_UNORM_PACK16"; + case VK_FORMAT_R8G8B8A8_SNORM: return "R8G8B8A8_SNORM"; + case VK_FORMAT_R8G8B8A8_SRGB: return "R8G8B8A8_SRGB"; + case VK_FORMAT_R8G8B8A8_UNORM: return "R8G8B8A8_UNORM"; + + case VK_FORMAT_D24_UNORM_S8_UINT: return "D24S8"; + case VK_FORMAT_D16_UNORM: return "D16"; + case VK_FORMAT_D16_UNORM_S8_UINT: return "D16S8"; + case VK_FORMAT_D32_SFLOAT: return "D32f"; + case VK_FORMAT_D32_SFLOAT_S8_UINT: return "D32fS8"; + case VK_FORMAT_S8_UINT: return "S8"; + case VK_FORMAT_UNDEFINED: return "UNDEFINED (BAD!)"; + + default: return "(format not added to string list)"; + } +} + +// I miss Rust where this is automatic :( +const char *VulkanColorSpaceToString(VkColorSpaceKHR colorSpace) { + switch (colorSpace) { + case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: return "SRGB_NONLINEAR"; + case VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT: return "DISPLAY_P3_NONLINEAR"; + case VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT: return "EXTENDED_SRGB_LINEAR"; + case VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT: return "DISPLAY_P3_LINEAR"; + case VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT: return "DCI_P3_NONLINEAR"; + case VK_COLOR_SPACE_BT709_LINEAR_EXT: return "BT709_LINEAR"; + case VK_COLOR_SPACE_BT709_NONLINEAR_EXT: return "BT709_NONLINEAR"; + case VK_COLOR_SPACE_BT2020_LINEAR_EXT: return "BT2020_LINEAR"; + case VK_COLOR_SPACE_HDR10_ST2084_EXT: return "HDR10_ST2084"; + case VK_COLOR_SPACE_DOLBYVISION_EXT: return "DOLBYVISION"; + case VK_COLOR_SPACE_HDR10_HLG_EXT: return "HDR10_HLG"; + case VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT: return "ADOBERGB_LINEAR"; + case VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT: return "ADOBERGB_NONLINEAR"; + case VK_COLOR_SPACE_PASS_THROUGH_EXT: return "PASS_THROUGH"; + case VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT: return "EXTENDED_SRGB_NONLINEAR"; + case VK_COLOR_SPACE_DISPLAY_NATIVE_AMD: return "DISPLAY_NATIVE_AMD"; + default: return "(unknown)"; + } +} diff --git a/Common/GPU/Vulkan/VulkanContext.h b/Common/GPU/Vulkan/VulkanContext.h index 5740dedfae..b5750c77ab 100644 --- a/Common/GPU/Vulkan/VulkanContext.h +++ b/Common/GPU/Vulkan/VulkanContext.h @@ -171,6 +171,7 @@ public: int GetBestPhysicalDevice(); int GetPhysicalDeviceByName(std::string name); void ChooseDevice(int physical_device); + bool EnableInstanceExtension(const char *extension); bool EnableDeviceExtension(const char *extension); VkResult CreateDevice(); @@ -338,6 +339,10 @@ public: return allocator_; } + const std::vector &SurfaceFormats() { + return surfFormats_; + } + private: bool ChooseQueue(); @@ -420,6 +425,7 @@ private: PhysicalDeviceFeatures deviceFeatures_; VkSurfaceCapabilitiesKHR surfCapabilities_{}; + std::vector surfFormats_{}; std::vector cmdQueue_; @@ -445,6 +451,9 @@ enum class GLSLVariant { bool GLSLtoSPV(const VkShaderStageFlagBits shader_type, const char *sourceCode, GLSLVariant variant, std::vector &spirv, std::string *errorMessage); const char *VulkanResultToString(VkResult res); +const char *VulkanColorSpaceToString(VkColorSpaceKHR colorSpace); +const char *VulkanFormatToString(VkFormat format); + std::string FormatDriverVersion(const VkPhysicalDeviceProperties &props); // Simple heuristic. diff --git a/Common/GPU/Vulkan/VulkanLoader.h b/Common/GPU/Vulkan/VulkanLoader.h index 7afacacc73..af2c0add64 100644 --- a/Common/GPU/Vulkan/VulkanLoader.h +++ b/Common/GPU/Vulkan/VulkanLoader.h @@ -238,6 +238,7 @@ struct VulkanExtensions { bool KHR_get_physical_device_properties2; bool KHR_depth_stencil_resolve; bool EXT_shader_stencil_export; + bool EXT_swapchain_colorspace; // bool EXT_depth_range_unrestricted; // Allows depth outside [0.0, 1.0] in 32-bit float depth buffers. }; diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index 62669ec27d..2d88d1c8d7 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -1395,20 +1395,6 @@ void AddFeature(std::vector &features, const char *name, VkBool32 a features.push_back(buf); } -// Limited to depth buffer formats as that's what we need right now. -static const char *VulkanFormatToString(VkFormat fmt) { - switch (fmt) { - case VkFormat::VK_FORMAT_D24_UNORM_S8_UINT: return "D24S8"; - case VkFormat::VK_FORMAT_D16_UNORM: return "D16"; - case VkFormat::VK_FORMAT_D16_UNORM_S8_UINT: return "D16S8"; - case VkFormat::VK_FORMAT_D32_SFLOAT: return "D32f"; - case VkFormat::VK_FORMAT_D32_SFLOAT_S8_UINT: return "D32fS8"; - case VkFormat::VK_FORMAT_S8_UINT: return "S8"; - case VkFormat::VK_FORMAT_UNDEFINED: return "UNDEFINED (BAD!)"; - default: return "UNKNOWN"; - } -} - std::vector VKContext::GetFeatureList() const { const VkPhysicalDeviceFeatures &available = vulkan_->GetDeviceFeatures().available; const VkPhysicalDeviceFeatures &enabled = vulkan_->GetDeviceFeatures().enabled; diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index 75bad266df..e1d0560569 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -24,6 +24,7 @@ #include "Common/System/NativeApp.h" #include "Common/System/System.h" #include "Common/GPU/OpenGL/GLFeatures.h" +#include "Common/GPU/Vulkan/VulkanContext.h" #include "Common/File/AndroidStorage.h" #include "Common/Data/Text/I18n.h" #include "Common/Net/HTTPClient.h" @@ -55,6 +56,7 @@ #include "UI/ControlMappingScreen.h" #include "UI/GameSettingsScreen.h" + #ifdef _WIN32 #include "Common/CommonWindows.h" // Want to avoid including the full header here as it includes d3dx.h @@ -720,6 +722,18 @@ void SystemInfoScreen::CreateViews() { for (auto &feature : features) { gpuExtensions->Add(new TextView(feature, new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true); } + + // Vulkan specific code here, can't be bothered to abstract. + gpuExtensions->Add(new ItemHeader(si->T("Display Color Formats"))); + VulkanContext *vk = (VulkanContext *)draw->GetNativeObject(Draw::NativeObject::CONTEXT); + if (vk) { + for (auto &format : vk->SurfaceFormats()) { + std::string line = StringFromFormat("%s : %s", VulkanFormatToString(format.format), VulkanColorSpaceToString(format.colorSpace)); + gpuExtensions->Add(new TextView(line, + new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true); + } + } + gpuExtensions->Add(new ItemHeader(si->T("Vulkan Extensions"))); std::vector extensions = draw->GetExtensionList(); for (auto &extension : extensions) {