0
0
Fork 0

Remove textures from cache on unmap if not mapped and modified (#4211)

This commit is contained in:
gdkchan 2023-01-10 22:53:56 -03:00 committed by GitHub
parent 2355c2af62
commit 94a64f2aea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 72 additions and 0 deletions

View file

@ -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();

View file

@ -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>

View file

@ -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.

View file

@ -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>