Bläddra i källkod

GAL: Send all buffer assignments at once rather than individually (#3881)

* GAL: Send all buffer assignments at once rather than individually

The `(int first, BufferRange[] ranges)` method call has very significant performance implications when the bindings are spread out, which they generally always are in Vulkan. This change makes it so that these methods are only called a maximum of one time per draw.

Significantly improves GPU thread performance in Pokemon Scarlet/Violet.

* Address Feedback

Removed SetUniformBuffers(int first, ReadOnlySpan<BufferRange> buffers)
riperiperi 3 år sedan
förälder
incheckning
ece36b274d

+ 14 - 0
Ryujinx.Graphics.GAL/BufferAssignment.cs

@@ -0,0 +1,14 @@
+namespace Ryujinx.Graphics.GAL
+{
+    public struct BufferAssignment
+    {
+        public readonly int Binding;
+        public readonly BufferRange Range;
+
+        public BufferAssignment(int binding, BufferRange range)
+        {
+            Binding = binding;
+            Range = range;
+        }
+    }
+}

+ 2 - 2
Ryujinx.Graphics.GAL/IPipeline.cs

@@ -86,12 +86,12 @@ namespace Ryujinx.Graphics.GAL
 
         void SetStencilTest(StencilTestDescriptor stencilTest);
 
-        void SetStorageBuffers(int first, ReadOnlySpan<BufferRange> buffers);
+        void SetStorageBuffers(ReadOnlySpan<BufferAssignment> buffers);
 
         void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler);
 
         void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers);
-        void SetUniformBuffers(int first, ReadOnlySpan<BufferRange> buffers);
+        void SetUniformBuffers(ReadOnlySpan<BufferAssignment> buffers);
 
         void SetUserClipDistance(int index, bool enableClip);
 

+ 24 - 0
Ryujinx.Graphics.GAL/Multithreading/BufferMap.cs

@@ -142,6 +142,30 @@ namespace Ryujinx.Graphics.GAL.Multithreading
             return ranges;
         }
 
+        internal Span<BufferAssignment> MapBufferRanges(Span<BufferAssignment> ranges)
+        {
+            // Rewrite the buffer ranges to point to the mapped handles.
+
+            lock (_bufferMap)
+            {
+                for (int i = 0; i < ranges.Length; i++)
+                {
+                    ref BufferAssignment assignment = ref ranges[i];
+                    BufferRange range = assignment.Range;
+                    BufferHandle result;
+
+                    if (!_bufferMap.TryGetValue(range.Handle, out result))
+                    {
+                        result = BufferHandle.Null;
+                    }
+
+                    assignment = new BufferAssignment(ranges[i].Binding, new BufferRange(result, range.Offset, range.Size));
+                }
+            }
+
+            return ranges;
+        }
+
         internal Span<VertexBufferDescriptor> MapBufferRanges(Span<VertexBufferDescriptor> ranges)
         {
             // Rewrite the buffer ranges to point to the mapped handles.

+ 4 - 6
Ryujinx.Graphics.GAL/Multithreading/Commands/SetStorageBuffersCommand.cs

@@ -6,19 +6,17 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands
     struct SetStorageBuffersCommand : IGALCommand
     {
         public CommandType CommandType => CommandType.SetStorageBuffers;
-        private int _first;
-        private SpanRef<BufferRange> _buffers;
+        private SpanRef<BufferAssignment> _buffers;
 
-        public void Set(int first, SpanRef<BufferRange> buffers)
+        public void Set(SpanRef<BufferAssignment> buffers)
         {
-            _first = first;
             _buffers = buffers;
         }
 
         public static void Run(ref SetStorageBuffersCommand command, ThreadedRenderer threaded, IRenderer renderer)
         {
-            Span<BufferRange> buffers = command._buffers.Get(threaded);
-            renderer.Pipeline.SetStorageBuffers(command._first, threaded.Buffers.MapBufferRanges(buffers));
+            Span<BufferAssignment> buffers = command._buffers.Get(threaded);
+            renderer.Pipeline.SetStorageBuffers(threaded.Buffers.MapBufferRanges(buffers));
             command._buffers.Dispose(threaded);
         }
     }

+ 4 - 6
Ryujinx.Graphics.GAL/Multithreading/Commands/SetUniformBuffersCommand.cs

@@ -6,19 +6,17 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands
     struct SetUniformBuffersCommand : IGALCommand
     {
         public CommandType CommandType => CommandType.SetUniformBuffers;
-        private int _first;
-        private SpanRef<BufferRange> _buffers;
+        private SpanRef<BufferAssignment> _buffers;
 
-        public void Set(int first, SpanRef<BufferRange> buffers)
+        public void Set(SpanRef<BufferAssignment> buffers)
         {
-            _first = first;
             _buffers = buffers;
         }
 
         public static void Run(ref SetUniformBuffersCommand command, ThreadedRenderer threaded, IRenderer renderer)
         {
-            Span<BufferRange> buffers = command._buffers.Get(threaded);
-            renderer.Pipeline.SetUniformBuffers(command._first, threaded.Buffers.MapBufferRanges(buffers));
+            Span<BufferAssignment> buffers = command._buffers.Get(threaded);
+            renderer.Pipeline.SetUniformBuffers(threaded.Buffers.MapBufferRanges(buffers));
             command._buffers.Dispose(threaded);
         }
     }

+ 4 - 4
Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs

@@ -275,9 +275,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading
             _renderer.QueueCommand();
         }
 
-        public void SetStorageBuffers(int first, ReadOnlySpan<BufferRange> buffers)
+        public void SetStorageBuffers(ReadOnlySpan<BufferAssignment> buffers)
         {
-            _renderer.New<SetStorageBuffersCommand>().Set(first, _renderer.CopySpan(buffers));
+            _renderer.New<SetStorageBuffersCommand>().Set(_renderer.CopySpan(buffers));
             _renderer.QueueCommand();
         }
 
@@ -293,9 +293,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading
             _renderer.QueueCommand();
         }
 
-        public void SetUniformBuffers(int first, ReadOnlySpan<BufferRange> buffers)
+        public void SetUniformBuffers(ReadOnlySpan<BufferAssignment> buffers)
         {
-            _renderer.New<SetUniformBuffersCommand>().Set(first, _renderer.CopySpan(buffers));
+            _renderer.New<SetUniformBuffersCommand>().Set(_renderer.CopySpan(buffers));
             _renderer.QueueCommand();
         }
 

+ 11 - 35
Ryujinx.Graphics.Gpu/Memory/BufferManager.cs

@@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
         private readonly VertexBuffer[] _vertexBuffers;
         private readonly BufferBounds[] _transformFeedbackBuffers;
         private readonly List<BufferTextureBinding> _bufferTextures;
-        private readonly BufferRange[] _ranges;
+        private readonly BufferAssignment[] _ranges;
 
         /// <summary>
         /// Holds shader stage buffer state and binding information.
@@ -134,7 +134,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
 
             _bufferTextures = new List<BufferTextureBinding>();
 
-            _ranges = new BufferRange[Constants.TotalGpUniformBuffers * Constants.ShaderStages];
+            _ranges = new BufferAssignment[Constants.TotalGpUniformBuffers * Constants.ShaderStages];
         }
 
 
@@ -618,10 +618,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         private void BindBuffers(BufferCache bufferCache, BuffersPerStage[] bindings, bool isStorage)
         {
-            int rangesFirst = 0;
             int rangesCount = 0;
 
-            Span<BufferRange> ranges = _ranges;
+            Span<BufferAssignment> ranges = _ranges;
 
             for (ShaderStage stage = ShaderStage.Vertex; stage <= ShaderStage.Fragment; stage++)
             {
@@ -640,25 +639,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
                             ? bufferCache.GetBufferRangeTillEnd(bounds.Address, bounds.Size, isWrite)
                             : bufferCache.GetBufferRange(bounds.Address, bounds.Size);
 
-                        if (rangesCount == 0)
-                        {
-                            rangesFirst = bindingInfo.Binding;
-                        }
-                        else if (bindingInfo.Binding != rangesFirst + rangesCount)
-                        {
-                            SetHostBuffers(ranges, rangesFirst, rangesCount, isStorage);
-                            rangesFirst = bindingInfo.Binding;
-                            rangesCount = 0;
-                        }
-
-                        ranges[rangesCount++] = range;
+                        ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range);
                     }
                 }
             }
 
             if (rangesCount != 0)
             {
-                SetHostBuffers(ranges, rangesFirst, rangesCount, isStorage);
+                SetHostBuffers(ranges, rangesCount, isStorage);
             }
         }
 
@@ -671,10 +659,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         private void BindBuffers(BufferCache bufferCache, BuffersPerStage buffers, bool isStorage)
         {
-            int rangesFirst = 0;
             int rangesCount = 0;
 
-            Span<BufferRange> ranges = _ranges;
+            Span<BufferAssignment> ranges = _ranges;
 
             for (int index = 0; index < buffers.Count; index++)
             {
@@ -689,24 +676,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
                         ? bufferCache.GetBufferRangeTillEnd(bounds.Address, bounds.Size, isWrite)
                         : bufferCache.GetBufferRange(bounds.Address, bounds.Size);
 
-                    if (rangesCount == 0)
-                    {
-                        rangesFirst = bindingInfo.Binding;
-                    }
-                    else if (bindingInfo.Binding != rangesFirst + rangesCount)
-                    {
-                        SetHostBuffers(ranges, rangesFirst, rangesCount, isStorage);
-                        rangesFirst = bindingInfo.Binding;
-                        rangesCount = 0;
-                    }
-
-                    ranges[rangesCount++] = range;
+                    ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range);
                 }
             }
 
             if (rangesCount != 0)
             {
-                SetHostBuffers(ranges, rangesFirst, rangesCount, isStorage);
+                SetHostBuffers(ranges, rangesCount, isStorage);
             }
         }
 
@@ -718,15 +694,15 @@ namespace Ryujinx.Graphics.Gpu.Memory
         /// <param name="count">Number of bindings</param>
         /// <param name="isStorage">Indicates if the buffers are storage or uniform buffers</param>
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private void SetHostBuffers(ReadOnlySpan<BufferRange> ranges, int first, int count, bool isStorage)
+        private void SetHostBuffers(ReadOnlySpan<BufferAssignment> ranges, int count, bool isStorage)
         {
             if (isStorage)
             {
-                _context.Renderer.Pipeline.SetStorageBuffers(first, ranges.Slice(0, count));
+                _context.Renderer.Pipeline.SetStorageBuffers(ranges.Slice(0, count));
             }
             else
             {
-                _context.Renderer.Pipeline.SetUniformBuffers(first, ranges.Slice(0, count));
+                _context.Renderer.Pipeline.SetUniformBuffers(ranges.Slice(0, count));
             }
         }
 

+ 9 - 8
Ryujinx.Graphics.OpenGL/Pipeline.cs

@@ -1296,9 +1296,9 @@ namespace Ryujinx.Graphics.OpenGL
             _stencilFrontMask = stencilTest.FrontMask;
         }
 
-        public void SetStorageBuffers(int first, ReadOnlySpan<BufferRange> buffers)
+        public void SetStorageBuffers(ReadOnlySpan<BufferAssignment> buffers)
         {
-            SetBuffers(first, buffers, isStorage: true);
+            SetBuffers(buffers, isStorage: true);
         }
 
         public void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler)
@@ -1366,9 +1366,9 @@ namespace Ryujinx.Graphics.OpenGL
             }
         }
 
-        public void SetUniformBuffers(int first, ReadOnlySpan<BufferRange> buffers)
+        public void SetUniformBuffers(ReadOnlySpan<BufferAssignment> buffers)
         {
-            SetBuffers(first, buffers, isStorage: false);
+            SetBuffers(buffers, isStorage: false);
         }
 
         public void SetUserClipDistance(int index, bool enableClip)
@@ -1460,21 +1460,22 @@ namespace Ryujinx.Graphics.OpenGL
             GL.MemoryBarrier(MemoryBarrierFlags.TextureFetchBarrierBit);
         }
 
-        private void SetBuffers(int first, ReadOnlySpan<BufferRange> buffers, bool isStorage)
+        private void SetBuffers(ReadOnlySpan<BufferAssignment> buffers, bool isStorage)
         {
             BufferRangeTarget target = isStorage ? BufferRangeTarget.ShaderStorageBuffer : BufferRangeTarget.UniformBuffer;
 
             for (int index = 0; index < buffers.Length; index++)
             {
-                BufferRange buffer = buffers[index];
+                BufferAssignment assignment = buffers[index];
+                BufferRange buffer = assignment.Range;
 
                 if (buffer.Handle == BufferHandle.Null)
                 {
-                    GL.BindBufferRange(target, first + index, 0, IntPtr.Zero, 0);
+                    GL.BindBufferRange(target, assignment.Binding, 0, IntPtr.Zero, 0);
                     continue;
                 }
 
-                GL.BindBufferRange(target, first + index, buffer.Handle.ToInt32(), (IntPtr)buffer.Offset, buffer.Size);
+                GL.BindBufferRange(target, assignment.Binding, buffer.Handle.ToInt32(), (IntPtr)buffer.Offset, buffer.Size);
             }
         }
 

+ 8 - 6
Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs

@@ -163,12 +163,13 @@ namespace Ryujinx.Graphics.Vulkan
             SignalDirty(DirtyFlags.Image);
         }
 
-        public void SetStorageBuffers(CommandBuffer commandBuffer, int first, ReadOnlySpan<BufferRange> buffers)
+        public void SetStorageBuffers(CommandBuffer commandBuffer, ReadOnlySpan<BufferAssignment> buffers)
         {
             for (int i = 0; i < buffers.Length; i++)
             {
-                var buffer = buffers[i];
-                int index = first + i;
+                var assignment = buffers[i];
+                var buffer = assignment.Range;
+                int index = assignment.Binding;
 
                 Auto<DisposableBuffer> vkBuffer = _gd.BufferManager.GetBuffer(commandBuffer, buffer.Handle, false);
                 ref Auto<DisposableBuffer> currentVkBuffer = ref _storageBufferRefs[index];
@@ -243,12 +244,13 @@ namespace Ryujinx.Graphics.Vulkan
             SignalDirty(DirtyFlags.Texture);
         }
 
-        public void SetUniformBuffers(CommandBuffer commandBuffer, int first, ReadOnlySpan<BufferRange> buffers)
+        public void SetUniformBuffers(CommandBuffer commandBuffer, ReadOnlySpan<BufferAssignment> buffers)
         {
             for (int i = 0; i < buffers.Length; i++)
             {
-                var buffer = buffers[i];
-                int index = first + i;
+                var assignment = buffers[i];
+                var buffer = assignment.Range;
+                int index = assignment.Binding;
 
                 Auto<DisposableBuffer> vkBuffer = _gd.BufferManager.GetBuffer(commandBuffer, buffer.Handle, false);
                 ref Auto<DisposableBuffer> currentVkBuffer = ref _uniformBufferRefs[index];

+ 7 - 7
Ryujinx.Graphics.Vulkan/HelperShader.cs

@@ -177,7 +177,7 @@ namespace Ryujinx.Graphics.Vulkan
 
             gd.BufferManager.SetData<float>(bufferHandle, 0, region);
 
-            _pipeline.SetUniformBuffers(1, stackalloc[] { new BufferRange(bufferHandle, 0, RegionBufferSize) });
+            _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, RegionBufferSize)) });
 
             Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1];
 
@@ -240,7 +240,7 @@ namespace Ryujinx.Graphics.Vulkan
 
             gd.BufferManager.SetData<float>(bufferHandle, 0, clearColor);
 
-            _pipeline.SetUniformBuffers(1, stackalloc[] { new BufferRange(bufferHandle, 0, ClearColorBufferSize) });
+            _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, ClearColorBufferSize)) });
 
             Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1];
 
@@ -302,7 +302,7 @@ namespace Ryujinx.Graphics.Vulkan
 
             gd.BufferManager.SetData<float>(bufferHandle, 0, region);
 
-            pipeline.SetUniformBuffers(1, stackalloc[] { new BufferRange(bufferHandle, 0, RegionBufferSize) });
+            pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, RegionBufferSize)) });
 
             Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1];
 
@@ -380,7 +380,7 @@ namespace Ryujinx.Graphics.Vulkan
 
                 _pipeline.SetCommandBuffer(cbs);
 
-                _pipeline.SetUniformBuffers(0, stackalloc[] { new BufferRange(bufferHandle, 0, ParamsBufferSize) });
+                _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) });
 
                 Span<Auto<DisposableBuffer>> sbRanges = new Auto<DisposableBuffer>[2];
 
@@ -571,7 +571,7 @@ namespace Ryujinx.Graphics.Vulkan
             int conversionType = srcIsMs ? src.Info.BytesPerPixel : -src.Info.BytesPerPixel;
             _pipeline.Specialize(conversionType);
 
-            _pipeline.SetUniformBuffers(0, stackalloc[] { new BufferRange(bufferHandle, 0, ParamsBufferSize) });
+            _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) });
 
             if (src.Info.Target == Target.Texture2DMultisampleArray ||
                 dst.Info.Target == Target.Texture2DMultisampleArray)
@@ -776,7 +776,7 @@ namespace Ryujinx.Graphics.Vulkan
                 srcIndirectBufferOffset,
                 indirectDataSize);
 
-            _pipeline.SetUniformBuffers(0, stackalloc[] { drawCountBufferAligned });
+            _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, drawCountBufferAligned) });
             _pipeline.SetStorageBuffers(1, new[] { srcIndirectBuffer.GetBuffer(), dstIndirectBuffer.GetBuffer(), patternBuffer.GetBuffer() });
 
             _pipeline.SetProgram(_programConvertIndirectData);
@@ -804,7 +804,7 @@ namespace Ryujinx.Graphics.Vulkan
                 0,
                 convertedCount * outputIndexSize);
 
-            _pipeline.SetUniformBuffers(0, stackalloc[] { new BufferRange(patternBufferHandle, 0, ParamsBufferSize) });
+            _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(patternBufferHandle, 0, ParamsBufferSize)) });
             _pipeline.SetStorageBuffers(1, new[] { srcIndexBuffer.GetBuffer(), dstIndexBuffer.GetBuffer() });
 
             _pipeline.SetProgram(_programConvertIndexBuffer);

+ 4 - 4
Ryujinx.Graphics.Vulkan/PipelineBase.cs

@@ -973,9 +973,9 @@ namespace Ryujinx.Graphics.Vulkan
             SignalStateChange();
         }
 
-        public void SetStorageBuffers(int first, ReadOnlySpan<BufferRange> buffers)
+        public void SetStorageBuffers(ReadOnlySpan<BufferAssignment> buffers)
         {
-            _descriptorSetUpdater.SetStorageBuffers(CommandBuffer, first, buffers);
+            _descriptorSetUpdater.SetStorageBuffers(CommandBuffer, buffers);
         }
 
         public void SetStorageBuffers(int first, ReadOnlySpan<Auto<DisposableBuffer>> buffers)
@@ -1013,9 +1013,9 @@ namespace Ryujinx.Graphics.Vulkan
             }
         }
 
-        public void SetUniformBuffers(int first, ReadOnlySpan<BufferRange> buffers)
+        public void SetUniformBuffers(ReadOnlySpan<BufferAssignment> buffers)
         {
-            _descriptorSetUpdater.SetUniformBuffers(CommandBuffer, first, buffers);
+            _descriptorSetUpdater.SetUniformBuffers(CommandBuffer, buffers);
         }
 
         public void SetUserClipDistance(int index, bool enableClip)