From 6e9bd4de138e6ddedef3d38d711d161a0e400d5c Mon Sep 17 00:00:00 2001 From: riperiperi Date: Sun, 12 Mar 2023 17:01:15 +0000 Subject: [PATCH] GPU: Scale counter results before addition (#4471) * GPU: Scale counter results before addition Counter results were being scaled on ReportCounter, which meant that the _total_ value of the counter was being scaled. Not only could this result in very large numbers and weird overflows if the game doesn't clear the counter, but it also caused the result to change drastically. This PR changes scaling to be done when the value is added to the counter on the backend. This should evaluate the scale at the same time as before, on report counter, but avoiding the issue with scaling the total. Fixes scaling in Warioware, at least in the demo, where it seems to compare old/new counters and broke down when scaling was enabled. * Fix issues when result is partially uploaded. Drivers tend to write the low half first, then the high half. Retry if the high half is FFFFFFFF. --- .../Engine/Threed/SemaphoreUpdater.cs | 11 ----------- Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs | 2 +- Ryujinx.Graphics.OpenGL/Pipeline.cs | 10 ++++++++++ Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs | 15 +++++++++++---- Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs | 8 ++++++-- .../Queries/CounterQueueEvent.cs | 7 +++++-- Ryujinx.Graphics.OpenGL/Queries/Counters.cs | 4 ++-- Ryujinx.Graphics.Vulkan/PipelineBase.cs | 10 ++++++++++ Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs | 13 ++++++++++--- Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs | 2 +- .../Queries/CounterQueueEvent.cs | 7 +++++-- 11 files changed, 61 insertions(+), 28 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs index f52f9f814..63a2c841c 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs @@ -152,21 +152,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed ulong ticks = _context.GetTimestamp(); - float divisor = type switch - { - ReportCounterType.SamplesPassed => _channel.TextureManager.RenderTargetScale * _channel.TextureManager.RenderTargetScale, - _ => 1f - }; - ICounterEvent counter = null; void resultHandler(object evt, ulong result) { - if (divisor != 1f) - { - result = (ulong)MathF.Ceiling(result / divisor); - } - CounterData counterData = new CounterData { Counter = result, diff --git a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs index 9490684cd..c79700fc2 100644 --- a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs +++ b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs @@ -178,7 +178,7 @@ namespace Ryujinx.Graphics.OpenGL } _pipeline.Initialize(this); - _counters.Initialize(); + _counters.Initialize(_pipeline); // This is required to disable [0, 1] clamping for SNorm outputs on compatibility profiles. // This call is expected to fail if we're running with a core profile, diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs index 970feea0c..62996bd0a 100644 --- a/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -773,6 +773,16 @@ namespace Ryujinx.Graphics.OpenGL _tfEnabled = false; } + public double GetCounterDivisor(CounterType type) + { + if (type == CounterType.SamplesPassed) + { + return _renderScale[0].X * _renderScale[0].X; + } + + return 1; + } + public void SetAlphaTest(bool enable, float reference, CompareOp op) { if (!enable) diff --git a/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs b/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs index 027495cdd..99ea6a5c7 100644 --- a/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs +++ b/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs @@ -10,6 +10,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries { private const int MaxQueryRetries = 5000; private const long DefaultValue = -1; + private const ulong HighMask = 0xFFFFFFFF00000000; public int Query { get; } @@ -63,11 +64,17 @@ namespace Ryujinx.Graphics.OpenGL.Queries } } + private bool WaitingForValue(long data) + { + return data == DefaultValue || + ((ulong)data & HighMask) == (unchecked((ulong)DefaultValue) & HighMask); + } + public bool TryGetResult(out long result) { result = Marshal.ReadInt64(_bufferMap); - return result != DefaultValue; + return WaitingForValue(result); } public long AwaitResult(AutoResetEvent wakeSignal = null) @@ -76,7 +83,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries if (wakeSignal == null) { - while (data == DefaultValue) + while (WaitingForValue(data)) { data = Marshal.ReadInt64(_bufferMap); } @@ -84,10 +91,10 @@ namespace Ryujinx.Graphics.OpenGL.Queries else { int iterations = 0; - while (data == DefaultValue && iterations++ < MaxQueryRetries) + while (WaitingForValue(data) && iterations++ < MaxQueryRetries) { data = Marshal.ReadInt64(_bufferMap); - if (data == DefaultValue) + if (WaitingForValue(data)) { wakeSignal.WaitOne(1); } diff --git a/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs b/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs index 84b2bfdc9..e0aafa6fa 100644 --- a/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs +++ b/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 Pipeline _pipeline; + private Queue _events = new Queue(); private CounterQueueEvent _current; @@ -28,10 +30,12 @@ namespace Ryujinx.Graphics.OpenGL.Queries private Thread _consumerThread; - internal CounterQueue(CounterType type) + internal CounterQueue(Pipeline pipeline, CounterType type) { Type = type; + _pipeline = pipeline; + QueryTarget glType = GetTarget(Type); _queryPool = new Queue(QueryPoolInitialSize); @@ -119,7 +123,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries _current.ReserveForHostAccess(); } - _current.Complete(draws > 0); + _current.Complete(draws > 0, _pipeline.GetCounterDivisor(Type)); _events.Enqueue(_current); _current.OnResult += resultHandler; diff --git a/Ryujinx.Graphics.OpenGL/Queries/CounterQueueEvent.cs b/Ryujinx.Graphics.OpenGL/Queries/CounterQueueEvent.cs index 81451389c..7297baab5 100644 --- a/Ryujinx.Graphics.OpenGL/Queries/CounterQueueEvent.cs +++ b/Ryujinx.Graphics.OpenGL/Queries/CounterQueueEvent.cs @@ -26,6 +26,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries private object _lock = new object(); private ulong _result = ulong.MaxValue; + private double _divisor = 1f; public CounterQueueEvent(CounterQueue queue, QueryTarget type, ulong drawIndex) { @@ -45,9 +46,11 @@ namespace Ryujinx.Graphics.OpenGL.Queries ClearCounter = true; } - internal void Complete(bool withResult) + internal void Complete(bool withResult, double divisor) { _counter.End(withResult); + + _divisor = divisor; } internal bool TryConsume(ref ulong result, bool block, AutoResetEvent wakeSignal = null) @@ -78,7 +81,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries } } - result += (ulong)queryResult; + result += _divisor == 1 ? (ulong)queryResult : (ulong)Math.Ceiling(queryResult / _divisor); _result = result; diff --git a/Ryujinx.Graphics.OpenGL/Queries/Counters.cs b/Ryujinx.Graphics.OpenGL/Queries/Counters.cs index ebfd899c5..e6c7ab2bd 100644 --- a/Ryujinx.Graphics.OpenGL/Queries/Counters.cs +++ b/Ryujinx.Graphics.OpenGL/Queries/Counters.cs @@ -14,12 +14,12 @@ namespace Ryujinx.Graphics.OpenGL.Queries _counterQueues = new CounterQueue[count]; } - public void Initialize() + public void Initialize(Pipeline pipeline) { for (int index = 0; index < _counterQueues.Length; index++) { CounterType type = (CounterType)index; - _counterQueues[index] = new CounterQueue(type); + _counterQueues[index] = new CounterQueue(pipeline, type); } } diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 583bb9539..3abab0657 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -684,6 +684,16 @@ namespace Ryujinx.Graphics.Vulkan _tfEnabled = false; } + public double GetCounterDivisor(CounterType type) + { + if (type == CounterType.SamplesPassed) + { + return _renderScale[0].X * _renderScale[0].X; + } + + return 1; + } + public bool IsCommandBufferActive(CommandBuffer cb) { return CommandBuffer.Handle == cb.Handle; diff --git a/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs b/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs index 29efd8e74..861155a30 100644 --- a/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs +++ b/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs @@ -12,6 +12,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries private const int MaxQueryRetries = 5000; private const long DefaultValue = -1; private const long DefaultValueInt = 0xFFFFFFFF; + private const ulong HighMask = 0xFFFFFFFF00000000; private readonly Vk _api; private readonly Device _device; @@ -125,6 +126,12 @@ namespace Ryujinx.Graphics.Vulkan.Queries } } + private bool WaitingForValue(long data) + { + return data == _defaultValue || + (!_result32Bit && ((ulong)data & HighMask) == ((ulong)_defaultValue & HighMask)); + } + public bool TryGetResult(out long result) { result = Marshal.ReadInt64(_bufferMap); @@ -138,7 +145,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries if (wakeSignal == null) { - while (data == _defaultValue) + while (WaitingForValue(data)) { data = Marshal.ReadInt64(_bufferMap); } @@ -146,10 +153,10 @@ namespace Ryujinx.Graphics.Vulkan.Queries else { int iterations = 0; - while (data == _defaultValue && iterations++ < MaxQueryRetries) + while (WaitingForValue(data) && iterations++ < MaxQueryRetries) { data = Marshal.ReadInt64(_bufferMap); - if (data == _defaultValue) + if (WaitingForValue(data)) { wakeSignal.WaitOne(1); } diff --git a/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs b/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs index 7293b74f9..c30d91c42 100644 --- a/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs +++ b/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs @@ -148,7 +148,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries _current.ReserveForHostAccess(); } - _current.Complete(draws > 0 && Type != CounterType.TransformFeedbackPrimitivesWritten); + _current.Complete(draws > 0 && Type != CounterType.TransformFeedbackPrimitivesWritten, _pipeline.GetCounterDivisor(Type)); _events.Enqueue(_current); _current.OnResult += resultHandler; diff --git a/Ryujinx.Graphics.Vulkan/Queries/CounterQueueEvent.cs b/Ryujinx.Graphics.Vulkan/Queries/CounterQueueEvent.cs index 6b780ba34..d3aedb2f3 100644 --- a/Ryujinx.Graphics.Vulkan/Queries/CounterQueueEvent.cs +++ b/Ryujinx.Graphics.Vulkan/Queries/CounterQueueEvent.cs @@ -24,6 +24,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries private object _lock = new object(); private ulong _result = ulong.MaxValue; + private double _divisor = 1f; public CounterQueueEvent(CounterQueue queue, CounterType type, ulong drawIndex) { @@ -52,9 +53,11 @@ namespace Ryujinx.Graphics.Vulkan.Queries ClearCounter = true; } - internal void Complete(bool withResult) + internal void Complete(bool withResult, double divisor) { _counter.End(withResult); + + _divisor = divisor; } internal bool TryConsume(ref ulong result, bool block, AutoResetEvent wakeSignal = null) @@ -85,7 +88,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries } } - result += (ulong)queryResult; + result += _divisor == 1 ? (ulong)queryResult : (ulong)Math.Ceiling(queryResult / _divisor); _result = result;