Jelajahi Sumber

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.
ameerj 4 tahun lalu
induk
melakukan
35e5a67a83

+ 1 - 1
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;

+ 26 - 3
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<VkSurfaceFormatKHR> formats)
 
 VkPresentModeKHR ChooseSwapPresentMode(vk::Span<VkPresentModeKHR> 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<u32>(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

+ 11 - 0
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{};
 };