From 3f104464dec13f9ba90eaca5dafca87ee4116a60 Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Thu, 26 Sep 2019 19:08:22 -0400
Subject: [PATCH] Core: Wait for GPU to be idle before shutting down.

---
 src/core/core.cpp             | 2 ++
 src/video_core/gpu.h          | 3 +++
 src/video_core/gpu_asynch.cpp | 4 ++++
 src/video_core/gpu_asynch.h   | 1 +
 src/video_core/gpu_synch.h    | 1 +
 src/video_core/gpu_thread.cpp | 5 +++++
 src/video_core/gpu_thread.h   | 3 +++
 7 files changed, 19 insertions(+)

diff --git a/src/core/core.cpp b/src/core/core.cpp
index 75a7ffb973..8f68bdbad8 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -252,6 +252,8 @@ struct System::Impl {
         is_powered_on = false;
         exit_lock = false;
 
+        gpu_core->WaitIdle();
+
         // Shutdown emulation session
         renderer.reset();
         GDBStub::Shutdown();
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index e20b0687a8..dbca19f35b 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -177,6 +177,9 @@ public:
     /// Returns a reference to the GPU DMA pusher.
     Tegra::DmaPusher& DmaPusher();
 
+    // Waits for the GPU to finish working
+    virtual void WaitIdle() const = 0;
+
     /// Allows the CPU/NvFlinger to wait on the GPU before presenting a frame.
     void WaitFence(u32 syncpoint_id, u32 value) const;
 
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp
index f2a3a390e1..04222d0607 100644
--- a/src/video_core/gpu_asynch.cpp
+++ b/src/video_core/gpu_asynch.cpp
@@ -44,4 +44,8 @@ void GPUAsynch::TriggerCpuInterrupt(const u32 syncpoint_id, const u32 value) con
     interrupt_manager.GPUInterruptSyncpt(syncpoint_id, value);
 }
 
+void GPUAsynch::WaitIdle() const {
+    gpu_thread.WaitIdle();
+}
+
 } // namespace VideoCommon
diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h
index a12f9bac41..1241ade1d7 100644
--- a/src/video_core/gpu_asynch.h
+++ b/src/video_core/gpu_asynch.h
@@ -25,6 +25,7 @@ public:
     void FlushRegion(CacheAddr addr, u64 size) override;
     void InvalidateRegion(CacheAddr addr, u64 size) override;
     void FlushAndInvalidateRegion(CacheAddr addr, u64 size) override;
+    void WaitIdle() const override;
 
 protected:
     void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const override;
diff --git a/src/video_core/gpu_synch.h b/src/video_core/gpu_synch.h
index 5eb1c461c2..c71baee89e 100644
--- a/src/video_core/gpu_synch.h
+++ b/src/video_core/gpu_synch.h
@@ -24,6 +24,7 @@ public:
     void FlushRegion(CacheAddr addr, u64 size) override;
     void InvalidateRegion(CacheAddr addr, u64 size) override;
     void FlushAndInvalidateRegion(CacheAddr addr, u64 size) override;
+    void WaitIdle() const override {}
 
 protected:
     void TriggerCpuInterrupt([[maybe_unused]] u32 syncpoint_id,
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index d7048b6ae7..4a42634d2c 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -90,6 +90,11 @@ void ThreadManager::FlushAndInvalidateRegion(CacheAddr addr, u64 size) {
     InvalidateRegion(addr, size);
 }
 
+void ThreadManager::WaitIdle() const {
+    while (state.last_fence > state.signaled_fence.load()) {
+    }
+}
+
 u64 ThreadManager::PushCommand(CommandData&& command_data) {
     const u64 fence{++state.last_fence};
     state.queue.Push(CommandDataContainer(std::move(command_data), fence));
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
index 108f456bd1..08dc96bb37 100644
--- a/src/video_core/gpu_thread.h
+++ b/src/video_core/gpu_thread.h
@@ -116,6 +116,9 @@ public:
     /// Notify rasterizer that any caches of the specified region should be flushed and invalidated
     void FlushAndInvalidateRegion(CacheAddr addr, u64 size);
 
+    // Wait until the gpu thread is idle.
+    void WaitIdle() const;
+
 private:
     /// Pushes a command to be executed by the GPU thread
     u64 PushCommand(CommandData&& command_data);