Procházet zdrojové kódy

Remove textures from cache on unmap if not mapped and modified (#4211)

gdkchan před 3 roky
rodič
revize
94a64f2aea

+ 26 - 0
Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs

@@ -1,4 +1,5 @@
 using System.Collections;
+using System.Collections.Concurrent;
 using System.Collections.Generic;
 
 namespace Ryujinx.Graphics.Gpu.Image
@@ -13,6 +14,7 @@ namespace Ryujinx.Graphics.Gpu.Image
         private const int MaxCapacity = 2048;
 
         private readonly LinkedList<Texture> _textures;
+        private readonly ConcurrentQueue<Texture> _deferredRemovals;
 
         /// <summary>
         /// Creates a new instance of the automatic deletion cache.
@@ -20,6 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Image
         public AutoDeleteCache()
         {
             _textures = new LinkedList<Texture>();
+            _deferredRemovals = new ConcurrentQueue<Texture>();
         }
 
         /// <summary>
@@ -56,6 +59,14 @@ namespace Ryujinx.Graphics.Gpu.Image
 
                 oldestTexture.CacheNode = null;
             }
+
+            if (_deferredRemovals.Count > 0)
+            {
+                while (_deferredRemovals.TryDequeue(out Texture textureToRemove))
+                {
+                    Remove(textureToRemove, false);
+                }
+            }
         }
 
         /// <summary>
@@ -84,6 +95,12 @@ namespace Ryujinx.Graphics.Gpu.Image
             }
         }
 
+        /// <summary>
+        /// Removes a texture from the cache.
+        /// </summary>
+        /// <param name="texture">The texture to be removed from the cache</param>
+        /// <param name="flush">True to remove the texture if it was on the cache</param>
+        /// <returns>True if the texture was found and removed, false otherwise</returns>
         public bool Remove(Texture texture, bool flush)
         {
             if (texture.CacheNode == null)
@@ -104,6 +121,15 @@ namespace Ryujinx.Graphics.Gpu.Image
             return texture.DecrementReferenceCount();
         }
 
+        /// <summary>
+        /// Queues removal of a texture from the cache in a thread safe way.
+        /// </summary>
+        /// <param name="texture">The texture to be removed from the cache</param>
+        public void RemoveDeferred(Texture texture)
+        {
+            _deferredRemovals.Enqueue(texture);
+        }
+
         public IEnumerator<Texture> GetEnumerator()
         {
             return _textures.GetEnumerator();

+ 7 - 0
Ryujinx.Graphics.Gpu/Image/Texture.cs

@@ -1676,6 +1676,13 @@ namespace Ryujinx.Graphics.Gpu.Image
             }
 
             RemoveFromPools(true);
+
+            // We only want to remove if there's no mapped region of the texture that was modified by the GPU,
+            // otherwise we could lose data.
+            if (!Group.AnyModified(this))
+            {
+                _physicalMemory.TextureCache.QueueAutoDeleteCacheRemoval(this);
+            }
         }
 
         /// <summary>

+ 13 - 0
Ryujinx.Graphics.Gpu/Image/TextureCache.cs

@@ -1165,6 +1165,19 @@ namespace Ryujinx.Graphics.Gpu.Image
             }
         }
 
+        /// <summary>
+        /// Queues the removal of a texture from the auto delete cache.
+        /// </summary>
+        /// <remarks>
+        /// This function is thread safe and can be called from any thread.
+        /// The texture will be deleted on the next time the cache is used.
+        /// </remarks>
+        /// <param name="texture">The texture to be removed</param>
+        public void QueueAutoDeleteCacheRemoval(Texture texture)
+        {
+            _cache.RemoveDeferred(texture);
+        }
+
         /// <summary>
         /// Disposes all textures and samplers in the cache.
         /// It's an error to use the texture cache after disposal.

+ 26 - 0
Ryujinx.Graphics.Gpu/Image/TextureGroup.cs

@@ -434,6 +434,32 @@ namespace Ryujinx.Graphics.Gpu.Image
             }
         }
 
+        /// <summary>
+        /// Checks if a texture was modified by the GPU.
+        /// </summary>
+        /// <param name="texture">The texture to be checked</param>
+        /// <returns>True if any region of the texture was modified by the GPU, false otherwise</returns>
+        public bool AnyModified(Texture texture)
+        {
+            bool anyModified = false;
+
+            EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
+            {
+                for (int i = 0; i < regionCount; i++)
+                {
+                    TextureGroupHandle group = _handles[baseHandle + i];
+
+                    if (group.Modified)
+                    {
+                        anyModified = true;
+                        break;
+                    }
+                }
+            });
+
+            return anyModified;
+        }
+
         /// <summary>
         /// Flush modified ranges for a given texture.
         /// </summary>