aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs71
-rw-r--r--Ryujinx.Graphics.Vulkan/PipelineBase.cs7
-rw-r--r--Ryujinx.Graphics.Vulkan/SyncManager.cs14
-rw-r--r--Ryujinx.Graphics.Vulkan/VulkanRenderer.cs14
4 files changed, 96 insertions, 10 deletions
diff --git a/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs b/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs
index 953316a6..4e2a9d6b 100644
--- a/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs
+++ b/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs
@@ -1,4 +1,5 @@
-using System;
+using Ryujinx.Common.Logging;
+using System;
using System.Diagnostics;
using System.Linq;
@@ -7,12 +8,26 @@ namespace Ryujinx.Graphics.Vulkan
internal class AutoFlushCounter
{
// How often to flush on framebuffer change.
- private readonly static long FramebufferFlushTimer = Stopwatch.Frequency / 1000;
+ private readonly static long FramebufferFlushTimer = Stopwatch.Frequency / 1000; // (1ms)
+
+ // How often to flush on draw when fast flush mode is enabled.
+ private readonly static long DrawFlushTimer = Stopwatch.Frequency / 666; // (1.5ms)
+
+ // Average wait time that triggers fast flush mode to be entered.
+ private readonly static long FastFlushEnterThreshold = Stopwatch.Frequency / 666; // (1.5ms)
+
+ // Average wait time that triggers fast flush mode to be exited.
+ private readonly static long FastFlushExitThreshold = Stopwatch.Frequency / 10000; // (0.1ms)
+
+ // Number of frames to average waiting times over.
+ private const int SyncWaitAverageCount = 20;
private const int MinDrawCountForFlush = 10;
private const int MinConsecutiveQueryForFlush = 10;
private const int InitialQueryCountForFlush = 32;
+ private readonly VulkanRenderer _gd;
+
private long _lastFlush;
private ulong _lastDrawCount;
private bool _hasPendingQuery;
@@ -23,6 +38,16 @@ namespace Ryujinx.Graphics.Vulkan
private int _queryCountHistoryIndex;
private int _remainingQueries;
+ private long[] _syncWaitHistory = new long[SyncWaitAverageCount];
+ private int _syncWaitHistoryIndex;
+
+ private bool _fastFlushMode;
+
+ public AutoFlushCounter(VulkanRenderer gd)
+ {
+ _gd = gd;
+ }
+
public void RegisterFlush(ulong drawCount)
{
_lastFlush = Stopwatch.GetTimestamp();
@@ -69,6 +94,32 @@ namespace Ryujinx.Graphics.Vulkan
return _hasPendingQuery;
}
+ public bool ShouldFlushDraw(ulong drawCount)
+ {
+ if (_fastFlushMode)
+ {
+ long draws = (long)(drawCount - _lastDrawCount);
+
+ if (draws < MinDrawCountForFlush)
+ {
+ if (draws == 0)
+ {
+ _lastFlush = Stopwatch.GetTimestamp();
+ }
+
+ return false;
+ }
+
+ long flushTimeout = DrawFlushTimer;
+
+ long now = Stopwatch.GetTimestamp();
+
+ return now > _lastFlush + flushTimeout;
+ }
+
+ return false;
+ }
+
public bool ShouldFlushAttachmentChange(ulong drawCount)
{
_queryCount = 0;
@@ -102,11 +153,27 @@ namespace Ryujinx.Graphics.Vulkan
public void Present()
{
+ // Query flush prediction.
+
_queryCountHistoryIndex = (_queryCountHistoryIndex + 1) % 3;
_remainingQueries = _queryCountHistory.Max() + 10;
_queryCountHistory[_queryCountHistoryIndex] = 0;
+
+ // Fast flush mode toggle.
+
+ _syncWaitHistory[_syncWaitHistoryIndex] = _gd.SyncManager.GetAndResetWaitTicks();
+
+ _syncWaitHistoryIndex = (_syncWaitHistoryIndex + 1) % SyncWaitAverageCount;
+
+ long averageWait = (long)_syncWaitHistory.Average();
+
+ if (_fastFlushMode ? averageWait < FastFlushExitThreshold : averageWait > FastFlushEnterThreshold)
+ {
+ _fastFlushMode = !_fastFlushMode;
+ Logger.Debug?.PrintMsg(LogClass.Gpu, $"Switched fast flush mode: ({_fastFlushMode})");
+ }
}
}
}
diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs
index 2bec5293..c54d7980 100644
--- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs
+++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs
@@ -86,7 +86,7 @@ namespace Ryujinx.Graphics.Vulkan
Gd = gd;
Device = device;
- AutoFlush = new AutoFlushCounter();
+ AutoFlush = new AutoFlushCounter(gd);
var pipelineCacheCreateInfo = new PipelineCacheCreateInfo()
{
@@ -1562,6 +1562,11 @@ namespace Ryujinx.Graphics.Vulkan
private void RecreatePipelineIfNeeded(PipelineBindPoint pbp)
{
+ if (AutoFlush.ShouldFlushDraw(DrawCount))
+ {
+ Gd.FlushAllCommands();
+ }
+
DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer);
// Commit changes to the support buffer before drawing.
diff --git a/Ryujinx.Graphics.Vulkan/SyncManager.cs b/Ryujinx.Graphics.Vulkan/SyncManager.cs
index c046dc3c..432d224f 100644
--- a/Ryujinx.Graphics.Vulkan/SyncManager.cs
+++ b/Ryujinx.Graphics.Vulkan/SyncManager.cs
@@ -1,6 +1,7 @@
using Ryujinx.Common.Logging;
using Silk.NET.Vulkan;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
namespace Ryujinx.Graphics.Vulkan
@@ -26,6 +27,7 @@ namespace Ryujinx.Graphics.Vulkan
private readonly Device _device;
private List<SyncHandle> _handles;
private ulong FlushId;
+ private long WaitTicks;
public SyncManager(VulkanRenderer gd, Device device)
{
@@ -130,6 +132,8 @@ namespace Ryujinx.Graphics.Vulkan
return;
}
+ long beforeTicks = Stopwatch.GetTimestamp();
+
if (result.NeedsFlush(FlushId))
{
_gd.InterruptAction(() =>
@@ -142,12 +146,14 @@ namespace Ryujinx.Graphics.Vulkan
}
bool signaled = result.Signalled || result.Waitable.WaitForFences(_gd.Api, _device, 1000000000);
+
if (!signaled)
{
Logger.Error?.PrintMsg(LogClass.Gpu, $"VK Sync Object {result.ID} failed to signal within 1000ms. Continuing...");
}
else
{
+ WaitTicks += Stopwatch.GetTimestamp() - beforeTicks;
result.Signalled = true;
}
}
@@ -188,5 +194,13 @@ namespace Ryujinx.Graphics.Vulkan
}
}
}
+
+ public long GetAndResetWaitTicks()
+ {
+ long result = WaitTicks;
+ WaitTicks = 0;
+
+ return result;
+ }
}
}
diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
index 193cdce3..92b453fb 100644
--- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
+++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
@@ -49,6 +49,7 @@ namespace Ryujinx.Graphics.Vulkan
internal PipelineLayoutCache PipelineLayoutCache { get; private set; }
internal BackgroundResources BackgroundResources { get; private set; }
internal Action<Action> InterruptAction { get; private set; }
+ internal SyncManager SyncManager { get; private set; }
internal BufferManager BufferManager { get; private set; }
@@ -58,7 +59,6 @@ namespace Ryujinx.Graphics.Vulkan
private VulkanDebugMessenger _debugMessenger;
private Counters _counters;
- private SyncManager _syncManager;
private PipelineFull _pipeline;
@@ -327,7 +327,7 @@ namespace Ryujinx.Graphics.Vulkan
BufferManager = new BufferManager(this, _device);
- _syncManager = new SyncManager(this, _device);
+ SyncManager = new SyncManager(this, _device);
_pipeline = new PipelineFull(this, _device);
_pipeline.Initialize();
@@ -436,7 +436,7 @@ namespace Ryujinx.Graphics.Vulkan
internal void RegisterFlush()
{
- _syncManager.RegisterFlush();
+ SyncManager.RegisterFlush();
}
public PinnedSpan<byte> GetBufferData(BufferHandle buffer, int offset, int size)
@@ -696,7 +696,7 @@ namespace Ryujinx.Graphics.Vulkan
public void PreFrame()
{
- _syncManager.Cleanup();
+ SyncManager.Cleanup();
}
public ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, bool hostReserved)
@@ -736,7 +736,7 @@ namespace Ryujinx.Graphics.Vulkan
public void CreateSync(ulong id, bool strict)
{
- _syncManager.Create(id, strict);
+ SyncManager.Create(id, strict);
}
public IProgram LoadProgramBinary(byte[] programBinary, bool isFragment, ShaderInfo info)
@@ -746,12 +746,12 @@ namespace Ryujinx.Graphics.Vulkan
public void WaitSync(ulong id)
{
- _syncManager.Wait(id);
+ SyncManager.Wait(id);
}
public ulong GetCurrentSync()
{
- return _syncManager.GetCurrent();
+ return SyncManager.GetCurrent();
}
public void SetInterruptAction(Action<Action> interruptAction)