summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/threadsafe_queue.h27
-rw-r--r--src/core/core.cpp6
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp8
-rw-r--r--src/video_core/gpu.cpp8
-rw-r--r--src/video_core/gpu.h3
-rw-r--r--src/video_core/gpu_thread.cpp57
-rw-r--r--src/video_core/gpu_thread.h13
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp19
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.h7
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.cpp29
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.h11
12 files changed, 94 insertions, 96 deletions
diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h
index 8430b9778..2c8c2b90e 100644
--- a/src/common/threadsafe_queue.h
+++ b/src/common/threadsafe_queue.h
@@ -14,7 +14,7 @@
14#include <utility> 14#include <utility>
15 15
16namespace Common { 16namespace Common {
17template <typename T> 17template <typename T, bool with_stop_token = false>
18class SPSCQueue { 18class SPSCQueue {
19public: 19public:
20 SPSCQueue() { 20 SPSCQueue() {
@@ -84,7 +84,7 @@ public:
84 void Wait() { 84 void Wait() {
85 if (Empty()) { 85 if (Empty()) {
86 std::unique_lock lock{cv_mutex}; 86 std::unique_lock lock{cv_mutex};
87 cv.wait(lock, [this]() { return !Empty(); }); 87 cv.wait(lock, [this] { return !Empty(); });
88 } 88 }
89 } 89 }
90 90
@@ -95,6 +95,19 @@ public:
95 return t; 95 return t;
96 } 96 }
97 97
98 T PopWait(std::stop_token stop_token) {
99 if (Empty()) {
100 std::unique_lock lock{cv_mutex};
101 cv.wait(lock, stop_token, [this] { return !Empty(); });
102 }
103 if (stop_token.stop_requested()) {
104 return T{};
105 }
106 T t;
107 Pop(t);
108 return t;
109 }
110
98 // not thread-safe 111 // not thread-safe
99 void Clear() { 112 void Clear() {
100 size.store(0); 113 size.store(0);
@@ -123,13 +136,13 @@ private:
123 ElementPtr* read_ptr; 136 ElementPtr* read_ptr;
124 std::atomic_size_t size{0}; 137 std::atomic_size_t size{0};
125 std::mutex cv_mutex; 138 std::mutex cv_mutex;
126 std::condition_variable cv; 139 std::conditional_t<with_stop_token, std::condition_variable_any, std::condition_variable> cv;
127}; 140};
128 141
129// a simple thread-safe, 142// a simple thread-safe,
130// single reader, multiple writer queue 143// single reader, multiple writer queue
131 144
132template <typename T> 145template <typename T, bool with_stop_token = false>
133class MPSCQueue { 146class MPSCQueue {
134public: 147public:
135 [[nodiscard]] std::size_t Size() const { 148 [[nodiscard]] std::size_t Size() const {
@@ -166,13 +179,17 @@ public:
166 return spsc_queue.PopWait(); 179 return spsc_queue.PopWait();
167 } 180 }
168 181
182 T PopWait(std::stop_token stop_token) {
183 return spsc_queue.PopWait(stop_token);
184 }
185
169 // not thread-safe 186 // not thread-safe
170 void Clear() { 187 void Clear() {
171 spsc_queue.Clear(); 188 spsc_queue.Clear();
172 } 189 }
173 190
174private: 191private:
175 SPSCQueue<T> spsc_queue; 192 SPSCQueue<T, with_stop_token> spsc_queue;
176 std::mutex write_lock; 193 std::mutex write_lock;
177}; 194};
178} // namespace Common 195} // namespace Common
diff --git a/src/core/core.cpp b/src/core/core.cpp
index b13350f6e..54ebed2c1 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -305,10 +305,7 @@ struct System::Impl {
305 is_powered_on = false; 305 is_powered_on = false;
306 exit_lock = false; 306 exit_lock = false;
307 307
308 if (gpu_core) { 308 gpu_core.reset();
309 gpu_core->ShutDown();
310 }
311
312 services.reset(); 309 services.reset();
313 service_manager.reset(); 310 service_manager.reset();
314 cheat_engine.reset(); 311 cheat_engine.reset();
@@ -317,7 +314,6 @@ struct System::Impl {
317 time_manager.Shutdown(); 314 time_manager.Shutdown();
318 core_timing.Shutdown(); 315 core_timing.Shutdown();
319 app_loader.reset(); 316 app_loader.reset();
320 gpu_core.reset();
321 perf_stats.reset(); 317 perf_stats.reset();
322 kernel.Shutdown(); 318 kernel.Shutdown();
323 memory.Reset(); 319 memory.Reset();
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
index 68f360b3c..6f60c6574 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
@@ -477,7 +477,13 @@ void EmitSetSampleMask(EmitContext& ctx, Id value) {
477} 477}
478 478
479void EmitSetFragDepth(EmitContext& ctx, Id value) { 479void EmitSetFragDepth(EmitContext& ctx, Id value) {
480 ctx.OpStore(ctx.frag_depth, value); 480 if (!ctx.runtime_info.convert_depth_mode) {
481 ctx.OpStore(ctx.frag_depth, value);
482 return;
483 }
484 const Id unit{ctx.Const(0.5f)};
485 const Id new_depth{ctx.OpFma(ctx.F32[1], value, unit, unit)};
486 ctx.OpStore(ctx.frag_depth, new_depth);
481} 487}
482 488
483void EmitGetZFlag(EmitContext&) { 489void EmitGetZFlag(EmitContext&) {
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index ff024f530..2ae3639b5 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -531,14 +531,6 @@ void GPU::TriggerCpuInterrupt(const u32 syncpoint_id, const u32 value) const {
531 interrupt_manager.GPUInterruptSyncpt(syncpoint_id, value); 531 interrupt_manager.GPUInterruptSyncpt(syncpoint_id, value);
532} 532}
533 533
534void GPU::ShutDown() {
535 // Signal that threads should no longer block on syncpoint fences
536 shutting_down.store(true, std::memory_order_relaxed);
537 sync_cv.notify_all();
538
539 gpu_thread.ShutDown();
540}
541
542void GPU::OnCommandListEnd() { 534void GPU::OnCommandListEnd() {
543 if (is_async) { 535 if (is_async) {
544 // This command only applies to asynchronous GPU mode 536 // This command only applies to asynchronous GPU mode
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index a8e98e51b..e6a02a71b 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -219,9 +219,6 @@ public:
219 return *shader_notify; 219 return *shader_notify;
220 } 220 }
221 221
222 // Stops the GPU execution and waits for the GPU to finish working
223 void ShutDown();
224
225 /// Allows the CPU/NvFlinger to wait on the GPU before presenting a frame. 222 /// Allows the CPU/NvFlinger to wait on the GPU before presenting a frame.
226 void WaitFence(u32 syncpoint_id, u32 value); 223 void WaitFence(u32 syncpoint_id, u32 value);
227 224
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index 46f642b19..9547f277a 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -17,9 +17,9 @@
17namespace VideoCommon::GPUThread { 17namespace VideoCommon::GPUThread {
18 18
19/// Runs the GPU thread 19/// Runs the GPU thread
20static void RunThread(Core::System& system, VideoCore::RendererBase& renderer, 20static void RunThread(std::stop_token stop_token, Core::System& system,
21 Core::Frontend::GraphicsContext& context, Tegra::DmaPusher& dma_pusher, 21 VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context,
22 SynchState& state) { 22 Tegra::DmaPusher& dma_pusher, SynchState& state) {
23 std::string name = "yuzu:GPU"; 23 std::string name = "yuzu:GPU";
24 MicroProfileOnThreadCreate(name.c_str()); 24 MicroProfileOnThreadCreate(name.c_str());
25 SCOPE_EXIT({ MicroProfileOnThreadExit(); }); 25 SCOPE_EXIT({ MicroProfileOnThreadExit(); });
@@ -28,20 +28,14 @@ static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
28 Common::SetCurrentThreadPriority(Common::ThreadPriority::High); 28 Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
29 system.RegisterHostThread(); 29 system.RegisterHostThread();
30 30
31 // Wait for first GPU command before acquiring the window context
32 state.queue.Wait();
33
34 // If emulation was stopped during disk shader loading, abort before trying to acquire context
35 if (!state.is_running) {
36 return;
37 }
38
39 auto current_context = context.Acquire(); 31 auto current_context = context.Acquire();
40 VideoCore::RasterizerInterface* const rasterizer = renderer.ReadRasterizer(); 32 VideoCore::RasterizerInterface* const rasterizer = renderer.ReadRasterizer();
41 33
42 CommandDataContainer next; 34 while (!stop_token.stop_requested()) {
43 while (state.is_running) { 35 CommandDataContainer next = state.queue.PopWait(stop_token);
44 next = state.queue.PopWait(); 36 if (stop_token.stop_requested()) {
37 break;
38 }
45 if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) { 39 if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) {
46 dma_pusher.Push(std::move(submit_list->entries)); 40 dma_pusher.Push(std::move(submit_list->entries));
47 dma_pusher.DispatchCalls(); 41 dma_pusher.DispatchCalls();
@@ -55,8 +49,6 @@ static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
55 rasterizer->FlushRegion(flush->addr, flush->size); 49 rasterizer->FlushRegion(flush->addr, flush->size);
56 } else if (const auto* invalidate = std::get_if<InvalidateRegionCommand>(&next.data)) { 50 } else if (const auto* invalidate = std::get_if<InvalidateRegionCommand>(&next.data)) {
57 rasterizer->OnCPUWrite(invalidate->addr, invalidate->size); 51 rasterizer->OnCPUWrite(invalidate->addr, invalidate->size);
58 } else if (std::holds_alternative<EndProcessingCommand>(next.data)) {
59 ASSERT(state.is_running == false);
60 } else { 52 } else {
61 UNREACHABLE(); 53 UNREACHABLE();
62 } 54 }
@@ -73,16 +65,14 @@ static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
73ThreadManager::ThreadManager(Core::System& system_, bool is_async_) 65ThreadManager::ThreadManager(Core::System& system_, bool is_async_)
74 : system{system_}, is_async{is_async_} {} 66 : system{system_}, is_async{is_async_} {}
75 67
76ThreadManager::~ThreadManager() { 68ThreadManager::~ThreadManager() = default;
77 ShutDown();
78}
79 69
80void ThreadManager::StartThread(VideoCore::RendererBase& renderer, 70void ThreadManager::StartThread(VideoCore::RendererBase& renderer,
81 Core::Frontend::GraphicsContext& context, 71 Core::Frontend::GraphicsContext& context,
82 Tegra::DmaPusher& dma_pusher) { 72 Tegra::DmaPusher& dma_pusher) {
83 rasterizer = renderer.ReadRasterizer(); 73 rasterizer = renderer.ReadRasterizer();
84 thread = std::thread(RunThread, std::ref(system), std::ref(renderer), std::ref(context), 74 thread = std::jthread(RunThread, std::ref(system), std::ref(renderer), std::ref(context),
85 std::ref(dma_pusher), std::ref(state)); 75 std::ref(dma_pusher), std::ref(state));
86} 76}
87 77
88void ThreadManager::SubmitList(Tegra::CommandList&& entries) { 78void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
@@ -117,26 +107,6 @@ void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) {
117 rasterizer->OnCPUWrite(addr, size); 107 rasterizer->OnCPUWrite(addr, size);
118} 108}
119 109
120void ThreadManager::ShutDown() {
121 if (!state.is_running) {
122 return;
123 }
124
125 {
126 std::lock_guard lk(state.write_lock);
127 state.is_running = false;
128 state.cv.notify_all();
129 }
130
131 if (!thread.joinable()) {
132 return;
133 }
134
135 // Notify GPU thread that a shutdown is pending
136 PushCommand(EndProcessingCommand());
137 thread.join();
138}
139
140void ThreadManager::OnCommandListEnd() { 110void ThreadManager::OnCommandListEnd() {
141 PushCommand(OnCommandListEndCommand()); 111 PushCommand(OnCommandListEndCommand());
142} 112}
@@ -152,9 +122,8 @@ u64 ThreadManager::PushCommand(CommandData&& command_data, bool block) {
152 state.queue.Push(CommandDataContainer(std::move(command_data), fence, block)); 122 state.queue.Push(CommandDataContainer(std::move(command_data), fence, block));
153 123
154 if (block) { 124 if (block) {
155 state.cv.wait(lk, [this, fence] { 125 state.cv.wait(lk, thread.get_stop_token(), [this, fence] {
156 return fence <= state.signaled_fence.load(std::memory_order_relaxed) || 126 return fence <= state.signaled_fence.load(std::memory_order_relaxed);
157 !state.is_running;
158 }); 127 });
159 } 128 }
160 129
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
index 11a648f38..91bada925 100644
--- a/src/video_core/gpu_thread.h
+++ b/src/video_core/gpu_thread.h
@@ -33,9 +33,6 @@ class RendererBase;
33 33
34namespace VideoCommon::GPUThread { 34namespace VideoCommon::GPUThread {
35 35
36/// Command to signal to the GPU thread that processing has ended
37struct EndProcessingCommand final {};
38
39/// Command to signal to the GPU thread that a command list is ready for processing 36/// Command to signal to the GPU thread that a command list is ready for processing
40struct SubmitListCommand final { 37struct SubmitListCommand final {
41 explicit SubmitListCommand(Tegra::CommandList&& entries_) : entries{std::move(entries_)} {} 38 explicit SubmitListCommand(Tegra::CommandList&& entries_) : entries{std::move(entries_)} {}
@@ -83,7 +80,7 @@ struct OnCommandListEndCommand final {};
83struct GPUTickCommand final {}; 80struct GPUTickCommand final {};
84 81
85using CommandData = 82using CommandData =
86 std::variant<EndProcessingCommand, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand, 83 std::variant<std::monostate, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand,
87 InvalidateRegionCommand, FlushAndInvalidateRegionCommand, OnCommandListEndCommand, 84 InvalidateRegionCommand, FlushAndInvalidateRegionCommand, OnCommandListEndCommand,
88 GPUTickCommand>; 85 GPUTickCommand>;
89 86
@@ -100,14 +97,12 @@ struct CommandDataContainer {
100 97
101/// Struct used to synchronize the GPU thread 98/// Struct used to synchronize the GPU thread
102struct SynchState final { 99struct SynchState final {
103 std::atomic_bool is_running{true}; 100 using CommandQueue = Common::SPSCQueue<CommandDataContainer, true>;
104
105 using CommandQueue = Common::SPSCQueue<CommandDataContainer>;
106 std::mutex write_lock; 101 std::mutex write_lock;
107 CommandQueue queue; 102 CommandQueue queue;
108 u64 last_fence{}; 103 u64 last_fence{};
109 std::atomic<u64> signaled_fence{}; 104 std::atomic<u64> signaled_fence{};
110 std::condition_variable cv; 105 std::condition_variable_any cv;
111}; 106};
112 107
113/// Class used to manage the GPU thread 108/// Class used to manage the GPU thread
@@ -149,7 +144,7 @@ private:
149 VideoCore::RasterizerInterface* rasterizer = nullptr; 144 VideoCore::RasterizerInterface* rasterizer = nullptr;
150 145
151 SynchState state; 146 SynchState state;
152 std::thread thread; 147 std::jthread thread;
153}; 148};
154 149
155} // namespace VideoCommon::GPUThread 150} // namespace VideoCommon::GPUThread
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 9ff0a28cd..adb6b7a3b 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) {
149 const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout(); 149 const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout();
150 swapchain.Create(layout.width, layout.height, is_srgb); 150 swapchain.Create(layout.width, layout.height, is_srgb);
151 }; 151 };
152 if (swapchain.IsSubOptimal() || swapchain.HasColorSpaceChanged(is_srgb)) { 152 if (swapchain.NeedsRecreation(is_srgb)) {
153 recreate_swapchain(); 153 recreate_swapchain();
154 } 154 }
155 bool is_outdated; 155 bool is_outdated;
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index 1d438787a..0c11c814f 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -43,17 +43,10 @@ VKScheduler::VKScheduler(const Device& device_, StateTracker& state_tracker_)
43 command_pool{std::make_unique<CommandPool>(*master_semaphore, device)} { 43 command_pool{std::make_unique<CommandPool>(*master_semaphore, device)} {
44 AcquireNewChunk(); 44 AcquireNewChunk();
45 AllocateWorkerCommandBuffer(); 45 AllocateWorkerCommandBuffer();
46 worker_thread = std::thread(&VKScheduler::WorkerThread, this); 46 worker_thread = std::jthread([this](std::stop_token token) { WorkerThread(token); });
47} 47}
48 48
49VKScheduler::~VKScheduler() { 49VKScheduler::~VKScheduler() = default;
50 {
51 std::lock_guard lock{work_mutex};
52 quit = true;
53 }
54 work_cv.notify_all();
55 worker_thread.join();
56}
57 50
58void VKScheduler::Flush(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { 51void VKScheduler::Flush(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
59 SubmitExecution(signal_semaphore, wait_semaphore); 52 SubmitExecution(signal_semaphore, wait_semaphore);
@@ -135,7 +128,7 @@ bool VKScheduler::UpdateGraphicsPipeline(GraphicsPipeline* pipeline) {
135 return true; 128 return true;
136} 129}
137 130
138void VKScheduler::WorkerThread() { 131void VKScheduler::WorkerThread(std::stop_token stop_token) {
139 Common::SetCurrentThreadName("yuzu:VulkanWorker"); 132 Common::SetCurrentThreadName("yuzu:VulkanWorker");
140 do { 133 do {
141 if (work_queue.empty()) { 134 if (work_queue.empty()) {
@@ -144,8 +137,8 @@ void VKScheduler::WorkerThread() {
144 std::unique_ptr<CommandChunk> work; 137 std::unique_ptr<CommandChunk> work;
145 { 138 {
146 std::unique_lock lock{work_mutex}; 139 std::unique_lock lock{work_mutex};
147 work_cv.wait(lock, [this] { return !work_queue.empty() || quit; }); 140 work_cv.wait(lock, stop_token, [this] { return !work_queue.empty(); });
148 if (quit) { 141 if (stop_token.stop_requested()) {
149 continue; 142 continue;
150 } 143 }
151 work = std::move(work_queue.front()); 144 work = std::move(work_queue.front());
@@ -158,7 +151,7 @@ void VKScheduler::WorkerThread() {
158 } 151 }
159 std::lock_guard reserve_lock{reserve_mutex}; 152 std::lock_guard reserve_lock{reserve_mutex};
160 chunk_reserve.push_back(std::move(work)); 153 chunk_reserve.push_back(std::move(work));
161 } while (!quit); 154 } while (!stop_token.stop_requested());
162} 155}
163 156
164void VKScheduler::AllocateWorkerCommandBuffer() { 157void VKScheduler::AllocateWorkerCommandBuffer() {
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
index 759ed5a48..bd22e4e83 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.h
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -187,7 +187,7 @@ private:
187 GraphicsPipeline* graphics_pipeline = nullptr; 187 GraphicsPipeline* graphics_pipeline = nullptr;
188 }; 188 };
189 189
190 void WorkerThread(); 190 void WorkerThread(std::stop_token stop_token);
191 191
192 void AllocateWorkerCommandBuffer(); 192 void AllocateWorkerCommandBuffer();
193 193
@@ -212,7 +212,7 @@ private:
212 vk::CommandBuffer current_cmdbuf; 212 vk::CommandBuffer current_cmdbuf;
213 213
214 std::unique_ptr<CommandChunk> chunk; 214 std::unique_ptr<CommandChunk> chunk;
215 std::thread worker_thread; 215 std::jthread worker_thread;
216 216
217 State state; 217 State state;
218 218
@@ -224,9 +224,8 @@ private:
224 std::vector<std::unique_ptr<CommandChunk>> chunk_reserve; 224 std::vector<std::unique_ptr<CommandChunk>> chunk_reserve;
225 std::mutex reserve_mutex; 225 std::mutex reserve_mutex;
226 std::mutex work_mutex; 226 std::mutex work_mutex;
227 std::condition_variable work_cv; 227 std::condition_variable_any work_cv;
228 std::condition_variable wait_cv; 228 std::condition_variable wait_cv;
229 std::atomic_bool quit{};
230}; 229};
231 230
232} // namespace Vulkan 231} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp
index aadf03cb0..8972a6921 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.cpp
+++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp
@@ -9,6 +9,7 @@
9 9
10#include "common/assert.h" 10#include "common/assert.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/settings.h"
12#include "core/core.h" 13#include "core/core.h"
13#include "core/frontend/framebuffer_layout.h" 14#include "core/frontend/framebuffer_layout.h"
14#include "video_core/renderer_vulkan/vk_scheduler.h" 15#include "video_core/renderer_vulkan/vk_scheduler.h"
@@ -36,8 +37,19 @@ VkSurfaceFormatKHR ChooseSwapSurfaceFormat(vk::Span<VkSurfaceFormatKHR> formats)
36 37
37VkPresentModeKHR ChooseSwapPresentMode(vk::Span<VkPresentModeKHR> modes) { 38VkPresentModeKHR ChooseSwapPresentMode(vk::Span<VkPresentModeKHR> modes) {
38 // Mailbox doesn't lock the application like fifo (vsync), prefer it 39 // Mailbox doesn't lock the application like fifo (vsync), prefer it
39 const auto found = std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_MAILBOX_KHR); 40 const auto found_mailbox = std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_MAILBOX_KHR);
40 return found != modes.end() ? *found : VK_PRESENT_MODE_FIFO_KHR; 41 if (found_mailbox != modes.end()) {
42 return VK_PRESENT_MODE_MAILBOX_KHR;
43 }
44 if (Settings::values.disable_fps_limit.GetValue()) {
45 // FIFO present mode locks the framerate to the monitor's refresh rate,
46 // Find an alternative to surpass this limitation if FPS is unlocked.
47 const auto found_imm = std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_IMMEDIATE_KHR);
48 if (found_imm != modes.end()) {
49 return VK_PRESENT_MODE_IMMEDIATE_KHR;
50 }
51 }
52 return VK_PRESENT_MODE_FIFO_KHR;
41} 53}
42 54
43VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, u32 width, u32 height) { 55VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, u32 width, u32 height) {
@@ -143,7 +155,7 @@ void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities,
143 const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)}; 155 const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)};
144 156
145 const VkSurfaceFormatKHR surface_format{ChooseSwapSurfaceFormat(formats)}; 157 const VkSurfaceFormatKHR surface_format{ChooseSwapSurfaceFormat(formats)};
146 const VkPresentModeKHR present_mode{ChooseSwapPresentMode(present_modes)}; 158 present_mode = ChooseSwapPresentMode(present_modes);
147 159
148 u32 requested_image_count{capabilities.minImageCount + 1}; 160 u32 requested_image_count{capabilities.minImageCount + 1};
149 if (capabilities.maxImageCount > 0 && requested_image_count > capabilities.maxImageCount) { 161 if (capabilities.maxImageCount > 0 && requested_image_count > capabilities.maxImageCount) {
@@ -196,6 +208,7 @@ void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities,
196 208
197 extent = swapchain_ci.imageExtent; 209 extent = swapchain_ci.imageExtent;
198 current_srgb = srgb; 210 current_srgb = srgb;
211 current_fps_unlocked = Settings::values.disable_fps_limit.GetValue();
199 212
200 images = swapchain.GetImages(); 213 images = swapchain.GetImages();
201 image_count = static_cast<u32>(images.size()); 214 image_count = static_cast<u32>(images.size());
@@ -248,4 +261,14 @@ void VKSwapchain::Destroy() {
248 swapchain.reset(); 261 swapchain.reset();
249} 262}
250 263
264bool VKSwapchain::HasFpsUnlockChanged() const {
265 return current_fps_unlocked != Settings::values.disable_fps_limit.GetValue();
266}
267
268bool VKSwapchain::NeedsPresentModeUpdate() const {
269 // Mailbox present mode is the ideal for all scenarios. If it is not available,
270 // A different present mode is needed to support unlocked FPS above the monitor's refresh rate.
271 return present_mode != VK_PRESENT_MODE_MAILBOX_KHR && HasFpsUnlockChanged();
272}
273
251} // namespace Vulkan 274} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h
index 5bce41e21..61a6d959e 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.h
+++ b/src/video_core/renderer_vulkan/vk_swapchain.h
@@ -33,6 +33,11 @@ public:
33 /// Presents the rendered image to the swapchain. 33 /// Presents the rendered image to the swapchain.
34 void Present(VkSemaphore render_semaphore); 34 void Present(VkSemaphore render_semaphore);
35 35
36 /// Returns true when the swapchain needs to be recreated.
37 bool NeedsRecreation(bool is_srgb) const {
38 return HasColorSpaceChanged(is_srgb) || IsSubOptimal() || NeedsPresentModeUpdate();
39 }
40
36 /// Returns true when the color space has changed. 41 /// Returns true when the color space has changed.
37 bool HasColorSpaceChanged(bool is_srgb) const { 42 bool HasColorSpaceChanged(bool is_srgb) const {
38 return current_srgb != is_srgb; 43 return current_srgb != is_srgb;
@@ -84,6 +89,10 @@ private:
84 89
85 void Destroy(); 90 void Destroy();
86 91
92 bool HasFpsUnlockChanged() const;
93
94 bool NeedsPresentModeUpdate() const;
95
87 const VkSurfaceKHR surface; 96 const VkSurfaceKHR surface;
88 const Device& device; 97 const Device& device;
89 VKScheduler& scheduler; 98 VKScheduler& scheduler;
@@ -102,8 +111,10 @@ private:
102 111
103 VkFormat image_view_format{}; 112 VkFormat image_view_format{};
104 VkExtent2D extent{}; 113 VkExtent2D extent{};
114 VkPresentModeKHR present_mode{};
105 115
106 bool current_srgb{}; 116 bool current_srgb{};
117 bool current_fps_unlocked{};
107 bool is_outdated{}; 118 bool is_outdated{};
108 bool is_suboptimal{}; 119 bool is_suboptimal{};
109}; 120};