Browse Source

Only enumerate cached textures that are modified when flushing. (#918)

* Only enumarate cached textures that are modified when flushing, rather than all of them.

* Remove locking.

* Add missing clear.

* Remove texture from modified list when data is disposed.

In case the game does not call either flush method at any point.

* Add ReferenceEqualityComparer from jD for the HashSet
riperiperi 6 years ago
parent
commit
6db16b4110

+ 19 - 0
Ryujinx.Common/ReferenceEqualityComparer.cs

@@ -0,0 +1,19 @@
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Ryujinx.Common
+{
+    public class ReferenceEqualityComparer<T> : IEqualityComparer<T>
+        where T : class
+    {
+        public bool Equals(T x, T y)
+        {
+            return x == y;
+        }
+
+        public int GetHashCode([DisallowNull] T obj)
+        {
+            return obj.GetHashCode();
+        }
+    }
+}

+ 1 - 1
Ryujinx.Graphics.Gpu/Engine/MethodCopyTexture.cs

@@ -94,7 +94,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
                 srcTexture.HostTexture.CopyTo(dstTexture.HostTexture, srcRegion, dstRegion, linearFilter);
             }
 
-            dstTexture.Modified = true;
+            dstTexture.SignalModified();
         }
     }
 }

+ 2 - 2
Ryujinx.Graphics.Gpu/Engine/Methods.cs

@@ -286,7 +286,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
 
                 if (color != null)
                 {
-                    color.Modified = true;
+                    color.SignalModified();
                 }
             }
 
@@ -306,7 +306,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
 
             if (depthStencil != null)
             {
-                depthStencil.Modified = true;
+                depthStencil.SignalModified();
             }
         }
 

+ 17 - 2
Ryujinx.Graphics.Gpu/Image/Texture.cs

@@ -54,9 +54,14 @@ namespace Ryujinx.Graphics.Gpu.Image
         public LinkedListNode<Texture> CacheNode { get; set; }
 
         /// <summary>
-        /// Texture data modified by the GPU.
+        /// Event to fire when texture data is modified by the GPU.
         /// </summary>
-        public bool Modified { get; set; }
+        public event Action<Texture> Modified;
+
+        /// <summary>
+        /// Event to fire when texture data is disposed.
+        /// </summary>
+        public event Action<Texture> Disposed;
 
         /// <summary>
         /// Start address of the texture in guest memory.
@@ -933,6 +938,14 @@ namespace Ryujinx.Graphics.Gpu.Image
             _layers = info.GetLayers();
         }
 
+        /// <summary>
+        /// Signals that the texture has been modified.
+        /// </summary>
+        public void SignalModified()
+        {
+            Modified?.Invoke(this);
+        }
+
         /// <summary>
         /// Replaces the host texture, while disposing of the old one if needed.
         /// </summary>
@@ -1012,6 +1025,8 @@ namespace Ryujinx.Graphics.Gpu.Image
 
             _arrayViewTexture?.Dispose();
             _arrayViewTexture = null;
+
+            Disposed?.Invoke(this);
         }
 
         /// <summary>

+ 32 - 8
Ryujinx.Graphics.Gpu/Image/TextureManager.cs

@@ -5,6 +5,7 @@ using Ryujinx.Graphics.Gpu.Memory;
 using Ryujinx.Graphics.Gpu.State;
 using Ryujinx.Graphics.Texture;
 using System;
+using System.Collections.Generic;
 
 namespace Ryujinx.Graphics.Gpu.Image
 {
@@ -35,6 +36,8 @@ namespace Ryujinx.Graphics.Gpu.Image
 
         private readonly AutoDeleteCache _cache;
 
+        private readonly HashSet<Texture> _modified;
+
         /// <summary>
         /// Constructs a new instance of the texture manager.
         /// </summary>
@@ -57,6 +60,8 @@ namespace Ryujinx.Graphics.Gpu.Image
             _textureOverlaps = new Texture[OverlapsBufferInitialCapacity];
 
             _cache = new AutoDeleteCache();
+
+            _modified = new HashSet<Texture>(new ReferenceEqualityComparer<Texture>());
         }
 
         /// <summary>
@@ -579,6 +584,8 @@ namespace Ryujinx.Graphics.Gpu.Image
             if (!isSamplerTexture)
             {
                 _cache.Add(texture);
+                texture.Modified += CacheTextureModified;
+                texture.Disposed += CacheTextureDisposed;
             }
 
             _textures.Add(texture);
@@ -588,6 +595,24 @@ namespace Ryujinx.Graphics.Gpu.Image
             return texture;
         }
 
+        /// <summary>
+        /// Signaled when a cache texture is modified, and adds it to a set to be enumerated when flushing textures.
+        /// </summary>
+        /// <param name="texture">The texture that was modified.</param>
+        private void CacheTextureModified(Texture texture)
+        {
+            _modified.Add(texture);
+        }
+
+        /// <summary>
+        /// Signaled when a cache texture is disposed, so it can be removed from the set of modified textures if present.
+        /// </summary>
+        /// <param name="texture">The texture that was diosposed.</param>
+        private void CacheTextureDisposed(Texture texture)
+        {
+            _modified.Remove(texture);
+        }
+
         /// <summary>
         /// Resizes the temporary buffer used for range list intersection results, if it has grown too much.
         /// </summary>
@@ -722,15 +747,14 @@ namespace Ryujinx.Graphics.Gpu.Image
         /// </summary>
         public void Flush()
         {
-            foreach (Texture texture in _cache)
+            foreach (Texture texture in _modified)
             {
-                if (texture.Info.IsLinear && texture.Modified)
+                if (texture.Info.IsLinear)
                 {
                     texture.Flush();
-
-                    texture.Modified = false;
                 }
             }
+            _modified.Clear();
         }
 
         /// <summary>
@@ -740,15 +764,14 @@ namespace Ryujinx.Graphics.Gpu.Image
         /// <param name="size">The range size</param>
         public void Flush(ulong address, ulong size)
         {
-            foreach (Texture texture in _cache)
+            foreach (Texture texture in _modified)
             {
-                if (texture.OverlapsWith(address, size) && texture.Modified)
+                if (texture.OverlapsWith(address, size))
                 {
                     texture.Flush();
-
-                    texture.Modified = false;
                 }
             }
+            _modified.Clear();
         }
 
         /// <summary>
@@ -772,6 +795,7 @@ namespace Ryujinx.Graphics.Gpu.Image
         {
             foreach (Texture texture in _textures)
             {
+                _modified.Remove(texture);
                 texture.Dispose();
             }
         }