/ src / Ryujinx.Graphics.Gpu / Image / PoolCache.cs
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  }