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 UnlockCache();
void Create(long key, IntPtr hostAddress, long size);
void Create(long key, byte[] data);
void Create(long key, IntPtr hostAddress, int size);
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,
GalVertexAttribArray[] arrays);
bool IsVboCached(long key, long size);
bool IsIboCached(long key, long size, out long vertexCount);
void CreateVbo(long Key, int DataSize, IntPtr HostAddress);
void CreateVbo(long Key, byte[] Data);
bool IsVboCached(long key, int size);
bool IsIboCached(long key, int size, out long vertexCount);
void CreateVbo(long key, IntPtr hostAddress, int size);
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 SetIndexArray(int Size, GalIndexFormat Format);

View file

@ -5,13 +5,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{
class OGLConstBuffer : IGalConstBuffer
{
private const long MaxConstBufferCacheSize = 64 * 1024 * 1024;
private OGLResourceCache<long, OGLStreamBuffer> Cache;
private OGLResourceCache<int, OGLStreamBuffer> Cache;
public OGLConstBuffer()
{
Cache = new OGLResourceCache<long, OGLStreamBuffer>(DeleteBuffer, MaxConstBufferCacheSize);
Cache = new OGLResourceCache<int, OGLStreamBuffer>(DeleteBuffer, OGLResourceLimits.ConstBufferLimit);
}
private static void DeleteBuffer(OGLStreamBuffer Buffer)
@ -29,19 +27,19 @@ namespace Ryujinx.Graphics.Gal.OpenGL
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)
@ -58,7 +56,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
return false;
}
private OGLStreamBuffer GetBuffer(long Key, long Size)
private OGLStreamBuffer GetBuffer(long Key, int Size)
{
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 }
};
private const long MaxVertexBufferCacheSize = 128 * 1024 * 1024;
private const long MaxIndexBufferCacheSize = 64 * 1024 * 1024;
private int[] VertexBuffers;
private struct CachedVao
@ -99,7 +96,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
private OGLResourceCache<int, CachedVao> VaoCache;
private OGLResourceCache<long, OGLStreamBuffer> VboCache;
private OGLResourceCache<int, OGLStreamBuffer> VboCache;
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
{
@ -129,11 +126,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{
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();
}
@ -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;
@ -523,19 +520,19 @@ namespace Ryujinx.Graphics.Gal.OpenGL
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);
}
public void CreateVbo(long Key, byte[] Data)
{
GetVbo(Key, Data.Length).SetData(Data);
GetVbo(key, size).SetData(hostAddress, size);
}
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)
@ -620,7 +617,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
return false;
}
private OGLStreamBuffer GetVbo(long Key, long Size)
private OGLStreamBuffer GetVbo(long Key, int Size)
{
if (!VboCache.TryReuseValue(Key, Size, out OGLStreamBuffer Buffer))
{
@ -632,7 +629,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
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))
{

View file

@ -7,9 +7,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL
class OGLResourceCache<TPoolKey, TValue>
{
private const int MinTimeDelta = 5 * 60000;
private const int MinTimeDeltaPool = 2500;
private const int MaxRemovalsPerRun = 10;
private const int DefaultMinTimeForPoolTransfer = 2500;
private class CacheBucket
{
public long Key { get; private set; }
@ -21,18 +22,18 @@ namespace Ryujinx.Graphics.Gal.OpenGL
private Queue<Action> _deleteDeps;
public long DataSize { get; private set; }
public int Size { get; private set; }
public long Timestamp { 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;
PoolKey = poolKey;
Value = value;
DataSize = dataSize;
Size = size;
_deleteDeps = new Queue<Action>();
}
@ -82,14 +83,18 @@ namespace Ryujinx.Graphics.Gal.OpenGL
private bool _locked;
private long _maxSize;
private long _totalSize;
private int _maxSize;
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>();
@ -120,7 +125,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
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)
{
@ -181,15 +186,23 @@ namespace Ryujinx.Graphics.Gal.OpenGL
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))
{
LinkedListNode<CacheBucket> node = queue.First;
CacheBucket bucket = node.Value;
bucket = node.Value;
Remove(bucket);
AddOrUpdate(key, poolKey, bucket.Value, bucket.DataSize);
AddOrUpdate(key, poolKey, bucket.Value, bucket.Size);
value = bucket.Value;
@ -201,11 +214,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
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))
{
size = bucket.DataSize;
size = bucket.Size;
return true;
}
@ -215,11 +228,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
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))
{
size = bucket.DataSize;
size = bucket.Size;
value = bucket.Value;
return true;
@ -267,7 +280,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
long timeDelta = timestamp - bucket.Timestamp;
if (timeDelta <= MinTimeDeltaPool)
if (timeDelta <= _minTimeForPoolTransfer)
{
break;
}
@ -319,7 +332,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
bucket.DeleteAllDependencies();
_totalSize -= bucket.DataSize;
_totalSize -= bucket.Size;
}
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; }
public OGLStreamBuffer(BufferTarget Target, long Size)
public OGLStreamBuffer(BufferTarget target, int size)
{
this.Target = Target;
this.Size = Size;
Target = target;
Size = size;
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.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.BufferSubData(Target, IntPtr.Zero, (IntPtr)Data.Length, Data);
GL.BufferSubData(Target, IntPtr.Zero, new IntPtr(buffer.Length), buffer);
}
public void Dispose()
@ -42,9 +42,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
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);

View file

@ -6,8 +6,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{
class OGLTexture : IGalTexture
{
private const long MaxTextureCacheSize = 768 * 1024 * 1024;
private struct ImageKey
{
public int Width { get; private set; }
@ -48,9 +46,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
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()
@ -90,7 +88,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{
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);
@ -128,9 +126,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
const int Level = 0; //TODO: Support mipmap textures.
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))
{
@ -195,9 +193,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
return;
}
if (NewImage.Format == OldImage.Format &&
NewImage.Width == OldImage.Width &&
NewImage.Height == OldImage.Height)
if (NewImage.Width == OldImage.Width &&
NewImage.Height == OldImage.Height &&
NewImage.Format == OldImage.Format)
{
return;
}
@ -208,16 +206,22 @@ namespace Ryujinx.Graphics.Gal.OpenGL
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.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))
{
throw new InvalidOperationException();
throw new ArgumentException(nameof(Key));
}
(_, PixelFormat Format, PixelType Type) = OGLEnumConverter.GetImageFormat(CachedImage.Format);

View file

@ -989,13 +989,13 @@ namespace Ryujinx.Graphics.Graphics3d
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 (Vmm.TryGetHostAddress(VbPosition, VbSize, out IntPtr VbPtr))
{
Gpu.Renderer.Rasterizer.CreateVbo(VboKey, (int)VbSize, VbPtr);
Gpu.Renderer.Rasterizer.CreateVbo(VboKey, VbPtr, (int)VbSize);
}
else
{