From 35e5a67a839c5b1b5f12d21b2d4e3911e1af33b7 Mon Sep 17 00:00:00 2001 From: ameerj <52414509+ameerj@users.noreply.github.com> Date: Sun, 12 Sep 2021 20:32:23 -0400 Subject: [PATCH] vk_swapchain: Use immediate present mode when mailbox is unavailable and FPS is unlocked Allows drivers that do not support VK_PRESENT_MODE_MAILBOX_KHR the ability to present at a framerate higher than the monitor's refresh rate when the FPS is unlocked. --- .../renderer_vulkan/renderer_vulkan.cpp | 2 +- .../renderer_vulkan/vk_swapchain.cpp | 29 +++++++++++++++++-- src/video_core/renderer_vulkan/vk_swapchain.h | 11 +++++++ 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 9ff0a28cdf..adb6b7a3b2 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -149,7 +149,7 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout(); swapchain.Create(layout.width, layout.height, is_srgb); }; - if (swapchain.IsSubOptimal() || swapchain.HasColorSpaceChanged(is_srgb)) { + if (swapchain.NeedsRecreation(is_srgb)) { recreate_swapchain(); } bool is_outdated; diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index aadf03cb08..8972a69210 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -9,6 +9,7 @@ #include "common/assert.h" #include "common/logging/log.h" +#include "common/settings.h" #include "core/core.h" #include "core/frontend/framebuffer_layout.h" #include "video_core/renderer_vulkan/vk_scheduler.h" @@ -36,8 +37,19 @@ VkSurfaceFormatKHR ChooseSwapSurfaceFormat(vk::Span formats) VkPresentModeKHR ChooseSwapPresentMode(vk::Span modes) { // Mailbox doesn't lock the application like fifo (vsync), prefer it - const auto found = std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_MAILBOX_KHR); - return found != modes.end() ? *found : VK_PRESENT_MODE_FIFO_KHR; + const auto found_mailbox = std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_MAILBOX_KHR); + if (found_mailbox != modes.end()) { + return VK_PRESENT_MODE_MAILBOX_KHR; + } + if (Settings::values.disable_fps_limit.GetValue()) { + // FIFO present mode locks the framerate to the monitor's refresh rate, + // Find an alternative to surpass this limitation if FPS is unlocked. + const auto found_imm = std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_IMMEDIATE_KHR); + if (found_imm != modes.end()) { + return VK_PRESENT_MODE_IMMEDIATE_KHR; + } + } + return VK_PRESENT_MODE_FIFO_KHR; } VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, u32 width, u32 height) { @@ -143,7 +155,7 @@ void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)}; const VkSurfaceFormatKHR surface_format{ChooseSwapSurfaceFormat(formats)}; - const VkPresentModeKHR present_mode{ChooseSwapPresentMode(present_modes)}; + present_mode = ChooseSwapPresentMode(present_modes); u32 requested_image_count{capabilities.minImageCount + 1}; if (capabilities.maxImageCount > 0 && requested_image_count > capabilities.maxImageCount) { @@ -196,6 +208,7 @@ void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, extent = swapchain_ci.imageExtent; current_srgb = srgb; + current_fps_unlocked = Settings::values.disable_fps_limit.GetValue(); images = swapchain.GetImages(); image_count = static_cast(images.size()); @@ -248,4 +261,14 @@ void VKSwapchain::Destroy() { swapchain.reset(); } +bool VKSwapchain::HasFpsUnlockChanged() const { + return current_fps_unlocked != Settings::values.disable_fps_limit.GetValue(); +} + +bool VKSwapchain::NeedsPresentModeUpdate() const { + // Mailbox present mode is the ideal for all scenarios. If it is not available, + // A different present mode is needed to support unlocked FPS above the monitor's refresh rate. + return present_mode != VK_PRESENT_MODE_MAILBOX_KHR && HasFpsUnlockChanged(); +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h index 5bce41e21f..61a6d959e3 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.h +++ b/src/video_core/renderer_vulkan/vk_swapchain.h @@ -33,6 +33,11 @@ public: /// Presents the rendered image to the swapchain. void Present(VkSemaphore render_semaphore); + /// Returns true when the swapchain needs to be recreated. + bool NeedsRecreation(bool is_srgb) const { + return HasColorSpaceChanged(is_srgb) || IsSubOptimal() || NeedsPresentModeUpdate(); + } + /// Returns true when the color space has changed. bool HasColorSpaceChanged(bool is_srgb) const { return current_srgb != is_srgb; @@ -84,6 +89,10 @@ private: void Destroy(); + bool HasFpsUnlockChanged() const; + + bool NeedsPresentModeUpdate() const; + const VkSurfaceKHR surface; const Device& device; VKScheduler& scheduler; @@ -102,8 +111,10 @@ private: VkFormat image_view_format{}; VkExtent2D extent{}; + VkPresentModeKHR present_mode{}; bool current_srgb{}; + bool current_fps_unlocked{}; bool is_outdated{}; bool is_suboptimal{}; };