ryujinx/Ryujinx.Graphics.Vulkan/Queries/CounterQueueEvent.cs
riperiperi e7cf4e6eaf
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
2023-01-24 13:32:56 -03:00

168 lines
3.8 KiB
C#

using Ryujinx.Graphics.GAL;
using System;
using System.Threading;
namespace Ryujinx.Graphics.Vulkan.Queries
{
class CounterQueueEvent : ICounterEvent
{
public event EventHandler<ulong> OnResult;
public CounterType Type { get; }
public bool ClearCounter { get; private set; }
public bool Disposed { get; private set; }
public bool Invalid { get; set; }
public ulong DrawIndex { get; }
private CounterQueue _queue;
private BufferedQuery _counter;
private bool _hostAccessReserved = false;
private int _refCount = 1; // Starts with a reference from the counter queue.
private object _lock = new object();
private ulong _result = ulong.MaxValue;
public CounterQueueEvent(CounterQueue queue, CounterType type, ulong drawIndex)
{
_queue = queue;
_counter = queue.GetQueryObject();
Type = type;
DrawIndex = drawIndex;
_counter.Begin(_queue.ResetSequence);
}
public Auto<DisposableBuffer> GetBuffer()
{
return _counter.GetBuffer();
}
internal void Clear(bool counterReset)
{
if (counterReset)
{
_counter.Reset();
}
ClearCounter = true;
}
internal void Complete(bool withResult)
{
_counter.End(withResult);
}
internal bool TryConsume(ref ulong result, bool block, AutoResetEvent wakeSignal = null)
{
lock (_lock)
{
if (Disposed)
{
return true;
}
if (ClearCounter)
{
result = 0;
}
long queryResult;
if (block)
{
queryResult = _counter.AwaitResult(wakeSignal);
}
else
{
if (!_counter.TryGetResult(out queryResult))
{
return false;
}
}
result += (ulong)queryResult;
_result = result;
OnResult?.Invoke(this, result);
Dispose(); // Return the our resources to the pool.
return true;
}
}
public void Flush()
{
if (Disposed)
{
return;
}
// Tell the queue to process all events up to this one.
_queue.FlushTo(this);
}
public void DecrementRefCount()
{
if (Interlocked.Decrement(ref _refCount) == 0)
{
DisposeInternal();
}
}
public bool ReserveForHostAccess()
{
if (_hostAccessReserved)
{
return true;
}
if (IsValueAvailable())
{
return false;
}
if (Interlocked.Increment(ref _refCount) == 1)
{
Interlocked.Decrement(ref _refCount);
return false;
}
_hostAccessReserved = true;
return true;
}
public void ReleaseHostAccess()
{
_hostAccessReserved = false;
DecrementRefCount();
}
private void DisposeInternal()
{
_queue.ReturnQueryObject(_counter);
}
private bool IsValueAvailable()
{
return _result != ulong.MaxValue || _counter.TryGetResult(out _);
}
public void Dispose()
{
Disposed = true;
DecrementRefCount();
}
}
}