diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp
index 08d82769c2..1e80ce4639 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.cpp
+++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp
@@ -14,6 +14,7 @@
 #include "video_core/renderer_vulkan/vk_swapchain.h"
 #include "video_core/vulkan_common/vulkan_device.h"
 #include "video_core/vulkan_common/vulkan_wrapper.h"
+#include "vulkan/vulkan_core.h"
 
 namespace Vulkan {
 
@@ -33,24 +34,47 @@ VkSurfaceFormatKHR ChooseSwapSurfaceFormat(vk::Span<VkSurfaceFormatKHR> formats)
     return found != formats.end() ? *found : formats[0];
 }
 
-VkPresentModeKHR ChooseSwapPresentMode(vk::Span<VkPresentModeKHR> modes) {
-    // Mailbox (triple buffering) doesn't lock the application like FIFO (vsync)
+static constexpr VkPresentModeKHR ChooseSwapPresentMode(bool has_imm, bool has_mailbox,
+                                                        bool has_fifo_relaxed) {
+    // Mailbox doesn't lock the application like FIFO (vsync)
     // FIFO present mode locks the framerate to the monitor's refresh rate
-    const bool has_mailbox =
-        std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_MAILBOX_KHR) != modes.end();
-    const bool has_imm =
-        std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_IMMEDIATE_KHR) != modes.end();
-    const Settings::VSyncMode mode = Settings::values.vsync_mode.GetValue();
-
-    if (mode == Settings::VSyncMode::Immediate && has_imm) {
-        LOG_INFO(Render_Vulkan, "Using swap present mode Immediate");
-        return VK_PRESENT_MODE_IMMEDIATE_KHR;
-    } else if (mode == Settings::VSyncMode::Mailbox && has_mailbox) {
-        LOG_INFO(Render_Vulkan, "Using swap present mode Mailbox");
-        return VK_PRESENT_MODE_MAILBOX_KHR;
+    Settings::VSyncMode setting = [has_imm, has_mailbox]() {
+        // Choose Mailbox or Immediate if unlocked and those modes are supported
+        const auto mode = Settings::values.vsync_mode.GetValue();
+        if (Settings::values.use_speed_limit.GetValue()) {
+            return mode;
+        }
+        switch (mode) {
+        case Settings::VSyncMode::FIFO:
+        case Settings::VSyncMode::FIFORelaxed:
+            if (has_mailbox) {
+                return Settings::VSyncMode::Mailbox;
+            } else if (has_imm) {
+                return Settings::VSyncMode::Immediate;
+            }
+            [[fallthrough]];
+        default:
+            return mode;
+        }
+    }();
+    if ((setting == Settings::VSyncMode::Mailbox && !has_mailbox) ||
+        (setting == Settings::VSyncMode::Immediate && !has_imm) ||
+        (setting == Settings::VSyncMode::FIFORelaxed && !has_fifo_relaxed)) {
+        setting = Settings::VSyncMode::FIFO;
+    }
+
+    switch (setting) {
+    case Settings::VSyncMode::Immediate:
+        return VK_PRESENT_MODE_IMMEDIATE_KHR;
+    case Settings::VSyncMode::Mailbox:
+        return VK_PRESENT_MODE_MAILBOX_KHR;
+    case Settings::VSyncMode::FIFO:
+        return VK_PRESENT_MODE_FIFO_KHR;
+    case Settings::VSyncMode::FIFORelaxed:
+        return VK_PRESENT_MODE_FIFO_RELAXED_KHR;
+    default:
+        return VK_PRESENT_MODE_FIFO_KHR;
     }
-    LOG_INFO(Render_Vulkan, "Using swap present mode FIFO");
-    return VK_PRESENT_MODE_FIFO_KHR;
 }
 
 VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, u32 width, u32 height) {
@@ -168,11 +192,17 @@ void Swapchain::Present(VkSemaphore render_semaphore) {
 void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bool srgb) {
     const auto physical_device{device.GetPhysical()};
     const auto formats{physical_device.GetSurfaceFormatsKHR(surface)};
-    const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)};
+    const auto present_modes = physical_device.GetSurfacePresentModesKHR(surface);
+    has_mailbox = std::find(present_modes.begin(), present_modes.end(),
+                            VK_PRESENT_MODE_MAILBOX_KHR) != present_modes.end();
+    has_imm = std::find(present_modes.begin(), present_modes.end(),
+                        VK_PRESENT_MODE_IMMEDIATE_KHR) != present_modes.end();
+    has_fifo_relaxed = std::find(present_modes.begin(), present_modes.end(),
+                                 VK_PRESENT_MODE_FIFO_RELAXED_KHR) != present_modes.end();
 
     const VkCompositeAlphaFlagBitsKHR alpha_flags{ChooseAlphaFlags(capabilities)};
     surface_format = ChooseSwapSurfaceFormat(formats);
-    present_mode = ChooseSwapPresentMode(present_modes);
+    present_mode = ChooseSwapPresentMode(has_imm, has_mailbox, has_fifo_relaxed);
 
     u32 requested_image_count{capabilities.minImageCount + 1};
     // Ensure Triple buffering if possible.
@@ -233,7 +263,6 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bo
 
     extent = swapchain_ci.imageExtent;
     current_srgb = srgb;
-    current_fps_unlocked = !Settings::values.use_speed_limit.GetValue();
 
     images = swapchain.GetImages();
     image_count = static_cast<u32>(images.size());
@@ -255,14 +284,9 @@ void Swapchain::Destroy() {
     swapchain.reset();
 }
 
-bool Swapchain::HasFpsUnlockChanged() const {
-    return current_fps_unlocked != !Settings::values.use_speed_limit.GetValue();
-}
-
 bool Swapchain::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();
+    const auto requested_mode = ChooseSwapPresentMode(has_imm, has_mailbox, has_fifo_relaxed);
+    return present_mode != requested_mode;
 }
 
 } // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h
index 4197425867..bf1ea7254c 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.h
+++ b/src/video_core/renderer_vulkan/vk_swapchain.h
@@ -116,8 +116,6 @@ private:
 
     void Destroy();
 
-    bool HasFpsUnlockChanged() const;
-
     bool NeedsPresentModeUpdate() const;
 
     const VkSurfaceKHR surface;
@@ -142,9 +140,11 @@ private:
     VkExtent2D extent{};
     VkPresentModeKHR present_mode{};
     VkSurfaceFormatKHR surface_format{};
+    bool has_imm{false};
+    bool has_mailbox{false};
+    bool has_fifo_relaxed{false};
 
     bool current_srgb{};
-    bool current_fps_unlocked{};
     bool is_outdated{};
     bool is_suboptimal{};
 };