From 1be668e68a1937f2af239e2707ab914286018892 Mon Sep 17 00:00:00 2001
From: riperiperi <rhy3756547@hotmail.com>
Date: Thu, 30 Nov 2023 10:39:42 -0800
Subject: HLE: Add OS-specific precise sleep methods to reduce spinwaiting
 (#5948)

* feat: add nanosleep for linux and macos

* Add Windows 0.5ms sleep

- Imprecise waits for longer waits with clock alignment
- 1/4 the spin time on vsync timer

* Remove old experiment

* Fix event leak

* Tweaking for MacOS

* Linux tweaks, nanosleep vsync improvement

* Fix overbias

* Cleanup

* Fix realignment

* Add some docs and some cleanup

NanosleepPool needs more, Nanosleep has some benchmark code that needs removed.

* Rename "Microsleep" to "PreciseSleep"

Might have been confused with "microseconds", which no measurement is performed in.

* Remove nanosleep measurement

* Remove unused debug logging

* Nanosleep Pool Documentation

* More cleanup

* Whitespace

* Formatting

* Address Feedback

* Allow SleepUntilTimePoint to take EventWaitHandle

* Remove `_chrono` stopwatch in SurfaceFlinger

* Move spinwaiting logic to PreciseSleepHelper

Technically, these achieve different things, but having them here makes them easier to reuse or tune.
---
 src/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs | 39 ++++++-----------------
 1 file changed, 10 insertions(+), 29 deletions(-)

(limited to 'src/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs')

diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs
index 499bc2c6..3c5fa067 100644
--- a/src/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs
+++ b/src/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs
@@ -1,4 +1,5 @@
 using Ryujinx.Common;
+using Ryujinx.Common.PreciseSleep;
 using System;
 using System.Collections.Generic;
 using System.Threading;
@@ -23,7 +24,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
 
         private readonly KernelContext _context;
         private readonly List<WaitingObject> _waitingObjects;
-        private AutoResetEvent _waitEvent;
+        private IPreciseSleepEvent _waitEvent;
         private bool _keepRunning;
         private long _enforceWakeupFromSpinWait;
 
@@ -54,6 +55,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
                 timePoint = long.MaxValue;
             }
 
+            timePoint = _waitEvent.AdjustTimePoint(timePoint, timeout);
+
             lock (_context.CriticalSection.Lock)
             {
                 _waitingObjects.Add(new WaitingObject(schedulerObj, timePoint));
@@ -64,7 +67,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
                 }
             }
 
-            _waitEvent.Set();
+            _waitEvent.Signal();
         }
 
         public void UnscheduleFutureInvocation(IKFutureSchedulerObject schedulerObj)
@@ -83,10 +86,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
 
         private void WaitAndCheckScheduledObjects()
         {
-            SpinWait spinWait = new();
             WaitingObject next;
 
-            using (_waitEvent = new AutoResetEvent(false))
+            using (_waitEvent = PreciseSleepHelper.CreateEvent())
             {
                 while (_keepRunning)
                 {
@@ -103,30 +105,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
 
                         if (next.TimePoint > timePoint)
                         {
-                            long ms = Math.Min((next.TimePoint - timePoint) / PerformanceCounter.TicksPerMillisecond, int.MaxValue);
-
-                            if (ms > 0)
-                            {
-                                _waitEvent.WaitOne((int)ms);
-                            }
-                            else
+                            if (!_waitEvent.SleepUntil(next.TimePoint))
                             {
-                                while (Interlocked.Read(ref _enforceWakeupFromSpinWait) != 1 && PerformanceCounter.ElapsedTicks < next.TimePoint)
-                                {
-                                    // Our time is close - don't let SpinWait go off and potentially Thread.Sleep().
-                                    if (spinWait.NextSpinWillYield)
-                                    {
-                                        Thread.Yield();
-
-                                        spinWait.Reset();
-                                    }
-                                    else
-                                    {
-                                        spinWait.SpinOnce();
-                                    }
-                                }
-
-                                spinWait.Reset();
+                                PreciseSleepHelper.SpinWaitUntilTimePoint(next.TimePoint, ref _enforceWakeupFromSpinWait);
                             }
                         }
 
@@ -145,7 +126,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
                     }
                     else
                     {
-                        _waitEvent.WaitOne();
+                        _waitEvent.Sleep();
                     }
                 }
             }
@@ -212,7 +193,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
         public void Dispose()
         {
             _keepRunning = false;
-            _waitEvent?.Set();
+            _waitEvent?.Signal();
         }
     }
 }
-- 
cgit v1.2.3-70-g09d2