2019-10-13 08:02:07 +02:00
|
|
|
using OpenTK.Graphics.OpenGL;
|
2019-12-29 18:41:50 +01:00
|
|
|
using Ryujinx.Graphics.GAL;
|
2019-10-13 08:02:07 +02:00
|
|
|
using System;
|
2022-08-12 01:21:56 +02:00
|
|
|
using System.Numerics;
|
2021-01-26 22:44:07 +01:00
|
|
|
using System.Runtime.CompilerServices;
|
2019-10-13 08:02:07 +02:00
|
|
|
|
|
|
|
namespace Ryujinx.Graphics.OpenGL
|
|
|
|
{
|
|
|
|
class VertexArray : IDisposable
|
|
|
|
{
|
2019-12-31 23:09:49 +01:00
|
|
|
public int Handle { get; private set; }
|
2019-10-13 08:02:07 +02:00
|
|
|
|
|
|
|
private bool _needsAttribsUpdate;
|
|
|
|
|
2020-05-23 11:46:09 +02:00
|
|
|
private readonly VertexAttribDescriptor[] _vertexAttribs;
|
|
|
|
private readonly VertexBufferDescriptor[] _vertexBuffers;
|
|
|
|
|
|
|
|
private int _vertexAttribsCount;
|
|
|
|
private int _vertexBuffersCount;
|
2022-08-12 01:21:56 +02:00
|
|
|
private int _minVertexCount;
|
2019-10-13 08:02:07 +02:00
|
|
|
|
2021-01-26 22:44:07 +01:00
|
|
|
private uint _vertexAttribsInUse;
|
|
|
|
private uint _vertexBuffersInUse;
|
2022-08-12 01:21:56 +02:00
|
|
|
private uint _vertexBuffersLimited;
|
2021-01-26 22:44:07 +01:00
|
|
|
|
2021-08-26 23:50:28 +02:00
|
|
|
private BufferRange _indexBuffer;
|
|
|
|
private BufferHandle _tempIndexBuffer;
|
2022-08-12 01:21:56 +02:00
|
|
|
private BufferHandle _tempVertexBuffer;
|
|
|
|
private int _tempVertexBufferSize;
|
2021-08-26 23:50:28 +02:00
|
|
|
|
2019-10-13 08:02:07 +02:00
|
|
|
public VertexArray()
|
|
|
|
{
|
|
|
|
Handle = GL.GenVertexArray();
|
2020-05-23 11:46:09 +02:00
|
|
|
|
|
|
|
_vertexAttribs = new VertexAttribDescriptor[Constants.MaxVertexAttribs];
|
|
|
|
_vertexBuffers = new VertexBufferDescriptor[Constants.MaxVertexBuffers];
|
2021-08-26 23:50:28 +02:00
|
|
|
|
|
|
|
_tempIndexBuffer = Buffer.Create();
|
2019-10-13 08:02:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void Bind()
|
|
|
|
{
|
|
|
|
GL.BindVertexArray(Handle);
|
|
|
|
}
|
|
|
|
|
2020-05-23 11:46:09 +02:00
|
|
|
public void SetVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers)
|
2019-10-13 08:02:07 +02:00
|
|
|
{
|
2022-08-12 01:21:56 +02:00
|
|
|
int minVertexCount = int.MaxValue;
|
|
|
|
|
2021-01-26 22:44:07 +01:00
|
|
|
int bindingIndex;
|
|
|
|
for (bindingIndex = 0; bindingIndex < vertexBuffers.Length; bindingIndex++)
|
2019-10-13 08:02:07 +02:00
|
|
|
{
|
2021-01-26 22:44:07 +01:00
|
|
|
VertexBufferDescriptor vb = vertexBuffers[bindingIndex];
|
2019-10-13 08:02:07 +02:00
|
|
|
|
2020-10-26 01:40:50 +01:00
|
|
|
if (vb.Buffer.Handle != BufferHandle.Null)
|
2020-05-23 11:46:09 +02:00
|
|
|
{
|
2022-08-12 01:21:56 +02:00
|
|
|
int vertexCount = vb.Stride <= 0 ? 0 : vb.Buffer.Size / vb.Stride;
|
|
|
|
if (minVertexCount > vertexCount)
|
|
|
|
{
|
|
|
|
minVertexCount = vertexCount;
|
|
|
|
}
|
|
|
|
|
2020-05-23 11:46:09 +02:00
|
|
|
GL.BindVertexBuffer(bindingIndex, vb.Buffer.Handle.ToInt32(), (IntPtr)vb.Buffer.Offset, vb.Stride);
|
2019-10-13 08:02:07 +02:00
|
|
|
GL.VertexBindingDivisor(bindingIndex, vb.Divisor);
|
2021-01-26 22:44:07 +01:00
|
|
|
_vertexBuffersInUse |= 1u << bindingIndex;
|
2019-10-13 08:02:07 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-01-26 22:44:07 +01:00
|
|
|
if ((_vertexBuffersInUse & (1u << bindingIndex)) != 0)
|
|
|
|
{
|
|
|
|
GL.BindVertexBuffer(bindingIndex, 0, IntPtr.Zero, 0);
|
|
|
|
_vertexBuffersInUse &= ~(1u << bindingIndex);
|
|
|
|
}
|
2019-10-13 08:02:07 +02:00
|
|
|
}
|
|
|
|
|
2021-01-26 22:44:07 +01:00
|
|
|
_vertexBuffers[bindingIndex] = vb;
|
2019-10-13 08:02:07 +02:00
|
|
|
}
|
|
|
|
|
2020-05-23 11:46:09 +02:00
|
|
|
_vertexBuffersCount = bindingIndex;
|
2022-08-12 01:21:56 +02:00
|
|
|
_minVertexCount = minVertexCount;
|
2019-10-13 08:02:07 +02:00
|
|
|
_needsAttribsUpdate = true;
|
|
|
|
}
|
|
|
|
|
2020-05-23 11:46:09 +02:00
|
|
|
public void SetVertexAttributes(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs)
|
2019-10-13 08:02:07 +02:00
|
|
|
{
|
2020-05-23 11:46:09 +02:00
|
|
|
int index = 0;
|
2019-10-13 08:02:07 +02:00
|
|
|
|
2020-05-23 11:46:09 +02:00
|
|
|
for (; index < vertexAttribs.Length; index++)
|
2019-10-13 08:02:07 +02:00
|
|
|
{
|
2020-05-23 11:46:09 +02:00
|
|
|
VertexAttribDescriptor attrib = vertexAttribs[index];
|
|
|
|
|
2021-01-26 22:44:07 +01:00
|
|
|
if (attrib.Equals(_vertexAttribs[index]))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-10-13 08:02:07 +02:00
|
|
|
FormatInfo fmtInfo = FormatTable.GetFormatInfo(attrib.Format);
|
|
|
|
|
2020-03-30 04:11:24 +02:00
|
|
|
if (attrib.IsZero)
|
|
|
|
{
|
|
|
|
// Disabling the attribute causes the shader to read a constant value.
|
2021-05-24 09:38:38 +02:00
|
|
|
// We currently set the constant to (0, 0, 0, 0).
|
2021-01-26 22:44:07 +01:00
|
|
|
DisableVertexAttrib(index);
|
2020-03-30 04:11:24 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-01-26 22:44:07 +01:00
|
|
|
EnableVertexAttrib(index);
|
2020-03-30 04:11:24 +02:00
|
|
|
}
|
2020-10-26 01:40:50 +01:00
|
|
|
|
2019-10-13 08:02:07 +02:00
|
|
|
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;
|
|
|
|
|
2020-05-23 11:46:09 +02:00
|
|
|
GL.VertexAttribFormat(index, size, type, fmtInfo.Normalized, offset);
|
2019-10-13 08:02:07 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
VertexAttribIntegerType type = (VertexAttribIntegerType)fmtInfo.PixelType;
|
|
|
|
|
2020-05-23 11:46:09 +02:00
|
|
|
GL.VertexAttribIFormat(index, size, type, offset);
|
2019-10-13 08:02:07 +02:00
|
|
|
}
|
|
|
|
|
2020-05-23 11:46:09 +02:00
|
|
|
GL.VertexAttribBinding(index, attrib.BufferIndex);
|
2019-10-13 08:02:07 +02:00
|
|
|
|
2020-05-23 11:46:09 +02:00
|
|
|
_vertexAttribs[index] = attrib;
|
2019-10-13 08:02:07 +02:00
|
|
|
}
|
|
|
|
|
2020-05-23 11:46:09 +02:00
|
|
|
_vertexAttribsCount = index;
|
|
|
|
|
|
|
|
for (; index < Constants.MaxVertexAttribs; index++)
|
2019-10-13 08:02:07 +02:00
|
|
|
{
|
2021-01-26 22:44:07 +01:00
|
|
|
DisableVertexAttrib(index);
|
2019-10-13 08:02:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-26 23:50:28 +02:00
|
|
|
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()
|
2019-10-13 08:02:07 +02:00
|
|
|
{
|
2021-08-26 23:50:28 +02:00
|
|
|
GL.BindBuffer(BufferTarget.ElementArrayBuffer, _indexBuffer.Handle.ToInt32());
|
2019-10-13 08:02:07 +02:00
|
|
|
}
|
|
|
|
|
2022-08-12 01:21:56 +02:00
|
|
|
public void PreDraw(int vertexCount)
|
|
|
|
{
|
|
|
|
LimitVertexBuffers(vertexCount);
|
|
|
|
Validate();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void PreDrawVbUnbounded()
|
|
|
|
{
|
|
|
|
UnlimitVertexBuffers();
|
|
|
|
Validate();
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-10-13 08:02:07 +02:00
|
|
|
public void Validate()
|
|
|
|
{
|
2020-05-23 11:46:09 +02:00
|
|
|
for (int attribIndex = 0; attribIndex < _vertexAttribsCount; attribIndex++)
|
2019-10-13 08:02:07 +02:00
|
|
|
{
|
|
|
|
VertexAttribDescriptor attrib = _vertexAttribs[attribIndex];
|
|
|
|
|
2021-01-26 22:44:07 +01:00
|
|
|
if (!attrib.IsZero)
|
2019-10-13 08:02:07 +02:00
|
|
|
{
|
2021-01-26 22:44:07 +01:00
|
|
|
if ((uint)attrib.BufferIndex >= _vertexBuffersCount)
|
|
|
|
{
|
|
|
|
DisableVertexAttrib(attribIndex);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_vertexBuffers[attrib.BufferIndex].Buffer.Handle == BufferHandle.Null)
|
|
|
|
{
|
|
|
|
DisableVertexAttrib(attribIndex);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_needsAttribsUpdate)
|
|
|
|
{
|
|
|
|
EnableVertexAttrib(attribIndex);
|
|
|
|
}
|
2019-10-13 08:02:07 +02:00
|
|
|
}
|
2021-01-26 22:44:07 +01:00
|
|
|
}
|
2019-10-13 08:02:07 +02:00
|
|
|
|
2021-01-26 22:44:07 +01:00
|
|
|
_needsAttribsUpdate = false;
|
|
|
|
}
|
2019-10-13 08:02:07 +02:00
|
|
|
|
2021-01-26 22:44:07 +01:00
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
|
|
private void EnableVertexAttrib(int index)
|
|
|
|
{
|
|
|
|
uint mask = 1u << index;
|
2019-10-13 08:02:07 +02:00
|
|
|
|
2021-01-26 22:44:07 +01:00
|
|
|
if ((_vertexAttribsInUse & mask) == 0)
|
|
|
|
{
|
|
|
|
_vertexAttribsInUse |= mask;
|
|
|
|
GL.EnableVertexAttribArray(index);
|
2019-10-13 08:02:07 +02:00
|
|
|
}
|
2021-01-26 22:44:07 +01:00
|
|
|
}
|
2019-10-13 08:02:07 +02:00
|
|
|
|
2021-01-26 22:44:07 +01:00
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
|
|
private void DisableVertexAttrib(int index)
|
|
|
|
{
|
|
|
|
uint mask = 1u << index;
|
|
|
|
|
|
|
|
if ((_vertexAttribsInUse & mask) != 0)
|
|
|
|
{
|
|
|
|
_vertexAttribsInUse &= ~mask;
|
|
|
|
GL.DisableVertexAttribArray(index);
|
Change disabled vertex attribute value to (0, 0, 0, 1) (#2573)
This seems to be the default value when the vertex attribute is disabled, or components aren't defined.
This fixes a regression from #2307 in SMO where a plant in the Wooded Kingdom would draw slightly differently in the depth prepass, leading to depth test failing later on.
GDK has stated that the specific case in Gundam only expects x and y to be 0, and Vulkan's undefined value for z does appear to be 0 when a vertex attribute type does not have that component, hence the value (0, 0, 0, 1).
This worked in Vulkan despite also providing an all 0s attribute due to the vertex attribute binding being R32Float, so the other values were undefined. It should be changed there separately.
2021-08-20 23:09:30 +02:00
|
|
|
GL.VertexAttrib4(index, 0f, 0f, 0f, 1f);
|
2021-01-26 22:44:07 +01:00
|
|
|
}
|
2019-10-13 08:02:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
{
|
2019-12-31 23:09:49 +01:00
|
|
|
if (Handle != 0)
|
|
|
|
{
|
|
|
|
GL.DeleteVertexArray(Handle);
|
|
|
|
|
|
|
|
Handle = 0;
|
|
|
|
}
|
2019-10-13 08:02:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|