diff --git a/Ryujinx.Common/ReferenceEqualityComparer.cs b/Ryujinx.Common/ReferenceEqualityComparer.cs new file mode 100644 index 0000000000..d8ec29d83a --- /dev/null +++ b/Ryujinx.Common/ReferenceEqualityComparer.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace Ryujinx.Common +{ + public class ReferenceEqualityComparer : IEqualityComparer + where T : class + { + public bool Equals(T x, T y) + { + return x == y; + } + + public int GetHashCode([DisallowNull] T obj) + { + return obj.GetHashCode(); + } + } +} diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodCopyTexture.cs b/Ryujinx.Graphics.Gpu/Engine/MethodCopyTexture.cs index 8d1b2b714f..4900db1b01 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MethodCopyTexture.cs +++ b/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(); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/Methods.cs b/Ryujinx.Graphics.Gpu/Engine/Methods.cs index d9e7582b7a..a2c9876e42 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Methods.cs +++ b/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(); } } diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs index 7d5e9079da..7511bdfad3 100644 --- a/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -54,9 +54,14 @@ namespace Ryujinx.Graphics.Gpu.Image public LinkedListNode CacheNode { get; set; } /// - /// Texture data modified by the GPU. + /// Event to fire when texture data is modified by the GPU. /// - public bool Modified { get; set; } + public event Action Modified; + + /// + /// Event to fire when texture data is disposed. + /// + public event Action Disposed; /// /// Start address of the texture in guest memory. @@ -933,6 +938,14 @@ namespace Ryujinx.Graphics.Gpu.Image _layers = info.GetLayers(); } + /// + /// Signals that the texture has been modified. + /// + public void SignalModified() + { + Modified?.Invoke(this); + } + /// /// Replaces the host texture, while disposing of the old one if needed. /// @@ -1012,6 +1025,8 @@ namespace Ryujinx.Graphics.Gpu.Image _arrayViewTexture?.Dispose(); _arrayViewTexture = null; + + Disposed?.Invoke(this); } /// diff --git a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs index 1572719118..87fb4161e7 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs +++ b/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 _modified; + /// /// Constructs a new instance of the texture manager. /// @@ -57,6 +60,8 @@ namespace Ryujinx.Graphics.Gpu.Image _textureOverlaps = new Texture[OverlapsBufferInitialCapacity]; _cache = new AutoDeleteCache(); + + _modified = new HashSet(new ReferenceEqualityComparer()); } /// @@ -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; } + /// + /// Signaled when a cache texture is modified, and adds it to a set to be enumerated when flushing textures. + /// + /// The texture that was modified. + private void CacheTextureModified(Texture texture) + { + _modified.Add(texture); + } + + /// + /// Signaled when a cache texture is disposed, so it can be removed from the set of modified textures if present. + /// + /// The texture that was diosposed. + private void CacheTextureDisposed(Texture texture) + { + _modified.Remove(texture); + } + /// /// Resizes the temporary buffer used for range list intersection results, if it has grown too much. /// @@ -722,15 +747,14 @@ namespace Ryujinx.Graphics.Gpu.Image /// 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(); } /// @@ -740,15 +764,14 @@ namespace Ryujinx.Graphics.Gpu.Image /// The range size 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(); } /// @@ -772,6 +795,7 @@ namespace Ryujinx.Graphics.Gpu.Image { foreach (Texture texture in _textures) { + _modified.Remove(texture); texture.Dispose(); } }