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:
parent
4d0dbbfae2
commit
14b2395290
6 changed files with 87 additions and 20 deletions
|
@ -45,8 +45,8 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
|
|
||||||
public OpenGLRenderer()
|
public OpenGLRenderer()
|
||||||
{
|
{
|
||||||
_pipeline = new Pipeline();
|
|
||||||
_counters = new Counters();
|
_counters = new Counters();
|
||||||
|
_pipeline = new Pipeline(_counters);
|
||||||
_window = new Window(this);
|
_window = new Window(this);
|
||||||
_textureCopy = new TextureCopy(this);
|
_textureCopy = new TextureCopy(this);
|
||||||
_backgroundTextureCopy = new TextureCopy(this);
|
_backgroundTextureCopy = new TextureCopy(this);
|
||||||
|
@ -241,7 +241,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
|
|
||||||
public void ResetCounter(CounterType type)
|
public void ResetCounter(CounterType type)
|
||||||
{
|
{
|
||||||
_counters.QueueReset(type);
|
_counters.QueueReset(type, _pipeline.DrawCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BackgroundContextAction(Action action, bool alwaysBackground = false)
|
public void BackgroundContextAction(Action action, bool alwaysBackground = false)
|
||||||
|
@ -283,6 +283,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
|
|
||||||
public void CreateSync(ulong id, bool strict)
|
public void CreateSync(ulong id, bool strict)
|
||||||
{
|
{
|
||||||
|
_counters.CopyPending();
|
||||||
_sync.Create(id);
|
_sync.Create(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,8 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
{
|
{
|
||||||
private const int SavedImages = 2;
|
private const int SavedImages = 2;
|
||||||
|
|
||||||
|
private readonly Counters _counters;
|
||||||
|
|
||||||
private readonly DrawTextureEmulation _drawTexture;
|
private readonly DrawTextureEmulation _drawTexture;
|
||||||
|
|
||||||
internal ulong DrawCount { get; private set; }
|
internal ulong DrawCount { get; private set; }
|
||||||
|
@ -68,8 +70,10 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
|
|
||||||
private ColorF _blendConstant;
|
private ColorF _blendConstant;
|
||||||
|
|
||||||
internal Pipeline()
|
internal Pipeline(Counters counters)
|
||||||
{
|
{
|
||||||
|
_counters = counters;
|
||||||
|
|
||||||
_drawTexture = new DrawTextureEmulation();
|
_drawTexture = new DrawTextureEmulation();
|
||||||
_rasterizerDiscard = false;
|
_rasterizerDiscard = false;
|
||||||
_clipOrigin = ClipOrigin.LowerLeft;
|
_clipOrigin = ClipOrigin.LowerLeft;
|
||||||
|
@ -1154,10 +1158,14 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
|
|
||||||
RestoreComponentMask(index, force: false);
|
RestoreComponentMask(index, force: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_counters.CopyPending();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetRenderTargets(ITexture[] colors, ITexture depthStencil)
|
public void SetRenderTargets(ITexture[] colors, ITexture depthStencil)
|
||||||
{
|
{
|
||||||
|
_counters.CopyPending();
|
||||||
|
|
||||||
EnsureFramebuffer();
|
EnsureFramebuffer();
|
||||||
|
|
||||||
for (int index = 0; index < colors.Length; index++)
|
for (int index = 0; index < colors.Length; index++)
|
||||||
|
@ -1504,6 +1512,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
|
|
||||||
private void PreDraw(int vertexCount)
|
private void PreDraw(int vertexCount)
|
||||||
{
|
{
|
||||||
|
_counters.PreDraw();
|
||||||
_vertexArray.PreDraw(vertexCount);
|
_vertexArray.PreDraw(vertexCount);
|
||||||
PreDraw();
|
PreDraw();
|
||||||
}
|
}
|
||||||
|
@ -1649,12 +1658,14 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
|
|
||||||
// The GPU will flush the queries to CPU and evaluate the condition there instead.
|
// 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.
|
GL.Flush(); // The thread will be stalled manually flushing the counter, so flush GL commands now.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryHostConditionalRendering(ICounterEvent value, ICounterEvent compare, bool isEqual)
|
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.
|
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.
|
return false; // We don't currently have a way to compare two counters for conditional rendering.
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,11 @@ namespace Ryujinx.Graphics.OpenGL.Queries
|
||||||
private readonly IntPtr _bufferMap;
|
private readonly IntPtr _bufferMap;
|
||||||
private readonly QueryTarget _type;
|
private readonly QueryTarget _type;
|
||||||
|
|
||||||
public BufferedQuery(QueryTarget type)
|
private readonly Counters _parent;
|
||||||
|
|
||||||
|
public BufferedQuery(Counters parent, QueryTarget type)
|
||||||
{
|
{
|
||||||
|
_parent = parent;
|
||||||
_buffer = GL.GenBuffer();
|
_buffer = GL.GenBuffer();
|
||||||
Query = GL.GenQuery();
|
Query = GL.GenQuery();
|
||||||
_type = type;
|
_type = type;
|
||||||
|
@ -42,20 +45,27 @@ namespace Ryujinx.Graphics.OpenGL.Queries
|
||||||
|
|
||||||
public void Begin()
|
public void Begin()
|
||||||
{
|
{
|
||||||
|
Marshal.WriteInt64(_bufferMap, -1L);
|
||||||
GL.BeginQuery(_type, Query);
|
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);
|
GL.EndQuery(_type);
|
||||||
|
|
||||||
if (withResult)
|
if (withResult)
|
||||||
{
|
{
|
||||||
GL.BindBuffer(BufferTarget.QueryBuffer, _buffer);
|
if (!_parent.QueueCopy(this))
|
||||||
|
{
|
||||||
Marshal.WriteInt64(_bufferMap, -1L);
|
CopyQueryResult();
|
||||||
GL.GetQueryObject(Query, GetQueryObjectParam.QueryResult, (long*)0);
|
}
|
||||||
GL.MemoryBarrier(MemoryBarrierFlags.QueryBufferBarrierBit | MemoryBarrierFlags.ClientMappedBufferBarrierBit);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,6 +13,8 @@ namespace Ryujinx.Graphics.OpenGL.Queries
|
||||||
public CounterType Type { get; }
|
public CounterType Type { get; }
|
||||||
public bool Disposed { get; private set; }
|
public bool Disposed { get; private set; }
|
||||||
|
|
||||||
|
private readonly Counters _parent;
|
||||||
|
|
||||||
private readonly Queue<CounterQueueEvent> _events = new();
|
private readonly Queue<CounterQueueEvent> _events = new();
|
||||||
private CounterQueueEvent _current;
|
private CounterQueueEvent _current;
|
||||||
|
|
||||||
|
@ -28,8 +30,9 @@ namespace Ryujinx.Graphics.OpenGL.Queries
|
||||||
|
|
||||||
private readonly Thread _consumerThread;
|
private readonly Thread _consumerThread;
|
||||||
|
|
||||||
internal CounterQueue(CounterType type)
|
internal CounterQueue(Counters parent, CounterType type)
|
||||||
{
|
{
|
||||||
|
_parent = parent;
|
||||||
Type = type;
|
Type = type;
|
||||||
|
|
||||||
QueryTarget glType = GetTarget(Type);
|
QueryTarget glType = GetTarget(Type);
|
||||||
|
@ -37,7 +40,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries
|
||||||
_queryPool = new Queue<BufferedQuery>(QueryPoolInitialSize);
|
_queryPool = new Queue<BufferedQuery>(QueryPoolInitialSize);
|
||||||
for (int i = 0; i < QueryPoolInitialSize; i++)
|
for (int i = 0; i < QueryPoolInitialSize; i++)
|
||||||
{
|
{
|
||||||
_queryPool.Enqueue(new BufferedQuery(glType));
|
_queryPool.Enqueue(new BufferedQuery(parent, glType));
|
||||||
}
|
}
|
||||||
|
|
||||||
_current = new CounterQueueEvent(this, glType, 0);
|
_current = new CounterQueueEvent(this, glType, 0);
|
||||||
|
@ -90,7 +93,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return new BufferedQuery(GetTarget(Type));
|
return new BufferedQuery(_parent, GetTarget(Type));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,11 +137,11 @@ namespace Ryujinx.Graphics.OpenGL.Queries
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void QueueReset()
|
public void QueueReset(ulong lastDrawIndex)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
_current.Clear();
|
_current.Clear(lastDrawIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries
|
||||||
public bool Disposed { get; private set; }
|
public bool Disposed { get; private set; }
|
||||||
public bool Invalid { get; set; }
|
public bool Invalid { get; set; }
|
||||||
|
|
||||||
public ulong DrawIndex { get; }
|
public ulong DrawIndex { get; private set; }
|
||||||
|
|
||||||
private readonly CounterQueue _queue;
|
private readonly CounterQueue _queue;
|
||||||
private readonly BufferedQuery _counter;
|
private readonly BufferedQuery _counter;
|
||||||
|
@ -40,10 +40,11 @@ namespace Ryujinx.Graphics.OpenGL.Queries
|
||||||
_counter.Begin();
|
_counter.Begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Clear()
|
internal void Clear(ulong drawIndex)
|
||||||
{
|
{
|
||||||
_counter.Reset();
|
_counter.Reset();
|
||||||
ClearCounter = true;
|
ClearCounter = true;
|
||||||
|
DrawIndex = drawIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Complete(bool withResult, double divisor)
|
internal void Complete(bool withResult, double divisor)
|
||||||
|
|
|
@ -1,17 +1,23 @@
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.OpenGL.Queries
|
namespace Ryujinx.Graphics.OpenGL.Queries
|
||||||
{
|
{
|
||||||
class Counters : IDisposable
|
class Counters : IDisposable
|
||||||
{
|
{
|
||||||
|
private const int ForceCopyThreshold = 32;
|
||||||
|
|
||||||
private readonly CounterQueue[] _counterQueues;
|
private readonly CounterQueue[] _counterQueues;
|
||||||
|
private readonly List<BufferedQuery> _queuedCopies;
|
||||||
|
private bool _flushedThisPass;
|
||||||
|
|
||||||
public Counters()
|
public Counters()
|
||||||
{
|
{
|
||||||
int count = Enum.GetNames<CounterType>().Length;
|
int count = Enum.GetNames<CounterType>().Length;
|
||||||
|
|
||||||
_counterQueues = new CounterQueue[count];
|
_counterQueues = new CounterQueue[count];
|
||||||
|
_queuedCopies = new List<BufferedQuery>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Initialize()
|
public void Initialize()
|
||||||
|
@ -19,7 +25,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries
|
||||||
for (int index = 0; index < _counterQueues.Length; index++)
|
for (int index = 0; index < _counterQueues.Length; index++)
|
||||||
{
|
{
|
||||||
CounterType type = (CounterType)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);
|
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()
|
public void Update()
|
||||||
|
@ -46,6 +52,41 @@ namespace Ryujinx.Graphics.OpenGL.Queries
|
||||||
_counterQueues[(int)type].Flush(true);
|
_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()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
foreach (var queue in _counterQueues)
|
foreach (var queue in _counterQueues)
|
||||||
|
|
Loading…
Reference in a new issue