VertexBufferState.cs
1 using Ryujinx.Graphics.GAL; 2 3 namespace Ryujinx.Graphics.Vulkan 4 { 5 internal struct VertexBufferState 6 { 7 private const int VertexBufferMaxMirrorable = 0x20000; 8 9 public static VertexBufferState Null => new(null, 0, 0, 0); 10 11 private readonly int _offset; 12 private readonly int _size; 13 private readonly int _stride; 14 15 private readonly BufferHandle _handle; 16 private Auto<DisposableBuffer> _buffer; 17 18 internal readonly int DescriptorIndex; 19 internal int AttributeScalarAlignment; 20 21 public VertexBufferState(Auto<DisposableBuffer> buffer, int descriptorIndex, int offset, int size, int stride = 0) 22 { 23 _buffer = buffer; 24 _handle = BufferHandle.Null; 25 26 _offset = offset; 27 _size = size; 28 _stride = stride; 29 30 DescriptorIndex = descriptorIndex; 31 AttributeScalarAlignment = 1; 32 33 buffer?.IncrementReferenceCount(); 34 } 35 36 public VertexBufferState(BufferHandle handle, int descriptorIndex, int offset, int size, int stride = 0) 37 { 38 // This buffer state may be rewritten at bind time, so it must be retrieved on bind. 39 40 _buffer = null; 41 _handle = handle; 42 43 _offset = offset; 44 _size = size; 45 _stride = stride; 46 47 DescriptorIndex = descriptorIndex; 48 AttributeScalarAlignment = 1; 49 } 50 51 public void BindVertexBuffer(VulkanRenderer gd, CommandBufferScoped cbs, uint binding, ref PipelineState state, VertexBufferUpdater updater) 52 { 53 var autoBuffer = _buffer; 54 55 if (_handle != BufferHandle.Null) 56 { 57 // May need to restride the vertex buffer. 58 59 if (gd.NeedsVertexBufferAlignment(AttributeScalarAlignment, out int alignment) && (_stride % alignment) != 0) 60 { 61 autoBuffer = gd.BufferManager.GetAlignedVertexBuffer(cbs, _handle, _offset, _size, _stride, alignment); 62 63 if (autoBuffer != null) 64 { 65 int stride = (_stride + (alignment - 1)) & -alignment; 66 int newSize = (_size / _stride) * stride; 67 68 var buffer = autoBuffer.Get(cbs, 0, newSize).Value; 69 70 updater.BindVertexBuffer(cbs, binding, buffer, 0, (ulong)newSize, (ulong)stride); 71 72 _buffer = autoBuffer; 73 74 state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)stride; 75 } 76 77 return; 78 } 79 80 autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int size); 81 82 // The original stride must be reapplied in case it was rewritten. 83 state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)_stride; 84 85 if (_offset >= size) 86 { 87 autoBuffer = null; 88 } 89 } 90 91 if (autoBuffer != null) 92 { 93 int offset = _offset; 94 bool mirrorable = _size <= VertexBufferMaxMirrorable; 95 var buffer = mirrorable ? autoBuffer.GetMirrorable(cbs, ref offset, _size, out _).Value : autoBuffer.Get(cbs, offset, _size).Value; 96 97 updater.BindVertexBuffer(cbs, binding, buffer, (ulong)offset, (ulong)_size, (ulong)_stride); 98 } 99 } 100 101 public readonly bool BoundEquals(Auto<DisposableBuffer> buffer) 102 { 103 return _buffer == buffer; 104 } 105 106 public readonly bool Overlaps(Auto<DisposableBuffer> buffer, int offset, int size) 107 { 108 return buffer == _buffer && offset < _offset + _size && offset + size > _offset; 109 } 110 111 public readonly bool Matches(Auto<DisposableBuffer> buffer, int descriptorIndex, int offset, int size, int stride = 0) 112 { 113 return _buffer == buffer && DescriptorIndex == descriptorIndex && _offset == offset && _size == size && _stride == stride; 114 } 115 116 public void Swap(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to) 117 { 118 if (_buffer == from) 119 { 120 _buffer.DecrementReferenceCount(); 121 to.IncrementReferenceCount(); 122 123 _buffer = to; 124 } 125 } 126 127 public readonly void Dispose() 128 { 129 // Only dispose if this buffer is not refetched on each bind. 130 131 if (_handle == BufferHandle.Null) 132 { 133 _buffer?.DecrementReferenceCount(); 134 } 135 } 136 } 137 }