Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
f0e27a23a5 | ||
|
e68650237d | ||
|
1faff14e73 |
@@ -82,7 +82,7 @@ namespace Ryujinx.Ava.UI.Renderer
|
|||||||
}
|
}
|
||||||
else if (OperatingSystem.IsMacOS())
|
else if (OperatingSystem.IsMacOS())
|
||||||
{
|
{
|
||||||
return CreateMacOs();
|
return CreateMacOS();
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.CreateNativeControlCore(control);
|
return base.CreateNativeControlCore(control);
|
||||||
@@ -113,11 +113,12 @@ namespace Ryujinx.Ava.UI.Renderer
|
|||||||
}
|
}
|
||||||
|
|
||||||
[SupportedOSPlatform("linux")]
|
[SupportedOSPlatform("linux")]
|
||||||
protected virtual IPlatformHandle CreateLinux(IPlatformHandle control)
|
private IPlatformHandle CreateLinux(IPlatformHandle control)
|
||||||
{
|
{
|
||||||
if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan)
|
if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan)
|
||||||
{
|
{
|
||||||
X11Window = new GLXWindow(new NativeHandle(X11.DefaultDisplay), new NativeHandle(control.Handle));
|
X11Window = new GLXWindow(new NativeHandle(X11.DefaultDisplay), new NativeHandle(control.Handle));
|
||||||
|
X11Window.Hide();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -227,7 +228,7 @@ namespace Ryujinx.Ava.UI.Renderer
|
|||||||
}
|
}
|
||||||
|
|
||||||
[SupportedOSPlatform("macos")]
|
[SupportedOSPlatform("macos")]
|
||||||
IPlatformHandle CreateMacOs()
|
IPlatformHandle CreateMacOS()
|
||||||
{
|
{
|
||||||
MetalLayer = MetalHelper.GetMetalLayer(out IntPtr nsView, out _updateBoundsCallback);
|
MetalLayer = MetalHelper.GetMetalLayer(out IntPtr nsView, out _updateBoundsCallback);
|
||||||
|
|
||||||
|
@@ -2,14 +2,13 @@ using Avalonia;
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Ui.Common.Configuration;
|
using Ryujinx.Ui.Common.Configuration;
|
||||||
using Silk.NET.Vulkan;
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Renderer
|
namespace Ryujinx.Ava.UI.Renderer
|
||||||
{
|
{
|
||||||
public partial class RendererHost : UserControl, IDisposable
|
public partial class RendererHost : UserControl, IDisposable
|
||||||
{
|
{
|
||||||
public EmbeddedWindow EmbeddedWindow;
|
public readonly EmbeddedWindow EmbeddedWindow;
|
||||||
|
|
||||||
public event EventHandler<EventArgs> WindowCreated;
|
public event EventHandler<EventArgs> WindowCreated;
|
||||||
public event Action<object, Size> SizeChanged;
|
public event Action<object, Size> SizeChanged;
|
||||||
@@ -18,8 +17,6 @@ namespace Ryujinx.Ava.UI.Renderer
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
Dispose();
|
|
||||||
|
|
||||||
if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.OpenGl)
|
if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.OpenGl)
|
||||||
{
|
{
|
||||||
EmbeddedWindow = new EmbeddedWindowOpenGL();
|
EmbeddedWindow = new EmbeddedWindowOpenGL();
|
||||||
@@ -47,6 +44,8 @@ namespace Ryujinx.Ava.UI.Renderer
|
|||||||
EmbeddedWindow.WindowCreated -= CurrentWindow_WindowCreated;
|
EmbeddedWindow.WindowCreated -= CurrentWindow_WindowCreated;
|
||||||
EmbeddedWindow.SizeChanged -= CurrentWindow_SizeChanged;
|
EmbeddedWindow.SizeChanged -= CurrentWindow_SizeChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||||
|
@@ -4,6 +4,28 @@ using System.Collections.Generic;
|
|||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Image
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An entry on the short duration texture cache.
|
||||||
|
/// </summary>
|
||||||
|
class ShortTextureCacheEntry
|
||||||
|
{
|
||||||
|
public readonly TextureDescriptor Descriptor;
|
||||||
|
public readonly int InvalidatedSequence;
|
||||||
|
public readonly Texture Texture;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new entry on the short duration texture cache.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="descriptor">Last descriptor that referenced the texture</param>
|
||||||
|
/// <param name="texture">The texture</param>
|
||||||
|
public ShortTextureCacheEntry(TextureDescriptor descriptor, Texture texture)
|
||||||
|
{
|
||||||
|
Descriptor = descriptor;
|
||||||
|
InvalidatedSequence = texture.InvalidatedSequence;
|
||||||
|
Texture = texture;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A texture cache that automatically removes older textures that are not used for some time.
|
/// A texture cache that automatically removes older textures that are not used for some time.
|
||||||
/// The cache works with a rotated list with a fixed size. When new textures are added, the
|
/// The cache works with a rotated list with a fixed size. When new textures are added, the
|
||||||
@@ -16,6 +38,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
private readonly LinkedList<Texture> _textures;
|
private readonly LinkedList<Texture> _textures;
|
||||||
private readonly ConcurrentQueue<Texture> _deferredRemovals;
|
private readonly ConcurrentQueue<Texture> _deferredRemovals;
|
||||||
|
|
||||||
|
private HashSet<ShortTextureCacheEntry> _shortCacheBuilder;
|
||||||
|
private HashSet<ShortTextureCacheEntry> _shortCache;
|
||||||
|
|
||||||
|
private Dictionary<TextureDescriptor, ShortTextureCacheEntry> _shortCacheLookup;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the automatic deletion cache.
|
/// Creates a new instance of the automatic deletion cache.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -23,6 +50,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
_textures = new LinkedList<Texture>();
|
_textures = new LinkedList<Texture>();
|
||||||
_deferredRemovals = new ConcurrentQueue<Texture>();
|
_deferredRemovals = new ConcurrentQueue<Texture>();
|
||||||
|
|
||||||
|
_shortCacheBuilder = new HashSet<ShortTextureCacheEntry>();
|
||||||
|
_shortCache = new HashSet<ShortTextureCacheEntry>();
|
||||||
|
|
||||||
|
_shortCacheLookup = new Dictionary<TextureDescriptor, ShortTextureCacheEntry>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -130,6 +162,85 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
_deferredRemovals.Enqueue(texture);
|
_deferredRemovals.Enqueue(texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempt to find a texture on the short duration cache.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="descriptor">The texture descriptor</param>
|
||||||
|
/// <returns>The texture if found, null otherwise</returns>
|
||||||
|
public Texture FindShortCache(in TextureDescriptor descriptor)
|
||||||
|
{
|
||||||
|
if (_shortCacheLookup.Count > 0 && _shortCacheLookup.TryGetValue(descriptor, out var entry))
|
||||||
|
{
|
||||||
|
if (entry.InvalidatedSequence == entry.Texture.InvalidatedSequence)
|
||||||
|
{
|
||||||
|
return entry.Texture;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_shortCacheLookup.Remove(descriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a texture from the short duration cache.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="texture">Texture to remove from the short cache</param>
|
||||||
|
public void RemoveShortCache(Texture texture)
|
||||||
|
{
|
||||||
|
bool removed = _shortCache.Remove(texture.ShortCacheEntry);
|
||||||
|
removed |= _shortCacheBuilder.Remove(texture.ShortCacheEntry);
|
||||||
|
|
||||||
|
if (removed)
|
||||||
|
{
|
||||||
|
texture.DecrementReferenceCount();
|
||||||
|
|
||||||
|
_shortCacheLookup.Remove(texture.ShortCacheEntry.Descriptor);
|
||||||
|
texture.ShortCacheEntry = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a texture to the short duration cache.
|
||||||
|
/// It starts in the builder set, and it is moved into the deletion set on next process.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="texture">Texture to add to the short cache</param>
|
||||||
|
/// <param name="descriptor">Last used texture descriptor</param>
|
||||||
|
public void AddShortCache(Texture texture, ref TextureDescriptor descriptor)
|
||||||
|
{
|
||||||
|
var entry = new ShortTextureCacheEntry(descriptor, texture);
|
||||||
|
|
||||||
|
_shortCacheBuilder.Add(entry);
|
||||||
|
_shortCacheLookup.Add(entry.Descriptor, entry);
|
||||||
|
|
||||||
|
texture.ShortCacheEntry = entry;
|
||||||
|
|
||||||
|
texture.IncrementReferenceCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delete textures from the short duration cache.
|
||||||
|
/// Moves the builder set to be deleted on next process.
|
||||||
|
/// </summary>
|
||||||
|
public void ProcessShortCache()
|
||||||
|
{
|
||||||
|
HashSet<ShortTextureCacheEntry> toRemove = _shortCache;
|
||||||
|
|
||||||
|
foreach (var entry in toRemove)
|
||||||
|
{
|
||||||
|
entry.Texture.DecrementReferenceCount();
|
||||||
|
|
||||||
|
_shortCacheLookup.Remove(entry.Descriptor);
|
||||||
|
entry.Texture.ShortCacheEntry = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
toRemove.Clear();
|
||||||
|
_shortCache = _shortCacheBuilder;
|
||||||
|
_shortCacheBuilder = toRemove;
|
||||||
|
}
|
||||||
|
|
||||||
public IEnumerator<Texture> GetEnumerator()
|
public IEnumerator<Texture> GetEnumerator()
|
||||||
{
|
{
|
||||||
return _textures.GetEnumerator();
|
return _textures.GetEnumerator();
|
||||||
|
@@ -91,7 +91,17 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <returns>A reference to the descriptor</returns>
|
/// <returns>A reference to the descriptor</returns>
|
||||||
public ref readonly T2 GetDescriptorRef(int id)
|
public ref readonly T2 GetDescriptorRef(int id)
|
||||||
{
|
{
|
||||||
return ref MemoryMarshal.Cast<byte, T2>(PhysicalMemory.GetSpan(Address + (ulong)id * DescriptorSize, DescriptorSize))[0];
|
return ref GetDescriptorRefAddress(Address + (ulong)id * DescriptorSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a reference to the descriptor for a given address.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">Address of the descriptor</param>
|
||||||
|
/// <returns>A reference to the descriptor</returns>
|
||||||
|
public ref readonly T2 GetDescriptorRefAddress(ulong address)
|
||||||
|
{
|
||||||
|
return ref MemoryMarshal.Cast<byte, T2>(PhysicalMemory.GetSpan(address, DescriptorSize))[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -138,6 +138,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
public LinkedListNode<Texture> CacheNode { get; set; }
|
public LinkedListNode<Texture> CacheNode { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
/// Entry for this texture in the short duration cache, if present.
|
||||||
|
/// </summary>
|
||||||
|
public ShortTextureCacheEntry ShortCacheEntry { get; set; }
|
||||||
|
|
||||||
/// Physical memory ranges where the texture data is located.
|
/// Physical memory ranges where the texture data is located.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MultiRange Range { get; private set; }
|
public MultiRange Range { get; private set; }
|
||||||
@@ -1555,6 +1559,20 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
_poolOwners.Add(new TexturePoolOwner { Pool = pool, ID = id });
|
_poolOwners.Add(new TexturePoolOwner { Pool = pool, ID = id });
|
||||||
}
|
}
|
||||||
_referenceCount++;
|
_referenceCount++;
|
||||||
|
|
||||||
|
if (ShortCacheEntry != null)
|
||||||
|
{
|
||||||
|
_physicalMemory.TextureCache.RemoveShortCache(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that the texture has one reference left, and will delete on reference decrement.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if there is one reference remaining, false otherwise</returns>
|
||||||
|
public bool HasOneReference()
|
||||||
|
{
|
||||||
|
return _referenceCount == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1624,6 +1642,14 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
_poolOwners.Clear();
|
_poolOwners.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ShortCacheEntry != null && _context.IsGpuThread())
|
||||||
|
{
|
||||||
|
// If this is called from another thread (unmapped), the short cache will
|
||||||
|
// have to remove this texture on a future tick.
|
||||||
|
|
||||||
|
_physicalMemory.TextureCache.RemoveShortCache(this);
|
||||||
|
}
|
||||||
|
|
||||||
InvalidatedSequence++;
|
InvalidatedSequence++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -894,6 +894,16 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempt to find a texture on the short duration cache.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="descriptor">The texture descriptor</param>
|
||||||
|
/// <returns>The texture if found, null otherwise</returns>
|
||||||
|
public Texture FindShortCache(in TextureDescriptor descriptor)
|
||||||
|
{
|
||||||
|
return _cache.FindShortCache(descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to find an existing texture matching the given buffer copy destination. If none is found, returns null.
|
/// Tries to find an existing texture matching the given buffer copy destination. If none is found, returns null.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1178,6 +1188,33 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
_cache.RemoveDeferred(texture);
|
_cache.RemoveDeferred(texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a texture to the short duration cache. This typically keeps it alive for two ticks.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="texture">Texture to add to the short cache</param>
|
||||||
|
/// <param name="descriptor">Last used texture descriptor</param>
|
||||||
|
public void AddShortCache(Texture texture, ref TextureDescriptor descriptor)
|
||||||
|
{
|
||||||
|
_cache.AddShortCache(texture, ref descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a texture from the short duration cache.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="texture">Texture to remove from the short cache</param>
|
||||||
|
public void RemoveShortCache(Texture texture)
|
||||||
|
{
|
||||||
|
_cache.RemoveShortCache(texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ticks periodic elements of the texture cache.
|
||||||
|
/// </summary>
|
||||||
|
public void Tick()
|
||||||
|
{
|
||||||
|
_cache.ProcessShortCache();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Disposes all textures and samplers in the cache.
|
/// Disposes all textures and samplers in the cache.
|
||||||
/// It's an error to use the texture cache after disposal.
|
/// It's an error to use the texture cache after disposal.
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.Intrinsics;
|
using System.Runtime.Intrinsics;
|
||||||
|
|
||||||
@@ -6,7 +7,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maxwell texture descriptor, as stored on the GPU texture pool memory region.
|
/// Maxwell texture descriptor, as stored on the GPU texture pool memory region.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
struct TextureDescriptor : ITextureDescriptor
|
struct TextureDescriptor : ITextureDescriptor, IEquatable<TextureDescriptor>
|
||||||
{
|
{
|
||||||
#pragma warning disable CS0649
|
#pragma warning disable CS0649
|
||||||
public uint Word0;
|
public uint Word0;
|
||||||
@@ -249,5 +250,24 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
return Unsafe.As<TextureDescriptor, Vector256<byte>>(ref this).Equals(Unsafe.As<TextureDescriptor, Vector256<byte>>(ref other));
|
return Unsafe.As<TextureDescriptor, Vector256<byte>>(ref this).Equals(Unsafe.As<TextureDescriptor, Vector256<byte>>(ref other));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if two descriptors are equal.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="other">The descriptor to compare against</param>
|
||||||
|
/// <returns>True if they are equal, false otherwise</returns>
|
||||||
|
public bool Equals(TextureDescriptor other)
|
||||||
|
{
|
||||||
|
return Equals(ref other);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a hash code for this descriptor.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The hash code for this descriptor.</returns>
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return Unsafe.As<TextureDescriptor, Vector256<byte>>(ref this).GetHashCode();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -50,6 +50,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
ref readonly TextureDescriptor descriptor = ref GetDescriptorRef(id);
|
ref readonly TextureDescriptor descriptor = ref GetDescriptorRef(id);
|
||||||
|
|
||||||
|
if (texture == null)
|
||||||
|
{
|
||||||
|
texture = PhysicalMemory.TextureCache.FindShortCache(descriptor);
|
||||||
|
|
||||||
if (texture == null)
|
if (texture == null)
|
||||||
{
|
{
|
||||||
TextureInfo info = GetInfo(descriptor, out int layerSize);
|
TextureInfo info = GetInfo(descriptor, out int layerSize);
|
||||||
@@ -63,6 +67,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
return ref descriptor;
|
return ref descriptor;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
texture.IncrementReferenceCount(this, id);
|
texture.IncrementReferenceCount(this, id);
|
||||||
|
|
||||||
@@ -208,15 +213,21 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
if (texture != null)
|
if (texture != null)
|
||||||
{
|
{
|
||||||
TextureDescriptor descriptor = PhysicalMemory.Read<TextureDescriptor>(address);
|
ref TextureDescriptor cachedDescriptor = ref DescriptorCache[id];
|
||||||
|
ref readonly TextureDescriptor descriptor = ref GetDescriptorRefAddress(address);
|
||||||
|
|
||||||
// If the descriptors are the same, the texture is the same,
|
// If the descriptors are the same, the texture is the same,
|
||||||
// we don't need to remove as it was not modified. Just continue.
|
// we don't need to remove as it was not modified. Just continue.
|
||||||
if (descriptor.Equals(ref DescriptorCache[id]))
|
if (descriptor.Equals(ref cachedDescriptor))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (texture.HasOneReference())
|
||||||
|
{
|
||||||
|
_channel.MemoryManager.Physical.TextureCache.AddShortCache(texture, ref cachedDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
texture.DecrementReferenceCount(this, id);
|
texture.DecrementReferenceCount(this, id);
|
||||||
|
|
||||||
Items[id] = null;
|
Items[id] = null;
|
||||||
|
@@ -204,6 +204,8 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
|
|
||||||
Texture texture = pt.Cache.FindOrCreateTexture(null, TextureSearchFlags.WithUpscale, pt.Info, 0, null, pt.Range);
|
Texture texture = pt.Cache.FindOrCreateTexture(null, TextureSearchFlags.WithUpscale, pt.Info, 0, null, pt.Range);
|
||||||
|
|
||||||
|
pt.Cache.Tick();
|
||||||
|
|
||||||
texture.SynchronizeMemory();
|
texture.SynchronizeMemory();
|
||||||
|
|
||||||
ImageCrop crop = pt.Crop;
|
ImageCrop crop = pt.Crop;
|
||||||
|
@@ -7,65 +7,33 @@ namespace Ryujinx.Ui.Helper
|
|||||||
{
|
{
|
||||||
public static int TimePlayedSort(ITreeModel model, TreeIter a, TreeIter b)
|
public static int TimePlayedSort(ITreeModel model, TreeIter a, TreeIter b)
|
||||||
{
|
{
|
||||||
string aValue = model.GetValue(a, 5).ToString();
|
static string ReverseFormat(string time)
|
||||||
string bValue = model.GetValue(b, 5).ToString();
|
|
||||||
float aFloat;
|
|
||||||
float bFloat;
|
|
||||||
|
|
||||||
if (aValue.Length > 7 && aValue[^7..] == "minutes")
|
|
||||||
{
|
{
|
||||||
aValue = aValue.Replace("minutes", "");
|
if (time == "Never")
|
||||||
aFloat = (float.Parse(aValue) * 60);
|
|
||||||
}
|
|
||||||
else if (aValue.Length > 5 && aValue[^5..] == "hours")
|
|
||||||
{
|
{
|
||||||
aValue = aValue.Replace("hours", "");
|
return "00";
|
||||||
aFloat = (float.Parse(aValue) * 3600);
|
|
||||||
}
|
|
||||||
else if (aValue.Length > 4 && aValue[^4..] == "days")
|
|
||||||
{
|
|
||||||
aValue = aValue.Replace("days", "");
|
|
||||||
aFloat = (float.Parse(aValue) * 86400);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
aValue = aValue.Replace("seconds", "");
|
|
||||||
aFloat = float.Parse(aValue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bValue.Length > 7 && bValue[^7..] == "minutes")
|
var numbers = time.Split(new char[] { 'd', 'h', 'm' });
|
||||||
|
|
||||||
|
time = time.Replace(" ", "").Replace("d", ".").Replace("h", ":").Replace("m", "");
|
||||||
|
|
||||||
|
if (numbers.Length == 2)
|
||||||
{
|
{
|
||||||
bValue = bValue.Replace("minutes", "");
|
return $"00.00:{time}";
|
||||||
bFloat = (float.Parse(bValue) * 60);
|
|
||||||
}
|
}
|
||||||
else if (bValue.Length > 5 && bValue[^5..] == "hours")
|
else if (numbers.Length == 3)
|
||||||
{
|
{
|
||||||
bValue = bValue.Replace("hours", "");
|
return $"00.{time}";
|
||||||
bFloat = (float.Parse(bValue) * 3600);
|
|
||||||
}
|
|
||||||
else if (bValue.Length > 4 && bValue[^4..] == "days")
|
|
||||||
{
|
|
||||||
bValue = bValue.Replace("days", "");
|
|
||||||
bFloat = (float.Parse(bValue) * 86400);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bValue = bValue[0..^8];
|
|
||||||
bFloat = float.Parse(bValue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aFloat > bFloat)
|
return time;
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
else if (bFloat > aFloat)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string aValue = ReverseFormat(model.GetValue(a, 5).ToString());
|
||||||
|
string bValue = ReverseFormat(model.GetValue(b, 5).ToString());
|
||||||
|
|
||||||
|
return TimeSpan.Compare(TimeSpan.Parse(aValue), TimeSpan.Parse(bValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int LastPlayedSort(ITreeModel model, TreeIter a, TreeIter b)
|
public static int LastPlayedSort(ITreeModel model, TreeIter a, TreeIter b)
|
||||||
|
Reference in New Issue
Block a user