aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Ryujinx.Graphics.Gpu/GpuChannel.cs15
-rw-r--r--Ryujinx.Graphics.Gpu/Image/PoolCache.cs129
-rw-r--r--Ryujinx.Graphics.Gpu/Image/SamplerPool.cs15
-rw-r--r--Ryujinx.Graphics.Gpu/Image/SamplerPoolCache.cs30
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs164
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureManager.cs27
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TexturePool.cs11
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs64
8 files changed, 320 insertions, 135 deletions
diff --git a/Ryujinx.Graphics.Gpu/GpuChannel.cs b/Ryujinx.Graphics.Gpu/GpuChannel.cs
index b9d91f93..f3bdd576 100644
--- a/Ryujinx.Graphics.Gpu/GpuChannel.cs
+++ b/Ryujinx.Graphics.Gpu/GpuChannel.cs
@@ -59,9 +59,24 @@ namespace Ryujinx.Graphics.Gpu
{
oldMemoryManager.Physical.BufferCache.NotifyBuffersModified -= BufferManager.Rebind;
oldMemoryManager.Physical.DecrementReferenceCount();
+ oldMemoryManager.MemoryUnmapped -= MemoryUnmappedHandler;
}
memoryManager.Physical.BufferCache.NotifyBuffersModified += BufferManager.Rebind;
+ memoryManager.MemoryUnmapped += MemoryUnmappedHandler;
+
+ // Since the memory manager changed, make sure we will get pools from addresses of the new memory manager.
+ TextureManager.ReloadPools();
+ }
+
+ /// <summary>
+ /// Memory mappings change event handler.
+ /// </summary>
+ /// <param name="sender">Memory manager where the mappings changed</param>
+ /// <param name="e">Information about the region that is being changed</param>
+ private void MemoryUnmappedHandler(object sender, UnmapEventArgs e)
+ {
+ TextureManager.ReloadPools();
}
/// <summary>
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
diff --git a/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs b/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs
index e95800ad..eb7222f9 100644
--- a/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs
+++ b/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs
@@ -1,16 +1,27 @@
using Ryujinx.Graphics.Gpu.Memory;
+using System.Collections.Generic;
namespace Ryujinx.Graphics.Gpu.Image
{
/// <summary>
/// Sampler pool.
/// </summary>
- class SamplerPool : Pool<Sampler, SamplerDescriptor>
+ class SamplerPool : Pool<Sampler, SamplerDescriptor>, IPool<SamplerPool>
{
private float _forcedAnisotropy;
/// <summary>
- /// Constructs a new instance of the sampler pool.
+ /// Linked list node used on the sampler pool cache.
+ /// </summary>
+ public LinkedListNode<SamplerPool> CacheNode { get; set; }
+
+ /// <summary>
+ /// Timestamp used by the sampler pool cache, updated on every use of this sampler pool.
+ /// </summary>
+ public ulong CacheTimestamp { get; set; }
+
+ /// <summary>
+ /// Creates a new instance of the sampler pool.
/// </summary>
/// <param name="context">GPU context that the sampler pool belongs to</param>
/// <param name="physicalMemory">Physical memory where the sampler descriptors are mapped</param>
diff --git a/Ryujinx.Graphics.Gpu/Image/SamplerPoolCache.cs b/Ryujinx.Graphics.Gpu/Image/SamplerPoolCache.cs
new file mode 100644
index 00000000..3b3350fb
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Image/SamplerPoolCache.cs
@@ -0,0 +1,30 @@
+namespace Ryujinx.Graphics.Gpu.Image
+{
+ /// <summary>
+ /// Sampler pool cache.
+ /// This can keep multiple sampler pools, and return the current one as needed.
+ /// It is useful for applications that uses multiple sampler pools.
+ /// </summary>
+ class SamplerPoolCache : PoolCache<SamplerPool>
+ {
+ /// <summary>
+ /// Constructs a new instance of the texture pool.
+ /// </summary>
+ /// <param name="context">GPU context that the texture pool belongs to</param>
+ public SamplerPoolCache(GpuContext context) : base(context)
+ {
+ }
+
+ /// <summary>
+ /// Creates a new instance of the sampler pool.
+ /// </summary>
+ /// <param name="context">GPU context that the sampler pool belongs to</param>
+ /// <param name="channel">GPU channel that the texture pool belongs to</param>
+ /// <param name="address">Address of the sampler pool in guest memory</param>
+ /// <param name="maximumId">Maximum sampler ID of the sampler pool (equal to maximum samplers minus one)</param>
+ protected override SamplerPool CreatePool(GpuContext context, GpuChannel channel, ulong address, int maximumId)
+ {
+ return new SamplerPool(context, channel.MemoryManager.Physical, address, maximumId);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
index fcd23441..067a1f9f 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
@@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <summary>
/// Texture bindings manager.
/// </summary>
- class TextureBindingsManager : IDisposable
+ class TextureBindingsManager
{
private const int InitialTextureStateSize = 32;
private const int InitialImageStateSize = 8;
@@ -22,15 +22,17 @@ namespace Ryujinx.Graphics.Gpu.Image
private readonly bool _isCompute;
- private SamplerPool _samplerPool;
-
+ private ulong _texturePoolGpuVa;
+ private int _texturePoolMaximumId;
+ private TexturePool _texturePool;
+ private ulong _samplerPoolGpuVa;
+ private int _samplerPoolMaximumId;
private SamplerIndex _samplerIndex;
-
- private ulong _texturePoolAddress;
- private int _texturePoolMaximumId;
+ private SamplerPool _samplerPool;
private readonly GpuChannel _channel;
private readonly TexturePoolCache _texturePoolCache;
+ private readonly SamplerPoolCache _samplerPoolCache;
private TexturePool _cachedTexturePool;
private SamplerPool _cachedSamplerPool;
@@ -72,16 +74,25 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary>
/// <param name="context">The GPU context that the texture bindings manager belongs to</param>
/// <param name="channel">The GPU channel that the texture bindings manager belongs to</param>
- /// <param name="poolCache">Texture pools cache used to get texture pools from</param>
+ /// <param name="texturePoolCache">Texture pools cache used to get texture pools from</param>
+ /// <param name="samplerPoolCache">Sampler pools cache used to get sampler pools from</param>
/// <param name="scales">Array where the scales for the currently bound textures are stored</param>
/// <param name="isCompute">True if the bindings manager is used for the compute engine</param>
- public TextureBindingsManager(GpuContext context, GpuChannel channel, TexturePoolCache poolCache, float[] scales, bool isCompute)
+ public TextureBindingsManager(
+ GpuContext context,
+ GpuChannel channel,
+ TexturePoolCache texturePoolCache,
+ SamplerPoolCache samplerPoolCache,
+ float[] scales,
+ bool isCompute)
{
- _context = context;
- _channel = channel;
- _texturePoolCache = poolCache;
- _scales = scales;
- _isCompute = isCompute;
+ _context = context;
+ _channel = channel;
+ _texturePoolCache = texturePoolCache;
+ _samplerPoolCache = samplerPoolCache;
+
+ _scales = scales;
+ _isCompute = isCompute;
int stages = isCompute ? 1 : Constants.ShaderStages;
@@ -173,25 +184,10 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="samplerIndex">Type of the sampler pool indexing used for bound samplers</param>
public void SetSamplerPool(ulong gpuVa, int maximumId, SamplerIndex samplerIndex)
{
- if (gpuVa != 0)
- {
- ulong address = _channel.MemoryManager.Translate(gpuVa);
-
- if (_samplerPool != null && _samplerPool.Address == address && _samplerPool.MaximumId >= maximumId)
- {
- return;
- }
-
- _samplerPool?.Dispose();
- _samplerPool = new SamplerPool(_context, _channel.MemoryManager.Physical, address, maximumId);
- }
- else
- {
- _samplerPool?.Dispose();
- _samplerPool = null;
- }
-
+ _samplerPoolGpuVa = gpuVa;
+ _samplerPoolMaximumId = maximumId;
_samplerIndex = samplerIndex;
+ _samplerPool = null;
}
/// <summary>
@@ -201,18 +197,9 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="maximumId">Maximum ID of the pool (total count minus one)</param>
public void SetTexturePool(ulong gpuVa, int maximumId)
{
- if (gpuVa != 0)
- {
- ulong address = _channel.MemoryManager.Translate(gpuVa);
-
- _texturePoolAddress = address;
- _texturePoolMaximumId = maximumId;
- }
- else
- {
- _texturePoolAddress = 0;
- _texturePoolMaximumId = 0;
- }
+ _texturePoolGpuVa = gpuVa;
+ _texturePoolMaximumId = maximumId;
+ _texturePool = null;
}
/// <summary>
@@ -222,13 +209,9 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="samplerId">ID of the sampler</param>
public (Texture, Sampler) GetTextureAndSampler(int textureId, int samplerId)
{
- ulong texturePoolAddress = _texturePoolAddress;
-
- TexturePool texturePool = texturePoolAddress != 0
- ? _texturePoolCache.FindOrCreate(_channel, texturePoolAddress, _texturePoolMaximumId)
- : null;
+ (TexturePool texturePool, SamplerPool samplerPool) = GetPools();
- return (texturePool.Get(textureId), _samplerPool.Get(samplerId));
+ return (texturePool.Get(textureId), samplerPool.Get(samplerId));
}
/// <summary>
@@ -340,13 +323,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <returns>True if all bound textures match the current shader specialiation state, false otherwise</returns>
public bool CommitBindings(ShaderSpecializationState specState)
{
- ulong texturePoolAddress = _texturePoolAddress;
-
- TexturePool texturePool = texturePoolAddress != 0
- ? _texturePoolCache.FindOrCreate(_channel, texturePoolAddress, _texturePoolMaximumId)
- : null;
-
- SamplerPool samplerPool = _samplerPool;
+ (TexturePool texturePool, SamplerPool samplerPool) = GetPools();
// Check if the texture pool has been modified since bindings were last committed.
// If it wasn't, then it's possible to avoid looking up textures again when the handle remains the same.
@@ -381,7 +358,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (_isCompute)
{
- specStateMatches &= CommitTextureBindings(texturePool, ShaderStage.Compute, 0, poolModified, specState);
+ specStateMatches &= CommitTextureBindings(texturePool, samplerPool, ShaderStage.Compute, 0, poolModified, specState);
specStateMatches &= CommitImageBindings(texturePool, ShaderStage.Compute, 0, poolModified, specState);
}
else
@@ -390,7 +367,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{
int stageIndex = (int)stage - 1;
- specStateMatches &= CommitTextureBindings(texturePool, stage, stageIndex, poolModified, specState);
+ specStateMatches &= CommitTextureBindings(texturePool, samplerPool, stage, stageIndex, poolModified, specState);
specStateMatches &= CommitImageBindings(texturePool, stage, stageIndex, poolModified, specState);
}
}
@@ -447,13 +424,20 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Ensures that the texture bindings are visible to the host GPU.
/// Note: this actually performs the binding using the host graphics API.
/// </summary>
- /// <param name="pool">The current texture pool</param>
+ /// <param name="texturePool">The current texture pool</param>
+ /// <param name="samplerPool">The current sampler pool</param>
/// <param name="stage">The shader stage using the textures to be bound</param>
/// <param name="stageIndex">The stage number of the specified shader stage</param
/// <param name="poolModified">True if either the texture or sampler pool was modified, false otherwise</param>
/// <param name="specState">Specialization state for the bound shader</param>
/// <returns>True if all bound textures match the current shader specialiation state, false otherwise</returns>
- private bool CommitTextureBindings(TexturePool pool, ShaderStage stage, int stageIndex, bool poolModified, ShaderSpecializationState specState)
+ private bool CommitTextureBindings(
+ TexturePool texturePool,
+ SamplerPool samplerPool,
+ ShaderStage stage,
+ int stageIndex,
+ bool poolModified,
+ ShaderSpecializationState specState)
{
int textureCount = _textureBindingsCount[stageIndex];
if (textureCount == 0)
@@ -461,9 +445,7 @@ namespace Ryujinx.Graphics.Gpu.Image
return true;
}
- var samplerPool = _samplerPool;
-
- if (pool == null)
+ if (texturePool == null)
{
Logger.Error?.Print(LogClass.Gpu, $"Shader stage \"{stage}\" uses textures, but texture pool was not set.");
return true;
@@ -528,7 +510,7 @@ namespace Ryujinx.Graphics.Gpu.Image
state.TextureHandle = textureId;
state.SamplerHandle = samplerId;
- ref readonly TextureDescriptor descriptor = ref pool.GetForBinding(textureId, out Texture texture);
+ ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(textureId, out Texture texture);
specStateMatches &= specState.MatchesTexture(stage, index, descriptor);
@@ -820,20 +802,60 @@ namespace Ryujinx.Graphics.Gpu.Image
}
/// <summary>
- /// Force all bound textures and images to be rebound the next time CommitBindings is called.
+ /// Gets the texture and sampler pool for the GPU virtual address that are currently set.
/// </summary>
- public void Rebind()
+ /// <returns>The texture and sampler pools</returns>
+ private (TexturePool, SamplerPool) GetPools()
{
- Array.Clear(_textureState);
- Array.Clear(_imageState);
+ MemoryManager memoryManager = _channel.MemoryManager;
+
+ TexturePool texturePool = _texturePool;
+ SamplerPool samplerPool = _samplerPool;
+
+ if (texturePool == null)
+ {
+ ulong poolAddress = memoryManager.Translate(_texturePoolGpuVa);
+
+ if (poolAddress != MemoryManager.PteUnmapped)
+ {
+ texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, _texturePoolMaximumId);
+ _texturePool = texturePool;
+ }
+ }
+
+ if (samplerPool == null)
+ {
+ ulong poolAddress = memoryManager.Translate(_samplerPoolGpuVa);
+
+ if (poolAddress != MemoryManager.PteUnmapped)
+ {
+ samplerPool = _samplerPoolCache.FindOrCreate(_channel, poolAddress, _samplerPoolMaximumId);
+ _samplerPool = samplerPool;
+ }
+ }
+
+ return (texturePool, samplerPool);
+ }
+
+ /// <summary>
+ /// Forces the texture and sampler pools to be re-loaded from the cache on next use.
+ /// </summary>
+ /// <remarks>
+ /// This should be called if the memory mappings change, to ensure the correct pools are being used.
+ /// </remarks>
+ public void ReloadPools()
+ {
+ _samplerPool = null;
+ _texturePool = null;
}
/// <summary>
- /// Disposes all textures and samplers in the cache.
+ /// Force all bound textures and images to be rebound the next time CommitBindings is called.
/// </summary>
- public void Dispose()
+ public void Rebind()
{
- _samplerPool?.Dispose();
+ Array.Clear(_textureState);
+ Array.Clear(_imageState);
}
}
} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
index 628c3159..fe0175a6 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
@@ -16,6 +16,7 @@ namespace Ryujinx.Graphics.Gpu.Image
private readonly TextureBindingsManager _cpBindingsManager;
private readonly TextureBindingsManager _gpBindingsManager;
private readonly TexturePoolCache _texturePoolCache;
+ private readonly SamplerPoolCache _samplerPoolCache;
private readonly Texture[] _rtColors;
private readonly ITexture[] _rtHostColors;
@@ -41,13 +42,15 @@ namespace Ryujinx.Graphics.Gpu.Image
_channel = channel;
TexturePoolCache texturePoolCache = new TexturePoolCache(context);
+ SamplerPoolCache samplerPoolCache = new SamplerPoolCache(context);
float[] scales = new float[64];
new Span<float>(scales).Fill(1f);
- _cpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, scales, isCompute: true);
- _gpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, scales, isCompute: false);
+ _cpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, samplerPoolCache, scales, isCompute: true);
+ _gpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, samplerPoolCache, scales, isCompute: false);
_texturePoolCache = texturePoolCache;
+ _samplerPoolCache = samplerPoolCache;
_rtColors = new Texture[Constants.TotalRenderTargets];
_rtHostColors = new ITexture[Constants.TotalRenderTargets];
@@ -368,6 +371,10 @@ namespace Ryujinx.Graphics.Gpu.Image
// we must rebind everything.
// Since compute work happens less often, we always do that
// before and after the compute dispatch.
+
+ _texturePoolCache.Tick();
+ _samplerPoolCache.Tick();
+
_cpBindingsManager.Rebind();
bool result = _cpBindingsManager.CommitBindings(specState);
_gpBindingsManager.Rebind();
@@ -382,6 +389,9 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <returns>True if all bound textures match the current shader specialization state, false otherwise</returns>
public bool CommitGraphicsBindings(ShaderSpecializationState specState)
{
+ _texturePoolCache.Tick();
+ _samplerPoolCache.Tick();
+
bool result = _gpBindingsManager.CommitBindings(specState);
UpdateRenderTargets();
@@ -502,6 +512,15 @@ namespace Ryujinx.Graphics.Gpu.Image
}
/// <summary>
+ /// Forces the texture and sampler pools to be re-loaded from the cache on next use.
+ /// </summary>
+ public void ReloadPools()
+ {
+ _cpBindingsManager.ReloadPools();
+ _gpBindingsManager.ReloadPools();
+ }
+
+ /// <summary>
/// Forces all textures, samplers, images and render targets to be rebound the next time
/// CommitGraphicsBindings is called.
/// </summary>
@@ -523,8 +542,8 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary>
public void Dispose()
{
- _cpBindingsManager.Dispose();
- _gpBindingsManager.Dispose();
+ // Textures are owned by the texture cache, so we shouldn't dispose the texture pool cache.
+ _samplerPoolCache.Dispose();
for (int i = 0; i < _rtColors.Length; i++)
{
diff --git a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
index 75974c43..4d2544e2 100644
--- a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
@@ -10,19 +10,24 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <summary>
/// Texture pool.
/// </summary>
- class TexturePool : Pool<Texture, TextureDescriptor>
+ class TexturePool : Pool<Texture, TextureDescriptor>, IPool<TexturePool>
{
private readonly GpuChannel _channel;
private readonly ConcurrentQueue<Texture> _dereferenceQueue = new ConcurrentQueue<Texture>();
private TextureDescriptor _defaultDescriptor;
/// <summary>
- /// Intrusive linked list node used on the texture pool cache.
+ /// Linked list node used on the texture pool cache.
/// </summary>
public LinkedListNode<TexturePool> CacheNode { get; set; }
/// <summary>
- /// Constructs a new instance of the texture pool.
+ /// Timestamp used by the texture pool cache, updated on every use of this texture pool.
+ /// </summary>
+ public ulong CacheTimestamp { get; set; }
+
+ /// <summary>
+ /// Creates a new instance of the texture pool.
/// </summary>
/// <param name="context">GPU context that the texture pool belongs to</param>
/// <param name="channel">GPU channel that the texture pool belongs to</param>
diff --git a/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs b/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs
index 99c5a88b..0017f4cc 100644
--- a/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs
@@ -1,6 +1,3 @@
-using System;
-using System.Collections.Generic;
-
namespace Ryujinx.Graphics.Gpu.Image
{
/// <summary>
@@ -8,69 +5,26 @@ namespace Ryujinx.Graphics.Gpu.Image
/// This can keep multiple texture pools, and return the current one as needed.
/// It is useful for applications that uses multiple texture pools.
/// </summary>
- class TexturePoolCache
+ class TexturePoolCache : PoolCache<TexturePool>
{
- private const int MaxCapacity = 4;
-
- private readonly GpuContext _context;
- private readonly LinkedList<TexturePool> _pools;
-
/// <summary>
/// Constructs a new instance of the texture pool.
/// </summary>
/// <param name="context">GPU context that the texture pool belongs to</param>
- public TexturePoolCache(GpuContext context)
+ public TexturePoolCache(GpuContext context) : base(context)
{
- _context = context;
- _pools = new LinkedList<TexturePool>();
}
/// <summary>
- /// Finds a cache texture pool, or creates a new one if not found.
+ /// Creates a new instance of the texture pool.
/// </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 TexturePool FindOrCreate(GpuChannel channel, ulong address, int maximumId)
+ /// <param name="context">GPU context that the texture pool belongs to</param>
+ /// <param name="channel">GPU channel that the texture pool belongs to</param>
+ /// <param name="address">Address of the texture pool in guest memory</param>
+ /// <param name="maximumId">Maximum texture ID of the texture pool (equal to maximum textures minus one)</param>
+ protected override TexturePool CreatePool(GpuContext context, GpuChannel channel, ulong address, int maximumId)
{
- TexturePool pool;
-
- // First we try to find the pool.
- for (LinkedListNode<TexturePool> 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);
- }
-
- return pool;
- }
- }
-
- // If not found, create a new one.
- pool = new TexturePool(_context, channel, address, maximumId);
-
- pool.CacheNode = _pools.AddLast(pool);
-
- if (_pools.Count > MaxCapacity)
- {
- TexturePool oldestPool = _pools.First.Value;
-
- _pools.RemoveFirst();
-
- oldestPool.Dispose();
-
- oldestPool.CacheNode = null;
- }
-
- return pool;
+ return new TexturePool(context, channel, address, maximumId);
}
}
} \ No newline at end of file