Compare commits

...

2 Commits

Author SHA1 Message Date
gdkchan
37b6e081da Fix DMA linear texture copy fast path (#3496)
* Fix DMA linear texture copy fast path

* Formatting
2022-07-28 13:46:12 -03:00
gdkchan
3c3bcd82fe 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
2022-07-27 21:07:48 -03:00
10 changed files with 333 additions and 145 deletions

View File

@@ -216,13 +216,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
{ {
var target = memoryManager.Physical.TextureCache.FindTexture( var target = memoryManager.Physical.TextureCache.FindTexture(
memoryManager, memoryManager,
dst,
dstGpuVa, dstGpuVa,
dstBpp, dstBpp,
dstStride, dstStride,
dst.Height,
xCount, xCount,
yCount, yCount,
dstLinear); dstLinear,
dst.MemoryLayout);
if (target != null) if (target != null)
{ {

View File

@@ -59,9 +59,24 @@ namespace Ryujinx.Graphics.Gpu
{ {
oldMemoryManager.Physical.BufferCache.NotifyBuffersModified -= BufferManager.Rebind; oldMemoryManager.Physical.BufferCache.NotifyBuffersModified -= BufferManager.Rebind;
oldMemoryManager.Physical.DecrementReferenceCount(); oldMemoryManager.Physical.DecrementReferenceCount();
oldMemoryManager.MemoryUnmapped -= MemoryUnmappedHandler;
} }
memoryManager.Physical.BufferCache.NotifyBuffersModified += BufferManager.Rebind; 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> /// <summary>

View 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();
}
}
}

View File

@@ -1,16 +1,27 @@
using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Gpu.Memory;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gpu.Image namespace Ryujinx.Graphics.Gpu.Image
{ {
/// <summary> /// <summary>
/// Sampler pool. /// Sampler pool.
/// </summary> /// </summary>
class SamplerPool : Pool<Sampler, SamplerDescriptor> class SamplerPool : Pool<Sampler, SamplerDescriptor>, IPool<SamplerPool>
{ {
private float _forcedAnisotropy; private float _forcedAnisotropy;
/// <summary> /// <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> /// </summary>
/// <param name="context">GPU context that the sampler pool belongs to</param> /// <param name="context">GPU context that the sampler pool belongs to</param>
/// <param name="physicalMemory">Physical memory where the sampler descriptors are mapped</param> /// <param name="physicalMemory">Physical memory where the sampler descriptors are mapped</param>

View 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);
}
}
}

View File

@@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <summary> /// <summary>
/// Texture bindings manager. /// Texture bindings manager.
/// </summary> /// </summary>
class TextureBindingsManager : IDisposable class TextureBindingsManager
{ {
private const int InitialTextureStateSize = 32; private const int InitialTextureStateSize = 32;
private const int InitialImageStateSize = 8; private const int InitialImageStateSize = 8;
@@ -22,15 +22,17 @@ namespace Ryujinx.Graphics.Gpu.Image
private readonly bool _isCompute; 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 SamplerIndex _samplerIndex;
private SamplerPool _samplerPool;
private ulong _texturePoolAddress;
private int _texturePoolMaximumId;
private readonly GpuChannel _channel; private readonly GpuChannel _channel;
private readonly TexturePoolCache _texturePoolCache; private readonly TexturePoolCache _texturePoolCache;
private readonly SamplerPoolCache _samplerPoolCache;
private TexturePool _cachedTexturePool; private TexturePool _cachedTexturePool;
private SamplerPool _cachedSamplerPool; private SamplerPool _cachedSamplerPool;
@@ -72,16 +74,25 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary> /// </summary>
/// <param name="context">The GPU context that the texture bindings manager belongs to</param> /// <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="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="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> /// <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; _context = context;
_channel = channel; _channel = channel;
_texturePoolCache = poolCache; _texturePoolCache = texturePoolCache;
_scales = scales; _samplerPoolCache = samplerPoolCache;
_isCompute = isCompute;
_scales = scales;
_isCompute = isCompute;
int stages = isCompute ? 1 : Constants.ShaderStages; 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> /// <param name="samplerIndex">Type of the sampler pool indexing used for bound samplers</param>
public void SetSamplerPool(ulong gpuVa, int maximumId, SamplerIndex samplerIndex) public void SetSamplerPool(ulong gpuVa, int maximumId, SamplerIndex samplerIndex)
{ {
if (gpuVa != 0) _samplerPoolGpuVa = gpuVa;
{ _samplerPoolMaximumId = maximumId;
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;
}
_samplerIndex = samplerIndex; _samplerIndex = samplerIndex;
_samplerPool = null;
} }
/// <summary> /// <summary>
@@ -201,18 +197,9 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="maximumId">Maximum ID of the pool (total count minus one)</param> /// <param name="maximumId">Maximum ID of the pool (total count minus one)</param>
public void SetTexturePool(ulong gpuVa, int maximumId) public void SetTexturePool(ulong gpuVa, int maximumId)
{ {
if (gpuVa != 0) _texturePoolGpuVa = gpuVa;
{ _texturePoolMaximumId = maximumId;
ulong address = _channel.MemoryManager.Translate(gpuVa); _texturePool = null;
_texturePoolAddress = address;
_texturePoolMaximumId = maximumId;
}
else
{
_texturePoolAddress = 0;
_texturePoolMaximumId = 0;
}
} }
/// <summary> /// <summary>
@@ -222,13 +209,9 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="samplerId">ID of the sampler</param> /// <param name="samplerId">ID of the sampler</param>
public (Texture, Sampler) GetTextureAndSampler(int textureId, int samplerId) public (Texture, Sampler) GetTextureAndSampler(int textureId, int samplerId)
{ {
ulong texturePoolAddress = _texturePoolAddress; (TexturePool texturePool, SamplerPool samplerPool) = GetPools();
TexturePool texturePool = texturePoolAddress != 0 return (texturePool.Get(textureId), samplerPool.Get(samplerId));
? _texturePoolCache.FindOrCreate(_channel, texturePoolAddress, _texturePoolMaximumId)
: null;
return (texturePool.Get(textureId), _samplerPool.Get(samplerId));
} }
/// <summary> /// <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> /// <returns>True if all bound textures match the current shader specialiation state, false otherwise</returns>
public bool CommitBindings(ShaderSpecializationState specState) public bool CommitBindings(ShaderSpecializationState specState)
{ {
ulong texturePoolAddress = _texturePoolAddress; (TexturePool texturePool, SamplerPool samplerPool) = GetPools();
TexturePool texturePool = texturePoolAddress != 0
? _texturePoolCache.FindOrCreate(_channel, texturePoolAddress, _texturePoolMaximumId)
: null;
SamplerPool samplerPool = _samplerPool;
// Check if the texture pool has been modified since bindings were last committed. // 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. // 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) 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); specStateMatches &= CommitImageBindings(texturePool, ShaderStage.Compute, 0, poolModified, specState);
} }
else else
@@ -390,7 +367,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
int stageIndex = (int)stage - 1; 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); 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. /// Ensures that the texture bindings are visible to the host GPU.
/// Note: this actually performs the binding using the host graphics API. /// Note: this actually performs the binding using the host graphics API.
/// </summary> /// </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="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="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="poolModified">True if either the texture or sampler pool was modified, false otherwise</param>
/// <param name="specState">Specialization state for the bound shader</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> /// <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]; int textureCount = _textureBindingsCount[stageIndex];
if (textureCount == 0) if (textureCount == 0)
@@ -461,9 +445,7 @@ namespace Ryujinx.Graphics.Gpu.Image
return true; return true;
} }
var samplerPool = _samplerPool; if (texturePool == null)
if (pool == null)
{ {
Logger.Error?.Print(LogClass.Gpu, $"Shader stage \"{stage}\" uses textures, but texture pool was not set."); Logger.Error?.Print(LogClass.Gpu, $"Shader stage \"{stage}\" uses textures, but texture pool was not set.");
return true; return true;
@@ -528,7 +510,7 @@ namespace Ryujinx.Graphics.Gpu.Image
state.TextureHandle = textureId; state.TextureHandle = textureId;
state.SamplerHandle = samplerId; 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); specStateMatches &= specState.MatchesTexture(stage, index, descriptor);
@@ -819,6 +801,54 @@ namespace Ryujinx.Graphics.Gpu.Image
return handle; 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> /// <summary>
/// Force all bound textures and images to be rebound the next time CommitBindings is called. /// Force all bound textures and images to be rebound the next time CommitBindings is called.
/// </summary> /// </summary>
@@ -827,13 +857,5 @@ namespace Ryujinx.Graphics.Gpu.Image
Array.Clear(_textureState); Array.Clear(_textureState);
Array.Clear(_imageState); Array.Clear(_imageState);
} }
/// <summary>
/// Disposes all textures and samplers in the cache.
/// </summary>
public void Dispose()
{
_samplerPool?.Dispose();
}
} }
} }

View File

@@ -900,23 +900,25 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Tries to find an existing texture matching the given buffer copy destination. If none is found, returns null. /// Tries to find an existing texture matching the given buffer copy destination. If none is found, returns null.
/// </summary> /// </summary>
/// <param name="memoryManager">GPU memory manager where the texture is mapped</param> /// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
/// <param name="tex">The texture information</param>
/// <param name="gpuVa">GPU virtual address of the texture</param> /// <param name="gpuVa">GPU virtual address of the texture</param>
/// <param name="bpp">Bytes per pixel</param> /// <param name="bpp">Bytes per pixel</param>
/// <param name="stride">If <paramref name="linear"/> is true, should have the texture stride, otherwise ignored</param> /// <param name="stride">If <paramref name="linear"/> is true, should have the texture stride, otherwise ignored</param>
/// <param name="height">If <paramref name="linear"/> is false, should have the texture height, otherwise ignored</param>
/// <param name="xCount">Number of pixels to be copied per line</param> /// <param name="xCount">Number of pixels to be copied per line</param>
/// <param name="yCount">Number of lines to be copied</param> /// <param name="yCount">Number of lines to be copied</param>
/// <param name="linear">True if the texture has a linear layout, false otherwise</param> /// <param name="linear">True if the texture has a linear layout, false otherwise</param>
/// <param name="memoryLayout">If <paramref name="linear"/> is false, should have the memory layout, otherwise ignored</param>
/// <returns>A matching texture, or null if there is no match</returns> /// <returns>A matching texture, or null if there is no match</returns>
public Texture FindTexture( public Texture FindTexture(
MemoryManager memoryManager, MemoryManager memoryManager,
DmaTexture tex,
ulong gpuVa, ulong gpuVa,
int bpp, int bpp,
int stride, int stride,
int height,
int xCount, int xCount,
int yCount, int yCount,
bool linear) bool linear,
MemoryLayout memoryLayout)
{ {
ulong address = memoryManager.Translate(gpuVa); ulong address = memoryManager.Translate(gpuVa);
@@ -945,7 +947,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
// Size is not available for linear textures. Use the stride and end of the copy region instead. // Size is not available for linear textures. Use the stride and end of the copy region instead.
match = texture.Info.IsLinear && texture.Info.Stride == stride && tex.RegionY + yCount <= texture.Info.Height; match = texture.Info.IsLinear && texture.Info.Stride == stride && yCount == texture.Info.Height;
} }
else else
{ {
@@ -953,10 +955,10 @@ namespace Ryujinx.Graphics.Gpu.Image
// Due to the way linear strided and block layouts work, widths can be multiplied by Bpp for comparison. // Due to the way linear strided and block layouts work, widths can be multiplied by Bpp for comparison.
// Note: tex.Width is the aligned texture size. Prefer param.XCount, as the destination should be a texture with that exact size. // Note: tex.Width is the aligned texture size. Prefer param.XCount, as the destination should be a texture with that exact size.
bool sizeMatch = xCount * bpp == texture.Info.Width * format.BytesPerPixel && tex.Height == texture.Info.Height; bool sizeMatch = xCount * bpp == texture.Info.Width * format.BytesPerPixel && height == texture.Info.Height;
bool formatMatch = !texture.Info.IsLinear && bool formatMatch = !texture.Info.IsLinear &&
texture.Info.GobBlocksInY == tex.MemoryLayout.UnpackGobBlocksInY() && texture.Info.GobBlocksInY == memoryLayout.UnpackGobBlocksInY() &&
texture.Info.GobBlocksInZ == tex.MemoryLayout.UnpackGobBlocksInZ(); texture.Info.GobBlocksInZ == memoryLayout.UnpackGobBlocksInZ();
match = sizeMatch && formatMatch; match = sizeMatch && formatMatch;
} }

View File

@@ -16,6 +16,7 @@ namespace Ryujinx.Graphics.Gpu.Image
private readonly TextureBindingsManager _cpBindingsManager; private readonly TextureBindingsManager _cpBindingsManager;
private readonly TextureBindingsManager _gpBindingsManager; private readonly TextureBindingsManager _gpBindingsManager;
private readonly TexturePoolCache _texturePoolCache; private readonly TexturePoolCache _texturePoolCache;
private readonly SamplerPoolCache _samplerPoolCache;
private readonly Texture[] _rtColors; private readonly Texture[] _rtColors;
private readonly ITexture[] _rtHostColors; private readonly ITexture[] _rtHostColors;
@@ -41,13 +42,15 @@ namespace Ryujinx.Graphics.Gpu.Image
_channel = channel; _channel = channel;
TexturePoolCache texturePoolCache = new TexturePoolCache(context); TexturePoolCache texturePoolCache = new TexturePoolCache(context);
SamplerPoolCache samplerPoolCache = new SamplerPoolCache(context);
float[] scales = new float[64]; float[] scales = new float[64];
new Span<float>(scales).Fill(1f); new Span<float>(scales).Fill(1f);
_cpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, scales, isCompute: true); _cpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, samplerPoolCache, scales, isCompute: true);
_gpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, scales, isCompute: false); _gpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, samplerPoolCache, scales, isCompute: false);
_texturePoolCache = texturePoolCache; _texturePoolCache = texturePoolCache;
_samplerPoolCache = samplerPoolCache;
_rtColors = new Texture[Constants.TotalRenderTargets]; _rtColors = new Texture[Constants.TotalRenderTargets];
_rtHostColors = new ITexture[Constants.TotalRenderTargets]; _rtHostColors = new ITexture[Constants.TotalRenderTargets];
@@ -368,6 +371,10 @@ namespace Ryujinx.Graphics.Gpu.Image
// we must rebind everything. // we must rebind everything.
// Since compute work happens less often, we always do that // Since compute work happens less often, we always do that
// before and after the compute dispatch. // before and after the compute dispatch.
_texturePoolCache.Tick();
_samplerPoolCache.Tick();
_cpBindingsManager.Rebind(); _cpBindingsManager.Rebind();
bool result = _cpBindingsManager.CommitBindings(specState); bool result = _cpBindingsManager.CommitBindings(specState);
_gpBindingsManager.Rebind(); _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> /// <returns>True if all bound textures match the current shader specialization state, false otherwise</returns>
public bool CommitGraphicsBindings(ShaderSpecializationState specState) public bool CommitGraphicsBindings(ShaderSpecializationState specState)
{ {
_texturePoolCache.Tick();
_samplerPoolCache.Tick();
bool result = _gpBindingsManager.CommitBindings(specState); bool result = _gpBindingsManager.CommitBindings(specState);
UpdateRenderTargets(); UpdateRenderTargets();
@@ -501,6 +511,15 @@ namespace Ryujinx.Graphics.Gpu.Image
_context.Renderer.Pipeline.SetRenderTargets(_rtHostColors, _rtHostDs); _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> /// <summary>
/// Forces all textures, samplers, images and render targets to be rebound the next time /// Forces all textures, samplers, images and render targets to be rebound the next time
/// CommitGraphicsBindings is called. /// CommitGraphicsBindings is called.
@@ -523,8 +542,8 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
_cpBindingsManager.Dispose(); // Textures are owned by the texture cache, so we shouldn't dispose the texture pool cache.
_gpBindingsManager.Dispose(); _samplerPoolCache.Dispose();
for (int i = 0; i < _rtColors.Length; i++) for (int i = 0; i < _rtColors.Length; i++)
{ {

View File

@@ -10,19 +10,24 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <summary> /// <summary>
/// Texture pool. /// Texture pool.
/// </summary> /// </summary>
class TexturePool : Pool<Texture, TextureDescriptor> class TexturePool : Pool<Texture, TextureDescriptor>, IPool<TexturePool>
{ {
private readonly GpuChannel _channel; private readonly GpuChannel _channel;
private readonly ConcurrentQueue<Texture> _dereferenceQueue = new ConcurrentQueue<Texture>(); private readonly ConcurrentQueue<Texture> _dereferenceQueue = new ConcurrentQueue<Texture>();
private TextureDescriptor _defaultDescriptor; private TextureDescriptor _defaultDescriptor;
/// <summary> /// <summary>
/// Intrusive linked list node used on the texture pool cache. /// Linked list node used on the texture pool cache.
/// </summary> /// </summary>
public LinkedListNode<TexturePool> CacheNode { get; set; } public LinkedListNode<TexturePool> CacheNode { get; set; }
/// <summary> /// <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> /// </summary>
/// <param name="context">GPU context that the texture pool belongs to</param> /// <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="channel">GPU channel that the texture pool belongs to</param>

View File

@@ -1,6 +1,3 @@
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gpu.Image namespace Ryujinx.Graphics.Gpu.Image
{ {
/// <summary> /// <summary>
@@ -8,69 +5,26 @@ namespace Ryujinx.Graphics.Gpu.Image
/// This can keep multiple texture pools, and return the current one as needed. /// This can keep multiple texture pools, and return the current one as needed.
/// It is useful for applications that uses multiple texture pools. /// It is useful for applications that uses multiple texture pools.
/// </summary> /// </summary>
class TexturePoolCache class TexturePoolCache : PoolCache<TexturePool>
{ {
private const int MaxCapacity = 4;
private readonly GpuContext _context;
private readonly LinkedList<TexturePool> _pools;
/// <summary> /// <summary>
/// Constructs a new instance of the texture pool. /// Constructs a new instance of the texture pool.
/// </summary> /// </summary>
/// <param name="context">GPU context that the texture pool belongs to</param> /// <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> /// <summary>
/// Finds a cache texture pool, or creates a new one if not found. /// Creates a new instance of the texture pool.
/// </summary> /// </summary>
/// <param name="channel">GPU channel that the texture pool cache belongs to</param> /// <param name="context">GPU context that the texture pool belongs to</param>
/// <param name="address">Start address of the texture pool</param> /// <param name="channel">GPU channel that the texture pool belongs to</param>
/// <param name="maximumId">Maximum ID of the texture pool</param> /// <param name="address">Address of the texture pool in guest memory</param>
/// <returns>The found or newly created texture pool</returns> /// <param name="maximumId">Maximum texture ID of the texture pool (equal to maximum textures minus one)</param>
public TexturePool FindOrCreate(GpuChannel channel, ulong address, int maximumId) protected override TexturePool CreatePool(GpuContext context, GpuChannel channel, ulong address, int maximumId)
{ {
TexturePool pool; return new TexturePool(context, channel, address, maximumId);
// 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;
} }
} }
} }