Make PBO cache actually work, also some style changes, use int instead of long for sizes

This commit is contained in:
gdkchan 2018-12-09 19:57:52 -03:00
commit b7c54a13d4
9 changed files with 128 additions and 98 deletions

View file

@ -7,9 +7,9 @@ namespace Ryujinx.Graphics.Gal
void LockCache(); void LockCache();
void UnlockCache(); void UnlockCache();
void Create(long key, IntPtr hostAddress, long size); void Create(long key, IntPtr hostAddress, int size);
void Create(long key, byte[] data); void Create(long key, byte[] buffer);
bool IsCached(long Key, long Size); bool IsCached(long Key, int Size);
} }
} }

View file

@ -24,13 +24,13 @@ namespace Ryujinx.Graphics.Gal
GalVertexAttrib[] attributes, GalVertexAttrib[] attributes,
GalVertexAttribArray[] arrays); GalVertexAttribArray[] arrays);
bool IsVboCached(long key, long size); bool IsVboCached(long key, int size);
bool IsIboCached(long key, long size, out long vertexCount); bool IsIboCached(long key, int size, out long vertexCount);
void CreateVbo(long Key, int DataSize, IntPtr HostAddress);
void CreateVbo(long Key, byte[] Data);
void CreateVbo(long key, IntPtr hostAddress, int size);
void CreateIbo(long key, IntPtr hostAddress, int size, long vertexCount); void CreateIbo(long key, IntPtr hostAddress, int size, long vertexCount);
void CreateVbo(long key, byte[] buffer);
void CreateIbo(long key, byte[] buffer, long vertexCount); void CreateIbo(long key, byte[] buffer, long vertexCount);
void SetIndexArray(int Size, GalIndexFormat Format); void SetIndexArray(int Size, GalIndexFormat Format);

View file

@ -5,13 +5,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ {
class OGLConstBuffer : IGalConstBuffer class OGLConstBuffer : IGalConstBuffer
{ {
private const long MaxConstBufferCacheSize = 64 * 1024 * 1024; private OGLResourceCache<int, OGLStreamBuffer> Cache;
private OGLResourceCache<long, OGLStreamBuffer> Cache;
public OGLConstBuffer() public OGLConstBuffer()
{ {
Cache = new OGLResourceCache<long, OGLStreamBuffer>(DeleteBuffer, MaxConstBufferCacheSize); Cache = new OGLResourceCache<int, OGLStreamBuffer>(DeleteBuffer, OGLResourceLimits.ConstBufferLimit);
} }
private static void DeleteBuffer(OGLStreamBuffer Buffer) private static void DeleteBuffer(OGLStreamBuffer Buffer)
@ -29,19 +27,19 @@ namespace Ryujinx.Graphics.Gal.OpenGL
Cache.Unlock(); Cache.Unlock();
} }
public bool IsCached(long key, long size) public bool IsCached(long key, int size)
{ {
return Cache.TryGetSize(key, out long cbSize) && cbSize >= size; return Cache.TryGetSize(key, out int cbSize) && cbSize >= size;
} }
public void Create(long key, IntPtr hostAddress, long size) public void Create(long key, IntPtr hostAddress, int size)
{ {
GetBuffer(key, size).SetData(size, hostAddress); GetBuffer(key, size).SetData(hostAddress, size);
} }
public void Create(long key, byte[] data) public void Create(long key, byte[] buffer)
{ {
GetBuffer(key, data.Length).SetData(data); GetBuffer(key, buffer.Length).SetData(buffer);
} }
public bool TryGetUbo(long key, out int uboHandle) public bool TryGetUbo(long key, out int uboHandle)
@ -58,7 +56,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
return false; return false;
} }
private OGLStreamBuffer GetBuffer(long Key, long Size) private OGLStreamBuffer GetBuffer(long Key, int Size)
{ {
if (!Cache.TryReuseValue(Key, Size, out OGLStreamBuffer Buffer)) if (!Cache.TryReuseValue(Key, Size, out OGLStreamBuffer Buffer))
{ {

View file

@ -75,9 +75,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ GalVertexAttribSize._11_11_10, VertexAttribPointerType.UnsignedInt10F11F11FRev } { GalVertexAttribSize._11_11_10, VertexAttribPointerType.UnsignedInt10F11F11FRev }
}; };
private const long MaxVertexBufferCacheSize = 128 * 1024 * 1024;
private const long MaxIndexBufferCacheSize = 64 * 1024 * 1024;
private int[] VertexBuffers; private int[] VertexBuffers;
private struct CachedVao private struct CachedVao
@ -99,7 +96,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
private OGLResourceCache<int, CachedVao> VaoCache; private OGLResourceCache<int, CachedVao> VaoCache;
private OGLResourceCache<long, OGLStreamBuffer> VboCache; private OGLResourceCache<int, OGLStreamBuffer> VboCache;
private class CachedIbo private class CachedIbo
{ {
@ -113,7 +110,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
} }
} }
private OGLResourceCache<long, CachedIbo> IboCache; private OGLResourceCache<int, CachedIbo> IboCache;
private struct IbInfo private struct IbInfo
{ {
@ -129,11 +126,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ {
VertexBuffers = new int[32]; VertexBuffers = new int[32];
VaoCache = new OGLResourceCache<int, CachedVao>(DeleteVao, 64 * 1024); VaoCache = new OGLResourceCache<int, CachedVao>(DeleteVao, OGLResourceLimits.VertexArrayLimit);
VboCache = new OGLResourceCache<long, OGLStreamBuffer>(DeleteBuffer, MaxVertexBufferCacheSize); VboCache = new OGLResourceCache<int, OGLStreamBuffer>(DeleteBuffer, OGLResourceLimits.VertexBufferLimit);
IboCache = new OGLResourceCache<long, CachedIbo>(DeleteIbo, MaxIndexBufferCacheSize); IboCache = new OGLResourceCache<int, CachedIbo>(DeleteIbo, OGLResourceLimits.IndexBufferLimit);
IndexBuffer = new IbInfo(); IndexBuffer = new IbInfo();
} }
@ -200,14 +197,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL
} }
} }
public bool IsVboCached(long key, long size) public bool IsVboCached(long key, int size)
{ {
return VboCache.TryGetSize(key, out long vbSize) && vbSize >= size; return VboCache.TryGetSize(key, out int vbSize) && vbSize >= size;
} }
public bool IsIboCached(long key, long size, out long vertexCount) public bool IsIboCached(long key, int size, out long vertexCount)
{ {
if (IboCache.TryGetSizeAndValue(key, out long ibSize, out CachedIbo ibo) && ibSize >= size) if (IboCache.TryGetSizeAndValue(key, out int ibSize, out CachedIbo ibo) && ibSize >= size)
{ {
vertexCount = ibo.VertexCount; vertexCount = ibo.VertexCount;
@ -523,19 +520,19 @@ namespace Ryujinx.Graphics.Gal.OpenGL
throw new NotImplementedException("Unsupported size \"" + Attrib.Size + "\" on type \"" + Attrib.Type + "\"!"); throw new NotImplementedException("Unsupported size \"" + Attrib.Size + "\" on type \"" + Attrib.Type + "\"!");
} }
public void CreateVbo(long Key, int DataSize, IntPtr HostAddress) public void CreateVbo(long key, IntPtr hostAddress, int size)
{ {
GetVbo(Key, DataSize).SetData(DataSize, HostAddress); GetVbo(key, size).SetData(hostAddress, size);
}
public void CreateVbo(long Key, byte[] Data)
{
GetVbo(Key, Data.Length).SetData(Data);
} }
public void CreateIbo(long key, IntPtr hostAddress, int size, long vertexCount) public void CreateIbo(long key, IntPtr hostAddress, int size, long vertexCount)
{ {
GetIbo(key, size, vertexCount).SetData(size, hostAddress); GetIbo(key, size, vertexCount).SetData(hostAddress, size);
}
public void CreateVbo(long key, byte[] buffer)
{
GetVbo(key, buffer.Length).SetData(buffer);
} }
public void CreateIbo(long key, byte[] buffer, long vertexCount) public void CreateIbo(long key, byte[] buffer, long vertexCount)
@ -620,7 +617,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
return false; return false;
} }
private OGLStreamBuffer GetVbo(long Key, long Size) private OGLStreamBuffer GetVbo(long Key, int Size)
{ {
if (!VboCache.TryReuseValue(Key, Size, out OGLStreamBuffer Buffer)) if (!VboCache.TryReuseValue(Key, Size, out OGLStreamBuffer Buffer))
{ {
@ -632,7 +629,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
return Buffer; return Buffer;
} }
private OGLStreamBuffer GetIbo(long Key, long Size, long VertexCount) private OGLStreamBuffer GetIbo(long Key, int Size, long VertexCount)
{ {
if (!IboCache.TryReuseValue(Key, Size, out CachedIbo Ibo)) if (!IboCache.TryReuseValue(Key, Size, out CachedIbo Ibo))
{ {

View file

@ -7,9 +7,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL
class OGLResourceCache<TPoolKey, TValue> class OGLResourceCache<TPoolKey, TValue>
{ {
private const int MinTimeDelta = 5 * 60000; private const int MinTimeDelta = 5 * 60000;
private const int MinTimeDeltaPool = 2500;
private const int MaxRemovalsPerRun = 10; private const int MaxRemovalsPerRun = 10;
private const int DefaultMinTimeForPoolTransfer = 2500;
private class CacheBucket private class CacheBucket
{ {
public long Key { get; private set; } public long Key { get; private set; }
@ -21,18 +22,18 @@ namespace Ryujinx.Graphics.Gal.OpenGL
private Queue<Action> _deleteDeps; private Queue<Action> _deleteDeps;
public long DataSize { get; private set; } public int Size { get; private set; }
public long Timestamp { get; private set; } public long Timestamp { get; private set; }
public bool Orphan { get; private set; } public bool Orphan { get; private set; }
public CacheBucket(long key, TPoolKey poolKey, TValue value, long dataSize) public CacheBucket(long key, TPoolKey poolKey, TValue value, int size)
{ {
Key = key; Key = key;
PoolKey = poolKey; PoolKey = poolKey;
Value = value; Value = value;
DataSize = dataSize; Size = size;
_deleteDeps = new Queue<Action>(); _deleteDeps = new Queue<Action>();
} }
@ -82,14 +83,18 @@ namespace Ryujinx.Graphics.Gal.OpenGL
private bool _locked; private bool _locked;
private long _maxSize; private int _maxSize;
private long _totalSize; private int _totalSize;
private int _minTimeForPoolTransfer;
public OGLResourceCache(Action<TValue> DeleteValueCallback, long MaxSize) public OGLResourceCache(
Action<TValue> deleteValueCallback,
int maxSize,
int minTimeForPoolTransfer = DefaultMinTimeForPoolTransfer)
{ {
_maxSize = MaxSize; _maxSize = maxSize;
_deleteValueCallback = DeleteValueCallback ?? throw new ArgumentNullException(nameof(DeleteValueCallback)); _deleteValueCallback = deleteValueCallback ?? throw new ArgumentNullException(nameof(deleteValueCallback));
_cache = new Dictionary<long, CacheBucket>(); _cache = new Dictionary<long, CacheBucket>();
@ -120,7 +125,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
ClearCacheIfNeeded(); ClearCacheIfNeeded();
} }
public void AddOrUpdate(long key, TPoolKey poolKey, TValue value, long size) public void AddOrUpdate(long key, TPoolKey poolKey, TValue value, int size)
{ {
if (!_locked) if (!_locked)
{ {
@ -181,15 +186,23 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public bool TryReuseValue(long key, TPoolKey poolKey, out TValue value) public bool TryReuseValue(long key, TPoolKey poolKey, out TValue value)
{ {
if (_cache.TryGetValue(key, out CacheBucket bucket) && bucket.PoolKey.Equals(poolKey))
{
//Value on key is already compatible, we don't need to do anything.
value = bucket.Value;
return true;
}
if (_pool.TryGetValue(poolKey, out LinkedList<CacheBucket> queue)) if (_pool.TryGetValue(poolKey, out LinkedList<CacheBucket> queue))
{ {
LinkedListNode<CacheBucket> node = queue.First; LinkedListNode<CacheBucket> node = queue.First;
CacheBucket bucket = node.Value; bucket = node.Value;
Remove(bucket); Remove(bucket);
AddOrUpdate(key, poolKey, bucket.Value, bucket.DataSize); AddOrUpdate(key, poolKey, bucket.Value, bucket.Size);
value = bucket.Value; value = bucket.Value;
@ -201,11 +214,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
return false; return false;
} }
public bool TryGetSize(long key, out long size) public bool TryGetSize(long key, out int size)
{ {
if (_cache.TryGetValue(key, out CacheBucket bucket)) if (_cache.TryGetValue(key, out CacheBucket bucket))
{ {
size = bucket.DataSize; size = bucket.Size;
return true; return true;
} }
@ -215,11 +228,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
return false; return false;
} }
public bool TryGetSizeAndValue(long key, out long size, out TValue value) public bool TryGetSizeAndValue(long key, out int size, out TValue value)
{ {
if (_cache.TryGetValue(key, out CacheBucket bucket)) if (_cache.TryGetValue(key, out CacheBucket bucket))
{ {
size = bucket.DataSize; size = bucket.Size;
value = bucket.Value; value = bucket.Value;
return true; return true;
@ -267,7 +280,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
long timeDelta = timestamp - bucket.Timestamp; long timeDelta = timestamp - bucket.Timestamp;
if (timeDelta <= MinTimeDeltaPool) if (timeDelta <= _minTimeForPoolTransfer)
{ {
break; break;
} }
@ -319,7 +332,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
bucket.DeleteAllDependencies(); bucket.DeleteAllDependencies();
_totalSize -= bucket.DataSize; _totalSize -= bucket.Size;
} }
private void RemoveFromSortedCache(LinkedListNode<CacheBucket> node) private void RemoveFromSortedCache(LinkedListNode<CacheBucket> node)

View file

@ -0,0 +1,18 @@
namespace Ryujinx.Graphics.Gal.OpenGL
{
static class OGLResourceLimits
{
private const int KB = 1024;
private const int MB = 1024 * KB;
public const int ConstBufferLimit = 64 * MB;
public const int VertexArrayLimit = 16384;
public const int VertexBufferLimit = 128 * MB;
public const int IndexBufferLimit = 64 * MB;
public const int TextureLimit = 768 * MB;
public const int PixelBufferLimit = 64 * MB;
}
}

View file

@ -11,30 +11,30 @@ namespace Ryujinx.Graphics.Gal.OpenGL
protected BufferTarget Target { get; private set; } protected BufferTarget Target { get; private set; }
public OGLStreamBuffer(BufferTarget Target, long Size) public OGLStreamBuffer(BufferTarget target, int size)
{ {
this.Target = Target; Target = target;
this.Size = Size; Size = size;
Handle = GL.GenBuffer(); Handle = GL.GenBuffer();
GL.BindBuffer(Target, Handle); GL.BindBuffer(target, Handle);
GL.BufferData(Target, (IntPtr)Size, IntPtr.Zero, BufferUsageHint.StreamDraw); GL.BufferData(target, new IntPtr(size), IntPtr.Zero, BufferUsageHint.StreamDraw);
} }
public void SetData(long Size, IntPtr HostAddress) public void SetData(IntPtr hostAddress, int size)
{ {
GL.BindBuffer(Target, Handle); GL.BindBuffer(Target, Handle);
GL.BufferSubData(Target, IntPtr.Zero, (IntPtr)Size, HostAddress); GL.BufferSubData(Target, IntPtr.Zero, new IntPtr(size), hostAddress);
} }
public void SetData(byte[] Data) public void SetData(byte[] buffer)
{ {
GL.BindBuffer(Target, Handle); GL.BindBuffer(Target, Handle);
GL.BufferSubData(Target, IntPtr.Zero, (IntPtr)Data.Length, Data); GL.BufferSubData(Target, IntPtr.Zero, new IntPtr(buffer.Length), buffer);
} }
public void Dispose() public void Dispose()
@ -42,9 +42,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
Dispose(true); Dispose(true);
} }
protected virtual void Dispose(bool Disposing) protected virtual void Dispose(bool disposing)
{ {
if (Disposing && Handle != 0) if (disposing && Handle != 0)
{ {
GL.DeleteBuffer(Handle); GL.DeleteBuffer(Handle);

View file

@ -6,8 +6,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ {
class OGLTexture : IGalTexture class OGLTexture : IGalTexture
{ {
private const long MaxTextureCacheSize = 768 * 1024 * 1024;
private struct ImageKey private struct ImageKey
{ {
public int Width { get; private set; } public int Width { get; private set; }
@ -48,9 +46,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public OGLTexture() public OGLTexture()
{ {
TextureCache = new OGLResourceCache<ImageKey, ImageHandler>(DeleteTexture, MaxTextureCacheSize); TextureCache = new OGLResourceCache<ImageKey, ImageHandler>(DeleteTexture, OGLResourceLimits.TextureLimit);
PboCache = new OGLResourceCache<int, int>(GL.DeleteBuffer, 256); PboCache = new OGLResourceCache<int, int>(GL.DeleteBuffer, OGLResourceLimits.PixelBufferLimit, 0);
} }
public void LockCache() public void LockCache()
@ -90,7 +88,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ {
CachedImage = new ImageHandler(GL.GenTexture(), Image); CachedImage = new ImageHandler(GL.GenTexture(), Image);
TextureCache.AddOrUpdate(Key, imageKey, CachedImage, (uint)Size); TextureCache.AddOrUpdate(Key, imageKey, CachedImage, Size);
} }
GL.BindTexture(TextureTarget.Texture2D, CachedImage.Handle); GL.BindTexture(TextureTarget.Texture2D, CachedImage.Handle);
@ -128,9 +126,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
const int Level = 0; //TODO: Support mipmap textures. const int Level = 0; //TODO: Support mipmap textures.
const int Border = 0; const int Border = 0;
ImageKey imgKey = new ImageKey(Image); ImageKey imageKey = new ImageKey(Image);
TextureCache.AddOrUpdate(Key, imgKey, new ImageHandler(Handle, Image), (uint)Data.Length); TextureCache.AddOrUpdate(Key, imageKey, new ImageHandler(Handle, Image), Data.Length);
if (ImageUtils.IsCompressed(Image.Format) && !IsAstc(Image.Format)) if (ImageUtils.IsCompressed(Image.Format) && !IsAstc(Image.Format))
{ {
@ -195,9 +193,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
return; return;
} }
if (NewImage.Format == OldImage.Format && if (NewImage.Width == OldImage.Width &&
NewImage.Width == OldImage.Width && NewImage.Height == OldImage.Height &&
NewImage.Height == OldImage.Height) NewImage.Format == OldImage.Format)
{ {
return; return;
} }
@ -208,16 +206,22 @@ namespace Ryujinx.Graphics.Gal.OpenGL
if (!PboCache.TryReuseValue(0, BufferSize, out int Handle)) if (!PboCache.TryReuseValue(0, BufferSize, out int Handle))
{ {
PboCache.AddOrUpdate(0, BufferSize, Handle = GL.GenBuffer(), BufferSize); Handle = GL.GenBuffer();
}
GL.BindBuffer(BufferTarget.PixelPackBuffer, Handle); GL.BindBuffer(BufferTarget.PixelPackBuffer, Handle);
GL.BufferData(BufferTarget.PixelPackBuffer, BufferSize, IntPtr.Zero, BufferUsageHint.StreamCopy); GL.BufferData(BufferTarget.PixelPackBuffer, BufferSize, IntPtr.Zero, BufferUsageHint.StreamCopy);
PboCache.AddOrUpdate(0, BufferSize, Handle, BufferSize);
}
else
{
GL.BindBuffer(BufferTarget.PixelPackBuffer, Handle);
}
if (!TryGetImageHandler(Key, out ImageHandler CachedImage)) if (!TryGetImageHandler(Key, out ImageHandler CachedImage))
{ {
throw new InvalidOperationException(); throw new ArgumentException(nameof(Key));
} }
(_, PixelFormat Format, PixelType Type) = OGLEnumConverter.GetImageFormat(CachedImage.Format); (_, PixelFormat Format, PixelType Type) = OGLEnumConverter.GetImageFormat(CachedImage.Format);

View file

@ -989,13 +989,13 @@ namespace Ryujinx.Graphics.Graphics3d
VbSize = MaxVbSize; VbSize = MaxVbSize;
} }
bool VboCached = Gpu.Renderer.Rasterizer.IsVboCached(VboKey, VbSize); bool VboCached = Gpu.Renderer.Rasterizer.IsVboCached(VboKey, (int)VbSize);
if (Gpu.ResourceManager.MemoryRegionModified(Vmm, VboKey, VbSize, NvGpuBufferType.Vertex) || !VboCached) if (Gpu.ResourceManager.MemoryRegionModified(Vmm, VboKey, VbSize, NvGpuBufferType.Vertex) || !VboCached)
{ {
if (Vmm.TryGetHostAddress(VbPosition, VbSize, out IntPtr VbPtr)) if (Vmm.TryGetHostAddress(VbPosition, VbSize, out IntPtr VbPtr))
{ {
Gpu.Renderer.Rasterizer.CreateVbo(VboKey, (int)VbSize, VbPtr); Gpu.Renderer.Rasterizer.CreateVbo(VboKey, VbPtr, (int)VbSize);
} }
else else
{ {