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:
riperiperi 2023-01-24 16:32:56 +00:00 committed by GitHub
parent a1a4771ac1
commit e7cf4e6eaf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 97 additions and 33 deletions

View file

@ -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;
}
} }
} }

View file

@ -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;

View file

@ -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);

View file

@ -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)

View file

@ -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);
} }
} }

View file

@ -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()

View file

@ -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);

View file

@ -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();

View file

@ -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)