From ba608d2b578b4f11784e6bac118d10790b1e7c2e Mon Sep 17 00:00:00 2001 From: MaranBr Date: Mon, 22 Jun 2026 17:05:18 +0200 Subject: [PATCH] [video_core] Add in-flight flush throttling to prevent CPU/GPU desync (#4085) Prevent the CPU from getting too far ahead of the GPU by limiting pending flushes. This fixes graphical issues in games that rely on better CPU and GPU synchronization in the Scheduler, such as the flickering bug inside shrines in The Legend of Zelda: Tears of the Kingdom. This is enabled when the GPU is in Accurate mode. Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4085 Reviewed-by: crueter Reviewed-by: Shinmegumi --- src/video_core/renderer_vulkan/vk_scheduler.cpp | 14 +++++++++++++- src/video_core/renderer_vulkan/vk_scheduler.h | 2 ++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 656e3dd3cd..ec8c852284 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -27,6 +27,7 @@ namespace Vulkan { +constexpr u64 MAX_PENDING_FLUSHES = 5; void Scheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf) { @@ -115,7 +116,18 @@ Scheduler::Scheduler(const Device& device_, StateTracker& state_tracker_) Scheduler::~Scheduler() = default; u64 Scheduler::Flush(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { - // When flushing, we only send data to the worker thread; no waiting is necessary. + // Prevent the CPU from getting too far ahead of the GPU by limiting pending flushes. + const bool should_throttle = Settings::IsGPULevelHigh(); + if (should_throttle) { + const u64 current_tick = master_semaphore->CurrentTick(); + const u64 gap = current_tick > last_submitted_tick ? current_tick - last_submitted_tick : 0; + const u64 step = (std::min)(MAX_PENDING_FLUSHES, gap); + const u64 new_tick = last_submitted_tick + step; + if (new_tick < current_tick) { + last_submitted_tick = new_tick; + master_semaphore->Wait(last_submitted_tick); + } + } const u64 signal_value = SubmitExecution(signal_semaphore, wait_semaphore); AllocateNewContext(); return signal_value; diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index fc11be7c0e..6f471f6247 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -296,6 +296,8 @@ private: double last_target_fps{}; u64 max_frame_count{}; u64 frame_counter{}; + + u64 last_submitted_tick = 0; }; } // namespace Vulkan