2019-10-13 07:02:07 +01:00
|
|
|
using System.Collections;
|
2023-01-11 01:53:56 +00:00
|
|
|
using System.Collections.Concurrent;
|
2019-10-13 07:02:07 +01:00
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
|
|
namespace Ryujinx.Graphics.Gpu.Image
|
|
|
|
{
|
2019-12-29 23:26:37 +00:00
|
|
|
/// <summary>
|
|
|
|
/// 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.
|
|
|
|
/// </summary>
|
2019-10-13 07:02:07 +01:00
|
|
|
class AutoDeleteCache : IEnumerable<Texture>
|
|
|
|
{
|
|
|
|
private const int MaxCapacity = 2048;
|
|
|
|
|
2019-12-29 23:26:37 +00:00
|
|
|
private readonly LinkedList<Texture> _textures;
|
2023-01-11 01:53:56 +00:00
|
|
|
private readonly ConcurrentQueue<Texture> _deferredRemovals;
|
2019-10-13 07:02:07 +01:00
|
|
|
|
2019-12-29 23:26:37 +00:00
|
|
|
/// <summary>
|
|
|
|
/// Creates a new instance of the automatic deletion cache.
|
|
|
|
/// </summary>
|
2019-10-13 07:02:07 +01:00
|
|
|
public AutoDeleteCache()
|
|
|
|
{
|
|
|
|
_textures = new LinkedList<Texture>();
|
2023-01-11 01:53:56 +00:00
|
|
|
_deferredRemovals = new ConcurrentQueue<Texture>();
|
2019-10-13 07:02:07 +01:00
|
|
|
}
|
|
|
|
|
2019-12-29 23:26:37 +00:00
|
|
|
/// <summary>
|
|
|
|
/// Adds a new texture to the cache, even if the texture added is already on the cache.
|
2020-01-01 15:39:09 +00:00
|
|
|
/// </summary>
|
|
|
|
/// <remarks>
|
2019-12-29 23:26:37 +00:00
|
|
|
/// 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.
|
2020-01-01 15:39:09 +00:00
|
|
|
/// </remarks>
|
2019-12-29 23:26:37 +00:00
|
|
|
/// <param name="texture">The texture to be added to the cache</param>
|
2019-10-13 07:02:07 +01:00
|
|
|
public void Add(Texture texture)
|
|
|
|
{
|
|
|
|
texture.IncrementReferenceCount();
|
|
|
|
|
|
|
|
texture.CacheNode = _textures.AddLast(texture);
|
|
|
|
|
|
|
|
if (_textures.Count > MaxCapacity)
|
|
|
|
{
|
|
|
|
Texture oldestTexture = _textures.First.Value;
|
|
|
|
|
2022-01-09 16:28:48 +00:00
|
|
|
if (!oldestTexture.CheckModified(false))
|
2020-09-10 20:44:04 +01:00
|
|
|
{
|
|
|
|
// 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.
|
2021-09-29 01:11:05 +01:00
|
|
|
|
|
|
|
oldestTexture.Group.SynchronizeDependents(oldestTexture);
|
2022-01-09 16:28:48 +00:00
|
|
|
oldestTexture.FlushModified(false);
|
2020-09-10 20:44:04 +01:00
|
|
|
}
|
|
|
|
|
2019-10-13 07:02:07 +01:00
|
|
|
_textures.RemoveFirst();
|
|
|
|
|
|
|
|
oldestTexture.DecrementReferenceCount();
|
|
|
|
|
|
|
|
oldestTexture.CacheNode = null;
|
|
|
|
}
|
2023-01-11 01:53:56 +00:00
|
|
|
|
|
|
|
if (_deferredRemovals.Count > 0)
|
|
|
|
{
|
|
|
|
while (_deferredRemovals.TryDequeue(out Texture textureToRemove))
|
|
|
|
{
|
|
|
|
Remove(textureToRemove, false);
|
|
|
|
}
|
|
|
|
}
|
2019-10-13 07:02:07 +01:00
|
|
|
}
|
|
|
|
|
2019-12-29 23:26:37 +00:00
|
|
|
/// <summary>
|
|
|
|
/// Adds a new texture to the cache, or just moves it to the top of the list if the
|
2020-01-01 15:39:09 +00:00
|
|
|
/// texture is already on the cache.
|
2019-12-29 23:26:37 +00:00
|
|
|
/// </summary>
|
2020-01-01 15:39:09 +00:00
|
|
|
/// <remarks>
|
|
|
|
/// 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.
|
|
|
|
/// </remarks>
|
2019-12-29 23:26:37 +00:00
|
|
|
/// <param name="texture">The texture to be added, or moved to the top</param>
|
2019-10-13 07:02:07 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-11 01:53:56 +00:00
|
|
|
/// <summary>
|
|
|
|
/// Removes a texture from the cache.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="texture">The texture to be removed from the cache</param>
|
|
|
|
/// <param name="flush">True to remove the texture if it was on the cache</param>
|
|
|
|
/// <returns>True if the texture was found and removed, false otherwise</returns>
|
2020-09-10 20:44:04 +01:00
|
|
|
public bool Remove(Texture texture, bool flush)
|
|
|
|
{
|
|
|
|
if (texture.CacheNode == null)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove our reference to this texture.
|
2022-01-09 16:28:48 +00:00
|
|
|
if (flush)
|
2020-09-10 20:44:04 +01:00
|
|
|
{
|
2022-01-09 16:28:48 +00:00
|
|
|
texture.FlushModified(false);
|
2020-09-10 20:44:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
_textures.Remove(texture.CacheNode);
|
|
|
|
|
|
|
|
texture.CacheNode = null;
|
|
|
|
|
|
|
|
return texture.DecrementReferenceCount();
|
|
|
|
}
|
|
|
|
|
2023-01-11 01:53:56 +00:00
|
|
|
/// <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);
|
|
|
|
}
|
|
|
|
|
2019-10-13 07:02:07 +01:00
|
|
|
public IEnumerator<Texture> GetEnumerator()
|
|
|
|
{
|
|
|
|
return _textures.GetEnumerator();
|
|
|
|
}
|
|
|
|
|
|
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
|
|
{
|
|
|
|
return _textures.GetEnumerator();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|