Fix various issues caused by Vertex/Index buffer conversions (#3762)

* Fix various issues caused by #3679

- The arguments for the 0th dummy vertex buffer were incorrect - it was given an offset of 16 rather than a size of 16.
- The wrong size was used when doing `autoBuffer.Get` on a converted vertex buffer.
- The possibility of a vertex buffer being disposed and then rebound can rebindings to find a different buffer where the current range is out of bounds. Avoid binding when out of range to prevent validation errors.
- The above also affects generation of converted buffers, which was a bit more fatal. Conversion functions now attempt to bound input offset/size.

* Fix offset for converted buffer
This commit is contained in:
riperiperi 2022-10-16 23:38:58 +01:00 committed by GitHub
parent 2b50e52e48
commit 0dbe45ae37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 65 additions and 22 deletions

View File

@ -386,8 +386,25 @@ namespace Ryujinx.Graphics.Vulkan
_waitable.WaitForFences(_gd.Api, _device, offset, size); _waitable.WaitForFences(_gd.Api, _device, offset, size);
} }
private bool BoundToRange(int offset, ref int size)
{
if (offset >= Size)
{
return false;
}
size = Math.Min(Size - offset, size);
return true;
}
public Auto<DisposableBuffer> GetBufferI8ToI16(CommandBufferScoped cbs, int offset, int size) public Auto<DisposableBuffer> GetBufferI8ToI16(CommandBufferScoped cbs, int offset, int size)
{ {
if (!BoundToRange(offset, ref size))
{
return null;
}
var key = new I8ToI16CacheKey(_gd); var key = new I8ToI16CacheKey(_gd);
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder)) if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder))
@ -407,6 +424,11 @@ namespace Ryujinx.Graphics.Vulkan
public Auto<DisposableBuffer> GetAlignedVertexBuffer(CommandBufferScoped cbs, int offset, int size, int stride, int alignment) public Auto<DisposableBuffer> GetAlignedVertexBuffer(CommandBufferScoped cbs, int offset, int size, int stride, int alignment)
{ {
if (!BoundToRange(offset, ref size))
{
return null;
}
var key = new AlignedVertexBufferCacheKey(_gd, stride, alignment); var key = new AlignedVertexBufferCacheKey(_gd, stride, alignment);
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder)) if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder))
@ -428,6 +450,11 @@ namespace Ryujinx.Graphics.Vulkan
public Auto<DisposableBuffer> GetBufferTopologyConversion(CommandBufferScoped cbs, int offset, int size, IndexBufferPattern pattern, int indexSize) public Auto<DisposableBuffer> GetBufferTopologyConversion(CommandBufferScoped cbs, int offset, int size, IndexBufferPattern pattern, int indexSize)
{ {
if (!BoundToRange(offset, ref size))
{
return null;
}
var key = new TopologyConversionCacheKey(_gd, pattern, indexSize); var key = new TopologyConversionCacheKey(_gd, pattern, indexSize);
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder)) if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder))

View File

@ -49,7 +49,12 @@ namespace Ryujinx.Graphics.Vulkan
} }
else else
{ {
autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int _); autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int bufferSize);
if (_offset >= bufferSize)
{
autoBuffer = null;
}
offset = _offset; offset = _offset;
size = _size; size = _size;

View File

@ -95,7 +95,7 @@ namespace Ryujinx.Graphics.Vulkan
using var emptyVb = gd.BufferManager.Create(gd, EmptyVbSize); using var emptyVb = gd.BufferManager.Create(gd, EmptyVbSize);
emptyVb.SetData(0, new byte[EmptyVbSize]); emptyVb.SetData(0, new byte[EmptyVbSize]);
_vertexBuffers[0] = new VertexBufferState(emptyVb.GetBuffer(), 0, EmptyVbSize, 0); _vertexBuffers[0] = new VertexBufferState(emptyVb.GetBuffer(), 0, 0, EmptyVbSize, 0);
_vertexBuffersDirty = ulong.MaxValue >> (64 - _vertexBuffers.Length); _vertexBuffersDirty = ulong.MaxValue >> (64 - _vertexBuffers.Length);
ClearScissor = new Rectangle<int>(0, 0, 0xffff, 0xffff); ClearScissor = new Rectangle<int>(0, 0, 0xffff, 0xffff);
@ -1243,7 +1243,7 @@ namespace Ryujinx.Graphics.Vulkan
_vertexBuffers[i].BindVertexBuffer(Gd, Cbs, (uint)i, ref _newState); _vertexBuffers[i].BindVertexBuffer(Gd, Cbs, (uint)i, ref _newState);
_vertexBuffersDirty &= ~(1u << i); _vertexBuffersDirty &= ~(1UL << i);
} }
} }

View File

@ -57,37 +57,48 @@ namespace Ryujinx.Graphics.Vulkan
if (gd.NeedsVertexBufferAlignment(AttributeScalarAlignment, out int alignment) && (_stride % alignment) != 0) if (gd.NeedsVertexBufferAlignment(AttributeScalarAlignment, out int alignment) && (_stride % alignment) != 0)
{ {
autoBuffer = gd.BufferManager.GetAlignedVertexBuffer(cbs, _handle, _offset, _size, _stride, alignment); autoBuffer = gd.BufferManager.GetAlignedVertexBuffer(cbs, _handle, _offset, _size, _stride, alignment);
int stride = (_stride + (alignment - 1)) & -alignment;
var buffer = autoBuffer.Get(cbs, _offset, _size).Value; if (autoBuffer != null)
{
int stride = (_stride + (alignment - 1)) & -alignment;
int newSize = (_size / _stride) * stride;
if (gd.Capabilities.SupportsExtendedDynamicState) var buffer = autoBuffer.Get(cbs, 0, newSize).Value;
{
gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2( if (gd.Capabilities.SupportsExtendedDynamicState)
cbs.CommandBuffer, {
binding, gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2(
1, cbs.CommandBuffer,
buffer, binding,
0, 1,
(ulong)(_size / _stride) * (ulong)stride, buffer,
(ulong)stride); 0,
} (ulong)newSize,
else (ulong)stride);
{ }
gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, binding, 1, buffer, 0); else
{
gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, binding, 1, buffer, 0);
}
_buffer = autoBuffer;
} }
_buffer = autoBuffer; state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)_stride;
state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)stride;
return; return;
} }
else else
{ {
autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int _); autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int size);
// The original stride must be reapplied in case it was rewritten. // The original stride must be reapplied in case it was rewritten.
state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)_stride; state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)_stride;
if (_offset >= size)
{
autoBuffer = null;
}
} }
} }