PersistentBuffers.cs
1 using OpenTK.Graphics.OpenGL; 2 using Ryujinx.Common.Logging; 3 using Ryujinx.Graphics.GAL; 4 using Ryujinx.Graphics.OpenGL.Image; 5 using System; 6 using System.Collections.Generic; 7 using System.Runtime.CompilerServices; 8 using System.Runtime.InteropServices; 9 10 namespace Ryujinx.Graphics.OpenGL 11 { 12 class PersistentBuffers : IDisposable 13 { 14 private readonly PersistentBuffer _main = new(); 15 private readonly PersistentBuffer _background = new(); 16 17 private readonly Dictionary<BufferHandle, IntPtr> _maps = new(); 18 19 public PersistentBuffer Default => BackgroundContextWorker.InBackground ? _background : _main; 20 21 public void Dispose() 22 { 23 _main?.Dispose(); 24 _background?.Dispose(); 25 } 26 27 public void Map(BufferHandle handle, int size) 28 { 29 GL.BindBuffer(BufferTarget.CopyWriteBuffer, handle.ToInt32()); 30 IntPtr ptr = GL.MapBufferRange(BufferTarget.CopyWriteBuffer, IntPtr.Zero, size, BufferAccessMask.MapReadBit | BufferAccessMask.MapPersistentBit); 31 32 _maps[handle] = ptr; 33 } 34 35 public void Unmap(BufferHandle handle) 36 { 37 if (_maps.ContainsKey(handle)) 38 { 39 GL.BindBuffer(BufferTarget.CopyWriteBuffer, handle.ToInt32()); 40 GL.UnmapBuffer(BufferTarget.CopyWriteBuffer); 41 42 _maps.Remove(handle); 43 } 44 } 45 46 public bool TryGet(BufferHandle handle, out IntPtr ptr) 47 { 48 return _maps.TryGetValue(handle, out ptr); 49 } 50 } 51 52 class PersistentBuffer : IDisposable 53 { 54 private IntPtr _bufferMap; 55 private int _copyBufferHandle; 56 private int _copyBufferSize; 57 58 private byte[] _data; 59 private IntPtr _dataMap; 60 61 private void EnsureBuffer(int requiredSize) 62 { 63 if (_copyBufferSize < requiredSize && _copyBufferHandle != 0) 64 { 65 GL.DeleteBuffer(_copyBufferHandle); 66 67 _copyBufferHandle = 0; 68 } 69 70 if (_copyBufferHandle == 0) 71 { 72 _copyBufferHandle = GL.GenBuffer(); 73 _copyBufferSize = requiredSize; 74 75 GL.BindBuffer(BufferTarget.CopyWriteBuffer, _copyBufferHandle); 76 GL.BufferStorage(BufferTarget.CopyWriteBuffer, requiredSize, IntPtr.Zero, BufferStorageFlags.MapReadBit | BufferStorageFlags.MapPersistentBit); 77 78 _bufferMap = GL.MapBufferRange(BufferTarget.CopyWriteBuffer, IntPtr.Zero, requiredSize, BufferAccessMask.MapReadBit | BufferAccessMask.MapPersistentBit); 79 } 80 } 81 82 public unsafe IntPtr GetHostArray(int requiredSize) 83 { 84 if (_data == null || _data.Length < requiredSize) 85 { 86 _data = GC.AllocateUninitializedArray<byte>(requiredSize, true); 87 88 _dataMap = (IntPtr)Unsafe.AsPointer(ref MemoryMarshal.GetArrayDataReference(_data)); 89 } 90 91 return _dataMap; 92 } 93 94 private static void Sync() 95 { 96 GL.MemoryBarrier(MemoryBarrierFlags.ClientMappedBufferBarrierBit); 97 98 IntPtr sync = GL.FenceSync(SyncCondition.SyncGpuCommandsComplete, WaitSyncFlags.None); 99 WaitSyncStatus syncResult = GL.ClientWaitSync(sync, ClientWaitSyncFlags.SyncFlushCommandsBit, 1000000000); 100 101 if (syncResult == WaitSyncStatus.TimeoutExpired) 102 { 103 Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to sync persistent buffer state within 1000ms. Continuing..."); 104 } 105 106 GL.DeleteSync(sync); 107 } 108 109 public unsafe ReadOnlySpan<byte> GetTextureData(TextureView view, int size) 110 { 111 EnsureBuffer(size); 112 113 GL.BindBuffer(BufferTarget.PixelPackBuffer, _copyBufferHandle); 114 115 view.WriteToPbo(0, false); 116 117 GL.BindBuffer(BufferTarget.PixelPackBuffer, 0); 118 119 Sync(); 120 121 return new ReadOnlySpan<byte>(_bufferMap.ToPointer(), size); 122 } 123 124 public unsafe ReadOnlySpan<byte> GetTextureData(TextureView view, int size, int layer, int level) 125 { 126 EnsureBuffer(size); 127 128 GL.BindBuffer(BufferTarget.PixelPackBuffer, _copyBufferHandle); 129 130 int offset = view.WriteToPbo2D(0, layer, level); 131 132 GL.BindBuffer(BufferTarget.PixelPackBuffer, 0); 133 134 Sync(); 135 136 return new ReadOnlySpan<byte>(_bufferMap.ToPointer(), size)[offset..]; 137 } 138 139 public unsafe ReadOnlySpan<byte> GetBufferData(BufferHandle buffer, int offset, int size) 140 { 141 EnsureBuffer(size); 142 143 GL.BindBuffer(BufferTarget.CopyReadBuffer, buffer.ToInt32()); 144 GL.BindBuffer(BufferTarget.CopyWriteBuffer, _copyBufferHandle); 145 146 GL.CopyBufferSubData(BufferTarget.CopyReadBuffer, BufferTarget.CopyWriteBuffer, (IntPtr)offset, IntPtr.Zero, size); 147 148 GL.BindBuffer(BufferTarget.CopyWriteBuffer, 0); 149 150 Sync(); 151 152 return new ReadOnlySpan<byte>(_bufferMap.ToPointer(), size); 153 } 154 155 public void Dispose() 156 { 157 if (_copyBufferHandle != 0) 158 { 159 GL.DeleteBuffer(_copyBufferHandle); 160 } 161 } 162 } 163 }