using System; using System.Buffers; using VkBuffer = Silk.NET.Vulkan.Buffer; namespace Ryujinx.Graphics.Vulkan { internal class VertexBufferUpdater : IDisposable { private readonly VulkanRenderer _gd; private uint _baseBinding; private uint _count; private readonly NativeArray _buffers; private readonly NativeArray _offsets; private readonly NativeArray _sizes; private readonly NativeArray _strides; private readonly Auto[] _bufferAutos; private readonly int[] _bufferOffsetsForGet; private readonly int[] _bufferSizesForGet; 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); _bufferAutos = new Auto[Constants.MaxVertexBuffers]; _bufferOffsetsForGet = new int[Constants.MaxVertexBuffers]; _bufferSizesForGet = new int[Constants.MaxVertexBuffers]; } public void BindVertexBuffer(CommandBufferScoped cbs, uint binding, Auto autoBuffer, int offset, int size, ulong stride) { if (_count == 0) { _baseBinding = binding; } else if (_baseBinding + _count != binding) { Commit(cbs); _baseBinding = binding; } int index = (int)_count; _bufferAutos[index] = autoBuffer; _bufferOffsetsForGet[index] = offset; _bufferSizesForGet[index] = size; _offsets[index] = (ulong)offset; _sizes[index] = (ulong)size; _strides[index] = stride; _count++; } public unsafe void Commit(CommandBufferScoped cbs) { if (_count != 0) { int count = (int)_count; uint baseBinding = _baseBinding; _count = 0; Auto[] autos = ArrayPool>.Shared.Rent(count); Span getOffsets = stackalloc int[Constants.MaxVertexBuffers]; Span getSizes = stackalloc int[Constants.MaxVertexBuffers]; Span offsets = stackalloc ulong[Constants.MaxVertexBuffers]; Span sizes = stackalloc ulong[Constants.MaxVertexBuffers]; Span strides = stackalloc ulong[Constants.MaxVertexBuffers]; Span buffers = stackalloc VkBuffer[Constants.MaxVertexBuffers]; for (int i = 0; i < count; i++) { autos[i] = _bufferAutos[i]; _bufferAutos[i] = null; getOffsets[i] = _bufferOffsetsForGet[i]; getSizes[i] = _bufferSizesForGet[i]; offsets[i] = _offsets[i]; sizes[i] = _sizes[i]; strides[i] = _strides[i]; } try { for (int i = 0; i < count; i++) { buffers[i] = autos[i].Get(cbs, getOffsets[i], getSizes[i]).Value; autos[i] = null; } for (int i = 0; i < count; i++) { _buffers[i] = buffers[i]; _offsets[i] = offsets[i]; _sizes[i] = sizes[i]; _strides[i] = strides[i]; } if (_gd.Capabilities.SupportsExtendedDynamicState) { _gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2( cbs.CommandBuffer, baseBinding, (uint)count, _buffers.Pointer, _offsets.Pointer, _sizes.Pointer, _strides.Pointer); } else { _gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, baseBinding, (uint)count, _buffers.Pointer, _offsets.Pointer); } } finally { ArrayPool>.Shared.Return(autos, clearArray: true); } } } public void Dispose() { _buffers.Dispose(); _offsets.Dispose(); _sizes.Dispose(); _strides.Dispose(); } } }