diff --git a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs index eabcb3c10..1dd08ff6a 100644 --- a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs +++ b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs @@ -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); } diff --git a/src/Ryujinx.Graphics.OpenGL/Pipeline.cs b/src/Ryujinx.Graphics.OpenGL/Pipeline.cs index 0757fcd99..a31a0ad12 100644 --- a/src/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/src/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -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. } diff --git a/src/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs b/src/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs index 0a85970d7..d9add25cd 100644 --- a/src/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs +++ b/src/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs @@ -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 { diff --git a/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs b/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs index 345a99ffa..2f36840f0 100644 --- a/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs +++ b/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs @@ -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 _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(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); } } diff --git a/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueueEvent.cs b/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueueEvent.cs index 32b75c615..a67d868e8 100644 --- a/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueueEvent.cs +++ b/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueueEvent.cs @@ -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) diff --git a/src/Ryujinx.Graphics.OpenGL/Queries/Counters.cs b/src/Ryujinx.Graphics.OpenGL/Queries/Counters.cs index 1530c9d26..de8fb1142 100644 --- a/src/Ryujinx.Graphics.OpenGL/Queries/Counters.cs +++ b/src/Ryujinx.Graphics.OpenGL/Queries/Counters.cs @@ -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 _queuedCopies; + private bool _flushedThisPass; public Counters() { int count = Enum.GetNames().Length; _counterQueues = new CounterQueue[count]; + _queuedCopies = new List(); } 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)