AutoDeleteCache.cs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. using System.Collections;
  2. using System.Collections.Concurrent;
  3. using System.Collections.Generic;
  4. namespace Ryujinx.Graphics.Gpu.Image
  5. {
  6. /// <summary>
  7. /// An entry on the short duration texture cache.
  8. /// </summary>
  9. class ShortTextureCacheEntry
  10. {
  11. public readonly TextureDescriptor Descriptor;
  12. public readonly int InvalidatedSequence;
  13. public readonly Texture Texture;
  14. /// <summary>
  15. /// Create a new entry on the short duration texture cache.
  16. /// </summary>
  17. /// <param name="descriptor">Last descriptor that referenced the texture</param>
  18. /// <param name="texture">The texture</param>
  19. public ShortTextureCacheEntry(TextureDescriptor descriptor, Texture texture)
  20. {
  21. Descriptor = descriptor;
  22. InvalidatedSequence = texture.InvalidatedSequence;
  23. Texture = texture;
  24. }
  25. }
  26. /// <summary>
  27. /// A texture cache that automatically removes older textures that are not used for some time.
  28. /// The cache works with a rotated list with a fixed size. When new textures are added, the
  29. /// old ones at the bottom of the list are deleted.
  30. /// </summary>
  31. class AutoDeleteCache : IEnumerable<Texture>
  32. {
  33. private const int MinCountForDeletion = 32;
  34. private const int MaxCapacity = 2048;
  35. private const ulong MaxTextureSizeCapacity = 512 * 1024 * 1024; // MB;
  36. private readonly LinkedList<Texture> _textures;
  37. private ulong _totalSize;
  38. private HashSet<ShortTextureCacheEntry> _shortCacheBuilder;
  39. private HashSet<ShortTextureCacheEntry> _shortCache;
  40. private Dictionary<TextureDescriptor, ShortTextureCacheEntry> _shortCacheLookup;
  41. /// <summary>
  42. /// Creates a new instance of the automatic deletion cache.
  43. /// </summary>
  44. public AutoDeleteCache()
  45. {
  46. _textures = new LinkedList<Texture>();
  47. _shortCacheBuilder = new HashSet<ShortTextureCacheEntry>();
  48. _shortCache = new HashSet<ShortTextureCacheEntry>();
  49. _shortCacheLookup = new Dictionary<TextureDescriptor, ShortTextureCacheEntry>();
  50. }
  51. /// <summary>
  52. /// Adds a new texture to the cache, even if the texture added is already on the cache.
  53. /// </summary>
  54. /// <remarks>
  55. /// Using this method is only recommended if you know that the texture is not yet on the cache,
  56. /// otherwise it would store the same texture more than once.
  57. /// </remarks>
  58. /// <param name="texture">The texture to be added to the cache</param>
  59. public void Add(Texture texture)
  60. {
  61. _totalSize += texture.Size;
  62. texture.IncrementReferenceCount();
  63. texture.CacheNode = _textures.AddLast(texture);
  64. if (_textures.Count > MaxCapacity ||
  65. (_totalSize > MaxTextureSizeCapacity && _textures.Count >= MinCountForDeletion))
  66. {
  67. RemoveLeastUsedTexture();
  68. }
  69. }
  70. /// <summary>
  71. /// Adds a new texture to the cache, or just moves it to the top of the list if the
  72. /// texture is already on the cache.
  73. /// </summary>
  74. /// <remarks>
  75. /// Moving the texture to the top of the list prevents it from being deleted,
  76. /// as the textures on the bottom of the list are deleted when new ones are added.
  77. /// </remarks>
  78. /// <param name="texture">The texture to be added, or moved to the top</param>
  79. public void Lift(Texture texture)
  80. {
  81. if (texture.CacheNode != null)
  82. {
  83. if (texture.CacheNode != _textures.Last)
  84. {
  85. _textures.Remove(texture.CacheNode);
  86. texture.CacheNode = _textures.AddLast(texture);
  87. }
  88. if (_totalSize > MaxTextureSizeCapacity && _textures.Count >= MinCountForDeletion)
  89. {
  90. RemoveLeastUsedTexture();
  91. }
  92. }
  93. else
  94. {
  95. Add(texture);
  96. }
  97. }
  98. /// <summary>
  99. /// Removes the least used texture from the cache.
  100. /// </summary>
  101. private void RemoveLeastUsedTexture()
  102. {
  103. Texture oldestTexture = _textures.First.Value;
  104. _totalSize -= oldestTexture.Size;
  105. if (!oldestTexture.CheckModified(false))
  106. {
  107. // The texture must be flushed if it falls out of the auto delete cache.
  108. // Flushes out of the auto delete cache do not trigger write tracking,
  109. // as it is expected that other overlapping textures exist that have more up-to-date contents.
  110. oldestTexture.Group.SynchronizeDependents(oldestTexture);
  111. oldestTexture.FlushModified(false);
  112. }
  113. _textures.RemoveFirst();
  114. oldestTexture.DecrementReferenceCount();
  115. oldestTexture.CacheNode = null;
  116. }
  117. /// <summary>
  118. /// Removes a texture from the cache.
  119. /// </summary>
  120. /// <param name="texture">The texture to be removed from the cache</param>
  121. /// <param name="flush">True to remove the texture if it was on the cache</param>
  122. /// <returns>True if the texture was found and removed, false otherwise</returns>
  123. public bool Remove(Texture texture, bool flush)
  124. {
  125. if (texture.CacheNode == null)
  126. {
  127. return false;
  128. }
  129. // Remove our reference to this texture.
  130. if (flush)
  131. {
  132. texture.FlushModified(false);
  133. }
  134. _textures.Remove(texture.CacheNode);
  135. _totalSize -= texture.Size;
  136. texture.CacheNode = null;
  137. return texture.DecrementReferenceCount();
  138. }
  139. /// <summary>
  140. /// Attempt to find a texture on the short duration cache.
  141. /// </summary>
  142. /// <param name="descriptor">The texture descriptor</param>
  143. /// <returns>The texture if found, null otherwise</returns>
  144. public Texture FindShortCache(in TextureDescriptor descriptor)
  145. {
  146. if (_shortCacheLookup.Count > 0 && _shortCacheLookup.TryGetValue(descriptor, out var entry))
  147. {
  148. if (entry.InvalidatedSequence == entry.Texture.InvalidatedSequence)
  149. {
  150. return entry.Texture;
  151. }
  152. else
  153. {
  154. _shortCacheLookup.Remove(descriptor);
  155. }
  156. }
  157. return null;
  158. }
  159. /// <summary>
  160. /// Removes a texture from the short duration cache.
  161. /// </summary>
  162. /// <param name="texture">Texture to remove from the short cache</param>
  163. public void RemoveShortCache(Texture texture)
  164. {
  165. bool removed = _shortCache.Remove(texture.ShortCacheEntry);
  166. removed |= _shortCacheBuilder.Remove(texture.ShortCacheEntry);
  167. if (removed)
  168. {
  169. texture.DecrementReferenceCount();
  170. _shortCacheLookup.Remove(texture.ShortCacheEntry.Descriptor);
  171. texture.ShortCacheEntry = null;
  172. }
  173. }
  174. /// <summary>
  175. /// Adds a texture to the short duration cache.
  176. /// It starts in the builder set, and it is moved into the deletion set on next process.
  177. /// </summary>
  178. /// <param name="texture">Texture to add to the short cache</param>
  179. /// <param name="descriptor">Last used texture descriptor</param>
  180. public void AddShortCache(Texture texture, ref TextureDescriptor descriptor)
  181. {
  182. var entry = new ShortTextureCacheEntry(descriptor, texture);
  183. _shortCacheBuilder.Add(entry);
  184. _shortCacheLookup.Add(entry.Descriptor, entry);
  185. texture.ShortCacheEntry = entry;
  186. texture.IncrementReferenceCount();
  187. }
  188. /// <summary>
  189. /// Delete textures from the short duration cache.
  190. /// Moves the builder set to be deleted on next process.
  191. /// </summary>
  192. public void ProcessShortCache()
  193. {
  194. HashSet<ShortTextureCacheEntry> toRemove = _shortCache;
  195. foreach (var entry in toRemove)
  196. {
  197. entry.Texture.DecrementReferenceCount();
  198. _shortCacheLookup.Remove(entry.Descriptor);
  199. entry.Texture.ShortCacheEntry = null;
  200. }
  201. toRemove.Clear();
  202. _shortCache = _shortCacheBuilder;
  203. _shortCacheBuilder = toRemove;
  204. }
  205. public IEnumerator<Texture> GetEnumerator()
  206. {
  207. return _textures.GetEnumerator();
  208. }
  209. IEnumerator IEnumerable.GetEnumerator()
  210. {
  211. return _textures.GetEnumerator();
  212. }
  213. }
  214. }