PoolCache.cs
1 using System; 2 using System.Collections.Generic; 3 4 namespace Ryujinx.Graphics.Gpu.Image 5 { 6 /// <summary> 7 /// Resource pool interface. 8 /// </summary> 9 /// <typeparam name="T">Resource pool type</typeparam> 10 interface IPool<T> 11 { 12 /// <summary> 13 /// Start address of the pool in memory. 14 /// </summary> 15 ulong Address { get; } 16 17 /// <summary> 18 /// Linked list node used on the texture pool cache. 19 /// </summary> 20 LinkedListNode<T> CacheNode { get; set; } 21 22 /// <summary> 23 /// Timestamp set on the last use of the pool by the cache. 24 /// </summary> 25 ulong CacheTimestamp { get; set; } 26 } 27 28 /// <summary> 29 /// Pool cache. 30 /// This can keep multiple pools, and return the current one as needed. 31 /// </summary> 32 abstract class PoolCache<T> : IDisposable where T : IPool<T>, IDisposable 33 { 34 private const int MaxCapacity = 2; 35 private const ulong MinDeltaForRemoval = 20000; 36 37 private readonly GpuContext _context; 38 private readonly LinkedList<T> _pools; 39 private ulong _currentTimestamp; 40 41 /// <summary> 42 /// Constructs a new instance of the pool. 43 /// </summary> 44 /// <param name="context">GPU context that the texture pool belongs to</param> 45 public PoolCache(GpuContext context) 46 { 47 _context = context; 48 _pools = new LinkedList<T>(); 49 } 50 51 /// <summary> 52 /// Increments the internal timestamp of the cache that is used to decide when old resources will be deleted. 53 /// </summary> 54 public void Tick() 55 { 56 _currentTimestamp++; 57 } 58 59 /// <summary> 60 /// Finds a cache texture pool, or creates a new one if not found. 61 /// </summary> 62 /// <param name="channel">GPU channel that the texture pool cache belongs to</param> 63 /// <param name="address">Start address of the texture pool</param> 64 /// <param name="maximumId">Maximum ID of the texture pool</param> 65 /// <param name="bindingsArrayCache">Cache of texture array bindings</param> 66 /// <returns>The found or newly created texture pool</returns> 67 public T FindOrCreate(GpuChannel channel, ulong address, int maximumId, TextureBindingsArrayCache bindingsArrayCache) 68 { 69 // Remove old entries from the cache, if possible. 70 while (_pools.Count > MaxCapacity && (_currentTimestamp - _pools.First.Value.CacheTimestamp) >= MinDeltaForRemoval) 71 { 72 T oldestPool = _pools.First.Value; 73 74 _pools.RemoveFirst(); 75 oldestPool.Dispose(); 76 oldestPool.CacheNode = null; 77 bindingsArrayCache.RemoveAllWithPool(oldestPool); 78 } 79 80 T pool; 81 82 // Try to find the pool on the cache. 83 for (LinkedListNode<T> node = _pools.First; node != null; node = node.Next) 84 { 85 pool = node.Value; 86 87 if (pool.Address == address) 88 { 89 if (pool.CacheNode != _pools.Last) 90 { 91 _pools.Remove(pool.CacheNode); 92 _pools.AddLast(pool.CacheNode); 93 } 94 95 pool.CacheTimestamp = _currentTimestamp; 96 97 return pool; 98 } 99 } 100 101 // If not found, create a new one. 102 pool = CreatePool(_context, channel, address, maximumId); 103 104 pool.CacheNode = _pools.AddLast(pool); 105 pool.CacheTimestamp = _currentTimestamp; 106 107 return pool; 108 } 109 110 /// <summary> 111 /// Creates a new instance of the pool. 112 /// </summary> 113 /// <param name="context">GPU context that the pool belongs to</param> 114 /// <param name="channel">GPU channel that the pool belongs to</param> 115 /// <param name="address">Address of the pool in guest memory</param> 116 /// <param name="maximumId">Maximum ID of the pool (equal to maximum minus one)</param> 117 protected abstract T CreatePool(GpuContext context, GpuChannel channel, ulong address, int maximumId); 118 119 public void Dispose() 120 { 121 foreach (T pool in _pools) 122 { 123 pool.Dispose(); 124 pool.CacheNode = null; 125 } 126 127 _pools.Clear(); 128 } 129 } 130 }