Ryujinx/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs
riperiperi 10e17ab423
Only use persistent buffers to flush on NVIDIA and Windows+AMD (#2489)
It seems like this method of flushing data is much slower on Mesa drivers, and slightly slower on Intel Windows. Have not tested Intel Mesa, but I'm assuming it is the same as AMD.

This also adds vendor detection for AMD on Unix, which counted as "Unknown" before.
2021-07-18 11:45:50 -03:00

114 lines
3.4 KiB
C#

using System;
using System.Runtime.InteropServices;
using OpenTK.Graphics.OpenGL;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.OpenGL.Image;
namespace Ryujinx.Graphics.OpenGL
{
class PersistentBuffers : IDisposable
{
private PersistentBuffer _main = new PersistentBuffer();
private PersistentBuffer _background = new PersistentBuffer();
public PersistentBuffer Default => BackgroundContextWorker.InBackground ? _background : _main;
public void Dispose()
{
_main?.Dispose();
_background?.Dispose();
}
}
class PersistentBuffer : IDisposable
{
private IntPtr _bufferMap;
private int _copyBufferHandle;
private int _copyBufferSize;
private void EnsureBuffer(int requiredSize)
{
if (_copyBufferSize < requiredSize && _copyBufferHandle != 0)
{
GL.DeleteBuffer(_copyBufferHandle);
_copyBufferHandle = 0;
}
if (_copyBufferHandle == 0)
{
_copyBufferHandle = GL.GenBuffer();
_copyBufferSize = requiredSize;
GL.BindBuffer(BufferTarget.CopyWriteBuffer, _copyBufferHandle);
GL.BufferStorage(BufferTarget.CopyWriteBuffer, requiredSize, IntPtr.Zero, BufferStorageFlags.MapReadBit | BufferStorageFlags.MapPersistentBit);
_bufferMap = GL.MapBufferRange(BufferTarget.CopyWriteBuffer, IntPtr.Zero, requiredSize, BufferAccessMask.MapReadBit | BufferAccessMask.MapPersistentBit);
}
}
private void Sync()
{
GL.MemoryBarrier(MemoryBarrierFlags.ClientMappedBufferBarrierBit);
IntPtr sync = GL.FenceSync(SyncCondition.SyncGpuCommandsComplete, WaitSyncFlags.None);
WaitSyncStatus syncResult = GL.ClientWaitSync(sync, ClientWaitSyncFlags.SyncFlushCommandsBit, 1000000000);
if (syncResult == WaitSyncStatus.TimeoutExpired)
{
Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to sync persistent buffer state within 1000ms. Continuing...");
}
GL.DeleteSync(sync);
}
public byte[] GetTextureData(TextureView view, int size)
{
EnsureBuffer(size);
GL.BindBuffer(BufferTarget.PixelPackBuffer, _copyBufferHandle);
view.WriteToPbo(0, false);
GL.BindBuffer(BufferTarget.PixelPackBuffer, 0);
byte[] data = new byte[size];
Sync();
Marshal.Copy(_bufferMap, data, 0, size);
return data;
}
public byte[] GetBufferData(BufferHandle buffer, int offset, int size)
{
EnsureBuffer(size);
GL.BindBuffer(BufferTarget.CopyReadBuffer, buffer.ToInt32());
GL.BindBuffer(BufferTarget.CopyWriteBuffer, _copyBufferHandle);
GL.CopyBufferSubData(BufferTarget.CopyReadBuffer, BufferTarget.CopyWriteBuffer, (IntPtr)offset, IntPtr.Zero, size);
GL.BindBuffer(BufferTarget.CopyWriteBuffer, 0);
byte[] data = new byte[size];
Sync();
Marshal.Copy(_bufferMap, data, 0, size);
return data;
}
public void Dispose()
{
if (_copyBufferHandle != 0)
{
GL.DeleteBuffer(_copyBufferHandle);
}
}
}
}