| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- using OpenTK.Graphics.OpenGL;
- using Ryujinx.Graphics.GAL;
- using System;
- using System.Numerics;
- using System.Runtime.CompilerServices;
- namespace Ryujinx.Graphics.OpenGL
- {
- class VertexArray : IDisposable
- {
- public int Handle { get; private set; }
- private readonly VertexAttribDescriptor[] _vertexAttribs;
- private readonly VertexBufferDescriptor[] _vertexBuffers;
- private int _minVertexCount;
- private uint _vertexAttribsInUse;
- private uint _vertexBuffersInUse;
- private uint _vertexBuffersLimited;
- private BufferRange _indexBuffer;
- private BufferHandle _tempIndexBuffer;
- private BufferHandle _tempVertexBuffer;
- private int _tempVertexBufferSize;
- public VertexArray()
- {
- Handle = GL.GenVertexArray();
- _vertexAttribs = new VertexAttribDescriptor[Constants.MaxVertexAttribs];
- _vertexBuffers = new VertexBufferDescriptor[Constants.MaxVertexBuffers];
- _tempIndexBuffer = Buffer.Create();
- }
- public void Bind()
- {
- GL.BindVertexArray(Handle);
- }
- public void SetVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers)
- {
- int minVertexCount = int.MaxValue;
- int bindingIndex;
- for (bindingIndex = 0; bindingIndex < vertexBuffers.Length; bindingIndex++)
- {
- VertexBufferDescriptor vb = vertexBuffers[bindingIndex];
- if (vb.Buffer.Handle != BufferHandle.Null)
- {
- int vertexCount = vb.Stride <= 0 ? 0 : vb.Buffer.Size / vb.Stride;
- if (minVertexCount > vertexCount)
- {
- minVertexCount = vertexCount;
- }
- GL.BindVertexBuffer(bindingIndex, vb.Buffer.Handle.ToInt32(), (IntPtr)vb.Buffer.Offset, vb.Stride);
- GL.VertexBindingDivisor(bindingIndex, vb.Divisor);
- _vertexBuffersInUse |= 1u << bindingIndex;
- }
- else
- {
- if ((_vertexBuffersInUse & (1u << bindingIndex)) != 0)
- {
- GL.BindVertexBuffer(bindingIndex, 0, IntPtr.Zero, 0);
- _vertexBuffersInUse &= ~(1u << bindingIndex);
- }
- }
- _vertexBuffers[bindingIndex] = vb;
- }
- _minVertexCount = minVertexCount;
- }
- public void SetVertexAttributes(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs)
- {
- int index = 0;
- for (; index < vertexAttribs.Length; index++)
- {
- VertexAttribDescriptor attrib = vertexAttribs[index];
- if (attrib.Equals(_vertexAttribs[index]))
- {
- continue;
- }
- FormatInfo fmtInfo = FormatTable.GetFormatInfo(attrib.Format);
- if (attrib.IsZero)
- {
- // Disabling the attribute causes the shader to read a constant value.
- // We currently set the constant to (0, 0, 0, 0).
- DisableVertexAttrib(index);
- }
- else
- {
- EnableVertexAttrib(index);
- }
- int offset = attrib.Offset;
- int size = fmtInfo.Components;
- bool isFloat = fmtInfo.PixelType == PixelType.Float ||
- fmtInfo.PixelType == PixelType.HalfFloat;
- if (isFloat || fmtInfo.Normalized || fmtInfo.Scaled)
- {
- VertexAttribType type = (VertexAttribType)fmtInfo.PixelType;
- GL.VertexAttribFormat(index, size, type, fmtInfo.Normalized, offset);
- }
- else
- {
- VertexAttribIntegerType type = (VertexAttribIntegerType)fmtInfo.PixelType;
- GL.VertexAttribIFormat(index, size, type, offset);
- }
- GL.VertexAttribBinding(index, attrib.BufferIndex);
- _vertexAttribs[index] = attrib;
- }
- for (; index < Constants.MaxVertexAttribs; index++)
- {
- DisableVertexAttrib(index);
- }
- }
- public void SetIndexBuffer(BufferRange range)
- {
- _indexBuffer = range;
- GL.BindBuffer(BufferTarget.ElementArrayBuffer, range.Handle.ToInt32());
- }
- public void SetRangeOfIndexBuffer()
- {
- Buffer.Resize(_tempIndexBuffer, _indexBuffer.Size);
- Buffer.Copy(_indexBuffer.Handle, _tempIndexBuffer, _indexBuffer.Offset, 0, _indexBuffer.Size);
- GL.BindBuffer(BufferTarget.ElementArrayBuffer, _tempIndexBuffer.ToInt32());
- }
- public void RestoreIndexBuffer()
- {
- GL.BindBuffer(BufferTarget.ElementArrayBuffer, _indexBuffer.Handle.ToInt32());
- }
- public void PreDraw(int vertexCount)
- {
- LimitVertexBuffers(vertexCount);
- }
- public void PreDrawVbUnbounded()
- {
- UnlimitVertexBuffers();
- }
- public void LimitVertexBuffers(int vertexCount)
- {
- // Is it possible for the draw to fetch outside the bounds of any vertex buffer currently bound?
- if (vertexCount <= _minVertexCount)
- {
- return;
- }
- // If the draw can fetch out of bounds, let's ensure that it will only fetch zeros rather than memory garbage.
- int currentTempVbOffset = 0;
- uint buffersInUse = _vertexBuffersInUse;
- while (buffersInUse != 0)
- {
- int vbIndex = BitOperations.TrailingZeroCount(buffersInUse);
- ref var vb = ref _vertexBuffers[vbIndex];
- int requiredSize = vertexCount * vb.Stride;
- if (vb.Buffer.Size < requiredSize)
- {
- BufferHandle tempVertexBuffer = EnsureTempVertexBufferSize(currentTempVbOffset + requiredSize);
- Buffer.Copy(vb.Buffer.Handle, tempVertexBuffer, vb.Buffer.Offset, currentTempVbOffset, vb.Buffer.Size);
- Buffer.Clear(tempVertexBuffer, currentTempVbOffset + vb.Buffer.Size, requiredSize - vb.Buffer.Size, 0);
- GL.BindVertexBuffer(vbIndex, tempVertexBuffer.ToInt32(), (IntPtr)currentTempVbOffset, vb.Stride);
- currentTempVbOffset += requiredSize;
- _vertexBuffersLimited |= 1u << vbIndex;
- }
- buffersInUse &= ~(1u << vbIndex);
- }
- }
- private BufferHandle EnsureTempVertexBufferSize(int size)
- {
- BufferHandle tempVertexBuffer = _tempVertexBuffer;
- if (_tempVertexBufferSize < size)
- {
- _tempVertexBufferSize = size;
- if (tempVertexBuffer == BufferHandle.Null)
- {
- tempVertexBuffer = Buffer.Create(size);
- _tempVertexBuffer = tempVertexBuffer;
- return tempVertexBuffer;
- }
- Buffer.Resize(_tempVertexBuffer, size);
- }
- return tempVertexBuffer;
- }
- public void UnlimitVertexBuffers()
- {
- uint buffersLimited = _vertexBuffersLimited;
- if (buffersLimited == 0)
- {
- return;
- }
- while (buffersLimited != 0)
- {
- int vbIndex = BitOperations.TrailingZeroCount(buffersLimited);
- ref var vb = ref _vertexBuffers[vbIndex];
- GL.BindVertexBuffer(vbIndex, vb.Buffer.Handle.ToInt32(), (IntPtr)vb.Buffer.Offset, vb.Stride);
- buffersLimited &= ~(1u << vbIndex);
- }
- _vertexBuffersLimited = 0;
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void EnableVertexAttrib(int index)
- {
- uint mask = 1u << index;
- if ((_vertexAttribsInUse & mask) == 0)
- {
- _vertexAttribsInUse |= mask;
- GL.EnableVertexAttribArray(index);
- }
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void DisableVertexAttrib(int index)
- {
- uint mask = 1u << index;
- if ((_vertexAttribsInUse & mask) != 0)
- {
- _vertexAttribsInUse &= ~mask;
- GL.DisableVertexAttribArray(index);
- GL.VertexAttrib4(index, 0f, 0f, 0f, 1f);
- }
- }
- public void Dispose()
- {
- if (Handle != 0)
- {
- GL.DeleteVertexArray(Handle);
- Handle = 0;
- }
- }
- }
- }
|