Fix crash on Mac and Android caused by a buffer validation error fix in #92 (#105)

This PR fixes a bug introduced in #92. In that PR, the problematic commit did its work directly on the updater's own arrays, calling Auto.Get() on each entry as it went, and nulling entries out along the way. The problem is that Get() can call back into Commit (via ClearMirrors -> Rebind), and when it did, that reentrant Commit would read from the same arrays the outer call was still in the middle of processing, hit one of the entries the outer had already nulled, and throw an NullReferenceException. The fix is to have Commit start by copying everything it needs into local variables and resetting _count to zero, so a reentrant call sees a clean updater and operates on its own data. The outer call then writes its snapshot back into the native arrays just before recording the Vulkan bind.

Co-authored-by: AsperTheDog <guillerman0000@gmail.com>
Co-authored-by: Max <randomgirlisweird@gmail.com>
Co-authored-by: Renovate Bot <renovatebot@ryujinx.app>
Co-authored-by: Babib3l <gab.chevanne@gmail.com>
Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/105
This commit is contained in:
AsperTheDog
2026-05-18 16:34:04 +00:00
committed by sh0inx
parent 3473044c6e
commit 4f037da53b

View File

@@ -1,4 +1,5 @@
using System;
using System.Buffers;
using VkBuffer = Silk.NET.Vulkan.Buffer;
namespace Ryujinx.Graphics.Vulkan
@@ -61,29 +62,65 @@ namespace Ryujinx.Graphics.Vulkan
{
if (_count != 0)
{
for (int i = 0; i < _count; i++)
{
_buffers[i] = _bufferAutos[i].Get(cbs, _bufferOffsetsForGet[i], _bufferSizesForGet[i]).Value;
_bufferAutos[i] = null;
}
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);
}
int count = (int)_count;
uint baseBinding = _baseBinding;
_count = 0;
Auto<DisposableBuffer>[] autos = ArrayPool<Auto<DisposableBuffer>>.Shared.Rent(count);
Span<int> getOffsets = stackalloc int[Constants.MaxVertexBuffers];
Span<int> getSizes = stackalloc int[Constants.MaxVertexBuffers];
Span<ulong> offsets = stackalloc ulong[Constants.MaxVertexBuffers];
Span<ulong> sizes = stackalloc ulong[Constants.MaxVertexBuffers];
Span<ulong> strides = stackalloc ulong[Constants.MaxVertexBuffers];
Span<VkBuffer> 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<Auto<DisposableBuffer>>.Shared.Return(autos, clearArray: true);
}
}
}