Remove textures from cache on unmap if not mapped and modified (#4211)
This commit is contained in:
parent
2355c2af62
commit
94a64f2aea
4 changed files with 72 additions and 0 deletions
|
@ -1,4 +1,5 @@
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Image
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
@ -13,6 +14,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
private const int MaxCapacity = 2048;
|
private const int MaxCapacity = 2048;
|
||||||
|
|
||||||
private readonly LinkedList<Texture> _textures;
|
private readonly LinkedList<Texture> _textures;
|
||||||
|
private readonly ConcurrentQueue<Texture> _deferredRemovals;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the automatic deletion cache.
|
/// Creates a new instance of the automatic deletion cache.
|
||||||
|
@ -20,6 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
public AutoDeleteCache()
|
public AutoDeleteCache()
|
||||||
{
|
{
|
||||||
_textures = new LinkedList<Texture>();
|
_textures = new LinkedList<Texture>();
|
||||||
|
_deferredRemovals = new ConcurrentQueue<Texture>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -56,6 +59,14 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
oldestTexture.CacheNode = null;
|
oldestTexture.CacheNode = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_deferredRemovals.Count > 0)
|
||||||
|
{
|
||||||
|
while (_deferredRemovals.TryDequeue(out Texture textureToRemove))
|
||||||
|
{
|
||||||
|
Remove(textureToRemove, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -84,6 +95,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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>
|
||||||
public bool Remove(Texture texture, bool flush)
|
public bool Remove(Texture texture, bool flush)
|
||||||
{
|
{
|
||||||
if (texture.CacheNode == null)
|
if (texture.CacheNode == null)
|
||||||
|
@ -104,6 +121,15 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
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);
|
||||||
|
}
|
||||||
|
|
||||||
public IEnumerator<Texture> GetEnumerator()
|
public IEnumerator<Texture> GetEnumerator()
|
||||||
{
|
{
|
||||||
return _textures.GetEnumerator();
|
return _textures.GetEnumerator();
|
||||||
|
|
|
@ -1676,6 +1676,13 @@ 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>
|
||||||
|
|
|
@ -1165,6 +1165,19 @@ 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>
|
||||||
/// Disposes all textures and samplers in the cache.
|
/// Disposes all textures and samplers in the cache.
|
||||||
/// It's an error to use the texture cache after disposal.
|
/// It's an error to use the texture cache after disposal.
|
||||||
|
|
|
@ -434,6 +434,32 @@ 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