From 117e32a6fffc30cdb895aa98483af7df353a8dd1 Mon Sep 17 00:00:00 2001
From: mpnico <mpnico@gmail.com>
Date: Sat, 11 Sep 2021 22:08:25 +0200
Subject: Implement a "Pause Emulation" option & hotkey (#2428)

* Add a "Pause Emulation" option and hotkey

Closes Ryujinx#1604

* Refactoring how pause is handled

* Applied suggested changes from review

* Applied suggested fixes

* Pass correct suspend type to threads for suspend/resume

* Fix NRE after stoping emulation

* Removing SimulateWakeUpMessage call after resuming emulation

* Skip suspending non game process

* Pause the tickCounter in the ExecutionContext

* Refactoring tickCounter pause/resume as suggested

* Fix Config migration to add pause hotkey

* Fixed pausing only application threads

* Fix exiting emulator while paused

* Avoid pause/resume while already paused/resumed

* Cleanup unused code

* Avoid restarting audio if stopping emulation while in pause.

* Added suggested changes

* Fix ConfigurationState
---
 Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs | 67 +++++++++++++++++------------
 1 file changed, 40 insertions(+), 27 deletions(-)

(limited to 'Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs')

diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs
index 7224cca1..396a79ba 100644
--- a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs
@@ -471,6 +471,29 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
             KernelContext.CriticalSection.Leave();
         }
 
+        public void Suspend(ThreadSchedState type)
+        {
+            _forcePauseFlags |= type;
+
+            CombineForcePauseFlags();
+        }
+
+        public void Resume(ThreadSchedState type)
+        {
+            ThreadSchedState oldForcePauseFlags = _forcePauseFlags;
+
+            _forcePauseFlags &= ~type;
+
+            if ((oldForcePauseFlags & ~type) == ThreadSchedState.None)
+            {
+                ThreadSchedState oldSchedFlags = SchedFlags;
+
+                SchedFlags &= ThreadSchedState.LowMask;
+
+                AdjustScheduling(oldSchedFlags);
+            }
+        }
+
         public KernelResult SetActivity(bool pause)
         {
             KernelResult result = KernelResult.Success;
@@ -495,9 +518,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
                     // Pause, the force pause flag should be clear (thread is NOT paused).
                     if ((_forcePauseFlags & ThreadSchedState.ThreadPauseFlag) == 0)
                     {
-                        _forcePauseFlags |= ThreadSchedState.ThreadPauseFlag;
-
-                        CombineForcePauseFlags();
+                        Suspend(ThreadSchedState.ThreadPauseFlag);
                     }
                     else
                     {
@@ -509,18 +530,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
                     // Unpause, the force pause flag should be set (thread is paused).
                     if ((_forcePauseFlags & ThreadSchedState.ThreadPauseFlag) != 0)
                     {
-                        ThreadSchedState oldForcePauseFlags = _forcePauseFlags;
-
-                        _forcePauseFlags &= ~ThreadSchedState.ThreadPauseFlag;
-
-                        if ((oldForcePauseFlags & ~ThreadSchedState.ThreadPauseFlag) == ThreadSchedState.None)
-                        {
-                            ThreadSchedState oldSchedFlags = SchedFlags;
-
-                            SchedFlags &= ThreadSchedState.LowMask;
-
-                            AdjustScheduling(oldSchedFlags);
-                        }
+                        Resume(ThreadSchedState.ThreadPauseFlag);
                     }
                     else
                     {
@@ -832,19 +842,22 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
 
             if (!IsSchedulable)
             {
-                // Ensure our thread is running and we have an event.
-                StartHostThread();
-
-                // If the thread is not schedulable, we want to just run or pause
-                // it directly as we don't care about priority or the core it is
-                // running on in this case.
-                if (SchedFlags == ThreadSchedState.Running)
-                {
-                    _schedulerWaitEvent.Set();
-                }
-                else
+                if (!_forcedUnschedulable)
                 {
-                    _schedulerWaitEvent.Reset();
+                    // Ensure our thread is running and we have an event.
+                    StartHostThread();
+
+                    // If the thread is not schedulable, we want to just run or pause
+                    // it directly as we don't care about priority or the core it is
+                    // running on in this case.
+                    if (SchedFlags == ThreadSchedState.Running)
+                    {
+                        _schedulerWaitEvent.Set();
+                    }
+                    else
+                    {
+                        _schedulerWaitEvent.Reset();
+                    }
                 }
 
                 return;
-- 
cgit v1.2.3-70-g09d2