diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index cf31a7f8..4aec0dbc 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -66,6 +66,8 @@ namespace Ryujinx.Graphics.Vulkan private ulong _vertexBuffersDirty; protected Rectangle ClearScissor; + private readonly VertexBufferUpdater _vertexBufferUpdater; + public SupportBufferUpdater SupportBufferUpdater; public IndexBufferPattern QuadsToTrisPattern; public IndexBufferPattern TriFanToTrisPattern; @@ -96,6 +98,7 @@ namespace Ryujinx.Graphics.Vulkan gd.Api.CreatePipelineCache(device, pipelineCacheCreateInfo, null, out PipelineCache).ThrowOnError(); _descriptorSetUpdater = new DescriptorSetUpdater(gd, this); + _vertexBufferUpdater = new VertexBufferUpdater(gd); _transformFeedbackBuffers = new BufferState[Constants.MaxTransformFeedbackBuffers]; _vertexBuffers = new VertexBufferState[Constants.MaxVertexBuffers + 1]; @@ -1185,6 +1188,9 @@ namespace Ryujinx.Graphics.Vulkan int validCount = 1; + BufferHandle lastHandle = default; + Auto lastBuffer = default; + for (int i = 0; i < count; i++) { var vertexBuffer = vertexBuffers[i]; @@ -1194,7 +1200,12 @@ namespace Ryujinx.Graphics.Vulkan if (vertexBuffer.Buffer.Handle != BufferHandle.Null) { - var vb = Gd.BufferManager.GetBuffer(CommandBuffer, vertexBuffer.Buffer.Handle, false); + Auto vb = (vertexBuffer.Buffer.Handle == lastHandle) ? lastBuffer : + Gd.BufferManager.GetBuffer(CommandBuffer, vertexBuffer.Buffer.Handle, false); + + lastHandle = vertexBuffer.Buffer.Handle; + lastBuffer = vb; + if (vb != null) { int binding = i + 1; @@ -1222,24 +1233,29 @@ namespace Ryujinx.Graphics.Vulkan ref var buffer = ref _vertexBuffers[binding]; int oldScalarAlign = buffer.AttributeScalarAlignment; - buffer.Dispose(); - if (Gd.Capabilities.VertexBufferAlignment < 2 && (vertexBuffer.Stride % FormatExtensions.MaxBufferFormatScalarSize) == 0) { - buffer = new VertexBufferState( - vb, - descriptorIndex, - vertexBuffer.Buffer.Offset, - vbSize, - vertexBuffer.Stride); + if (!buffer.Matches(vb, descriptorIndex, vertexBuffer.Buffer.Offset, vbSize, vertexBuffer.Stride)) + { + buffer.Dispose(); - buffer.BindVertexBuffer(Gd, Cbs, (uint)binding, ref _newState); + buffer = new VertexBufferState( + vb, + descriptorIndex, + vertexBuffer.Buffer.Offset, + vbSize, + vertexBuffer.Stride); + + buffer.BindVertexBuffer(Gd, Cbs, (uint)binding, ref _newState, _vertexBufferUpdater); + } } else { // May need to be rewritten. Bind this buffer before draw. + buffer.Dispose(); + buffer = new VertexBufferState( vertexBuffer.Buffer.Handle, descriptorIndex, @@ -1255,6 +1271,8 @@ namespace Ryujinx.Graphics.Vulkan } } + _vertexBufferUpdater.Commit(Cbs); + _newState.VertexBindingDescriptionsCount = (uint)validCount; SignalStateChange(); } @@ -1596,10 +1614,12 @@ namespace Ryujinx.Graphics.Vulkan { int i = BitOperations.TrailingZeroCount(_vertexBuffersDirty); - _vertexBuffers[i].BindVertexBuffer(Gd, Cbs, (uint)i, ref _newState); + _vertexBuffers[i].BindVertexBuffer(Gd, Cbs, (uint)i, ref _newState, _vertexBufferUpdater); _vertexBuffersDirty &= ~(1UL << i); } + + _vertexBufferUpdater.Commit(Cbs); } if (_stateDirty || Pbp != pbp) @@ -1712,6 +1732,7 @@ namespace Ryujinx.Graphics.Vulkan _framebuffer?.Dispose(); _newState.Dispose(); _descriptorSetUpdater.Dispose(); + _vertexBufferUpdater.Dispose(); for (int i = 0; i < _vertexBuffers.Length; i++) { diff --git a/src/Ryujinx.Graphics.Vulkan/VertexBufferState.cs b/src/Ryujinx.Graphics.Vulkan/VertexBufferState.cs index c4856019..2118bfae 100644 --- a/src/Ryujinx.Graphics.Vulkan/VertexBufferState.cs +++ b/src/Ryujinx.Graphics.Vulkan/VertexBufferState.cs @@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.Vulkan AttributeScalarAlignment = 1; } - public void BindVertexBuffer(VulkanRenderer gd, CommandBufferScoped cbs, uint binding, ref PipelineState state) + public void BindVertexBuffer(VulkanRenderer gd, CommandBufferScoped cbs, uint binding, ref PipelineState state, VertexBufferUpdater updater) { var autoBuffer = _buffer; @@ -65,21 +65,7 @@ namespace Ryujinx.Graphics.Vulkan var buffer = autoBuffer.Get(cbs, 0, newSize).Value; - if (gd.Capabilities.SupportsExtendedDynamicState) - { - gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2( - cbs.CommandBuffer, - binding, - 1, - buffer, - 0, - (ulong)newSize, - (ulong)stride); - } - else - { - gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, binding, 1, buffer, 0); - } + updater.BindVertexBuffer(cbs, binding, buffer, 0, (ulong)newSize, (ulong)stride); _buffer = autoBuffer; @@ -106,21 +92,7 @@ namespace Ryujinx.Graphics.Vulkan { var buffer = autoBuffer.Get(cbs, _offset, _size).Value; - if (gd.Capabilities.SupportsExtendedDynamicState) - { - gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2( - cbs.CommandBuffer, - binding, - 1, - buffer, - (ulong)_offset, - (ulong)_size, - (ulong)_stride); - } - else - { - gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, binding, 1, buffer, (ulong)_offset); - } + updater.BindVertexBuffer(cbs, binding, buffer, (ulong)_offset, (ulong)_size, (ulong)_stride); } } @@ -129,6 +101,11 @@ namespace Ryujinx.Graphics.Vulkan return _buffer == buffer; } + public bool Matches(Auto buffer, int descriptorIndex, int offset, int size, int stride = 0) + { + return _buffer == buffer && DescriptorIndex == descriptorIndex && _offset == offset && _size == size && _stride == stride; + } + public void Swap(Auto from, Auto to) { if (_buffer == from) 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(); + } + } +}