From 895d9b53bc37507fed6829a7f91a1b8e3237ab0b Mon Sep 17 00:00:00 2001 From: riperiperi Date: Mon, 8 May 2023 09:59:26 +0100 Subject: Vulkan: Batch vertex buffer updates (#4843) * Vulkan: Batch vertex buffer updates Some games can bind a large number of vertex buffers for draws. This PR allows for vertex buffers to be updated with one call rather than one per buffer. This mostly affects the AMD Mesa driver, the testing platform was Steam Deck with Super Mario Odyssey. It was taking about 12% before, should be greatly reduced now. A small optimization has been added to avoid looking up the same buffer multiple times, as a common pattern is for the same buffer to be bound many times in a row with different ranges. * Only rebind vertex buffers if they have changed * Address feedback --- src/Ryujinx.Graphics.Vulkan/VertexBufferUpdater.cs | 84 ++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 src/Ryujinx.Graphics.Vulkan/VertexBufferUpdater.cs (limited to 'src/Ryujinx.Graphics.Vulkan/VertexBufferUpdater.cs') diff --git a/src/Ryujinx.Graphics.Vulkan/VertexBufferUpdater.cs b/src/Ryujinx.Graphics.Vulkan/VertexBufferUpdater.cs new file mode 100644 index 00000000..bceaeb8c --- /dev/null +++ b/src/Ryujinx.Graphics.Vulkan/VertexBufferUpdater.cs @@ -0,0 +1,84 @@ +using Silk.NET.Vulkan; +using System; + +using VkBuffer = Silk.NET.Vulkan.Buffer; + +namespace Ryujinx.Graphics.Vulkan +{ + internal class VertexBufferUpdater : IDisposable + { + private VulkanRenderer _gd; + + private uint _baseBinding; + private uint _count; + + private NativeArray _buffers; + private NativeArray _offsets; + private NativeArray _sizes; + private NativeArray _strides; + + public VertexBufferUpdater(VulkanRenderer gd) + { + _gd = gd; + + _buffers = new NativeArray(Constants.MaxVertexBuffers); + _offsets = new NativeArray(Constants.MaxVertexBuffers); + _sizes = new NativeArray(Constants.MaxVertexBuffers); + _strides = new NativeArray(Constants.MaxVertexBuffers); + } + + public void BindVertexBuffer(CommandBufferScoped cbs, uint binding, VkBuffer buffer, ulong offset, ulong size, ulong stride) + { + if (_count == 0) + { + _baseBinding = binding; + } + else if (_baseBinding + _count != binding) + { + Commit(cbs); + _baseBinding = binding; + } + + int index = (int)_count; + + _buffers[index] = buffer; + _offsets[index] = offset; + _sizes[index] = size; + _strides[index] = stride; + + _count++; + } + + public unsafe void Commit(CommandBufferScoped cbs) + { + if (_count != 0) + { + if (_gd.Capabilities.SupportsExtendedDynamicState) + { + _gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2( + cbs.CommandBuffer, + _baseBinding, + _count, + _buffers.Pointer, + _offsets.Pointer, + _sizes.Pointer, + _strides.Pointer); + } + else + { + _gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, _baseBinding, _count, _buffers.Pointer, _offsets.Pointer); + } + + _count = 0; + } + } + + public void Dispose() + { + _buffers.Dispose(); + _offsets.Dispose(); + _sizes.Dispose(); + _strides.Dispose(); + } + } +} -- cgit v1.2.3