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.Common/PreciseSleep/NanosleepEvent.cs | |
| 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.Common/PreciseSleep/NanosleepEvent.cs')
| -rw-r--r-- | src/Ryujinx.Common/PreciseSleep/NanosleepEvent.cs | 84 |
1 files changed, 84 insertions, 0 deletions
diff --git a/src/Ryujinx.Common/PreciseSleep/NanosleepEvent.cs b/src/Ryujinx.Common/PreciseSleep/NanosleepEvent.cs new file mode 100644 index 00000000..f54fb09c --- /dev/null +++ b/src/Ryujinx.Common/PreciseSleep/NanosleepEvent.cs @@ -0,0 +1,84 @@ +using System; +using System.Runtime.Versioning; +using System.Threading; + +namespace Ryujinx.Common.PreciseSleep +{ + /// <summary> + /// A precise sleep event for linux and macos that uses nanosleep for more precise timeouts. + /// </summary> + [SupportedOSPlatform("macos")] + [SupportedOSPlatform("linux")] + [SupportedOSPlatform("android")] + [SupportedOSPlatform("ios")] + internal class NanosleepEvent : IPreciseSleepEvent + { + private readonly AutoResetEvent _waitEvent = new(false); + private readonly NanosleepPool _pool; + + public NanosleepEvent() + { + _pool = new NanosleepPool(_waitEvent); + } + + public long AdjustTimePoint(long timePoint, long timeoutNs) + { + // No adjustment + return timePoint; + } + + public bool SleepUntil(long timePoint) + { + long now = PerformanceCounter.ElapsedTicks; + long delta = (timePoint - now); + long ms = Math.Min(delta / PerformanceCounter.TicksPerMillisecond, int.MaxValue); + long ns = (delta * 1_000_000) / PerformanceCounter.TicksPerMillisecond; + + if (ms > 0) + { + _waitEvent.WaitOne((int)ms); + + return true; + } + else if (ns - Nanosleep.Bias > 0) + { + // Don't bother starting a sleep if there's already a signal active. + if (_waitEvent.WaitOne(0)) + { + return true; + } + + // The 1ms wait will be interrupted by the nanosleep timeout if it completes. + if (!_pool.SleepAndSignal(ns, timePoint)) + { + // Too many threads on the pool. + return false; + } + _waitEvent.WaitOne(1); + _pool.IgnoreSignal(); + + return true; + } + + return false; + } + + public void Sleep() + { + _waitEvent.WaitOne(); + } + + public void Signal() + { + _waitEvent.Set(); + } + + public void Dispose() + { + GC.SuppressFinalize(this); + + _pool.Dispose(); + _waitEvent.Dispose(); + } + } +} |
