Compare commits

...

9 commits

Author SHA1 Message Date
riperiperi
004aeaba70
Merge a46b1bd88c into 6ce49a2dc7 2024-07-26 18:50:29 -04:00
Isaac Marovitz
6ce49a2dc7
Ava UI: Handle updates containing non numeric characters (#7043)
* Handle updates containing non numeric characters

Smh

Dont be stupid

* Use Berry’s method

* Thanks gdk

* Remove using
2024-07-25 16:44:33 -03:00
riperiperi
ccd330ba0f
Vulkan: Add missing barriers for texture to buffer copy (#7092)
This barrier has always been missing, but it only became apparent when #7012 merged.

I also added some barriers in case the target buffer used here is used by other commands, though right now it isn't.

Fixes a regression where water would turn white on AMD GPUs with the proprietary driver. May fix other issues on this driver.
2024-07-25 16:34:30 -03:00
riperiperi
a46b1bd88c When batching, don't place barriers between every copy 2024-02-24 20:13:31 +00:00
riperiperi
1a85c9a00b Disable delayed counter copy on NVIDIA 2024-02-24 20:13:31 +00:00
riperiperi
71901a54b8 Fix host reserved counter 2024-02-24 20:13:31 +00:00
riperiperi
711360f775 Fix host access reservation for real 2024-02-24 20:13:31 +00:00
riperiperi
0aa6b31b39 Fix host reserved counter 2024-02-24 20:13:31 +00:00
riperiperi
14b2395290 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.
2024-02-24 20:13:31 +00:00
22 changed files with 193 additions and 93 deletions

View file

@ -107,7 +107,7 @@ namespace Ryujinx.Graphics.GAL
void TextureBarrier();
void TextureBarrierTiled();
bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual);
bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual, bool forwarded = false);
bool TryHostConditionalRendering(ICounterEvent value, ICounterEvent compare, bool isEqual);
void EndHostConditionalRendering();
}

View file

@ -48,7 +48,7 @@ namespace Ryujinx.Graphics.GAL
void PreFrame();
ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, float divisor, bool hostReserved);
ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, float divisor, int hostReserved);
void ResetCounter(CounterType type);

View file

@ -11,9 +11,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer
private CounterType _type;
private TableRef<EventHandler<ulong>> _resultHandler;
private float _divisor;
private bool _hostReserved;
private int _hostReserved;
public void Set(TableRef<ThreadedCounterEvent> evt, CounterType type, TableRef<EventHandler<ulong>> resultHandler, float divisor, bool hostReserved)
public void Set(TableRef<ThreadedCounterEvent> evt, CounterType type, TableRef<EventHandler<ulong>> resultHandler, float divisor, int hostReserved)
{
_event = evt;
_type = type;

View file

@ -19,7 +19,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands
public static void Run(ref TryHostConditionalRenderingCommand command, ThreadedRenderer threaded, IRenderer renderer)
{
renderer.Pipeline.TryHostConditionalRendering(command._value.Get(threaded)?.Base, command._compare, command._isEqual);
renderer.Pipeline.TryHostConditionalRendering(command._value.Get(threaded)?.Base, command._compare, command._isEqual, true);
}
}
}

View file

@ -14,7 +14,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources
public CounterType Type { get; }
public bool ClearCounter { get; }
private bool _reserved;
private int _reserved;
private int _createLock;
public ThreadedCounterEvent(ThreadedRenderer renderer, CounterType type, bool clearCounter)
@ -61,7 +61,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources
}
else
{
_reserved = true;
Interlocked.Increment(ref _reserved);
}
Volatile.Write(ref _createLock, 0);
@ -70,10 +70,10 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources
}
}
public void Create(IRenderer renderer, CounterType type, System.EventHandler<ulong> eventHandler, float divisor, bool hostReserved)
public void Create(IRenderer renderer, CounterType type, System.EventHandler<ulong> eventHandler, float divisor, int hostReserved)
{
ThreadedHelpers.SpinUntilExchange(ref _createLock, 1, 0);
Base = renderer.ReportCounter(type, eventHandler, divisor, hostReserved || _reserved);
Base = renderer.ReportCounter(type, eventHandler, divisor, hostReserved + _reserved);
Volatile.Write(ref _createLock, 0);
}
}

View file

@ -357,7 +357,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_renderer.QueueCommand();
}
public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual)
public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual, bool forwarded = false)
{
var evt = value as ThreadedCounterEvent;
if (evt != null)

View file

@ -427,7 +427,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
QueueCommand();
}
public ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, float divisor, bool hostReserved)
public ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, float divisor, int hostReserved)
{
ThreadedCounterEvent evt = new(this, type, _lastSampleCounterClear);
New<ReportCounterCommand>().Set(Ref(evt), type, Ref(resultHandler), divisor, hostReserved);

View file

@ -179,13 +179,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
case ReportCounterType.SamplesPassed:
float scale = _channel.TextureManager.RenderTargetScale;
float divisor = scale * scale;
counter = _context.Renderer.ReportCounter(CounterType.SamplesPassed, resultHandler, divisor, false);
counter = _context.Renderer.ReportCounter(CounterType.SamplesPassed, resultHandler, divisor, 0);
break;
case ReportCounterType.PrimitivesGenerated:
counter = _context.Renderer.ReportCounter(CounterType.PrimitivesGenerated, resultHandler, 1f, false);
counter = _context.Renderer.ReportCounter(CounterType.PrimitivesGenerated, resultHandler, 1f, 0);
break;
case ReportCounterType.TransformFeedbackPrimitivesWritten:
counter = _context.Renderer.ReportCounter(CounterType.TransformFeedbackPrimitivesWritten, resultHandler, 1f, false);
counter = _context.Renderer.ReportCounter(CounterType.TransformFeedbackPrimitivesWritten, resultHandler, 1f, 0);
break;
}

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);
@ -221,7 +221,7 @@ namespace Ryujinx.Graphics.OpenGL
ResourcePool.Tick();
}
public ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, float divisor, bool hostReserved)
public ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, float divisor, int hostReserved)
{
return _counters.QueueReport(type, resultHandler, divisor, _pipeline.DrawCount, hostReserved);
}
@ -257,7 +257,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)
@ -299,6 +299,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;
@ -1164,10 +1168,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++)
@ -1523,6 +1531,7 @@ namespace Ryujinx.Graphics.OpenGL
private void PreDraw(int vertexCount)
{
_counters.PreDraw();
_vertexArray.PreDraw(vertexCount);
PreDraw();
}
@ -1639,7 +1648,7 @@ namespace Ryujinx.Graphics.OpenGL
}
}
public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual)
public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual, bool forwarded = false)
{
// Compare an event and a constant value.
if (value is CounterQueueEvent evt)
@ -1652,7 +1661,8 @@ namespace Ryujinx.Graphics.OpenGL
if (compare == 0 && evt.Type == QueryTarget.SamplesPassed && evt.ClearCounter)
{
if (!value.ReserveForHostAccess())
// If the call is forwarded from backend threading, then we have already reserved host access.
if (!forwarded && !value.ReserveForHostAccess())
{
// If the event has been flushed, then just use the values on the CPU.
// The query object may already be repurposed for another draw (eg. begin + end).
@ -1668,12 +1678,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,31 @@ 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(bool withBarrier)
{
GL.BindBuffer(BufferTarget.QueryBuffer, _buffer);
GL.GetQueryObject(Query, GetQueryObjectParam.QueryResult, (long*)0);
if (withBarrier)
{
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(true);
}
}
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));
}
}
}
@ -103,7 +106,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries
}
}
public CounterQueueEvent QueueReport(EventHandler<ulong> resultHandler, float divisor, ulong lastDrawIndex, bool hostReserved)
public CounterQueueEvent QueueReport(EventHandler<ulong> resultHandler, float divisor, ulong lastDrawIndex, int hostReserved)
{
CounterQueueEvent result;
ulong draws = lastDrawIndex - _current.DrawIndex;
@ -113,9 +116,9 @@ namespace Ryujinx.Graphics.OpenGL.Queries
// A query's result only matters if more than one draw was performed during it.
// Otherwise, dummy it out and return 0 immediately.
if (hostReserved)
while (hostReserved-- > 0)
{
// This counter event is guaranteed to be available for host conditional rendering.
// This counter event is guaranteed to be available for host conditional rendering for the given number of uses.
_current.ReserveForHostAccess();
}
@ -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,12 +16,12 @@ 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;
private bool _hostAccessReserved = false;
private int _hostAccessReserved = 0;
private int _refCount = 1; // Starts with a reference from the counter queue.
private readonly object _lock = new();
@ -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)
@ -114,12 +115,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries
public bool ReserveForHostAccess()
{
if (_hostAccessReserved)
{
return true;
}
if (IsValueAvailable())
if (_hostAccessReserved == 0 && IsValueAvailable())
{
return false;
}
@ -131,14 +127,14 @@ namespace Ryujinx.Graphics.OpenGL.Queries
return false;
}
_hostAccessReserved = true;
Interlocked.Increment(ref _hostAccessReserved);
return true;
}
public void ReleaseHostAccess()
{
_hostAccessReserved = false;
Interlocked.Decrement(ref _hostAccessReserved);
DecrementRefCount();
}

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,18 +25,18 @@ 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);
}
}
public CounterQueueEvent QueueReport(CounterType type, EventHandler<ulong> resultHandler, float divisor, ulong lastDrawIndex, bool hostReserved)
public CounterQueueEvent QueueReport(CounterType type, EventHandler<ulong> resultHandler, float divisor, ulong lastDrawIndex, int 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()
@ -46,6 +52,51 @@ 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)
{
int i = 0;
foreach (BufferedQuery query in _queuedCopies)
{
query.CopyQueryResult(_queuedCopies.Count == ++i);
}
_queuedCopies.Clear();
}
if (newPass)
{
_flushedThisPass = false;
}
}
public bool QueueCopy(BufferedQuery query)
{
if (HwCapabilities.Vendor == HwCapabilities.GpuVendor.Nvidia)
{
// NVIDIA seems to make up a rule where query results can't be copied to buffers
// when unrelated query objects are in use.
return false;
}
else
{
_queuedCopies.Add(query);
return true;
}
}
public void Dispose()
{
foreach (var queue in _counterQueues)

View file

@ -133,7 +133,7 @@ namespace Ryujinx.Graphics.Vulkan
_activeConditionalRender = null;
}
public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual)
public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual, bool forwarded = false)
{
// Compare an event and a constant value.
if (value is CounterQueueEvent evt)
@ -146,7 +146,8 @@ namespace Ryujinx.Graphics.Vulkan
if (compare == 0 && evt.Type == CounterType.SamplesPassed && evt.ClearCounter)
{
if (!value.ReserveForHostAccess())
// If the call is forwarded from backend threading, then we have already reserved host access.
if (!forwarded && !value.ReserveForHostAccess())
{
// If the event has been flushed, then just use the values on the CPU.
// The query object may already be repurposed for another draw (eg. begin + end).

View file

@ -139,7 +139,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries
}
}
public CounterQueueEvent QueueReport(EventHandler<ulong> resultHandler, float divisor, ulong lastDrawIndex, bool hostReserved)
public CounterQueueEvent QueueReport(EventHandler<ulong> resultHandler, float divisor, ulong lastDrawIndex, int hostReserved)
{
CounterQueueEvent result;
ulong draws = lastDrawIndex - _current.DrawIndex;
@ -149,9 +149,9 @@ namespace Ryujinx.Graphics.Vulkan.Queries
// A query's result only matters if more than one draw was performed during it.
// Otherwise, dummy it out and return 0 immediately.
if (hostReserved)
while (hostReserved-- > 0)
{
// This counter event is guaranteed to be available for host conditional rendering.
// This counter event is guaranteed to be available for host conditional rendering for the given number of uses.
_current.ReserveForHostAccess();
}

View file

@ -19,7 +19,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries
private readonly CounterQueue _queue;
private readonly BufferedQuery _counter;
private bool _hostAccessReserved;
private int _hostAccessReserved;
private int _refCount = 1; // Starts with a reference from the counter queue.
private readonly object _lock = new();
@ -121,12 +121,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries
public bool ReserveForHostAccess()
{
if (_hostAccessReserved)
{
return true;
}
if (IsValueAvailable())
if (_hostAccessReserved == 0 && IsValueAvailable())
{
return false;
}
@ -138,14 +133,14 @@ namespace Ryujinx.Graphics.Vulkan.Queries
return false;
}
_hostAccessReserved = true;
Interlocked.Increment(ref _hostAccessReserved);
return true;
}
public void ReleaseHostAccess()
{
_hostAccessReserved = false;
Interlocked.Decrement(ref _hostAccessReserved);
DecrementRefCount();
}

View file

@ -37,7 +37,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries
_counterQueues[(int)CounterType.SamplesPassed].ResetFutureCounters(cmd, count);
}
public CounterQueueEvent QueueReport(CounterType type, EventHandler<ulong> resultHandler, float divisor, bool hostReserved)
public CounterQueueEvent QueueReport(CounterType type, EventHandler<ulong> resultHandler, float divisor, int hostReserved)
{
return _counterQueues[(int)type].QueueReport(resultHandler, divisor, _pipeline.DrawCount, hostReserved);
}

View file

@ -667,8 +667,36 @@ namespace Ryujinx.Graphics.Vulkan
if (PrepareOutputBuffer(cbs, hostSize, buffer, out VkBuffer copyToBuffer, out BufferHolder tempCopyHolder))
{
// No barrier necessary, as this is a temporary copy buffer.
offset = 0;
}
else
{
BufferHolder.InsertBufferBarrier(
_gd,
cbs.CommandBuffer,
copyToBuffer,
BufferHolder.DefaultAccessFlags,
AccessFlags.TransferWriteBit,
PipelineStageFlags.AllCommandsBit,
PipelineStageFlags.TransferBit,
offset,
outSize);
}
InsertImageBarrier(
_gd.Api,
cbs.CommandBuffer,
image,
TextureStorage.DefaultAccessMask,
AccessFlags.TransferReadBit,
PipelineStageFlags.AllCommandsBit,
PipelineStageFlags.TransferBit,
Info.Format.ConvertAspectFlags(),
FirstLayer + layer,
FirstLevel + level,
1,
1);
CopyFromOrToBuffer(cbs.CommandBuffer, copyToBuffer, image, hostSize, true, layer, level, 1, 1, singleSlice: true, offset, stride);
@ -677,6 +705,19 @@ namespace Ryujinx.Graphics.Vulkan
CopyDataToOutputBuffer(cbs, tempCopyHolder, autoBuffer, hostSize, range.Offset);
tempCopyHolder.Dispose();
}
else
{
BufferHolder.InsertBufferBarrier(
_gd,
cbs.CommandBuffer,
copyToBuffer,
AccessFlags.TransferWriteBit,
BufferHolder.DefaultAccessFlags,
PipelineStageFlags.TransferBit,
PipelineStageFlags.AllCommandsBit,
offset,
outSize);
}
}
private ReadOnlySpan<byte> GetData(CommandBufferPool cbp, PersistentFlushBuffer flushBuffer)

View file

@ -869,7 +869,7 @@ namespace Ryujinx.Graphics.Vulkan
SyncManager.Cleanup();
}
public ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, float divisor, bool hostReserved)
public ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, float divisor, int hostReserved)
{
return _counters.QueueReport(type, resultHandler, divisor, hostReserved);
}

View file

@ -1,21 +1,20 @@
using LibHac.Ns;
using Ryujinx.Ava.Common.Locale;
namespace Ryujinx.Ava.UI.Models
{
public class TitleUpdateModel
{
public ApplicationControlProperty Control { get; }
public uint Version { get; }
public string Path { get; }
public string Label { get; }
public string Label => LocaleManager.Instance.UpdateAndGetDynamicValue(
System.IO.Path.GetExtension(Path)?.ToLower() == ".xci" ? LocaleKeys.TitleBundledUpdateVersionLabel : LocaleKeys.TitleUpdateVersionLabel,
Control.DisplayVersionString.ToString()
);
public TitleUpdateModel(ApplicationControlProperty control, string path)
public TitleUpdateModel(uint version, string displayVersion, string path)
{
Control = control;
Version = version;
Label = LocaleManager.Instance.UpdateAndGetDynamicValue(
System.IO.Path.GetExtension(path)?.ToLower() == ".xci" ? LocaleKeys.TitleBundledUpdateVersionLabel : LocaleKeys.TitleUpdateVersionLabel,
displayVersion
);
Path = path;
}
}

View file

@ -131,26 +131,11 @@ namespace Ryujinx.Ava.UI.ViewModels
public void SortUpdates()
{
var list = TitleUpdates.ToList();
list.Sort((first, second) =>
{
if (string.IsNullOrEmpty(first.Control.DisplayVersionString.ToString()))
{
return -1;
}
if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString()))
{
return 1;
}
return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1;
});
var sortedUpdates = TitleUpdates.OrderByDescending(update => update.Version);
Views.Clear();
Views.Add(new BaseModel());
Views.AddRange(list);
Views.AddRange(sortedUpdates);
if (SelectedUpdate == null)
{
@ -204,7 +189,9 @@ namespace Ryujinx.Ava.UI.ViewModels
controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
var update = new TitleUpdateModel(controlData, path);
var displayVersion = controlData.DisplayVersionString.ToString();
var update = new TitleUpdateModel(content.Version.Version, displayVersion, path);
TitleUpdates.Add(update);
if (selected)