diff options
Diffstat (limited to 'Ryujinx.Graphics.OpenGL/PersistentBuffers.cs')
| -rw-r--r-- | Ryujinx.Graphics.OpenGL/PersistentBuffers.cs | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs b/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs new file mode 100644 index 00000000..a7cefca3 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs @@ -0,0 +1,120 @@ +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 = 0; + + for (int level = 0; level < view.Info.Levels; level++) + { + size += view.Info.GetMipSize(level); + } + + 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); + } + } + } +} |
