diff --git a/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs b/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs index 379eb715..a0b9f57b 100644 --- a/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs +++ b/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs @@ -33,10 +33,12 @@ namespace Ryujinx.Graphics.Gpu.Image /// class AutoDeleteCache : IEnumerable { + private const int MinCountForDeletion = 32; private const int MaxCapacity = 2048; + private const ulong MaxTextureSizeCapacity = 512 * 1024 * 1024; // MB; private readonly LinkedList _textures; - private readonly ConcurrentQueue _deferredRemovals; + private ulong _totalSize; private HashSet _shortCacheBuilder; private HashSet _shortCache; @@ -49,7 +51,6 @@ namespace Ryujinx.Graphics.Gpu.Image public AutoDeleteCache() { _textures = new LinkedList(); - _deferredRemovals = new ConcurrentQueue(); _shortCacheBuilder = new HashSet(); _shortCache = new HashSet(); @@ -67,37 +68,15 @@ namespace Ryujinx.Graphics.Gpu.Image /// The texture to be added to the cache public void Add(Texture texture) { - texture.IncrementReferenceCount(); + _totalSize += texture.Size; + texture.IncrementReferenceCount(); texture.CacheNode = _textures.AddLast(texture); - if (_textures.Count > MaxCapacity) + if (_textures.Count > MaxCapacity || + (_totalSize > MaxTextureSizeCapacity && _textures.Count >= MinCountForDeletion)) { - Texture oldestTexture = _textures.First.Value; - - if (!oldestTexture.CheckModified(false)) - { - // The texture must be flushed if it falls out of the auto delete cache. - // Flushes out of the auto delete cache do not trigger write tracking, - // as it is expected that other overlapping textures exist that have more up-to-date contents. - - oldestTexture.Group.SynchronizeDependents(oldestTexture); - oldestTexture.FlushModified(false); - } - - _textures.RemoveFirst(); - - oldestTexture.DecrementReferenceCount(); - - oldestTexture.CacheNode = null; - } - - if (_deferredRemovals.Count > 0) - { - while (_deferredRemovals.TryDequeue(out Texture textureToRemove)) - { - Remove(textureToRemove, false); - } + RemoveLeastUsedTexture(); } } @@ -120,6 +99,11 @@ namespace Ryujinx.Graphics.Gpu.Image texture.CacheNode = _textures.AddLast(texture); } + + if (_totalSize > MaxTextureSizeCapacity && _textures.Count >= MinCountForDeletion) + { + RemoveLeastUsedTexture(); + } } else { @@ -127,6 +111,31 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// + /// Removes the least used texture from the cache. + /// + private void RemoveLeastUsedTexture() + { + Texture oldestTexture = _textures.First.Value; + + _totalSize -= oldestTexture.Size; + + if (!oldestTexture.CheckModified(false)) + { + // The texture must be flushed if it falls out of the auto delete cache. + // Flushes out of the auto delete cache do not trigger write tracking, + // as it is expected that other overlapping textures exist that have more up-to-date contents. + + oldestTexture.Group.SynchronizeDependents(oldestTexture); + oldestTexture.FlushModified(false); + } + + _textures.RemoveFirst(); + + oldestTexture.DecrementReferenceCount(); + oldestTexture.CacheNode = null; + } + /// /// Removes a texture from the cache. /// @@ -148,20 +157,13 @@ namespace Ryujinx.Graphics.Gpu.Image _textures.Remove(texture.CacheNode); + _totalSize -= texture.Size; + texture.CacheNode = null; return texture.DecrementReferenceCount(); } - /// - /// Queues removal of a texture from the cache in a thread safe way. - /// - /// The texture to be removed from the cache - public void RemoveDeferred(Texture texture) - { - _deferredRemovals.Enqueue(texture); - } - /// /// Attempt to find a texture on the short duration cache. /// diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs index 352a828d..6c0de536 100644 --- a/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -1637,13 +1637,6 @@ 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); - } } /// diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs index 1d5b1851..27bec786 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs @@ -1079,19 +1079,6 @@ namespace Ryujinx.Graphics.Gpu.Image } } - /// - /// Queues the removal of a texture from the auto delete cache. - /// - /// - /// 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. - /// - /// The texture to be removed - public void QueueAutoDeleteCacheRemoval(Texture texture) - { - _cache.RemoveDeferred(texture); - } - /// /// Adds a texture to the short duration cache. This typically keeps it alive for two ticks. /// diff --git a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs index 896e11a5..942fa2f8 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs @@ -434,32 +434,6 @@ namespace Ryujinx.Graphics.Gpu.Image } } - /// - /// Checks if a texture was modified by the GPU. - /// - /// The texture to be checked - /// True if any region of the texture was modified by the GPU, false otherwise - 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; - } - /// /// Flush modified ranges for a given texture. ///