From c19c8ac92c4554dd1189ebb8d03a7adb79c9980a Mon Sep 17 00:00:00 2001
From: Liam <byteslice@airmail.cc>
Date: Sat, 7 Jan 2023 11:56:31 -0500
Subject: [PATCH] renderer_vulkan: pause turbo submissions on inactive queue

---
 .../renderer_vulkan/renderer_vulkan.cpp         |  2 ++
 src/video_core/renderer_vulkan/vk_scheduler.cpp |  5 +++++
 src/video_core/renderer_vulkan/vk_scheduler.h   |  7 +++++++
 .../renderer_vulkan/vk_turbo_mode.cpp           | 17 +++++++++++++++++
 src/video_core/renderer_vulkan/vk_turbo_mode.h  |  9 +++++++++
 5 files changed, 40 insertions(+)

diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 52855120c..8233c07d3 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -112,6 +112,7 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
                  state_tracker, scheduler) {
     if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) {
         turbo_mode.emplace(instance, dld);
+        scheduler.RegisterOnSubmit([this] { turbo_mode->QueueSubmitted(); });
     }
     Report();
 } catch (const vk::Exception& exception) {
@@ -120,6 +121,7 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
 }
 
 RendererVulkan::~RendererVulkan() {
+    scheduler.RegisterOnSubmit([] {});
     void(device.GetLogical().WaitIdle());
 }
 
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index c2e53a5d5..e03685af1 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -213,6 +213,11 @@ void Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_s
             .signalSemaphoreCount = num_signal_semaphores,
             .pSignalSemaphores = signal_semaphores.data(),
         };
+
+        if (on_submit) {
+            on_submit();
+        }
+
         switch (const VkResult result = device.GetGraphicsQueue().Submit(submit_info)) {
         case VK_SUCCESS:
             break;
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
index 3858c506c..bd4cb0f7e 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.h
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -5,6 +5,7 @@
 
 #include <condition_variable>
 #include <cstddef>
+#include <functional>
 #include <memory>
 #include <thread>
 #include <utility>
@@ -66,6 +67,11 @@ public:
         query_cache = &query_cache_;
     }
 
+    // Registers a callback to perform on queue submission.
+    void RegisterOnSubmit(std::function<void()>&& func) {
+        on_submit = std::move(func);
+    }
+
     /// Send work to a separate thread.
     template <typename T>
     void Record(T&& command) {
@@ -216,6 +222,7 @@ private:
     vk::CommandBuffer current_cmdbuf;
 
     std::unique_ptr<CommandChunk> chunk;
+    std::function<void()> on_submit;
 
     State state;
 
diff --git a/src/video_core/renderer_vulkan/vk_turbo_mode.cpp b/src/video_core/renderer_vulkan/vk_turbo_mode.cpp
index 852b86f84..c42594149 100644
--- a/src/video_core/renderer_vulkan/vk_turbo_mode.cpp
+++ b/src/video_core/renderer_vulkan/vk_turbo_mode.cpp
@@ -14,11 +14,21 @@ using namespace Common::Literals;
 
 TurboMode::TurboMode(const vk::Instance& instance, const vk::InstanceDispatch& dld)
     : m_device{CreateDevice(instance, dld, VK_NULL_HANDLE)}, m_allocator{m_device, false} {
+    {
+        std::scoped_lock lk{m_submission_lock};
+        m_submission_time = std::chrono::steady_clock::now();
+    }
     m_thread = std::jthread([&](auto stop_token) { Run(stop_token); });
 }
 
 TurboMode::~TurboMode() = default;
 
+void TurboMode::QueueSubmitted() {
+    std::scoped_lock lk{m_submission_lock};
+    m_submission_time = std::chrono::steady_clock::now();
+    m_submission_cv.notify_one();
+}
+
 void TurboMode::Run(std::stop_token stop_token) {
     auto& dld = m_device.GetLogical();
 
@@ -199,6 +209,13 @@ void TurboMode::Run(std::stop_token stop_token) {
 
         // Wait for completion.
         fence.Wait();
+
+        // Wait for the next graphics queue submission if necessary.
+        std::unique_lock lk{m_submission_lock};
+        Common::CondvarWait(m_submission_cv, lk, stop_token, [this] {
+            return (std::chrono::steady_clock::now() - m_submission_time) <=
+                   std::chrono::milliseconds{100};
+        });
     }
 }
 
diff --git a/src/video_core/renderer_vulkan/vk_turbo_mode.h b/src/video_core/renderer_vulkan/vk_turbo_mode.h
index 2060e2395..99b5ac50b 100644
--- a/src/video_core/renderer_vulkan/vk_turbo_mode.h
+++ b/src/video_core/renderer_vulkan/vk_turbo_mode.h
@@ -3,6 +3,9 @@
 
 #pragma once
 
+#include <chrono>
+#include <mutex>
+
 #include "common/polyfill_thread.h"
 #include "video_core/vulkan_common/vulkan_device.h"
 #include "video_core/vulkan_common/vulkan_memory_allocator.h"
@@ -15,11 +18,17 @@ public:
     explicit TurboMode(const vk::Instance& instance, const vk::InstanceDispatch& dld);
     ~TurboMode();
 
+    void QueueSubmitted();
+
 private:
     void Run(std::stop_token stop_token);
 
     Device m_device;
     MemoryAllocator m_allocator;
+    std::mutex m_submission_lock;
+    std::condition_variable_any m_submission_cv;
+    std::chrono::time_point<std::chrono::steady_clock> m_submission_time{};
+
     std::jthread m_thread;
 };