From cee712105850ac3385cd0091a923438167433f9f Mon Sep 17 00:00:00 2001 From: TSR Berry <20988865+TSRBerry@users.noreply.github.com> Date: Sat, 8 Apr 2023 01:22:00 +0200 Subject: Move solution and projects to src --- src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs | 256 ++++++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs (limited to 'src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs') diff --git a/src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs b/src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs new file mode 100644 index 00000000..a0b9f57b --- /dev/null +++ b/src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs @@ -0,0 +1,256 @@ +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gpu.Image +{ + /// + /// An entry on the short duration texture cache. + /// + class ShortTextureCacheEntry + { + public readonly TextureDescriptor Descriptor; + public readonly int InvalidatedSequence; + public readonly Texture Texture; + + /// + /// Create a new entry on the short duration texture cache. + /// + /// Last descriptor that referenced the texture + /// The texture + public ShortTextureCacheEntry(TextureDescriptor descriptor, Texture texture) + { + Descriptor = descriptor; + InvalidatedSequence = texture.InvalidatedSequence; + Texture = texture; + } + } + + /// + /// 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 + /// old ones at the bottom of the list are deleted. + /// + 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 ulong _totalSize; + + private HashSet _shortCacheBuilder; + private HashSet _shortCache; + + private Dictionary _shortCacheLookup; + + /// + /// Creates a new instance of the automatic deletion cache. + /// + public AutoDeleteCache() + { + _textures = new LinkedList(); + + _shortCacheBuilder = new HashSet(); + _shortCache = new HashSet(); + + _shortCacheLookup = new Dictionary(); + } + + /// + /// Adds a new texture to the cache, even if the texture added is already on the cache. + /// + /// + /// Using this method is only recommended if you know that the texture is not yet on the cache, + /// otherwise it would store the same texture more than once. + /// + /// The texture to be added to the cache + public void Add(Texture texture) + { + _totalSize += texture.Size; + + texture.IncrementReferenceCount(); + texture.CacheNode = _textures.AddLast(texture); + + if (_textures.Count > MaxCapacity || + (_totalSize > MaxTextureSizeCapacity && _textures.Count >= MinCountForDeletion)) + { + RemoveLeastUsedTexture(); + } + } + + /// + /// Adds a new texture to the cache, or just moves it to the top of the list if the + /// texture is already on the cache. + /// + /// + /// Moving the texture to the top of the list prevents it from being deleted, + /// as the textures on the bottom of the list are deleted when new ones are added. + /// + /// The texture to be added, or moved to the top + public void Lift(Texture texture) + { + if (texture.CacheNode != null) + { + if (texture.CacheNode != _textures.Last) + { + _textures.Remove(texture.CacheNode); + + texture.CacheNode = _textures.AddLast(texture); + } + + if (_totalSize > MaxTextureSizeCapacity && _textures.Count >= MinCountForDeletion) + { + RemoveLeastUsedTexture(); + } + } + else + { + Add(texture); + } + } + + /// + /// 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. + /// + /// The texture to be removed from the cache + /// True to remove the texture if it was on the cache + /// True if the texture was found and removed, false otherwise + public bool Remove(Texture texture, bool flush) + { + if (texture.CacheNode == null) + { + return false; + } + + // Remove our reference to this texture. + if (flush) + { + texture.FlushModified(false); + } + + _textures.Remove(texture.CacheNode); + + _totalSize -= texture.Size; + + texture.CacheNode = null; + + return texture.DecrementReferenceCount(); + } + + /// + /// Attempt to find a texture on the short duration cache. + /// + /// The texture descriptor + /// The texture if found, null otherwise + 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; + } + + /// + /// Removes a texture from the short duration cache. + /// + /// Texture to remove from the short cache + 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; + } + } + + /// + /// 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. + /// + /// Texture to add to the short cache + /// Last used texture descriptor + 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(); + } + + /// + /// Delete textures from the short duration cache. + /// Moves the builder set to be deleted on next process. + /// + public void ProcessShortCache() + { + HashSet 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 GetEnumerator() + { + return _textures.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _textures.GetEnumerator(); + } + } +} \ No newline at end of file -- cgit v1.2.3