From 65daf1d6a0894749745a8f7c0c4c74c6752938d8 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Tue, 16 Feb 2021 02:57:35 +1000 Subject: [PATCH] Vulkan: Support fullscreen resolution enumeration --- src/common/vulkan/swap_chain.cpp | 72 +++++++++++++++++++- src/common/vulkan/swap_chain.h | 10 +++ src/duckstation-qt/displaysettingswidget.cpp | 2 +- src/frontend-common/vulkan_host_display.cpp | 23 ++++++- src/frontend-common/vulkan_host_display.h | 2 +- 5 files changed, 103 insertions(+), 6 deletions(-) diff --git a/src/common/vulkan/swap_chain.cpp b/src/common/vulkan/swap_chain.cpp index 1738021f5..5d8c5cd6e 100644 --- a/src/common/vulkan/swap_chain.cpp +++ b/src/common/vulkan/swap_chain.cpp @@ -132,7 +132,7 @@ static VkSurfaceKHR CreateDisplaySurface(VkInstance instance, VkPhysicalDevice p const VkDisplayModePropertiesKHR* matched_mode = nullptr; for (const VkDisplayModePropertiesKHR& mode : modes) { - const float refresh_rate = static_cast(mode.parameters.refreshRate) * 1000.0f; + const float refresh_rate = static_cast(mode.parameters.refreshRate) / 1000.0f; Log_DevPrintf(" Mode %ux%u @ %f", mode.parameters.visibleRegion.width, mode.parameters.visibleRegion.height, refresh_rate); @@ -225,6 +225,67 @@ static VkSurfaceKHR CreateDisplaySurface(VkInstance instance, VkPhysicalDevice p return VK_NULL_HANDLE; } +static std::vector GetDisplayModes(VkInstance instance, VkPhysicalDevice physical_device, + const WindowInfo& wi) +{ + + u32 num_displays; + VkResult res = vkGetPhysicalDeviceDisplayPropertiesKHR(physical_device, &num_displays, nullptr); + if (res != VK_SUCCESS || num_displays == 0) + { + LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceDisplayPropertiesKHR() failed:"); + return {}; + } + + std::vector displays(num_displays); + res = vkGetPhysicalDeviceDisplayPropertiesKHR(physical_device, &num_displays, displays.data()); + if (res != VK_SUCCESS || num_displays != displays.size()) + { + LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceDisplayPropertiesKHR() failed:"); + return {}; + } + + std::vector result; + for (u32 display_index = 0; display_index < num_displays; display_index++) + { + const VkDisplayPropertiesKHR& props = displays[display_index]; + + u32 num_modes; + res = vkGetDisplayModePropertiesKHR(physical_device, props.display, &num_modes, nullptr); + if (res != VK_SUCCESS || num_modes == 0) + { + LOG_VULKAN_ERROR(res, "vkGetDisplayModePropertiesKHR() failed:"); + continue; + } + + std::vector modes(num_modes); + res = vkGetDisplayModePropertiesKHR(physical_device, props.display, &num_modes, modes.data()); + if (res != VK_SUCCESS || num_modes != modes.size()) + { + LOG_VULKAN_ERROR(res, "vkGetDisplayModePropertiesKHR() failed:"); + continue; + } + + for (const VkDisplayModePropertiesKHR& mode : modes) + { + const float refresh_rate = static_cast(mode.parameters.refreshRate) / 1000.0f; + if (std::find_if(result.begin(), result.end(), [&mode, refresh_rate](const SwapChain::FullscreenModeInfo& mi) { + return (mi.width == mode.parameters.visibleRegion.width && + mi.height == mode.parameters.visibleRegion.height && mode.parameters.refreshRate == refresh_rate); + }) != result.end()) + { + continue; + } + + result.push_back(SwapChain::FullscreenModeInfo{static_cast(mode.parameters.visibleRegion.width), + static_cast(mode.parameters.visibleRegion.height), + refresh_rate}); + } + } + + return result; +} + VkSurfaceKHR SwapChain::CreateVulkanSurface(VkInstance instance, VkPhysicalDevice physical_device, WindowInfo& wi) { #if defined(VK_USE_PLATFORM_WIN32_KHR) @@ -367,6 +428,15 @@ void SwapChain::DestroyVulkanSurface(VkInstance instance, WindowInfo& wi, VkSurf #endif } +std::vector +SwapChain::GetSurfaceFullscreenModes(VkInstance instance, VkPhysicalDevice physical_device, const WindowInfo& wi) +{ + if (wi.type == WindowInfo::Type::Display) + return GetDisplayModes(instance, physical_device, wi); + + return {}; +} + std::unique_ptr SwapChain::Create(const WindowInfo& wi, VkSurfaceKHR surface, bool vsync) { std::unique_ptr swap_chain = std::make_unique(wi, surface, vsync); diff --git a/src/common/vulkan/swap_chain.h b/src/common/vulkan/swap_chain.h index 6fd872e32..728752e81 100644 --- a/src/common/vulkan/swap_chain.h +++ b/src/common/vulkan/swap_chain.h @@ -26,6 +26,16 @@ public: // Destroys a previously-created surface. static void DestroyVulkanSurface(VkInstance instance, WindowInfo& wi, VkSurfaceKHR surface); + // Enumerates fullscreen modes for window info. + struct FullscreenModeInfo + { + u32 width; + u32 height; + float refresh_rate; + }; + static std::vector + GetSurfaceFullscreenModes(VkInstance instance, VkPhysicalDevice physical_device, const WindowInfo& wi); + // Create a new swap chain from a pre-existing surface. static std::unique_ptr Create(const WindowInfo& wi, VkSurfaceKHR surface, bool vsync); diff --git a/src/duckstation-qt/displaysettingswidget.cpp b/src/duckstation-qt/displaysettingswidget.cpp index 42aaead0a..a1848187e 100644 --- a/src/duckstation-qt/displaysettingswidget.cpp +++ b/src/duckstation-qt/displaysettingswidget.cpp @@ -183,7 +183,7 @@ void DisplaySettingsWidget::populateGPUAdaptersAndResolutions() #endif case GPURenderer::HardwareVulkan: - aml = FrontendCommon::VulkanHostDisplay::StaticGetAdapterAndModeList(); + aml = FrontendCommon::VulkanHostDisplay::StaticGetAdapterAndModeList(nullptr); threaded_presentation_supported = true; break; diff --git a/src/frontend-common/vulkan_host_display.cpp b/src/frontend-common/vulkan_host_display.cpp index 6c5e40fdd..a617f2f5b 100644 --- a/src/frontend-common/vulkan_host_display.cpp +++ b/src/frontend-common/vulkan_host_display.cpp @@ -9,6 +9,7 @@ #include "common/vulkan/stream_buffer.h" #include "common/vulkan/swap_chain.h" #include "common/vulkan/util.h" +#include "common_host_interface.h" #include "core/shader_cache_version.h" #include "imgui.h" #include "imgui_impl_vulkan.h" @@ -141,7 +142,7 @@ bool VulkanHostDisplay::SetFullscreen(bool fullscreen, u32 width, u32 height, fl HostDisplay::AdapterAndModeList VulkanHostDisplay::GetAdapterAndModeList() { - return StaticGetAdapterAndModeList(); + return StaticGetAdapterAndModeList(m_window_info.type != WindowInfo::Type::Surfaceless ? &m_window_info : nullptr); } void VulkanHostDisplay::DestroyRenderSurface() @@ -752,13 +753,19 @@ void VulkanHostDisplay::RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 h vkCmdDraw(cmdbuffer, 3, 1, 0, 0); } -HostDisplay::AdapterAndModeList VulkanHostDisplay::StaticGetAdapterAndModeList() +HostDisplay::AdapterAndModeList VulkanHostDisplay::StaticGetAdapterAndModeList(const WindowInfo* wi) { AdapterAndModeList ret; + std::vector fsmodes; if (g_vulkan_context) { ret.adapter_names = Vulkan::Context::EnumerateGPUNames(g_vulkan_context->GetVulkanInstance()); + if (wi) + { + fsmodes = Vulkan::SwapChain::GetSurfaceFullscreenModes(g_vulkan_context->GetVulkanInstance(), + g_vulkan_context->GetPhysicalDevice(), *wi); + } } else if (Vulkan::LoadVulkanLibrary()) { @@ -774,7 +781,17 @@ HostDisplay::AdapterAndModeList VulkanHostDisplay::StaticGetAdapterAndModeList() } } - return {}; + if (!fsmodes.empty()) + { + ret.fullscreen_modes.reserve(fsmodes.size()); + for (const Vulkan::SwapChain::FullscreenModeInfo& fmi : fsmodes) + { + ret.fullscreen_modes.push_back( + CommonHostInterface::GetFullscreenModeString(fmi.width, fmi.height, fmi.refresh_rate)); + } + } + + return ret; } VulkanHostDisplay::PostProcessingStage::PostProcessingStage(PostProcessingStage&& move) diff --git a/src/frontend-common/vulkan_host_display.h b/src/frontend-common/vulkan_host_display.h index 3d8f54234..924fe8a3b 100644 --- a/src/frontend-common/vulkan_host_display.h +++ b/src/frontend-common/vulkan_host_display.h @@ -65,7 +65,7 @@ public: virtual bool Render() override; - static AdapterAndModeList StaticGetAdapterAndModeList(); + static AdapterAndModeList StaticGetAdapterAndModeList(const WindowInfo* wi); protected: struct PushConstants