aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs111
-rw-r--r--Ryujinx.Graphics.Gpu/Image/Pool.cs12
-rw-r--r--Ryujinx.Graphics.Gpu/Image/Texture.cs26
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureCache.cs37
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs22
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TexturePool.cs29
-rw-r--r--Ryujinx.Graphics.Gpu/Window.cs2
7 files changed, 228 insertions, 11 deletions
diff --git a/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs b/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs
index 4a1615f0..379eb715 100644
--- a/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs
+++ b/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs
@@ -5,6 +5,28 @@ using System.Collections.Generic;
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>
/// 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
/// old ones at the bottom of the list are deleted.
@@ -16,6 +38,11 @@ namespace Ryujinx.Graphics.Gpu.Image
private readonly LinkedList<Texture> _textures;
private readonly ConcurrentQueue<Texture> _deferredRemovals;
+ private HashSet<ShortTextureCacheEntry> _shortCacheBuilder;
+ private HashSet<ShortTextureCacheEntry> _shortCache;
+
+ private Dictionary<TextureDescriptor, ShortTextureCacheEntry> _shortCacheLookup;
+
/// <summary>
/// Creates a new instance of the automatic deletion cache.
/// </summary>
@@ -23,6 +50,11 @@ namespace Ryujinx.Graphics.Gpu.Image
{
_textures = new LinkedList<Texture>();
_deferredRemovals = new ConcurrentQueue<Texture>();
+
+ _shortCacheBuilder = new HashSet<ShortTextureCacheEntry>();
+ _shortCache = new HashSet<ShortTextureCacheEntry>();
+
+ _shortCacheLookup = new Dictionary<TextureDescriptor, ShortTextureCacheEntry>();
}
/// <summary>
@@ -130,6 +162,85 @@ namespace Ryujinx.Graphics.Gpu.Image
_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()
{
return _textures.GetEnumerator();
diff --git a/Ryujinx.Graphics.Gpu/Image/Pool.cs b/Ryujinx.Graphics.Gpu/Image/Pool.cs
index ddd69807..ee4c051f 100644
--- a/Ryujinx.Graphics.Gpu/Image/Pool.cs
+++ b/Ryujinx.Graphics.Gpu/Image/Pool.cs
@@ -91,7 +91,17 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <returns>A reference to the descriptor</returns>
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>
diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs
index f0c31be6..cfe57756 100644
--- a/Ryujinx.Graphics.Gpu/Image/Texture.cs
+++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs
@@ -138,6 +138,10 @@ namespace Ryujinx.Graphics.Gpu.Image
public LinkedListNode<Texture> CacheNode { get; set; }
/// <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.
/// </summary>
public MultiRange Range { get; private set; }
@@ -1555,6 +1559,20 @@ namespace Ryujinx.Graphics.Gpu.Image
_poolOwners.Add(new TexturePoolOwner { Pool = pool, ID = id });
}
_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>
@@ -1624,6 +1642,14 @@ namespace Ryujinx.Graphics.Gpu.Image
_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++;
}
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
index c020f4c8..49adecdc 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
@@ -895,6 +895,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>
/// Tries to find an existing texture matching the given buffer copy destination. If none is found, returns null.
/// </summary>
/// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
@@ -1179,6 +1189,33 @@ namespace Ryujinx.Graphics.Gpu.Image
}
/// <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>
/// Disposes all textures and samplers in the cache.
/// It's an error to use the texture cache after disposal.
/// </summary>
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs b/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs
index 52cc8ee0..3e35f8d2 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs
@@ -1,3 +1,4 @@
+using System;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
@@ -6,7 +7,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <summary>
/// Maxwell texture descriptor, as stored on the GPU texture pool memory region.
/// </summary>
- struct TextureDescriptor : ITextureDescriptor
+ struct TextureDescriptor : ITextureDescriptor, IEquatable<TextureDescriptor>
{
#pragma warning disable CS0649
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));
}
+
+ /// <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();
+ }
}
}
diff --git a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
index 4d2544e2..fc99fc99 100644
--- a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
@@ -52,16 +52,21 @@ namespace Ryujinx.Graphics.Gpu.Image
if (texture == null)
{
- TextureInfo info = GetInfo(descriptor, out int layerSize);
+ texture = PhysicalMemory.TextureCache.FindShortCache(descriptor);
- ProcessDereferenceQueue();
-
- texture = PhysicalMemory.TextureCache.FindOrCreateTexture(_channel.MemoryManager, TextureSearchFlags.ForSampler, info, layerSize);
-
- // If this happens, then the texture address is invalid, we can't add it to the cache.
if (texture == null)
{
- return ref descriptor;
+ TextureInfo info = GetInfo(descriptor, out int layerSize);
+
+ ProcessDereferenceQueue();
+
+ texture = PhysicalMemory.TextureCache.FindOrCreateTexture(_channel.MemoryManager, TextureSearchFlags.ForSampler, info, layerSize);
+
+ // If this happens, then the texture address is invalid, we can't add it to the cache.
+ if (texture == null)
+ {
+ return ref descriptor;
+ }
}
texture.IncrementReferenceCount(this, id);
@@ -208,15 +213,21 @@ namespace Ryujinx.Graphics.Gpu.Image
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,
// 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;
}
+ if (texture.HasOneReference())
+ {
+ _channel.MemoryManager.Physical.TextureCache.AddShortCache(texture, ref cachedDescriptor);
+ }
+
texture.DecrementReferenceCount(this, id);
Items[id] = null;
diff --git a/Ryujinx.Graphics.Gpu/Window.cs b/Ryujinx.Graphics.Gpu/Window.cs
index c116d946..90f8e40f 100644
--- a/Ryujinx.Graphics.Gpu/Window.cs
+++ b/Ryujinx.Graphics.Gpu/Window.cs
@@ -204,6 +204,8 @@ namespace Ryujinx.Graphics.Gpu
Texture texture = pt.Cache.FindOrCreateTexture(null, TextureSearchFlags.WithUpscale, pt.Info, 0, null, pt.Range);
+ pt.Cache.Tick();
+
texture.SynchronizeMemory();
ImageCrop crop = pt.Crop;