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>
|
/// </summary>
|
||||||
class AutoDeleteCache : IEnumerable<Texture>
|
class AutoDeleteCache : IEnumerable<Texture>
|
||||||
{
|
{
|
||||||
|
private const int MinCountForDeletion = 32;
|
||||||
private const int MaxCapacity = 2048;
|
private const int MaxCapacity = 2048;
|
||||||
|
private const ulong MaxTextureSizeCapacity = 512 * 1024 * 1024; // MB;
|
||||||
|
|
||||||
private readonly LinkedList<Texture> _textures;
|
private readonly LinkedList<Texture> _textures;
|
||||||
private readonly ConcurrentQueue<Texture> _deferredRemovals;
|
private ulong _totalSize;
|
||||||
|
|
||||||
private HashSet<ShortTextureCacheEntry> _shortCacheBuilder;
|
private HashSet<ShortTextureCacheEntry> _shortCacheBuilder;
|
||||||
private HashSet<ShortTextureCacheEntry> _shortCache;
|
private HashSet<ShortTextureCacheEntry> _shortCache;
|
||||||
|
@ -49,7 +51,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
public AutoDeleteCache()
|
public AutoDeleteCache()
|
||||||
{
|
{
|
||||||
_textures = new LinkedList<Texture>();
|
_textures = new LinkedList<Texture>();
|
||||||
_deferredRemovals = new ConcurrentQueue<Texture>();
|
|
||||||
|
|
||||||
_shortCacheBuilder = new HashSet<ShortTextureCacheEntry>();
|
_shortCacheBuilder = new HashSet<ShortTextureCacheEntry>();
|
||||||
_shortCache = 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>
|
/// <param name="texture">The texture to be added to the cache</param>
|
||||||
public void Add(Texture texture)
|
public void Add(Texture texture)
|
||||||
{
|
{
|
||||||
texture.IncrementReferenceCount();
|
_totalSize += texture.Size;
|
||||||
|
|
||||||
|
texture.IncrementReferenceCount();
|
||||||
texture.CacheNode = _textures.AddLast(texture);
|
texture.CacheNode = _textures.AddLast(texture);
|
||||||
|
|
||||||
if (_textures.Count > MaxCapacity)
|
if (_textures.Count > MaxCapacity ||
|
||||||
|
(_totalSize > MaxTextureSizeCapacity && _textures.Count >= MinCountForDeletion))
|
||||||
{
|
{
|
||||||
Texture oldestTexture = _textures.First.Value;
|
RemoveLeastUsedTexture();
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,6 +99,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
texture.CacheNode = _textures.AddLast(texture);
|
texture.CacheNode = _textures.AddLast(texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_totalSize > MaxTextureSizeCapacity && _textures.Count >= MinCountForDeletion)
|
||||||
|
{
|
||||||
|
RemoveLeastUsedTexture();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
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>
|
/// <summary>
|
||||||
/// Removes a texture from the cache.
|
/// Removes a texture from the cache.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -148,20 +157,13 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
_textures.Remove(texture.CacheNode);
|
_textures.Remove(texture.CacheNode);
|
||||||
|
|
||||||
|
_totalSize -= texture.Size;
|
||||||
|
|
||||||
texture.CacheNode = null;
|
texture.CacheNode = null;
|
||||||
|
|
||||||
return texture.DecrementReferenceCount();
|
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>
|
/// <summary>
|
||||||
/// Attempt to find a texture on the short duration cache.
|
/// Attempt to find a texture on the short duration cache.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -1637,13 +1637,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
}
|
}
|
||||||
|
|
||||||
RemoveFromPools(true);
|
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>
|
/// <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>
|
/// <summary>
|
||||||
/// Adds a texture to the short duration cache. This typically keeps it alive for two ticks.
|
/// Adds a texture to the short duration cache. This typically keeps it alive for two ticks.
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
/// Flush modified ranges for a given texture.
|
/// Flush modified ranges for a given texture.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
Reference in a new issue