From ece36b274da3957d727387d2f7c96adbd0f29bc3 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Thu, 24 Nov 2022 07:50:59 +0000 Subject: [PATCH] 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 buffers) --- Ryujinx.Graphics.GAL/BufferAssignment.cs | 14 ++++++ Ryujinx.Graphics.GAL/IPipeline.cs | 4 +- .../Multithreading/BufferMap.cs | 24 ++++++++++ .../Commands/SetStorageBuffersCommand.cs | 10 ++-- .../Commands/SetUniformBuffersCommand.cs | 10 ++-- .../Multithreading/ThreadedPipeline.cs | 8 ++-- Ryujinx.Graphics.Gpu/Memory/BufferManager.cs | 46 +++++-------------- Ryujinx.Graphics.OpenGL/Pipeline.cs | 17 +++---- .../DescriptorSetUpdater.cs | 14 +++--- Ryujinx.Graphics.Vulkan/HelperShader.cs | 14 +++--- Ryujinx.Graphics.Vulkan/PipelineBase.cs | 8 ++-- 11 files changed, 91 insertions(+), 78 deletions(-) create mode 100644 Ryujinx.Graphics.GAL/BufferAssignment.cs diff --git a/Ryujinx.Graphics.GAL/BufferAssignment.cs b/Ryujinx.Graphics.GAL/BufferAssignment.cs new file mode 100644 index 00000000..9f0f56c5 --- /dev/null +++ b/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; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.GAL/IPipeline.cs b/Ryujinx.Graphics.GAL/IPipeline.cs index 38bf54f7..26d019eb 100644 --- a/Ryujinx.Graphics.GAL/IPipeline.cs +++ b/Ryujinx.Graphics.GAL/IPipeline.cs @@ -86,12 +86,12 @@ namespace Ryujinx.Graphics.GAL void SetStencilTest(StencilTestDescriptor stencilTest); - void SetStorageBuffers(int first, ReadOnlySpan buffers); + void SetStorageBuffers(ReadOnlySpan buffers); void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler); void SetTransformFeedbackBuffers(ReadOnlySpan buffers); - void SetUniformBuffers(int first, ReadOnlySpan buffers); + void SetUniformBuffers(ReadOnlySpan buffers); void SetUserClipDistance(int index, bool enableClip); diff --git a/Ryujinx.Graphics.GAL/Multithreading/BufferMap.cs b/Ryujinx.Graphics.GAL/Multithreading/BufferMap.cs index fcf09f9f..24b0af2d 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/BufferMap.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/BufferMap.cs @@ -142,6 +142,30 @@ namespace Ryujinx.Graphics.GAL.Multithreading return ranges; } + internal Span MapBufferRanges(Span 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 MapBufferRanges(Span ranges) { // Rewrite the buffer ranges to point to the mapped handles. diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStorageBuffersCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStorageBuffersCommand.cs index c2963373..610603ca 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetStorageBuffersCommand.cs +++ b/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 _buffers; + private SpanRef _buffers; - public void Set(int first, SpanRef buffers) + public void Set(SpanRef buffers) { - _first = first; _buffers = buffers; } public static void Run(ref SetStorageBuffersCommand command, ThreadedRenderer threaded, IRenderer renderer) { - Span buffers = command._buffers.Get(threaded); - renderer.Pipeline.SetStorageBuffers(command._first, threaded.Buffers.MapBufferRanges(buffers)); + Span buffers = command._buffers.Get(threaded); + renderer.Pipeline.SetStorageBuffers(threaded.Buffers.MapBufferRanges(buffers)); command._buffers.Dispose(threaded); } } diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUniformBuffersCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUniformBuffersCommand.cs index 750d8dac..e4abb403 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetUniformBuffersCommand.cs +++ b/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 _buffers; + private SpanRef _buffers; - public void Set(int first, SpanRef buffers) + public void Set(SpanRef buffers) { - _first = first; _buffers = buffers; } public static void Run(ref SetUniformBuffersCommand command, ThreadedRenderer threaded, IRenderer renderer) { - Span buffers = command._buffers.Get(threaded); - renderer.Pipeline.SetUniformBuffers(command._first, threaded.Buffers.MapBufferRanges(buffers)); + Span buffers = command._buffers.Get(threaded); + renderer.Pipeline.SetUniformBuffers(threaded.Buffers.MapBufferRanges(buffers)); command._buffers.Dispose(threaded); } } diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs b/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs index 52d69933..ba120867 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs @@ -275,9 +275,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading _renderer.QueueCommand(); } - public void SetStorageBuffers(int first, ReadOnlySpan buffers) + public void SetStorageBuffers(ReadOnlySpan buffers) { - _renderer.New().Set(first, _renderer.CopySpan(buffers)); + _renderer.New().Set(_renderer.CopySpan(buffers)); _renderer.QueueCommand(); } @@ -293,9 +293,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading _renderer.QueueCommand(); } - public void SetUniformBuffers(int first, ReadOnlySpan buffers) + public void SetUniformBuffers(ReadOnlySpan buffers) { - _renderer.New().Set(first, _renderer.CopySpan(buffers)); + _renderer.New().Set(_renderer.CopySpan(buffers)); _renderer.QueueCommand(); } diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs index 1b67f650..f0831e15 100644 --- a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs +++ b/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 _bufferTextures; - private readonly BufferRange[] _ranges; + private readonly BufferAssignment[] _ranges; /// /// Holds shader stage buffer state and binding information. @@ -134,7 +134,7 @@ namespace Ryujinx.Graphics.Gpu.Memory _bufferTextures = new List(); - _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 ranges = _ranges; + Span 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 ranges = _ranges; + Span 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 /// Number of bindings /// Indicates if the buffers are storage or uniform buffers [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void SetHostBuffers(ReadOnlySpan ranges, int first, int count, bool isStorage) + private void SetHostBuffers(ReadOnlySpan 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)); } } diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs index 3b234eb0..8bcaf4c7 100644 --- a/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -1296,9 +1296,9 @@ namespace Ryujinx.Graphics.OpenGL _stencilFrontMask = stencilTest.FrontMask; } - public void SetStorageBuffers(int first, ReadOnlySpan buffers) + public void SetStorageBuffers(ReadOnlySpan 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 buffers) + public void SetUniformBuffers(ReadOnlySpan 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 buffers, bool isStorage) + private void SetBuffers(ReadOnlySpan 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); } } diff --git a/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs index 8479bcf7..4cf9ce87 100644 --- a/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs +++ b/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs @@ -163,12 +163,13 @@ namespace Ryujinx.Graphics.Vulkan SignalDirty(DirtyFlags.Image); } - public void SetStorageBuffers(CommandBuffer commandBuffer, int first, ReadOnlySpan buffers) + public void SetStorageBuffers(CommandBuffer commandBuffer, ReadOnlySpan 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 vkBuffer = _gd.BufferManager.GetBuffer(commandBuffer, buffer.Handle, false); ref Auto currentVkBuffer = ref _storageBufferRefs[index]; @@ -243,12 +244,13 @@ namespace Ryujinx.Graphics.Vulkan SignalDirty(DirtyFlags.Texture); } - public void SetUniformBuffers(CommandBuffer commandBuffer, int first, ReadOnlySpan buffers) + public void SetUniformBuffers(CommandBuffer commandBuffer, ReadOnlySpan 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 vkBuffer = _gd.BufferManager.GetBuffer(commandBuffer, buffer.Handle, false); ref Auto currentVkBuffer = ref _uniformBufferRefs[index]; diff --git a/Ryujinx.Graphics.Vulkan/HelperShader.cs b/Ryujinx.Graphics.Vulkan/HelperShader.cs index 1ef22dc2..076a3baa 100644 --- a/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -177,7 +177,7 @@ namespace Ryujinx.Graphics.Vulkan gd.BufferManager.SetData(bufferHandle, 0, region); - _pipeline.SetUniformBuffers(1, stackalloc[] { new BufferRange(bufferHandle, 0, RegionBufferSize) }); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, RegionBufferSize)) }); Span viewports = stackalloc GAL.Viewport[1]; @@ -240,7 +240,7 @@ namespace Ryujinx.Graphics.Vulkan gd.BufferManager.SetData(bufferHandle, 0, clearColor); - _pipeline.SetUniformBuffers(1, stackalloc[] { new BufferRange(bufferHandle, 0, ClearColorBufferSize) }); + _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, ClearColorBufferSize)) }); Span viewports = stackalloc GAL.Viewport[1]; @@ -302,7 +302,7 @@ namespace Ryujinx.Graphics.Vulkan gd.BufferManager.SetData(bufferHandle, 0, region); - pipeline.SetUniformBuffers(1, stackalloc[] { new BufferRange(bufferHandle, 0, RegionBufferSize) }); + pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, RegionBufferSize)) }); Span 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> sbRanges = new Auto[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); diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index 5d2263aa..87155a0d 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -973,9 +973,9 @@ namespace Ryujinx.Graphics.Vulkan SignalStateChange(); } - public void SetStorageBuffers(int first, ReadOnlySpan buffers) + public void SetStorageBuffers(ReadOnlySpan buffers) { - _descriptorSetUpdater.SetStorageBuffers(CommandBuffer, first, buffers); + _descriptorSetUpdater.SetStorageBuffers(CommandBuffer, buffers); } public void SetStorageBuffers(int first, ReadOnlySpan> buffers) @@ -1013,9 +1013,9 @@ namespace Ryujinx.Graphics.Vulkan } } - public void SetUniformBuffers(int first, ReadOnlySpan buffers) + public void SetUniformBuffers(ReadOnlySpan buffers) { - _descriptorSetUpdater.SetUniformBuffers(CommandBuffer, first, buffers); + _descriptorSetUpdater.SetUniformBuffers(CommandBuffer, buffers); } public void SetUserClipDistance(int index, bool enableClip)