AutoDeleteCache.cs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  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 MaxCapacity = 2048;
  34. private readonly LinkedList<Texture> _textures;
  35. private readonly ConcurrentQueue<Texture> _deferredRemovals;
  36. private HashSet<ShortTextureCacheEntry> _shortCacheBuilder;
  37. private HashSet<ShortTextureCacheEntry> _shortCache;
  38. private Dictionary<TextureDescriptor, ShortTextureCacheEntry> _shortCacheLookup;
  39. /// <summary>
  40. /// Creates a new instance of the automatic deletion cache.
  41. /// </summary>
  42. public AutoDeleteCache()
  43. {
  44. _textures = new LinkedList<Texture>();
  45. _deferredRemovals = new ConcurrentQueue<Texture>();
  46. _shortCacheBuilder = new HashSet<ShortTextureCacheEntry>();
  47. _shortCache = new HashSet<ShortTextureCacheEntry>();
  48. _shortCacheLookup = new Dictionary<TextureDescriptor, ShortTextureCacheEntry>();
  49. }
  50. /// <summary>
  51. /// Adds a new texture to the cache, even if the texture added is already on the cache.
  52. /// </summary>
  53. /// <remarks>
  54. /// Using this method is only recommended if you know that the texture is not yet on the cache,
  55. /// otherwise it would store the same texture more than once.
  56. /// </remarks>
  57. /// <param name="texture">The texture to be added to the cache</param>
  58. public void Add(Texture texture)
  59. {
  60. texture.IncrementReferenceCount();
  61. texture.CacheNode = _textures.AddLast(texture);
  62. if (_textures.Count > MaxCapacity)
  63. {
  64. Texture oldestTexture = _textures.First.Value;
  65. if (!oldestTexture.CheckModified(false))
  66. {
  67. // The texture must be flushed if it falls out of the auto delete cache.
  68. // Flushes out of the auto delete cache do not trigger write tracking,
  69. // as it is expected that other overlapping textures exist that have more up-to-date contents.
  70. oldestTexture.Group.SynchronizeDependents(oldestTexture);
  71. oldestTexture.FlushModified(false);
  72. }
  73. _textures.RemoveFirst();
  74. oldestTexture.DecrementReferenceCount();
  75. oldestTexture.CacheNode = null;
  76. }
  77. if (_deferredRemovals.Count > 0)
  78. {
  79. while (_deferredRemovals.TryDequeue(out Texture textureToRemove))
  80. {
  81. Remove(textureToRemove, false);
  82. }
  83. }
  84. }
  85. /// <summary>
  86. /// Adds a new texture to the cache, or just moves it to the top of the list if the
  87. /// texture is already on the cache.
  88. /// </summary>
  89. /// <remarks>
  90. /// Moving the texture to the top of the list prevents it from being deleted,
  91. /// as the textures on the bottom of the list are deleted when new ones are added.
  92. /// </remarks>
  93. /// <param name="texture">The texture to be added, or moved to the top</param>
  94. public void Lift(Texture texture)
  95. {
  96. if (texture.CacheNode != null)
  97. {
  98. if (texture.CacheNode != _textures.Last)
  99. {
  100. _textures.Remove(texture.CacheNode);
  101. texture.CacheNode = _textures.AddLast(texture);
  102. }
  103. }
  104. else
  105. {
  106. Add(texture);
  107. }
  108. }
  109. /// <summary>
  110. /// Removes a texture from the cache.
  111. /// </summary>
  112. /// <param name="texture">The texture to be removed from the cache</param>
  113. /// <param name="flush">True to remove the texture if it was on the cache</param>
  114. /// <returns>True if the texture was found and removed, false otherwise</returns>
  115. public bool Remove(Texture texture, bool flush)
  116. {
  117. if (texture.CacheNode == null)
  118. {
  119. return false;
  120. }
  121. // Remove our reference to this texture.
  122. if (flush)
  123. {
  124. texture.FlushModified(false);
  125. }
  126. _textures.Remove(texture.CacheNode);
  127. texture.CacheNode = null;
  128. return texture.DecrementReferenceCount();
  129. }
  130. /// <summary>
  131. /// Queues removal of a texture from the cache in a thread safe way.
  132. /// </summary>
  133. /// <param name="texture">The texture to be removed from the cache</param>
  134. public void RemoveDeferred(Texture texture)
  135. {
  136. _deferredRemovals.Enqueue(texture);
  137. }
  138. /// <summary>
  139. /// Attempt to find a texture on the short duration cache.
  140. /// </summary>
  141. /// <param name="descriptor">The texture descriptor</param>
  142. /// <returns>The texture if found, null otherwise</returns>
  143. public Texture FindShortCache(in TextureDescriptor descriptor)
  144. {
  145. if (_shortCacheLookup.Count > 0 && _shortCacheLookup.TryGetValue(descriptor, out var entry))
  146. {
  147. if (entry.InvalidatedSequence == entry.Texture.InvalidatedSequence)
  148. {
  149. return entry.Texture;
  150. }
  151. else
  152. {
  153. _shortCacheLookup.Remove(descriptor);
  154. }
  155. }
  156. return null;
  157. }
  158. /// <summary>
  159. /// Removes a texture from the short duration cache.
  160. /// </summary>
  161. /// <param name="texture">Texture to remove from the short cache</param>
  162. public void RemoveShortCache(Texture texture)
  163. {
  164. bool removed = _shortCache.Remove(texture.ShortCacheEntry);
  165. removed |= _shortCacheBuilder.Remove(texture.ShortCacheEntry);
  166. if (removed)
  167. {
  168. texture.DecrementReferenceCount();
  169. _shortCacheLookup.Remove(texture.ShortCacheEntry.Descriptor);
  170. texture.ShortCacheEntry = null;
  171. }
  172. }
  173. /// <summary>
  174. /// Adds a texture to the short duration cache.
  175. /// It starts in the builder set, and it is moved into the deletion set on next process.
  176. /// </summary>
  177. /// <param name="texture">Texture to add to the short cache</param>
  178. /// <param name="descriptor">Last used texture descriptor</param>
  179. public void AddShortCache(Texture texture, ref TextureDescriptor descriptor)
  180. {
  181. var entry = new ShortTextureCacheEntry(descriptor, texture);
  182. _shortCacheBuilder.Add(entry);
  183. _shortCacheLookup.Add(entry.Descriptor, entry);
  184. texture.ShortCacheEntry = entry;
  185. texture.IncrementReferenceCount();
  186. }
  187. /// <summary>
  188. /// Delete textures from the short duration cache.
  189. /// Moves the builder set to be deleted on next process.
  190. /// </summary>
  191. public void ProcessShortCache()
  192. {
  193. HashSet<ShortTextureCacheEntry> toRemove = _shortCache;
  194. foreach (var entry in toRemove)
  195. {
  196. entry.Texture.DecrementReferenceCount();
  197. _shortCacheLookup.Remove(entry.Descriptor);
  198. entry.Texture.ShortCacheEntry = null;
  199. }
  200. toRemove.Clear();
  201. _shortCache = _shortCacheBuilder;
  202. _shortCacheBuilder = toRemove;
  203. }
  204. public IEnumerator<Texture> GetEnumerator()
  205. {
  206. return _textures.GetEnumerator();
  207. }
  208. IEnumerator IEnumerable.GetEnumerator()
  209. {
  210. return _textures.GetEnumerator();
  211. }
  212. }
  213. }