Jelajahi Sumber

Flush buffers and texture data through a persistent mapped buffer. (#2481)

* Use persistent buffers to flush texture data

* Flush buffers via copy to persistent buffers.

* Log error when timing out, small refactoring.
riperiperi 4 tahun lalu
induk
melakukan
ca5ac37cd6

+ 2 - 2
Ryujinx.Graphics.Gpu/Image/Texture.cs

@@ -876,9 +876,9 @@ namespace Ryujinx.Graphics.Gpu.Image
         /// This is not cheap, avoid doing that unless strictly needed.
         /// This is not cheap, avoid doing that unless strictly needed.
         /// </remarks>
         /// </remarks>
         /// <returns>Host texture data</returns>
         /// <returns>Host texture data</returns>
-        private Span<byte> GetTextureDataFromGpu(bool blacklist, ITexture texture = null)
+        private ReadOnlySpan<byte> GetTextureDataFromGpu(bool blacklist, ITexture texture = null)
         {
         {
-            Span<byte> data;
+            ReadOnlySpan<byte> data;
 
 
             if (texture != null)
             if (texture != null)
             {
             {

+ 1 - 18
Ryujinx.Graphics.OpenGL/Image/TextureView.cs

@@ -121,24 +121,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
 
 
         public byte[] GetData()
         public byte[] GetData()
         {
         {
-            int size = 0;
-
-            for (int level = 0; level < Info.Levels; level++)
-            {
-                size += Info.GetMipSize(level);
-            }
-
-            byte[] data = new byte[size];
-
-            unsafe
-            {
-                fixed (byte* ptr = data)
-                {
-                    WriteTo((IntPtr)ptr);
-                }
-            }
-
-            return data;
+            return _renderer.PersistentBuffers.Default.GetTextureData(this);
         }
         }
 
 
         public void WriteToPbo(int offset, bool forceBgra)
         public void WriteToPbo(int offset, bool forceBgra)

+ 120 - 0
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);
+            }
+        }
+    }
+}

+ 5 - 1
Ryujinx.Graphics.OpenGL/Renderer.cs

@@ -30,6 +30,8 @@ namespace Ryujinx.Graphics.OpenGL
 
 
         public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured;
         public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured;
 
 
+        internal PersistentBuffers PersistentBuffers { get; }
+
         internal ResourcePool ResourcePool { get; }
         internal ResourcePool ResourcePool { get; }
 
 
         internal int BufferCount { get; private set; }
         internal int BufferCount { get; private set; }
@@ -46,6 +48,7 @@ namespace Ryujinx.Graphics.OpenGL
             _textureCopy = new TextureCopy(this);
             _textureCopy = new TextureCopy(this);
             _backgroundTextureCopy = new TextureCopy(this);
             _backgroundTextureCopy = new TextureCopy(this);
             _sync = new Sync();
             _sync = new Sync();
+            PersistentBuffers = new PersistentBuffers();
             ResourcePool = new ResourcePool();
             ResourcePool = new ResourcePool();
         }
         }
 
 
@@ -90,7 +93,7 @@ namespace Ryujinx.Graphics.OpenGL
 
 
         public byte[] GetBufferData(BufferHandle buffer, int offset, int size)
         public byte[] GetBufferData(BufferHandle buffer, int offset, int size)
         {
         {
-            return Buffer.GetData(buffer, offset, size);
+            return PersistentBuffers.Default.GetBufferData(buffer, offset, size);
         }
         }
 
 
         public Capabilities GetCapabilities()
         public Capabilities GetCapabilities()
@@ -177,6 +180,7 @@ namespace Ryujinx.Graphics.OpenGL
         {
         {
             _textureCopy.Dispose();
             _textureCopy.Dispose();
             _backgroundTextureCopy.Dispose();
             _backgroundTextureCopy.Dispose();
+            PersistentBuffers.Dispose();
             ResourcePool.Dispose();
             ResourcePool.Dispose();
             _pipeline.Dispose();
             _pipeline.Dispose();
             _window.Dispose();
             _window.Dispose();

+ 7 - 2
Ryujinx.Graphics.Texture/LayoutConverter.cs

@@ -358,7 +358,7 @@ namespace Ryujinx.Graphics.Texture
             };
             };
         }
         }
 
 
-        public static Span<byte> ConvertLinearToBlockLinear(
+        public static ReadOnlySpan<byte> ConvertLinearToBlockLinear(
             int width,
             int width,
             int height,
             int height,
             int depth,
             int depth,
@@ -499,7 +499,7 @@ namespace Ryujinx.Graphics.Texture
             return output;
             return output;
         }
         }
 
 
-        public static Span<byte> ConvertLinearToLinearStrided(
+        public static ReadOnlySpan<byte> ConvertLinearToLinearStrided(
             int width,
             int width,
             int height,
             int height,
             int blockWidth,
             int blockWidth,
@@ -514,6 +514,11 @@ namespace Ryujinx.Graphics.Texture
             int inStride = BitUtils.AlignUp(w * bytesPerPixel, HostStrideAlignment);
             int inStride = BitUtils.AlignUp(w * bytesPerPixel, HostStrideAlignment);
             int lineSize = width * bytesPerPixel;
             int lineSize = width * bytesPerPixel;
 
 
+            if (inStride == stride)
+            {
+                return data;
+            }
+
             Span<byte> output = new byte[h * stride];
             Span<byte> output = new byte[h * stride];
 
 
             int inOffs = 0;
             int inOffs = 0;