Limit texture cache based on total texture size (#4350)
* Limit texture cache based on total texture size * Formatting
This commit is contained in:
parent
96cf242bcf
commit
26bf13a65d
4 changed files with 40 additions and 84 deletions
|
@ -33,10 +33,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
/// </summary>
|
||||
class AutoDeleteCache : IEnumerable<Texture>
|
||||
{
|
||||
private const int MinCountForDeletion = 32;
|
||||
private const int MaxCapacity = 2048;
|
||||
private const ulong MaxTextureSizeCapacity = 512 * 1024 * 1024; // MB;
|
||||
|
||||
private readonly LinkedList<Texture> _textures;
|
||||
private readonly ConcurrentQueue<Texture> _deferredRemovals;
|
||||
private ulong _totalSize;
|
||||
|
||||
private HashSet<ShortTextureCacheEntry> _shortCacheBuilder;
|
||||
private HashSet<ShortTextureCacheEntry> _shortCache;
|
||||
|
@ -49,7 +51,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
public AutoDeleteCache()
|
||||
{
|
||||
_textures = new LinkedList<Texture>();
|
||||
_deferredRemovals = new ConcurrentQueue<Texture>();
|
||||
|
||||
_shortCacheBuilder = new HashSet<ShortTextureCacheEntry>();
|
||||
_shortCache = new HashSet<ShortTextureCacheEntry>();
|
||||
|
@ -67,37 +68,15 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
/// <param name="texture">The texture to be added to the cache</param>
|
||||
public void Add(Texture texture)
|
||||
{
|
||||
texture.IncrementReferenceCount();
|
||||
_totalSize += texture.Size;
|
||||
|
||||
texture.IncrementReferenceCount();
|
||||
texture.CacheNode = _textures.AddLast(texture);
|
||||
|
||||
if (_textures.Count > MaxCapacity)
|
||||
if (_textures.Count > MaxCapacity ||
|
||||
(_totalSize > MaxTextureSizeCapacity && _textures.Count >= MinCountForDeletion))
|
||||
{
|
||||
Texture oldestTexture = _textures.First.Value;
|
||||
|
||||
if (!oldestTexture.CheckModified(false))
|
||||
{
|
||||
// The texture must be flushed if it falls out of the auto delete cache.
|
||||
// Flushes out of the auto delete cache do not trigger write tracking,
|
||||
// as it is expected that other overlapping textures exist that have more up-to-date contents.
|
||||
|
||||
oldestTexture.Group.SynchronizeDependents(oldestTexture);
|
||||
oldestTexture.FlushModified(false);
|
||||
}
|
||||
|
||||
_textures.RemoveFirst();
|
||||
|
||||
oldestTexture.DecrementReferenceCount();
|
||||
|
||||
oldestTexture.CacheNode = null;
|
||||
}
|
||||
|
||||
if (_deferredRemovals.Count > 0)
|
||||
{
|
||||
while (_deferredRemovals.TryDequeue(out Texture textureToRemove))
|
||||
{
|
||||
Remove(textureToRemove, false);
|
||||
}
|
||||
RemoveLeastUsedTexture();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,6 +99,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
|
||||
texture.CacheNode = _textures.AddLast(texture);
|
||||
}
|
||||
|
||||
if (_totalSize > MaxTextureSizeCapacity && _textures.Count >= MinCountForDeletion)
|
||||
{
|
||||
RemoveLeastUsedTexture();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -127,6 +111,31 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the least used texture from the cache.
|
||||
/// </summary>
|
||||
private void RemoveLeastUsedTexture()
|
||||
{
|
||||
Texture oldestTexture = _textures.First.Value;
|
||||
|
||||
_totalSize -= oldestTexture.Size;
|
||||
|
||||
if (!oldestTexture.CheckModified(false))
|
||||
{
|
||||
// The texture must be flushed if it falls out of the auto delete cache.
|
||||
// Flushes out of the auto delete cache do not trigger write tracking,
|
||||
// as it is expected that other overlapping textures exist that have more up-to-date contents.
|
||||
|
||||
oldestTexture.Group.SynchronizeDependents(oldestTexture);
|
||||
oldestTexture.FlushModified(false);
|
||||
}
|
||||
|
||||
_textures.RemoveFirst();
|
||||
|
||||
oldestTexture.DecrementReferenceCount();
|
||||
oldestTexture.CacheNode = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a texture from the cache.
|
||||
/// </summary>
|
||||
|
@ -148,20 +157,13 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
|
||||
_textures.Remove(texture.CacheNode);
|
||||
|
||||
_totalSize -= texture.Size;
|
||||
|
||||
texture.CacheNode = null;
|
||||
|
||||
return texture.DecrementReferenceCount();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queues removal of a texture from the cache in a thread safe way.
|
||||
/// </summary>
|
||||
/// <param name="texture">The texture to be removed from the cache</param>
|
||||
public void RemoveDeferred(Texture texture)
|
||||
{
|
||||
_deferredRemovals.Enqueue(texture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to find a texture on the short duration cache.
|
||||
/// </summary>
|
||||
|
|
|
@ -1637,13 +1637,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
}
|
||||
|
||||
RemoveFromPools(true);
|
||||
|
||||
// We only want to remove if there's no mapped region of the texture that was modified by the GPU,
|
||||
// otherwise we could lose data.
|
||||
if (!Group.AnyModified(this))
|
||||
{
|
||||
_physicalMemory.TextureCache.QueueAutoDeleteCacheRemoval(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -1079,19 +1079,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queues the removal of a texture from the auto delete cache.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This function is thread safe and can be called from any thread.
|
||||
/// The texture will be deleted on the next time the cache is used.
|
||||
/// </remarks>
|
||||
/// <param name="texture">The texture to be removed</param>
|
||||
public void QueueAutoDeleteCacheRemoval(Texture texture)
|
||||
{
|
||||
_cache.RemoveDeferred(texture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a texture to the short duration cache. This typically keeps it alive for two ticks.
|
||||
/// </summary>
|
||||
|
|
|
@ -434,32 +434,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a texture was modified by the GPU.
|
||||
/// </summary>
|
||||
/// <param name="texture">The texture to be checked</param>
|
||||
/// <returns>True if any region of the texture was modified by the GPU, false otherwise</returns>
|
||||
public bool AnyModified(Texture texture)
|
||||
{
|
||||
bool anyModified = false;
|
||||
|
||||
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
|
||||
{
|
||||
for (int i = 0; i < regionCount; i++)
|
||||
{
|
||||
TextureGroupHandle group = _handles[baseHandle + i];
|
||||
|
||||
if (group.Modified)
|
||||
{
|
||||
anyModified = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return anyModified;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flush modified ranges for a given texture.
|
||||
/// </summary>
|
||||
|
|
Loading…
Reference in a new issue