diff --git a/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs b/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs new file mode 100644 index 00000000..4ba689cc --- /dev/null +++ b/Ryujinx.Graphics.Vulkan/AutoFlushCounter.cs @@ -0,0 +1,73 @@ +using System; +using System.Diagnostics; + +namespace Ryujinx.Graphics.Vulkan +{ + internal class AutoFlushCounter + { + // How often to flush on framebuffer change. + private readonly static long FramebufferFlushTimer = Stopwatch.Frequency / 1000; + + private const int MinDrawCountForFlush = 10; + private const int InitialQueryCountForFlush = 32; + + private long _lastFlush; + private ulong _lastDrawCount; + private bool _hasPendingQuery; + private int _queryCount; + + public void RegisterFlush(ulong drawCount) + { + _lastFlush = Stopwatch.GetTimestamp(); + _lastDrawCount = drawCount; + + _hasPendingQuery = false; + } + + public bool RegisterPendingQuery() + { + _hasPendingQuery = true; + + // Interrupt render passes to flush queries, so that early results arrive sooner. + if (++_queryCount == InitialQueryCountForFlush) + { + return true; + } + + return false; + } + + public bool ShouldFlushQuery() + { + return _hasPendingQuery; + } + + public bool ShouldFlush(ulong drawCount) + { + _queryCount = 0; + + if (_hasPendingQuery) + { + return true; + } + + long draws = (long)(drawCount - _lastDrawCount); + + if (draws < MinDrawCountForFlush) + { + if (draws == 0) + { + _lastFlush = Stopwatch.GetTimestamp(); + } + + return false; + } + + long flushTimeout = FramebufferFlushTimer; + + long now = Stopwatch.GetTimestamp(); + + return now > _lastFlush + flushTimeout; + } + } +} diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 769d4594..5c666b09 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -19,6 +19,8 @@ namespace Ryujinx.Graphics.Vulkan protected readonly Device Device; public readonly PipelineCache PipelineCache; + protected readonly AutoFlushCounter AutoFlush; + private PipelineDynamicState _dynamicState; private PipelineState _newState; private bool _stateDirty; @@ -70,6 +72,8 @@ namespace Ryujinx.Graphics.Vulkan Gd = gd; Device = device; + AutoFlush = new AutoFlushCounter(); + var pipelineCacheCreateInfo = new PipelineCacheCreateInfo() { SType = StructureType.PipelineCacheCreateInfo diff --git a/Ryujinx.Graphics.Vulkan/PipelineFull.cs b/Ryujinx.Graphics.Vulkan/PipelineFull.cs index ca3a33ef..c8692993 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineFull.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineFull.cs @@ -10,8 +10,6 @@ namespace Ryujinx.Graphics.Vulkan { private const ulong MinByteWeightForFlush = 256 * 1024 * 1024; // MB - private bool _hasPendingQuery; - private readonly List _activeQueries; private CounterQueueEvent _activeConditionalRender; @@ -158,9 +156,8 @@ namespace Ryujinx.Graphics.Vulkan private void FlushPendingQuery() { - if (_hasPendingQuery) + if (AutoFlush.ShouldFlushQuery()) { - _hasPendingQuery = false; FlushCommandsImpl(); } } @@ -211,6 +208,7 @@ namespace Ryujinx.Graphics.Vulkan public void FlushCommandsImpl() { + AutoFlush.RegisterFlush(DrawCount); EndRenderPass(); foreach (var queryPool in _activeQueries) @@ -277,12 +275,18 @@ namespace Ryujinx.Graphics.Vulkan { _pendingQueryCopies.Add(query); - _hasPendingQuery = true; + if (AutoFlush.RegisterPendingQuery()) + { + FlushCommandsImpl(); + } } protected override void SignalAttachmentChange() { - FlushPendingQuery(); + if (AutoFlush.ShouldFlush(DrawCount)) + { + FlushCommandsImpl(); + } } protected override void SignalRenderPassEnd()