aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/HOS
diff options
context:
space:
mode:
authorriperiperi <rhy3756547@hotmail.com>2020-12-17 18:39:52 +0000
committerGitHub <noreply@github.com>2020-12-17 19:39:52 +0100
commit10aa11ce13291cf2ea2aeb751838c65c45fdc0ba (patch)
tree3ace1ad77af0fdbfc37ffdee86c24afb5fec97c8 /Ryujinx.HLE/HOS
parenteae39f80e7ca9ab220e9884c2457c934c2488e00 (diff)
Interrupt GPU command processing when a frame's fence is reached. (#1741)
* Interrupt GPU command processing when a frame's fence is reached. * Accumulate times rather than %s * Accurate timer for vsync Spin wait for the last .667ms of a frame. Avoids issues caused by signalling 16ms vsync. (periodic stutters in smo) * Use event wait for better timing. * Fix lazy wait Windows doesn't seem to want to do 1ms consistently, so force a spin if we're less than 2ms. * A bit more efficiency on frame waits. Should now wait the remainder 0.6667 instead of 1.6667 sometimes (odd waits above 1ms are reliable, unlike 1ms waits) * Better swap interval 0 solution 737 fps without breaking a sweat. Downside: Vsync can no longer be disabled on games that use the event heavily (link's awakening - which is ok since it breaks anyways) * Fix comment. * Address Comments.
Diffstat (limited to 'Ryujinx.HLE/HOS')
-rw-r--r--Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs7
-rw-r--r--Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs2
-rw-r--r--Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs69
-rw-r--r--Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/AndroidFence.cs7
4 files changed, 78 insertions, 7 deletions
diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs
index 1043dac9..b8fc71f3 100644
--- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs
+++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs
@@ -46,6 +46,8 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
public const int BufferHistoryArraySize = 8;
+ public event Action BufferQueued;
+
public BufferQueueCore(Switch device, long pid)
{
Slots = new BufferSlotArray();
@@ -197,6 +199,11 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
WaitForLock();
}
+ public void SignalQueueEvent()
+ {
+ BufferQueued?.Invoke();
+ }
+
private void WaitForLock()
{
if (Active)
diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs
index 03df04ad..d4227f01 100644
--- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs
+++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs
@@ -486,6 +486,8 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
Monitor.PulseAll(_callbackLock);
}
+ Core.SignalQueueEvent();
+
return Status.Success;
}
diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs
index bdffb499..1fef6860 100644
--- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs
+++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs
@@ -25,8 +25,12 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
private Stopwatch _chrono;
+ private ManualResetEvent _event = new ManualResetEvent(false);
+ private AutoResetEvent _nextFrameEvent = new AutoResetEvent(true);
private long _ticks;
private long _ticksPerFrame;
+ private long _spinTicks;
+ private long _1msTicks;
private int _swapInterval;
@@ -61,8 +65,11 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
};
_chrono = new Stopwatch();
+ _chrono.Start();
_ticks = 0;
+ _spinTicks = Stopwatch.Frequency / 500;
+ _1msTicks = Stopwatch.Frequency / 1000;
UpdateSwapInterval(1);
@@ -76,6 +83,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
// If the swap interval is 0, Game VSync is disabled.
if (_swapInterval == 0)
{
+ _nextFrameEvent.Set();
_ticksPerFrame = 1;
}
else
@@ -129,6 +137,11 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
BufferQueueCore core = BufferQueue.CreateBufferQueue(_device, pid, out BufferQueueProducer producer, out BufferQueueConsumer consumer);
+ core.BufferQueued += () =>
+ {
+ _nextFrameEvent.Set();
+ };
+
_layers.Add(layerId, new Layer
{
ProducerBinderId = HOSBinderDriverServer.RegisterBinderObject(producer),
@@ -189,23 +202,59 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
{
_isRunning = true;
+ long lastTicks = _chrono.ElapsedTicks;
+
while (_isRunning)
{
- _ticks += _chrono.ElapsedTicks;
+ long ticks = _chrono.ElapsedTicks;
- _chrono.Restart();
-
- if (_ticks >= _ticksPerFrame)
+ if (_swapInterval == 0)
{
Compose();
_device.System?.SignalVsync();
- _ticks = Math.Min(_ticks - _ticksPerFrame, _ticksPerFrame);
+ _nextFrameEvent.WaitOne(17);
+ lastTicks = ticks;
}
+ else
+ {
+ _ticks += ticks - lastTicks;
+ lastTicks = ticks;
+
+ if (_ticks >= _ticksPerFrame)
+ {
+ Compose();
+
+ _device.System?.SignalVsync();
+
+ // Apply a maximum bound of 3 frames to the tick remainder, in case some event causes Ryujinx to pause for a long time or messes with the timer.
+ _ticks = Math.Min(_ticks - _ticksPerFrame, _ticksPerFrame * 3);
+ }
- // Sleep the minimal amount of time to avoid being too expensive.
- Thread.Sleep(1);
+ // Sleep if possible. If the time til the next frame is too low, spin wait instead.
+ long diff = _ticksPerFrame - (_ticks + _chrono.ElapsedTicks - ticks);
+ if (diff > 0)
+ {
+ 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);
+ }
+ else
+ {
+ _event.WaitOne((int)(diff / _1msTicks));
+ }
+ }
+ }
}
}
@@ -299,6 +348,12 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
Item = item,
};
+ item.Fence.RegisterCallback(_device.Gpu, () =>
+ {
+ _device.Gpu.Window.SignalFrameReady();
+ _device.Gpu.GPFifo.Interrupt();
+ });
+
_device.Gpu.Window.EnqueueFrameThreadSafe(
frameBufferAddress,
frameBufferWidth,
diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/AndroidFence.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/AndroidFence.cs
index 7c5c9ba1..62c53f2d 100644
--- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/AndroidFence.cs
+++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/AndroidFence.cs
@@ -66,6 +66,13 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
return false;
}
+ public void RegisterCallback(GpuContext gpuContext, Action callback)
+ {
+ ref NvFence fence = ref NvFences[FenceCount - 1];
+
+ gpuContext.Synchronization.RegisterCallbackOnSyncpoint(fence.Id, fence.Value, callback);
+ }
+
public uint GetFlattenedSize()
{
return (uint)Unsafe.SizeOf<AndroidFence>();