aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs')
-rw-r--r--src/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs136
1 files changed, 136 insertions, 0 deletions
diff --git a/src/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs b/src/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs
new file mode 100644
index 00000000..654e25b9
--- /dev/null
+++ b/src/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs
@@ -0,0 +1,136 @@
+using OpenTK.Graphics.OpenGL;
+using Ryujinx.Common.Logging;
+using Ryujinx.Graphics.GAL;
+using Ryujinx.Graphics.OpenGL.Image;
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+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 byte[] _data;
+ private IntPtr _dataMap;
+
+ 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);
+ }
+ }
+
+ public unsafe IntPtr GetHostArray(int requiredSize)
+ {
+ if (_data == null || _data.Length < requiredSize)
+ {
+ _data = GC.AllocateUninitializedArray<byte>(requiredSize, true);
+
+ _dataMap = (IntPtr)Unsafe.AsPointer(ref MemoryMarshal.GetArrayDataReference(_data));
+ }
+
+ return _dataMap;
+ }
+
+ 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 unsafe ReadOnlySpan<byte> GetTextureData(TextureView view, int size)
+ {
+ EnsureBuffer(size);
+
+ GL.BindBuffer(BufferTarget.PixelPackBuffer, _copyBufferHandle);
+
+ view.WriteToPbo(0, false);
+
+ GL.BindBuffer(BufferTarget.PixelPackBuffer, 0);
+
+ Sync();
+
+ return new ReadOnlySpan<byte>(_bufferMap.ToPointer(), size);
+ }
+
+ public unsafe ReadOnlySpan<byte> GetTextureData(TextureView view, int size, int layer, int level)
+ {
+ EnsureBuffer(size);
+
+ GL.BindBuffer(BufferTarget.PixelPackBuffer, _copyBufferHandle);
+
+ int offset = view.WriteToPbo2D(0, layer, level);
+
+ GL.BindBuffer(BufferTarget.PixelPackBuffer, 0);
+
+ Sync();
+
+ return new ReadOnlySpan<byte>(_bufferMap.ToPointer(), size).Slice(offset);
+ }
+
+ public unsafe ReadOnlySpan<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);
+
+ Sync();
+
+ return new ReadOnlySpan<byte>(_bufferMap.ToPointer(), size);
+ }
+
+ public void Dispose()
+ {
+ if (_copyBufferHandle != 0)
+ {
+ GL.DeleteBuffer(_copyBufferHandle);
+ }
+ }
+ }
+}