d4187aaa9d
* (Re)Implement format reinterpretation, other changes * Implement writeback to guest memory, some refactoring * More refactoring, implement reinterpretation the old way again * Clean up * Some fixes on M2MF (old Dma engine), added partial support for P2MF, fix conditional ssy, add Z24S8 zeta format, other fixes * nit: Formatting * Address PR feedback
186 lines
No EOL
4.4 KiB
C#
186 lines
No EOL
4.4 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace Ryujinx.Graphics.Gal.OpenGL
|
|
{
|
|
class OGLCachedResource<T>
|
|
{
|
|
public delegate void DeleteValue(T Value);
|
|
|
|
private const int MaxTimeDelta = 5 * 60000;
|
|
private const int MaxRemovalsPerRun = 10;
|
|
|
|
private struct CacheBucket
|
|
{
|
|
public T Value { get; private set; }
|
|
|
|
public LinkedListNode<long> Node { get; private set; }
|
|
|
|
public long DataSize { get; private set; }
|
|
|
|
public int Timestamp { get; private set; }
|
|
|
|
public CacheBucket(T Value, long DataSize, LinkedListNode<long> Node)
|
|
{
|
|
this.Value = Value;
|
|
this.DataSize = DataSize;
|
|
this.Node = Node;
|
|
|
|
Timestamp = Environment.TickCount;
|
|
}
|
|
}
|
|
|
|
private Dictionary<long, CacheBucket> Cache;
|
|
|
|
private LinkedList<long> SortedCache;
|
|
|
|
private DeleteValue DeleteValueCallback;
|
|
|
|
private Queue<T> DeletePending;
|
|
|
|
private bool Locked;
|
|
|
|
public OGLCachedResource(DeleteValue DeleteValueCallback)
|
|
{
|
|
if (DeleteValueCallback == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(DeleteValueCallback));
|
|
}
|
|
|
|
this.DeleteValueCallback = DeleteValueCallback;
|
|
|
|
Cache = new Dictionary<long, CacheBucket>();
|
|
|
|
SortedCache = new LinkedList<long>();
|
|
|
|
DeletePending = new Queue<T>();
|
|
}
|
|
|
|
public void Lock()
|
|
{
|
|
Locked = true;
|
|
}
|
|
|
|
public void Unlock()
|
|
{
|
|
Locked = false;
|
|
|
|
while (DeletePending.TryDequeue(out T Value))
|
|
{
|
|
DeleteValueCallback(Value);
|
|
}
|
|
|
|
ClearCacheIfNeeded();
|
|
}
|
|
|
|
public void AddOrUpdate(long Key, T Value, long Size)
|
|
{
|
|
if (!Locked)
|
|
{
|
|
ClearCacheIfNeeded();
|
|
}
|
|
|
|
LinkedListNode<long> Node = SortedCache.AddLast(Key);
|
|
|
|
CacheBucket NewBucket = new CacheBucket(Value, Size, Node);
|
|
|
|
if (Cache.TryGetValue(Key, out CacheBucket Bucket))
|
|
{
|
|
if (Locked)
|
|
{
|
|
DeletePending.Enqueue(Bucket.Value);
|
|
}
|
|
else
|
|
{
|
|
DeleteValueCallback(Bucket.Value);
|
|
}
|
|
|
|
SortedCache.Remove(Bucket.Node);
|
|
|
|
Cache[Key] = NewBucket;
|
|
}
|
|
else
|
|
{
|
|
Cache.Add(Key, NewBucket);
|
|
}
|
|
}
|
|
|
|
public bool TryGetValue(long Key, out T Value)
|
|
{
|
|
if (Cache.TryGetValue(Key, out CacheBucket Bucket))
|
|
{
|
|
Value = Bucket.Value;
|
|
|
|
SortedCache.Remove(Bucket.Node);
|
|
|
|
LinkedListNode<long> Node = SortedCache.AddLast(Key);
|
|
|
|
Cache[Key] = new CacheBucket(Value, Bucket.DataSize, Node);
|
|
|
|
return true;
|
|
}
|
|
|
|
Value = default(T);
|
|
|
|
return false;
|
|
}
|
|
|
|
public bool TryGetSize(long Key, out long Size)
|
|
{
|
|
if (Cache.TryGetValue(Key, out CacheBucket Bucket))
|
|
{
|
|
Size = Bucket.DataSize;
|
|
|
|
return true;
|
|
}
|
|
|
|
Size = 0;
|
|
|
|
return false;
|
|
}
|
|
|
|
private void ClearCacheIfNeeded()
|
|
{
|
|
int Timestamp = Environment.TickCount;
|
|
|
|
int Count = 0;
|
|
|
|
while (Count++ < MaxRemovalsPerRun)
|
|
{
|
|
LinkedListNode<long> Node = SortedCache.First;
|
|
|
|
if (Node == null)
|
|
{
|
|
break;
|
|
}
|
|
|
|
CacheBucket Bucket = Cache[Node.Value];
|
|
|
|
int TimeDelta = RingDelta(Bucket.Timestamp, Timestamp);
|
|
|
|
if ((uint)TimeDelta <= (uint)MaxTimeDelta)
|
|
{
|
|
break;
|
|
}
|
|
|
|
SortedCache.Remove(Node);
|
|
|
|
Cache.Remove(Node.Value);
|
|
|
|
DeleteValueCallback(Bucket.Value);
|
|
}
|
|
}
|
|
|
|
private int RingDelta(int Old, int New)
|
|
{
|
|
if ((uint)New < (uint)Old)
|
|
{
|
|
return New + (~Old + 1);
|
|
}
|
|
else
|
|
{
|
|
return New - Old;
|
|
}
|
|
}
|
|
}
|
|
} |