OpenGL: Batch counter copies

Similar to Vulkan, batches counter results to be copied back on render pass switch. We don't control render passes on OpenGL so it just guesses where to flush, as well as forcing it when the host is going to wait.
This commit is contained in:
riperiperi 2024-02-14 22:27:21 +00:00
parent 4d0dbbfae2
commit 14b2395290
6 changed files with 87 additions and 20 deletions

View file

@ -45,8 +45,8 @@ namespace Ryujinx.Graphics.OpenGL
public OpenGLRenderer()
{
_pipeline = new Pipeline();
_counters = new Counters();
_pipeline = new Pipeline(_counters);
_window = new Window(this);
_textureCopy = new TextureCopy(this);
_backgroundTextureCopy = new TextureCopy(this);
@ -241,7 +241,7 @@ namespace Ryujinx.Graphics.OpenGL
public void ResetCounter(CounterType type)
{
_counters.QueueReset(type);
_counters.QueueReset(type, _pipeline.DrawCount);
}
public void BackgroundContextAction(Action action, bool alwaysBackground = false)
@ -283,6 +283,7 @@ namespace Ryujinx.Graphics.OpenGL
public void CreateSync(ulong id, bool strict)
{
_counters.CopyPending();
_sync.Create(id);
}

View file

@ -12,6 +12,8 @@ namespace Ryujinx.Graphics.OpenGL
{
private const int SavedImages = 2;
private readonly Counters _counters;
private readonly DrawTextureEmulation _drawTexture;
internal ulong DrawCount { get; private set; }
@ -68,8 +70,10 @@ namespace Ryujinx.Graphics.OpenGL
private ColorF _blendConstant;
internal Pipeline()
internal Pipeline(Counters counters)
{
_counters = counters;
_drawTexture = new DrawTextureEmulation();
_rasterizerDiscard = false;
_clipOrigin = ClipOrigin.LowerLeft;
@ -1154,10 +1158,14 @@ namespace Ryujinx.Graphics.OpenGL
RestoreComponentMask(index, force: false);
}
_counters.CopyPending();
}
public void SetRenderTargets(ITexture[] colors, ITexture depthStencil)
{
_counters.CopyPending();
EnsureFramebuffer();
for (int index = 0; index < colors.Length; index++)
@ -1504,6 +1512,7 @@ namespace Ryujinx.Graphics.OpenGL
private void PreDraw(int vertexCount)
{
_counters.PreDraw();
_vertexArray.PreDraw(vertexCount);
PreDraw();
}
@ -1649,12 +1658,14 @@ namespace Ryujinx.Graphics.OpenGL
// The GPU will flush the queries to CPU and evaluate the condition there instead.
_counters.CopyPending(false);
GL.Flush(); // The thread will be stalled manually flushing the counter, so flush GL commands now.
return false;
}
public bool TryHostConditionalRendering(ICounterEvent value, ICounterEvent compare, bool isEqual)
{
_counters.CopyPending(false);
GL.Flush(); // The GPU thread will be stalled manually flushing the counter, so flush GL commands now.
return false; // We don't currently have a way to compare two counters for conditional rendering.
}

View file

@ -18,8 +18,11 @@ namespace Ryujinx.Graphics.OpenGL.Queries
private readonly IntPtr _bufferMap;
private readonly QueryTarget _type;
public BufferedQuery(QueryTarget type)
private readonly Counters _parent;
public BufferedQuery(Counters parent, QueryTarget type)
{
_parent = parent;
_buffer = GL.GenBuffer();
Query = GL.GenQuery();
_type = type;
@ -42,20 +45,27 @@ namespace Ryujinx.Graphics.OpenGL.Queries
public void Begin()
{
Marshal.WriteInt64(_bufferMap, -1L);
GL.BeginQuery(_type, Query);
}
public unsafe void End(bool withResult)
public unsafe void CopyQueryResult()
{
GL.BindBuffer(BufferTarget.QueryBuffer, _buffer);
GL.GetQueryObject(Query, GetQueryObjectParam.QueryResult, (long*)0);
GL.MemoryBarrier(MemoryBarrierFlags.QueryBufferBarrierBit | MemoryBarrierFlags.ClientMappedBufferBarrierBit);
}
public void End(bool withResult)
{
GL.EndQuery(_type);
if (withResult)
{
GL.BindBuffer(BufferTarget.QueryBuffer, _buffer);
Marshal.WriteInt64(_bufferMap, -1L);
GL.GetQueryObject(Query, GetQueryObjectParam.QueryResult, (long*)0);
GL.MemoryBarrier(MemoryBarrierFlags.QueryBufferBarrierBit | MemoryBarrierFlags.ClientMappedBufferBarrierBit);
if (!_parent.QueueCopy(this))
{
CopyQueryResult();
}
}
else
{

View file

@ -13,6 +13,8 @@ namespace Ryujinx.Graphics.OpenGL.Queries
public CounterType Type { get; }
public bool Disposed { get; private set; }
private readonly Counters _parent;
private readonly Queue<CounterQueueEvent> _events = new();
private CounterQueueEvent _current;
@ -28,8 +30,9 @@ namespace Ryujinx.Graphics.OpenGL.Queries
private readonly Thread _consumerThread;
internal CounterQueue(CounterType type)
internal CounterQueue(Counters parent, CounterType type)
{
_parent = parent;
Type = type;
QueryTarget glType = GetTarget(Type);
@ -37,7 +40,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries
_queryPool = new Queue<BufferedQuery>(QueryPoolInitialSize);
for (int i = 0; i < QueryPoolInitialSize; i++)
{
_queryPool.Enqueue(new BufferedQuery(glType));
_queryPool.Enqueue(new BufferedQuery(parent, glType));
}
_current = new CounterQueueEvent(this, glType, 0);
@ -90,7 +93,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries
}
else
{
return new BufferedQuery(GetTarget(Type));
return new BufferedQuery(_parent, GetTarget(Type));
}
}
}
@ -134,11 +137,11 @@ namespace Ryujinx.Graphics.OpenGL.Queries
return result;
}
public void QueueReset()
public void QueueReset(ulong lastDrawIndex)
{
lock (_lock)
{
_current.Clear();
_current.Clear(lastDrawIndex);
}
}

View file

@ -16,7 +16,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries
public bool Disposed { get; private set; }
public bool Invalid { get; set; }
public ulong DrawIndex { get; }
public ulong DrawIndex { get; private set; }
private readonly CounterQueue _queue;
private readonly BufferedQuery _counter;
@ -40,10 +40,11 @@ namespace Ryujinx.Graphics.OpenGL.Queries
_counter.Begin();
}
internal void Clear()
internal void Clear(ulong drawIndex)
{
_counter.Reset();
ClearCounter = true;
DrawIndex = drawIndex;
}
internal void Complete(bool withResult, double divisor)

View file

@ -1,17 +1,23 @@
using Ryujinx.Graphics.GAL;
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.OpenGL.Queries
{
class Counters : IDisposable
{
private const int ForceCopyThreshold = 32;
private readonly CounterQueue[] _counterQueues;
private readonly List<BufferedQuery> _queuedCopies;
private bool _flushedThisPass;
public Counters()
{
int count = Enum.GetNames<CounterType>().Length;
_counterQueues = new CounterQueue[count];
_queuedCopies = new List<BufferedQuery>();
}
public void Initialize()
@ -19,7 +25,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries
for (int index = 0; index < _counterQueues.Length; index++)
{
CounterType type = (CounterType)index;
_counterQueues[index] = new CounterQueue(type);
_counterQueues[index] = new CounterQueue(this, type);
}
}
@ -28,9 +34,9 @@ namespace Ryujinx.Graphics.OpenGL.Queries
return _counterQueues[(int)type].QueueReport(resultHandler, divisor, lastDrawIndex, hostReserved);
}
public void QueueReset(CounterType type)
public void QueueReset(CounterType type, ulong lastDrawIndex)
{
_counterQueues[(int)type].QueueReset();
_counterQueues[(int)type].QueueReset(lastDrawIndex);
}
public void Update()
@ -46,6 +52,41 @@ namespace Ryujinx.Graphics.OpenGL.Queries
_counterQueues[(int)type].Flush(true);
}
public void PreDraw()
{
// Force results to be copied some time into an occlusion query pass.
if (!_flushedThisPass && _queuedCopies.Count > ForceCopyThreshold)
{
_flushedThisPass = true;
CopyPending(false);
}
}
public void CopyPending(bool newPass = true)
{
if (_queuedCopies.Count > 0)
{
foreach (BufferedQuery query in _queuedCopies)
{
query.CopyQueryResult();
}
_queuedCopies.Clear();
}
if (newPass)
{
_flushedThisPass = false;
}
}
public bool QueueCopy(BufferedQuery query)
{
_queuedCopies.Add(query);
return true;
}
public void Dispose()
{
foreach (var queue in _counterQueues)