Vulkan: Reset queries on same command buffer (#4329)
* Reset queries on same command buffer Vulkan seems to complain when the queries are reset on another command buffer. No idea why, the spec really could be written better in this regard. This fixes complaints, and hopefully any implementations that care extensively about them. This change _guesses_ how many queries need to be reset and resets as many as possible at the same time to avoid splitting render passes. If it resets too many queries, we didn't waste too much time - if it runs out of resets it will batch reset 10 more. The number of queries reset is the maximum number of queries in the last 3 frames. This has been worked into the AutoFlushCounter so that it only resets up to 32 if it is yet to force a command buffer submission in this attachment. This is only done for samples passed queries right now, as they have by far the most resets. * Address Feedback
This commit is contained in:
parent
a1a4771ac1
commit
e7cf4e6eaf
9 changed files with 97 additions and 33 deletions
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
|
@ -16,6 +17,10 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
private bool _hasPendingQuery;
|
private bool _hasPendingQuery;
|
||||||
private int _queryCount;
|
private int _queryCount;
|
||||||
|
|
||||||
|
private int[] _queryCountHistory = new int[3];
|
||||||
|
private int _queryCountHistoryIndex;
|
||||||
|
private int _remainingQueries;
|
||||||
|
|
||||||
public void RegisterFlush(ulong drawCount)
|
public void RegisterFlush(ulong drawCount)
|
||||||
{
|
{
|
||||||
_lastFlush = Stopwatch.GetTimestamp();
|
_lastFlush = Stopwatch.GetTimestamp();
|
||||||
|
@ -27,6 +32,9 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
public bool RegisterPendingQuery()
|
public bool RegisterPendingQuery()
|
||||||
{
|
{
|
||||||
_hasPendingQuery = true;
|
_hasPendingQuery = true;
|
||||||
|
_remainingQueries--;
|
||||||
|
|
||||||
|
_queryCountHistory[_queryCountHistoryIndex]++;
|
||||||
|
|
||||||
// Interrupt render passes to flush queries, so that early results arrive sooner.
|
// Interrupt render passes to flush queries, so that early results arrive sooner.
|
||||||
if (++_queryCount == InitialQueryCountForFlush)
|
if (++_queryCount == InitialQueryCountForFlush)
|
||||||
|
@ -37,6 +45,21 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int GetRemainingQueries()
|
||||||
|
{
|
||||||
|
if (_remainingQueries <= 0)
|
||||||
|
{
|
||||||
|
_remainingQueries = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_queryCount < InitialQueryCountForFlush)
|
||||||
|
{
|
||||||
|
return Math.Min(InitialQueryCountForFlush - _queryCount, _remainingQueries);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _remainingQueries;
|
||||||
|
}
|
||||||
|
|
||||||
public bool ShouldFlushQuery()
|
public bool ShouldFlushQuery()
|
||||||
{
|
{
|
||||||
return _hasPendingQuery;
|
return _hasPendingQuery;
|
||||||
|
@ -69,5 +92,14 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
return now > _lastFlush + flushTimeout;
|
return now > _lastFlush + flushTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Present()
|
||||||
|
{
|
||||||
|
_queryCountHistoryIndex = (_queryCountHistoryIndex + 1) % 3;
|
||||||
|
|
||||||
|
_remainingQueries = _queryCountHistory.Max() + 10;
|
||||||
|
|
||||||
|
_queryCountHistory[_queryCountHistoryIndex] = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
protected readonly Device Device;
|
protected readonly Device Device;
|
||||||
public readonly PipelineCache PipelineCache;
|
public readonly PipelineCache PipelineCache;
|
||||||
|
|
||||||
protected readonly AutoFlushCounter AutoFlush;
|
public readonly AutoFlushCounter AutoFlush;
|
||||||
|
|
||||||
protected PipelineDynamicState DynamicState;
|
protected PipelineDynamicState DynamicState;
|
||||||
private PipelineState _newState;
|
private PipelineState _newState;
|
||||||
|
|
|
@ -14,7 +14,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
private CounterQueueEvent _activeConditionalRender;
|
private CounterQueueEvent _activeConditionalRender;
|
||||||
|
|
||||||
private readonly List<BufferedQuery> _pendingQueryCopies;
|
private readonly List<BufferedQuery> _pendingQueryCopies;
|
||||||
private readonly List<BufferedQuery> _pendingQueryResets;
|
|
||||||
|
|
||||||
private ulong _byteWeight;
|
private ulong _byteWeight;
|
||||||
|
|
||||||
|
@ -22,7 +21,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
_activeQueries = new List<QueryPool>();
|
_activeQueries = new List<QueryPool>();
|
||||||
_pendingQueryCopies = new();
|
_pendingQueryCopies = new();
|
||||||
_pendingQueryResets = new List<BufferedQuery>();
|
|
||||||
|
|
||||||
CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer;
|
CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer;
|
||||||
}
|
}
|
||||||
|
@ -34,16 +32,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
query.PoolCopy(Cbs);
|
query.PoolCopy(Cbs);
|
||||||
}
|
}
|
||||||
|
|
||||||
lock (_pendingQueryResets)
|
|
||||||
{
|
|
||||||
foreach (var query in _pendingQueryResets)
|
|
||||||
{
|
|
||||||
query.PoolReset(CommandBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
_pendingQueryResets.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
_pendingQueryCopies.Clear();
|
_pendingQueryCopies.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,10 +226,12 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
Gd.Api.CmdBeginQuery(CommandBuffer, queryPool, 0, Gd.Capabilities.SupportsPreciseOcclusionQueries ? QueryControlFlags.PreciseBit : 0);
|
Gd.Api.CmdBeginQuery(CommandBuffer, queryPool, 0, Gd.Capabilities.SupportsPreciseOcclusionQueries ? QueryControlFlags.PreciseBit : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Gd.ResetCounterPool();
|
||||||
|
|
||||||
Restore();
|
Restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BeginQuery(BufferedQuery query, QueryPool pool, bool needsReset)
|
public void BeginQuery(BufferedQuery query, QueryPool pool, bool needsReset, bool fromSamplePool)
|
||||||
{
|
{
|
||||||
if (needsReset)
|
if (needsReset)
|
||||||
{
|
{
|
||||||
|
@ -249,9 +239,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
Gd.Api.CmdResetQueryPool(CommandBuffer, pool, 0, 1);
|
Gd.Api.CmdResetQueryPool(CommandBuffer, pool, 0, 1);
|
||||||
|
|
||||||
lock (_pendingQueryResets)
|
if (fromSamplePool)
|
||||||
{
|
{
|
||||||
_pendingQueryResets.Remove(query); // Might be present on here.
|
// Try reset some additional queries in advance.
|
||||||
|
|
||||||
|
Gd.ResetFutureCounters(CommandBuffer, AutoFlush.GetRemainingQueries());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,14 +259,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
_activeQueries.Remove(pool);
|
_activeQueries.Remove(pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetQuery(BufferedQuery query)
|
|
||||||
{
|
|
||||||
lock (_pendingQueryResets)
|
|
||||||
{
|
|
||||||
_pendingQueryResets.Add(query);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CopyQueryResults(BufferedQuery query)
|
public void CopyQueryResults(BufferedQuery query)
|
||||||
{
|
{
|
||||||
_pendingQueryCopies.Add(query);
|
_pendingQueryCopies.Add(query);
|
||||||
|
|
|
@ -18,7 +18,6 @@ namespace Ryujinx.Graphics.Vulkan.Queries
|
||||||
private readonly PipelineFull _pipeline;
|
private readonly PipelineFull _pipeline;
|
||||||
|
|
||||||
private QueryPool _queryPool;
|
private QueryPool _queryPool;
|
||||||
private bool _isReset;
|
|
||||||
|
|
||||||
private readonly BufferHolder _buffer;
|
private readonly BufferHolder _buffer;
|
||||||
private readonly IntPtr _bufferMap;
|
private readonly IntPtr _bufferMap;
|
||||||
|
@ -27,6 +26,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries
|
||||||
private bool _isSupported;
|
private bool _isSupported;
|
||||||
|
|
||||||
private long _defaultValue;
|
private long _defaultValue;
|
||||||
|
private int? _resetSequence;
|
||||||
|
|
||||||
public unsafe BufferedQuery(VulkanRenderer gd, Device device, PipelineFull pipeline, CounterType type, bool result32Bit)
|
public unsafe BufferedQuery(VulkanRenderer gd, Device device, PipelineFull pipeline, CounterType type, bool result32Bit)
|
||||||
{
|
{
|
||||||
|
@ -92,16 +92,17 @@ namespace Ryujinx.Graphics.Vulkan.Queries
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
End(false);
|
End(false);
|
||||||
Begin();
|
Begin(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Begin()
|
public void Begin(int? resetSequence)
|
||||||
{
|
{
|
||||||
if (_isSupported)
|
if (_isSupported)
|
||||||
{
|
{
|
||||||
_pipeline.BeginQuery(this, _queryPool, !_isReset);
|
bool needsReset = resetSequence == null || _resetSequence == null || resetSequence.Value != _resetSequence.Value;
|
||||||
|
_pipeline.BeginQuery(this, _queryPool, needsReset, _type == CounterType.SamplesPassed && resetSequence != null);
|
||||||
}
|
}
|
||||||
_isReset = false;
|
_resetSequence = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void End(bool withResult)
|
public unsafe void End(bool withResult)
|
||||||
|
@ -162,13 +163,14 @@ namespace Ryujinx.Graphics.Vulkan.Queries
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PoolReset(CommandBuffer cmd)
|
public void PoolReset(CommandBuffer cmd, int resetSequence)
|
||||||
{
|
{
|
||||||
if (_isSupported)
|
if (_isSupported)
|
||||||
{
|
{
|
||||||
_api.CmdResetQueryPool(cmd, _queryPool, 0, 1);
|
_api.CmdResetQueryPool(cmd, _queryPool, 0, 1);
|
||||||
}
|
}
|
||||||
_isReset = true;
|
|
||||||
|
_resetSequence = resetSequence;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PoolCopy(CommandBufferScoped cbs)
|
public void PoolCopy(CommandBufferScoped cbs)
|
||||||
|
|
|
@ -3,6 +3,7 @@ using Silk.NET.Vulkan;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan.Queries
|
namespace Ryujinx.Graphics.Vulkan.Queries
|
||||||
{
|
{
|
||||||
|
@ -32,6 +33,8 @@ namespace Ryujinx.Graphics.Vulkan.Queries
|
||||||
|
|
||||||
private Thread _consumerThread;
|
private Thread _consumerThread;
|
||||||
|
|
||||||
|
public int ResetSequence { get; private set; }
|
||||||
|
|
||||||
internal CounterQueue(VulkanRenderer gd, Device device, PipelineFull pipeline, CounterType type)
|
internal CounterQueue(VulkanRenderer gd, Device device, PipelineFull pipeline, CounterType type)
|
||||||
{
|
{
|
||||||
_gd = gd;
|
_gd = gd;
|
||||||
|
@ -53,6 +56,24 @@ namespace Ryujinx.Graphics.Vulkan.Queries
|
||||||
_consumerThread.Start();
|
_consumerThread.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ResetCounterPool()
|
||||||
|
{
|
||||||
|
ResetSequence++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ResetFutureCounters(CommandBuffer cmd, int count)
|
||||||
|
{
|
||||||
|
// Pre-emptively reset queries to avoid render pass splitting.
|
||||||
|
lock (_queryPool)
|
||||||
|
{
|
||||||
|
count = Math.Min(count, _queryPool.Count);
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
_queryPool.ElementAt(i).PoolReset(cmd, ResetSequence);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void EventConsumer()
|
private void EventConsumer()
|
||||||
{
|
{
|
||||||
while (!Disposed)
|
while (!Disposed)
|
||||||
|
@ -106,7 +127,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
_pipeline.ResetQuery(query);
|
// The query will be reset when it dequeues.
|
||||||
_queryPool.Enqueue(query);
|
_queryPool.Enqueue(query);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries
|
||||||
|
|
||||||
DrawIndex = drawIndex;
|
DrawIndex = drawIndex;
|
||||||
|
|
||||||
_counter.Begin();
|
_counter.Begin(_queue.ResetSequence);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Auto<DisposableBuffer> GetBuffer()
|
public Auto<DisposableBuffer> GetBuffer()
|
||||||
|
|
|
@ -24,6 +24,19 @@ namespace Ryujinx.Graphics.Vulkan.Queries
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ResetCounterPool()
|
||||||
|
{
|
||||||
|
foreach (var queue in _counterQueues)
|
||||||
|
{
|
||||||
|
queue.ResetCounterPool();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ResetFutureCounters(CommandBuffer cmd, int count)
|
||||||
|
{
|
||||||
|
_counterQueues[(int)CounterType.SamplesPassed].ResetFutureCounters(cmd, count);
|
||||||
|
}
|
||||||
|
|
||||||
public CounterQueueEvent QueueReport(CounterType type, EventHandler<ulong> resultHandler, bool hostReserved)
|
public CounterQueueEvent QueueReport(CounterType type, EventHandler<ulong> resultHandler, bool hostReserved)
|
||||||
{
|
{
|
||||||
return _counterQueues[(int)type].QueueReport(resultHandler, _pipeline.DrawCount, hostReserved);
|
return _counterQueues[(int)type].QueueReport(resultHandler, _pipeline.DrawCount, hostReserved);
|
||||||
|
|
|
@ -679,6 +679,16 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
_counters.Update();
|
_counters.Update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ResetCounterPool()
|
||||||
|
{
|
||||||
|
_counters.ResetCounterPool();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ResetFutureCounters(CommandBuffer cmd, int count)
|
||||||
|
{
|
||||||
|
_counters?.ResetFutureCounters(cmd, count);
|
||||||
|
}
|
||||||
|
|
||||||
public void BackgroundContextAction(Action action, bool alwaysBackground = false)
|
public void BackgroundContextAction(Action action, bool alwaysBackground = false)
|
||||||
{
|
{
|
||||||
action();
|
action();
|
||||||
|
|
|
@ -225,6 +225,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
public unsafe override void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback)
|
public unsafe override void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback)
|
||||||
{
|
{
|
||||||
|
_gd.PipelineInternal.AutoFlush.Present();
|
||||||
|
|
||||||
uint nextImage = 0;
|
uint nextImage = 0;
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
|
|
Loading…
Reference in a new issue