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();
} }
@ -169,13 +166,13 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public void ClearBuffers( public void ClearBuffers(
GalClearBufferFlags Flags, GalClearBufferFlags Flags,
int Attachment, int Attachment,
float Red, float Red,
float Green, float Green,
float Blue, float Blue,
float Alpha, float Alpha,
float Depth, float Depth,
int Stencil) int Stencil)
{ {
GL.ColorMask( GL.ColorMask(
Attachment, Attachment,
@ -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.BufferData(BufferTarget.PixelPackBuffer, BufferSize, IntPtr.Zero, BufferUsageHint.StreamCopy);
PboCache.AddOrUpdate(0, BufferSize, Handle, BufferSize);
}
else
{
GL.BindBuffer(BufferTarget.PixelPackBuffer, Handle);
} }
GL.BindBuffer(BufferTarget.PixelPackBuffer, Handle);
GL.BufferData(BufferTarget.PixelPackBuffer, BufferSize, IntPtr.Zero, BufferUsageHint.StreamCopy);
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
{ {