VertexArray.cs
1 using OpenTK.Graphics.OpenGL; 2 using Ryujinx.Graphics.GAL; 3 using System; 4 using System.Numerics; 5 using System.Runtime.CompilerServices; 6 7 namespace Ryujinx.Graphics.OpenGL 8 { 9 class VertexArray : IDisposable 10 { 11 public int Handle { get; private set; } 12 13 private readonly VertexAttribDescriptor[] _vertexAttribs; 14 private readonly VertexBufferDescriptor[] _vertexBuffers; 15 16 private int _minVertexCount; 17 18 private uint _vertexAttribsInUse; 19 private uint _vertexBuffersInUse; 20 private uint _vertexBuffersLimited; 21 22 private BufferRange _indexBuffer; 23 private readonly BufferHandle _tempIndexBuffer; 24 private BufferHandle _tempVertexBuffer; 25 private int _tempVertexBufferSize; 26 27 public VertexArray() 28 { 29 Handle = GL.GenVertexArray(); 30 31 _vertexAttribs = new VertexAttribDescriptor[Constants.MaxVertexAttribs]; 32 _vertexBuffers = new VertexBufferDescriptor[Constants.MaxVertexBuffers]; 33 34 _tempIndexBuffer = Buffer.Create(); 35 } 36 37 public void Bind() 38 { 39 GL.BindVertexArray(Handle); 40 } 41 42 public void SetVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers) 43 { 44 int minVertexCount = int.MaxValue; 45 46 int bindingIndex; 47 for (bindingIndex = 0; bindingIndex < vertexBuffers.Length; bindingIndex++) 48 { 49 VertexBufferDescriptor vb = vertexBuffers[bindingIndex]; 50 51 if (vb.Buffer.Handle != BufferHandle.Null) 52 { 53 int vertexCount = vb.Stride <= 0 ? 0 : vb.Buffer.Size / vb.Stride; 54 if (minVertexCount > vertexCount) 55 { 56 minVertexCount = vertexCount; 57 } 58 59 GL.BindVertexBuffer(bindingIndex, vb.Buffer.Handle.ToInt32(), (IntPtr)vb.Buffer.Offset, vb.Stride); 60 GL.VertexBindingDivisor(bindingIndex, vb.Divisor); 61 _vertexBuffersInUse |= 1u << bindingIndex; 62 } 63 else 64 { 65 if ((_vertexBuffersInUse & (1u << bindingIndex)) != 0) 66 { 67 GL.BindVertexBuffer(bindingIndex, 0, IntPtr.Zero, 0); 68 _vertexBuffersInUse &= ~(1u << bindingIndex); 69 } 70 } 71 72 _vertexBuffers[bindingIndex] = vb; 73 } 74 75 _minVertexCount = minVertexCount; 76 } 77 78 public void SetVertexAttributes(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs) 79 { 80 int index = 0; 81 82 for (; index < vertexAttribs.Length; index++) 83 { 84 VertexAttribDescriptor attrib = vertexAttribs[index]; 85 86 if (attrib.Equals(_vertexAttribs[index])) 87 { 88 continue; 89 } 90 91 FormatInfo fmtInfo = FormatTable.GetFormatInfo(attrib.Format); 92 93 if (attrib.IsZero) 94 { 95 // Disabling the attribute causes the shader to read a constant value. 96 // We currently set the constant to (0, 0, 0, 0). 97 DisableVertexAttrib(index); 98 } 99 else 100 { 101 EnableVertexAttrib(index); 102 } 103 104 int offset = attrib.Offset; 105 int size = fmtInfo.Components; 106 107 bool isFloat = fmtInfo.PixelType == PixelType.Float || 108 fmtInfo.PixelType == PixelType.HalfFloat; 109 110 if (isFloat || fmtInfo.Normalized || fmtInfo.Scaled) 111 { 112 VertexAttribType type = (VertexAttribType)fmtInfo.PixelType; 113 114 GL.VertexAttribFormat(index, size, type, fmtInfo.Normalized, offset); 115 } 116 else 117 { 118 VertexAttribIntegerType type = (VertexAttribIntegerType)fmtInfo.PixelType; 119 120 GL.VertexAttribIFormat(index, size, type, offset); 121 } 122 123 GL.VertexAttribBinding(index, attrib.BufferIndex); 124 125 _vertexAttribs[index] = attrib; 126 } 127 128 for (; index < Constants.MaxVertexAttribs; index++) 129 { 130 DisableVertexAttrib(index); 131 } 132 } 133 134 public void SetIndexBuffer(BufferRange range) 135 { 136 _indexBuffer = range; 137 GL.BindBuffer(BufferTarget.ElementArrayBuffer, range.Handle.ToInt32()); 138 } 139 140 public void SetRangeOfIndexBuffer() 141 { 142 Buffer.Resize(_tempIndexBuffer, _indexBuffer.Size); 143 Buffer.Copy(_indexBuffer.Handle, _tempIndexBuffer, _indexBuffer.Offset, 0, _indexBuffer.Size); 144 GL.BindBuffer(BufferTarget.ElementArrayBuffer, _tempIndexBuffer.ToInt32()); 145 } 146 147 public void RestoreIndexBuffer() 148 { 149 GL.BindBuffer(BufferTarget.ElementArrayBuffer, _indexBuffer.Handle.ToInt32()); 150 } 151 152 public void PreDraw(int vertexCount) 153 { 154 LimitVertexBuffers(vertexCount); 155 } 156 157 public void PreDrawVbUnbounded() 158 { 159 UnlimitVertexBuffers(); 160 } 161 162 public void LimitVertexBuffers(int vertexCount) 163 { 164 // Is it possible for the draw to fetch outside the bounds of any vertex buffer currently bound? 165 166 if (vertexCount <= _minVertexCount) 167 { 168 return; 169 } 170 171 // If the draw can fetch out of bounds, let's ensure that it will only fetch zeros rather than memory garbage. 172 173 int currentTempVbOffset = 0; 174 uint buffersInUse = _vertexBuffersInUse; 175 176 while (buffersInUse != 0) 177 { 178 int vbIndex = BitOperations.TrailingZeroCount(buffersInUse); 179 180 ref var vb = ref _vertexBuffers[vbIndex]; 181 182 int requiredSize = vertexCount * vb.Stride; 183 184 if (vb.Buffer.Size < requiredSize) 185 { 186 BufferHandle tempVertexBuffer = EnsureTempVertexBufferSize(currentTempVbOffset + requiredSize); 187 188 Buffer.Copy(vb.Buffer.Handle, tempVertexBuffer, vb.Buffer.Offset, currentTempVbOffset, vb.Buffer.Size); 189 Buffer.Clear(tempVertexBuffer, currentTempVbOffset + vb.Buffer.Size, requiredSize - vb.Buffer.Size, 0); 190 191 GL.BindVertexBuffer(vbIndex, tempVertexBuffer.ToInt32(), (IntPtr)currentTempVbOffset, vb.Stride); 192 193 currentTempVbOffset += requiredSize; 194 _vertexBuffersLimited |= 1u << vbIndex; 195 } 196 197 buffersInUse &= ~(1u << vbIndex); 198 } 199 } 200 201 private BufferHandle EnsureTempVertexBufferSize(int size) 202 { 203 BufferHandle tempVertexBuffer = _tempVertexBuffer; 204 205 if (_tempVertexBufferSize < size) 206 { 207 _tempVertexBufferSize = size; 208 209 if (tempVertexBuffer == BufferHandle.Null) 210 { 211 tempVertexBuffer = Buffer.Create(size); 212 _tempVertexBuffer = tempVertexBuffer; 213 return tempVertexBuffer; 214 } 215 216 Buffer.Resize(_tempVertexBuffer, size); 217 } 218 219 return tempVertexBuffer; 220 } 221 222 public void UnlimitVertexBuffers() 223 { 224 uint buffersLimited = _vertexBuffersLimited; 225 226 if (buffersLimited == 0) 227 { 228 return; 229 } 230 231 while (buffersLimited != 0) 232 { 233 int vbIndex = BitOperations.TrailingZeroCount(buffersLimited); 234 235 ref var vb = ref _vertexBuffers[vbIndex]; 236 237 GL.BindVertexBuffer(vbIndex, vb.Buffer.Handle.ToInt32(), (IntPtr)vb.Buffer.Offset, vb.Stride); 238 239 buffersLimited &= ~(1u << vbIndex); 240 } 241 242 _vertexBuffersLimited = 0; 243 } 244 245 [MethodImpl(MethodImplOptions.AggressiveInlining)] 246 private void EnableVertexAttrib(int index) 247 { 248 uint mask = 1u << index; 249 250 if ((_vertexAttribsInUse & mask) == 0) 251 { 252 _vertexAttribsInUse |= mask; 253 GL.EnableVertexAttribArray(index); 254 } 255 } 256 257 [MethodImpl(MethodImplOptions.AggressiveInlining)] 258 private void DisableVertexAttrib(int index) 259 { 260 uint mask = 1u << index; 261 262 if ((_vertexAttribsInUse & mask) != 0) 263 { 264 _vertexAttribsInUse &= ~mask; 265 GL.DisableVertexAttribArray(index); 266 GL.VertexAttrib4(index, 0f, 0f, 0f, 1f); 267 } 268 } 269 270 public void Dispose() 271 { 272 if (Handle != 0) 273 { 274 GL.DeleteVertexArray(Handle); 275 276 Handle = 0; 277 } 278 } 279 } 280 }