using System; using System.Collections.Generic; namespace Ryujinx.Graphics.Gpu.Image { /// /// Resource pool interface. /// /// Resource pool type interface IPool { /// /// Start address of the pool in memory. /// ulong Address { get; } /// /// Linked list node used on the texture pool cache. /// LinkedListNode CacheNode { get; set; } /// /// Timestamp set on the last use of the pool by the cache. /// ulong CacheTimestamp { get; set; } } /// /// Pool cache. /// This can keep multiple pools, and return the current one as needed. /// abstract class PoolCache : IDisposable where T : IPool, IDisposable { private const int MaxCapacity = 2; private const ulong MinDeltaForRemoval = 20000; private readonly GpuContext _context; private readonly LinkedList _pools; private ulong _currentTimestamp; /// /// Constructs a new instance of the pool. /// /// GPU context that the texture pool belongs to public PoolCache(GpuContext context) { _context = context; _pools = new LinkedList(); } /// /// Increments the internal timestamp of the cache that is used to decide when old resources will be deleted. /// public void Tick() { _currentTimestamp++; } /// /// Finds a cache texture pool, or creates a new one if not found. /// /// GPU channel that the texture pool cache belongs to /// Start address of the texture pool /// Maximum ID of the texture pool /// The found or newly created texture pool public T FindOrCreate(GpuChannel channel, ulong address, int maximumId) { // Remove old entries from the cache, if possible. while (_pools.Count > MaxCapacity && (_currentTimestamp - _pools.First.Value.CacheTimestamp) >= MinDeltaForRemoval) { T oldestPool = _pools.First.Value; _pools.RemoveFirst(); oldestPool.Dispose(); oldestPool.CacheNode = null; } T pool; // Try to find the pool on the cache. for (LinkedListNode node = _pools.First; node != null; node = node.Next) { pool = node.Value; if (pool.Address == address) { if (pool.CacheNode != _pools.Last) { _pools.Remove(pool.CacheNode); pool.CacheNode = _pools.AddLast(pool); } pool.CacheTimestamp = _currentTimestamp; return pool; } } // If not found, create a new one. pool = CreatePool(_context, channel, address, maximumId); pool.CacheNode = _pools.AddLast(pool); pool.CacheTimestamp = _currentTimestamp; return pool; } /// /// Creates a new instance of the pool. /// /// GPU context that the pool belongs to /// GPU channel that the pool belongs to /// Address of the pool in guest memory /// Maximum ID of the pool (equal to maximum minus one) protected abstract T CreatePool(GpuContext context, GpuChannel channel, ulong address, int maximumId); public void Dispose() { foreach (T pool in _pools) { pool.Dispose(); pool.CacheNode = null; } _pools.Clear(); } } }