forked from Mirror/Ryujinx
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
This commit is contained in:
parent
a00c59a46c
commit
3c3bcd82fe
8 changed files with 321 additions and 136 deletions
|
@ -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>
|
||||
|
|
129
Ryujinx.Graphics.Gpu/Image/PoolCache.cs
Normal file
129
Ryujinx.Graphics.Gpu/Image/PoolCache.cs
Normal file
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
30
Ryujinx.Graphics.Gpu/Image/SamplerPoolCache.cs
Normal file
30
Ryujinx.Graphics.Gpu/Image/SamplerPoolCache.cs
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 SamplerIndex _samplerIndex;
|
||||
|
||||
private ulong _texturePoolAddress;
|
||||
private ulong _texturePoolGpuVa;
|
||||
private int _texturePoolMaximumId;
|
||||
private TexturePool _texturePool;
|
||||
private ulong _samplerPoolGpuVa;
|
||||
private int _samplerPoolMaximumId;
|
||||
private SamplerIndex _samplerIndex;
|
||||
private SamplerPool _samplerPool;
|
||||
|
||||
private readonly GpuChannel _channel;
|
||||
private readonly TexturePoolCache _texturePoolCache;
|
||||
private readonly SamplerPoolCache _samplerPoolCache;
|
||||
|
||||
private TexturePool _cachedTexturePool;
|
||||
private SamplerPool _cachedSamplerPool;
|
||||
|
@ -72,14 +74,23 @@ 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;
|
||||
_texturePoolCache = texturePoolCache;
|
||||
_samplerPoolCache = samplerPoolCache;
|
||||
|
||||
_scales = scales;
|
||||
_isCompute = isCompute;
|
||||
|
||||
|
@ -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;
|
||||
_texturePoolGpuVa = gpuVa;
|
||||
_texturePoolMaximumId = maximumId;
|
||||
}
|
||||
else
|
||||
{
|
||||
_texturePoolAddress = 0;
|
||||
_texturePoolMaximumId = 0;
|
||||
}
|
||||
_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, SamplerPool samplerPool) = GetPools();
|
||||
|
||||
TexturePool texturePool = texturePoolAddress != 0
|
||||
? _texturePoolCache.FindOrCreate(_channel, texturePoolAddress, _texturePoolMaximumId)
|
||||
: null;
|
||||
|
||||
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);
|
||||
|
||||
|
@ -819,6 +801,54 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
return handle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the texture and sampler pool for the GPU virtual address that are currently set.
|
||||
/// </summary>
|
||||
/// <returns>The texture and sampler pools</returns>
|
||||
private (TexturePool, SamplerPool) GetPools()
|
||||
{
|
||||
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>
|
||||
/// Force all bound textures and images to be rebound the next time CommitBindings is called.
|
||||
/// </summary>
|
||||
|
@ -827,13 +857,5 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
Array.Clear(_textureState);
|
||||
Array.Clear(_imageState);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes all textures and samplers in the cache.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_samplerPool?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
@ -501,6 +511,15 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
_context.Renderer.Pipeline.SetRenderTargets(_rtHostColors, _rtHostDs);
|
||||
}
|
||||
|
||||
/// <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.
|
||||
|
@ -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++)
|
||||
{
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
Reference in a new issue