From 7dca0bc11ddcbbd38b2cc46b5fc8c0842df5f483 Mon Sep 17 00:00:00 2001
From: FernandoS27 <fsahmkow27@gmail.com>
Date: Tue, 5 Oct 2021 22:57:18 +0200
Subject: [PATCH 1/3] NVHost_Ctrl: Force wait if the gpu falls behind too long.

---
 src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp | 11 +++++++++++
 src/core/hle/service/nvdrv/nvdrv.h                 |  2 ++
 2 files changed, 13 insertions(+)

diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 8b4867ca73..8bbb2c06ee 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -92,6 +92,7 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector
     if (syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) {
         params.value = syncpoint_manager.GetSyncpointMin(params.syncpt_id);
         std::memcpy(output.data(), &params, sizeof(params));
+        events_interface.failed[event_id] = false;
         return NvResult::Success;
     }
 
@@ -99,6 +100,7 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector
         syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) {
         params.value = new_value;
         std::memcpy(output.data(), &params, sizeof(params));
+        events_interface.failed[event_id] = false;
         return NvResult::Success;
     }
 
@@ -117,6 +119,7 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector
         event.event->GetWritableEvent().Signal();
         params.value = current_syncpoint_value;
         std::memcpy(output.data(), &params, sizeof(params));
+        events_interface.failed[event_id] = false;
         return NvResult::Success;
     }
     const u32 target_value = current_syncpoint_value - diff;
@@ -146,6 +149,13 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector
     }
     params.value |= event_id;
     event.event->GetWritableEvent().Clear();
+    if (events_interface.failed[event_id]) {
+        lock.unlock();
+        gpu.WaitFence(params.syncpt_id, target_value);
+        std::memcpy(output.data(), &params, sizeof(params));
+        events_interface.failed[event_id] = false;
+        return NvResult::Success;
+    }
     gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value);
     std::memcpy(output.data(), &params, sizeof(params));
     return NvResult::Timeout;
@@ -201,6 +211,7 @@ NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::v
     if (events_interface.status[event_id] == EventState::Waiting) {
         events_interface.LiberateEvent(event_id);
     }
+    events_interface.failed[event_id] = true;
 
     syncpoint_manager.RefreshSyncpoint(events_interface.events[event_id].fence.id);
 
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index e2a1dde5b6..a5af5b7850 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -49,6 +49,8 @@ struct EventInterface {
     std::array<EventState, MaxNvEvents> status{};
     // Tells if an NVEvent is registered or not
     std::array<bool, MaxNvEvents> registered{};
+    // Tells the NVEvent that it has failed.
+    std::array<bool, MaxNvEvents> failed{};
     // When an NVEvent is waiting on GPU interrupt, this is the sync_point
     // associated with it.
     std::array<u32, MaxNvEvents> assigned_syncpt{};

From 198c6ad0d728447dc97e467c8fb08f0b54c8a742 Mon Sep 17 00:00:00 2001
From: FernandoS27 <fsahmkow27@gmail.com>
Date: Tue, 5 Oct 2021 23:54:33 +0200
Subject: [PATCH 2/3] Suspend temporally

---
 src/core/core.cpp                             | 27 +++++++++++++++++++
 src/core/core.h                               |  2 ++
 .../hle/service/nvdrv/devices/nvhost_ctrl.cpp |  3 ++-
 3 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/src/core/core.cpp b/src/core/core.cpp
index 3532839dfa..4abf037e29 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -140,25 +140,45 @@ struct System::Impl {
           cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {}
 
     SystemResultStatus Run() {
+        std::unique_lock<std::mutex> lk(suspend_guard);
         status = SystemResultStatus::Success;
 
         kernel.Suspend(false);
         core_timing.SyncPause(false);
         cpu_manager.Pause(false);
+        is_paused = false;
 
         return status;
     }
 
     SystemResultStatus Pause() {
+        std::unique_lock<std::mutex> lk(suspend_guard);
         status = SystemResultStatus::Success;
 
         core_timing.SyncPause(true);
         kernel.Suspend(true);
         cpu_manager.Pause(true);
+        is_paused = true;
 
         return status;
     }
 
+    void stallForGPU(bool pause) {
+        if (pause) {
+            suspend_guard.lock();
+            kernel.Suspend(pause);
+            core_timing.SyncPause(pause);
+            cpu_manager.Pause(pause);
+        } else {
+            if (!is_paused) {
+                core_timing.SyncPause(pause);
+                kernel.Suspend(pause);
+                cpu_manager.Pause(pause);
+            }
+            suspend_guard.unlock();
+        }
+    }
+
     SystemResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
         LOG_DEBUG(Core, "initialized OK");
 
@@ -367,6 +387,9 @@ struct System::Impl {
         return perf_stats->GetAndResetStats(core_timing.GetGlobalTimeUs());
     }
 
+    std::mutex suspend_guard;
+    bool is_paused{};
+
     Timing::CoreTiming core_timing;
     Kernel::KernelCore kernel;
     /// RealVfsFilesystem instance
@@ -464,6 +487,10 @@ void System::Shutdown() {
     impl->Shutdown();
 }
 
+void System::stallForGPU(bool pause) {
+    impl->stallForGPU(pause);
+}
+
 SystemResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
                                 u64 program_id, std::size_t program_index) {
     return impl->Load(*this, emu_window, filepath, program_id, program_index);
diff --git a/src/core/core.h b/src/core/core.h
index c1234ef773..8b21816cce 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -160,6 +160,8 @@ public:
     /// Shutdown the emulated system.
     void Shutdown();
 
+    void stallForGPU(bool pause);
+
     /**
      * Load an executable application.
      * @param emu_window Reference to the host-system window used for video output and keyboard
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 8bbb2c06ee..b59eae55cd 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -150,8 +150,9 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector
     params.value |= event_id;
     event.event->GetWritableEvent().Clear();
     if (events_interface.failed[event_id]) {
-        lock.unlock();
+        system.stallForGPU(true);
         gpu.WaitFence(params.syncpt_id, target_value);
+        system.stallForGPU(false);
         std::memcpy(output.data(), &params, sizeof(params));
         events_interface.failed[event_id] = false;
         return NvResult::Success;

From 53cf91d151d1e3d289917b63cf17ca254674f1ce Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Sat, 16 Oct 2021 00:20:19 +0200
Subject: [PATCH 3/3] NvHost/Core: Address Feedback.

---
 src/core/core.cpp                             | 34 +++++++++++--------
 src/core/core.h                               |  4 ++-
 .../hle/service/nvdrv/devices/nvhost_ctrl.cpp |  8 +++--
 3 files changed, 27 insertions(+), 19 deletions(-)

diff --git a/src/core/core.cpp b/src/core/core.cpp
index 4abf037e29..3042d611b6 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -163,19 +163,19 @@ struct System::Impl {
         return status;
     }
 
-    void stallForGPU(bool pause) {
-        if (pause) {
-            suspend_guard.lock();
-            kernel.Suspend(pause);
-            core_timing.SyncPause(pause);
-            cpu_manager.Pause(pause);
-        } else {
-            if (!is_paused) {
-                core_timing.SyncPause(pause);
-                kernel.Suspend(pause);
-                cpu_manager.Pause(pause);
-            }
-            suspend_guard.unlock();
+    std::unique_lock<std::mutex> StallCPU() {
+        std::unique_lock<std::mutex> lk(suspend_guard);
+        kernel.Suspend(true);
+        core_timing.SyncPause(true);
+        cpu_manager.Pause(true);
+        return lk;
+    }
+
+    void UnstallCPU() {
+        if (!is_paused) {
+            core_timing.SyncPause(false);
+            kernel.Suspend(false);
+            cpu_manager.Pause(false);
         }
     }
 
@@ -487,8 +487,12 @@ void System::Shutdown() {
     impl->Shutdown();
 }
 
-void System::stallForGPU(bool pause) {
-    impl->stallForGPU(pause);
+std::unique_lock<std::mutex> System::StallCPU() {
+    return impl->StallCPU();
+}
+
+void System::UnstallCPU() {
+    impl->UnstallCPU();
 }
 
 SystemResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
diff --git a/src/core/core.h b/src/core/core.h
index 8b21816cce..1cfe1bba6e 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -7,6 +7,7 @@
 #include <cstddef>
 #include <functional>
 #include <memory>
+#include <mutex>
 #include <string>
 #include <vector>
 
@@ -160,7 +161,8 @@ public:
     /// Shutdown the emulated system.
     void Shutdown();
 
-    void stallForGPU(bool pause);
+    std::unique_lock<std::mutex> StallCPU();
+    void UnstallCPU();
 
     /**
      * Load an executable application.
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index b59eae55cd..f9b82b5047 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -150,9 +150,11 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector
     params.value |= event_id;
     event.event->GetWritableEvent().Clear();
     if (events_interface.failed[event_id]) {
-        system.stallForGPU(true);
-        gpu.WaitFence(params.syncpt_id, target_value);
-        system.stallForGPU(false);
+        {
+            auto lk = system.StallCPU();
+            gpu.WaitFence(params.syncpt_id, target_value);
+            system.UnstallCPU();
+        }
         std::memcpy(output.data(), &params, sizeof(params));
         events_interface.failed[event_id] = false;
         return NvResult::Success;