diff --git a/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs b/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs
index 4a1615f043..379eb71596 100644
--- a/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs
+++ b/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs
@@ -4,6 +4,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
@@ -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 ddd6980773..ee4c051f4c 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 f0c31be6ff..cfe5775663 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 c020f4c825..49adecdca5 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
@@ -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>
         /// Tries to find an existing texture matching the given buffer copy destination. If none is found, returns null.
         /// </summary>
@@ -1178,6 +1188,33 @@ namespace Ryujinx.Graphics.Gpu.Image
             _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>
         /// Disposes all textures and samplers in the cache.
         /// It's an error to use the texture cache after disposal.
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs b/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs
index 52cc8ee011..3e35f8d2c0 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 4d2544e270..fc99fc9977 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 c116d94661..90f8e40f92 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;