diff options
| author | riperiperi <rhy3756547@hotmail.com> | 2023-11-30 10:39:42 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-11-30 15:39:42 -0300 |
| commit | 1be668e68a1937f2af239e2707ab914286018892 (patch) | |
| tree | d3ecb590d444ab9b392928ae1e223930313615e6 /src/Ryujinx.HLE | |
| parent | 21cd4c0c00e4a06e399c93419c8f9eff0e663bfb (diff) | |
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.
Diffstat (limited to 'src/Ryujinx.HLE')
| -rw-r--r-- | src/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs | 39 | ||||
| -rw-r--r-- | src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs | 33 |
2 files changed, 23 insertions, 49 deletions
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(); } } } diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs index d3d9dc03..712d640c 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs @@ -1,5 +1,7 @@ -using Ryujinx.Common.Configuration; +using Ryujinx.Common; +using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; +using Ryujinx.Common.PreciseSleep; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap; @@ -23,9 +25,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger private readonly Thread _composerThread; - private readonly Stopwatch _chrono; - - private readonly ManualResetEvent _event = new(false); + private readonly AutoResetEvent _event = new(false); private readonly AutoResetEvent _nextFrameEvent = new(true); private long _ticks; private long _ticksPerFrame; @@ -64,11 +64,9 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger _composerThread = new Thread(HandleComposition) { Name = "SurfaceFlinger.Composer", + Priority = ThreadPriority.AboveNormal }; - _chrono = new Stopwatch(); - _chrono.Start(); - _ticks = 0; _spinTicks = Stopwatch.Frequency / 500; _1msTicks = Stopwatch.Frequency / 1000; @@ -299,11 +297,11 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { _isRunning = true; - long lastTicks = _chrono.ElapsedTicks; + long lastTicks = PerformanceCounter.ElapsedTicks; while (_isRunning) { - long ticks = _chrono.ElapsedTicks; + long ticks = PerformanceCounter.ElapsedTicks; if (_swapInterval == 0) { @@ -336,21 +334,16 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger } // Sleep if possible. If the time til the next frame is too low, spin wait instead. - long diff = _ticksPerFrame - (_ticks + _chrono.ElapsedTicks - ticks); + long diff = _ticksPerFrame - (_ticks + PerformanceCounter.ElapsedTicks - ticks); if (diff > 0) { + PreciseSleepHelper.SleepUntilTimePoint(_event, PerformanceCounter.ElapsedTicks + diff); + + diff = _ticksPerFrame - (_ticks + PerformanceCounter.ElapsedTicks - ticks); + if (diff < _spinTicks) { - do - { - // SpinWait is a little more HT/SMT friendly than aggressively updating/checking ticks. - // The value of 5 still gives us quite a bit of precision (~0.0003ms variance at worst) while waiting a reasonable amount of time. - Thread.SpinWait(5); - - ticks = _chrono.ElapsedTicks; - _ticks += ticks - lastTicks; - lastTicks = ticks; - } while (_ticks < _ticksPerFrame); + PreciseSleepHelper.SpinWaitUntilTimePoint(PerformanceCounter.ElapsedTicks + diff); } else { |
