using Ryujinx.Common.Logging;
using System.Collections;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gpu.Image
{
///
/// A texture cache that automatically removes older textures that are not used for some time.
/// The cache works with a rotated list with a fixed size. When new textures are added, the
/// old ones at the bottom of the list are deleted.
///
class AutoDeleteCache : IEnumerable
{
private const int MaxCapacity = 2048;
private readonly LinkedList _textures;
///
/// Creates a new instance of the automatic deletion cache.
///
public AutoDeleteCache()
{
_textures = new LinkedList();
}
///
/// Adds a new texture to the cache, even if the texture added is already on the cache.
///
///
/// Using this method is only recommended if you know that the texture is not yet on the cache,
/// otherwise it would store the same texture more than once.
///
/// The texture to be added to the cache
public void Add(Texture texture)
{
texture.IncrementReferenceCount();
texture.CacheNode = _textures.AddLast(texture);
if (_textures.Count > MaxCapacity)
{
Texture oldestTexture = _textures.First.Value;
oldestTexture.SynchronizeMemory();
if (oldestTexture.IsModified)
{
// 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.Flush(false);
}
_textures.RemoveFirst();
oldestTexture.DecrementReferenceCount();
oldestTexture.CacheNode = null;
}
}
///
/// Adds a new texture to the cache, or just moves it to the top of the list if the
/// texture is already on the cache.
///
///
/// Moving the texture to the top of the list prevents it from being deleted,
/// as the textures on the bottom of the list are deleted when new ones are added.
///
/// The texture to be added, or moved to the top
public void Lift(Texture texture)
{
if (texture.CacheNode != null)
{
if (texture.CacheNode != _textures.Last)
{
_textures.Remove(texture.CacheNode);
texture.CacheNode = _textures.AddLast(texture);
}
}
else
{
Add(texture);
}
}
public bool Remove(Texture texture, bool flush)
{
if (texture.CacheNode == null)
{
return false;
}
// Remove our reference to this texture.
if (flush && texture.IsModified)
{
texture.Flush(false);
}
_textures.Remove(texture.CacheNode);
texture.CacheNode = null;
return texture.DecrementReferenceCount();
}
public IEnumerator GetEnumerator()
{
return _textures.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _textures.GetEnumerator();
}
}
}