From 3c3bcd82fe6dfa8bdc2c9a9f33724ebfacd7dd40 Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Wed, 27 Jul 2022 21:07:48 -0300
Subject: Add a sampler pool cache and improve texture pool cache (#3487)

* Add a sampler pool cache and improve texture pool cache

* Increase disposal timestamp delta more to be on the safe side

* Nits

* Use abstract class for PoolCache, remove factory callback
---
 Ryujinx.Graphics.Gpu/Image/PoolCache.cs | 129 ++++++++++++++++++++++++++++++++
 1 file changed, 129 insertions(+)
 create mode 100644 Ryujinx.Graphics.Gpu/Image/PoolCache.cs

(limited to 'Ryujinx.Graphics.Gpu/Image/PoolCache.cs')

diff --git a/Ryujinx.Graphics.Gpu/Image/PoolCache.cs b/Ryujinx.Graphics.Gpu/Image/PoolCache.cs
new file mode 100644
index 00000000..e1493f38
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Image/PoolCache.cs
@@ -0,0 +1,129 @@
+using System;
+using System.Collections.Generic;
+
+namespace Ryujinx.Graphics.Gpu.Image
+{
+    /// <summary>
+    /// Resource pool interface.
+    /// </summary>
+    /// <typeparam name="T">Resource pool type</typeparam>
+    interface IPool<T>
+    {
+        /// <summary>
+        /// Start address of the pool in memory.
+        /// </summary>
+        ulong Address { get; }
+
+        /// <summary>
+        /// Linked list node used on the texture pool cache.
+        /// </summary>
+        LinkedListNode<T> CacheNode { get; set; }
+
+        /// <summary>
+        /// Timestamp set on the last use of the pool by the cache.
+        /// </summary>
+        ulong CacheTimestamp { get; set; }
+    }
+
+    /// <summary>
+    /// Pool cache.
+    /// This can keep multiple pools, and return the current one as needed.
+    /// </summary>
+    abstract class PoolCache<T> : IDisposable where T : IPool<T>, IDisposable
+    {
+        private const int MaxCapacity = 2;
+        private const ulong MinDeltaForRemoval = 20000;
+
+        private readonly GpuContext _context;
+        private readonly LinkedList<T> _pools;
+        private ulong _currentTimestamp;
+
+        /// <summary>
+        /// Constructs a new instance of the pool.
+        /// </summary>
+        /// <param name="context">GPU context that the texture pool belongs to</param>
+        public PoolCache(GpuContext context)
+        {
+            _context = context;
+            _pools = new LinkedList<T>();
+        }
+
+        /// <summary>
+        /// Increments the internal timestamp of the cache that is used to decide when old resources will be deleted.
+        /// </summary>
+        public void Tick()
+        {
+            _currentTimestamp++;
+        }
+
+        /// <summary>
+        /// Finds a cache texture pool, or creates a new one if not found.
+        /// </summary>
+        /// <param name="channel">GPU channel that the texture pool cache belongs to</param>
+        /// <param name="address">Start address of the texture pool</param>
+        /// <param name="maximumId">Maximum ID of the texture pool</param>
+        /// <returns>The found or newly created texture pool</returns>
+        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<T> 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;
+        }
+
+        /// <summary>
+        /// Creates a new instance of the pool.
+        /// </summary>
+        /// <param name="context">GPU context that the pool belongs to</param>
+        /// <param name="channel">GPU channel that the pool belongs to</param>
+        /// <param name="address">Address of the pool in guest memory</param>
+        /// <param name="maximumId">Maximum ID of the pool (equal to maximum minus one)</param>
+        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();
+        }
+    }
+}
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2