[Feat] (MG_Impl, MG_State): implement the DSA vertex array queries

glGetVertexArrayiv, glGetVertexArrayIndexediv and glGetVertexArrayIndexed64iv
were stubs, so nothing could read a vertex array's state without binding it
first -- the exact thing direct state access exists to avoid.

They read the state the vertex array already holds. Two accessors were needed for
that: the relative offset and the binding points, which are the binding-point
view the flat per-attribute state was resolved from and cannot be reconstructed
from the resolved form.

Note the index means different things by entry point: for the 32-bit indexed
query it is an attribute, but GL_VERTEX_BINDING_OFFSET names a vertex buffer
binding point directly (GL 4.6 core 10.3.1). GL_VERTEX_ATTRIB_ARRAY_LONG is
answered GL_FALSE throughout, which is honest while 64-bit vertex attributes are
unsupported.

Takes direct_state_access.vertex_arrays_* from 8 to 12 of 19 on Espryt.
GL_VERTEX_BINDING_OFFSET still reads back 0: the query is right but the offset is
not reaching the binding point, which is a separate defect further up.
This commit is contained in:
BZLZHH
2026-08-04 21:16:59 -04:00
parent c186f5f255
commit 6359b0002b
4 changed files with 98 additions and 3 deletions
@@ -1092,9 +1092,9 @@ DECLARE_GL_FUNCTION_HEAD(void, VertexArrayAttribFormat, GLuint vaobj, GLuint att
DECLARE_GL_FUNCTION_HEAD(void, VertexArrayAttribIFormat, GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexArrayAttribIFormat, vaobj, attribindex, size, type, relativeoffset)
DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexArrayAttribLFormat, GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexArrayAttribLFormat, vaobj, attribindex, size, type, relativeoffset)
DECLARE_GL_FUNCTION_HEAD(void, VertexArrayBindingDivisor, GLuint vaobj, GLuint bindingindex, GLuint divisor) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexArrayBindingDivisor, vaobj, bindingindex, divisor)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetVertexArrayiv, GLuint vaobj, GLenum pname, GLint* param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetVertexArrayiv, vaobj, pname, param)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetVertexArrayIndexediv, GLuint vaobj, GLuint index, GLenum pname, GLint* param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetVertexArrayIndexediv, vaobj, index, pname, param)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetVertexArrayIndexed64iv, GLuint vaobj, GLuint index, GLenum pname, GLint64* param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetVertexArrayIndexed64iv, vaobj, index, pname, param)
DECLARE_GL_FUNCTION_HEAD(void, GetVertexArrayiv, GLuint vaobj, GLenum pname, GLint* param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetVertexArrayiv, vaobj, pname, param)
DECLARE_GL_FUNCTION_HEAD(void, GetVertexArrayIndexediv, GLuint vaobj, GLuint index, GLenum pname, GLint* param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetVertexArrayIndexediv, vaobj, index, pname, param)
DECLARE_GL_FUNCTION_HEAD(void, GetVertexArrayIndexed64iv, GLuint vaobj, GLuint index, GLenum pname, GLint64* param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetVertexArrayIndexed64iv, vaobj, index, pname, param)
DECLARE_GL_FUNCTION_HEAD(void, CreateSamplers, GLsizei n, GLuint* samplers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateSamplers, n, samplers)
DECLARE_GL_FUNCTION_STUB_HEAD(void, CreateProgramPipelines, GLsizei n, GLuint* pipelines) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CreateProgramPipelines, n, pipelines)
DECLARE_GL_FUNCTION_HEAD(void, CreateQueries, GLenum target, GLsizei n, GLuint* ids) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateQueries, target, n, ids)
@@ -1044,6 +1044,83 @@ namespace MobileGL::MG_Impl::GLImpl {
VertexArrayVertexBuffer_State(vaobj, bindingindex, buffer, offset, stride);
}
// glGetVertexArrayiv reports exactly one thing (GL 4.6 core table 23.4): which buffer the
// named vertex array takes its indices from. Everything else about a vertex array is
// per-attribute and belongs to the indexed queries below.
void GetVertexArrayiv(GLuint vaobj, GLenum pname, GLint* param) {
auto vao = GetNamedVertexArrayObject_State(vaobj, __func__);
if (!vao || !param) return;
if (pname != GL_ELEMENT_ARRAY_BUFFER_BINDING) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
"pname must be GL_ELEMENT_ARRAY_BUFFER_BINDING."));
return;
}
const auto& indexBuffer = vao->GetIndexBufferBindingSlot().GetBoundObject();
*param = indexBuffer ? static_cast<GLint>(indexBuffer->GetExternalIndex()) : 0;
}
void GetVertexArrayIndexediv(GLuint vaobj, GLuint index, GLenum pname, GLint* param) {
auto vao = GetNamedVertexArrayObject_State(vaobj, __func__);
if (!vao || !param) return;
if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return;
const auto& attr = vao->GetAttribute(index);
switch (pname) {
case GL_VERTEX_ATTRIB_ARRAY_ENABLED:
*param = attr.Enabled ? GL_TRUE : GL_FALSE;
return;
case GL_VERTEX_ATTRIB_ARRAY_SIZE:
*param = static_cast<GLint>(attr.Size);
return;
case GL_VERTEX_ATTRIB_ARRAY_STRIDE:
*param = static_cast<GLint>(attr.Stride);
return;
case GL_VERTEX_ATTRIB_ARRAY_TYPE:
*param = static_cast<GLint>(MG_Util::ConvertDataTypeToGLEnum(attr.Type));
return;
case GL_VERTEX_ATTRIB_ARRAY_NORMALIZED:
*param = attr.Normalized ? GL_TRUE : GL_FALSE;
return;
case GL_VERTEX_ATTRIB_ARRAY_INTEGER:
*param = attr.IsInteger ? GL_TRUE : GL_FALSE;
return;
case GL_VERTEX_ATTRIB_ARRAY_LONG:
// 64-bit attributes are not supported, so no attribute is ever a long one.
*param = GL_FALSE;
return;
case GL_VERTEX_ATTRIB_ARRAY_DIVISOR:
*param = static_cast<GLint>(attr.Divisor);
return;
case GL_VERTEX_ATTRIB_RELATIVE_OFFSET:
*param = static_cast<GLint>(vao->GetAttributeRelativeOffset(index));
return;
default:
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
"pname is not an accepted indexed vertex array query."));
return;
}
}
// Only GL_VERTEX_BINDING_OFFSET needs 64 bits. Its `index` names a vertex buffer binding
// point directly (GL 4.6 core 10.3.1), not an attribute - unlike every pname the 32-bit
// indexed query above accepts, which is why this one does not go through an attribute's
// binding index.
void GetVertexArrayIndexed64iv(GLuint vaobj, GLuint index, GLenum pname, GLint64* param) {
auto vao = GetNamedVertexArrayObject_State(vaobj, __func__);
if (!vao || !param) return;
if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return;
if (pname != GL_VERTEX_BINDING_OFFSET) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "pname must be GL_VERTEX_BINDING_OFFSET."));
return;
}
*param = static_cast<GLint64>(vao->GetBindingPoint(index).Offset);
}
void VertexArrayAttribFormat(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized,
GLuint relativeoffset) {
VertexArrayAttribFormat_State(vaobj, attribindex, size, type, normalized, relativeoffset);
@@ -92,6 +92,9 @@ namespace MobileGL::MG_Impl::GLImpl {
void EnableVertexArrayAttrib(GLuint vaobj, GLuint index);
void VertexArrayElementBuffer(GLuint vaobj, GLuint buffer);
void VertexArrayVertexBuffer(GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride);
void GetVertexArrayiv(GLuint vaobj, GLenum pname, GLint* param);
void GetVertexArrayIndexediv(GLuint vaobj, GLuint index, GLenum pname, GLint* param);
void GetVertexArrayIndexed64iv(GLuint vaobj, GLuint index, GLenum pname, GLint64* param);
void VertexArrayAttribFormat(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized,
GLuint relativeoffset);
void VertexArrayAttribIFormat(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset);
@@ -84,6 +84,21 @@ namespace MobileGL {
void SetAttributeFormatSeparate(Uint attribIndex, int size, DataType type, Bool normalized,
Bool isInteger, Uint relativeOffset);
// The binding-point view the attributes were resolved from. Kept queryable
// because glGetVertexArrayIndexed[64]iv reports it verbatim, and the resolved
// flat attribute cannot always be inverted back into it.
Uint GetAttributeRelativeOffset(Uint attribIndex) const {
return attribIndex < m_attributeRelativeOffset.size() ? m_attributeRelativeOffset[attribIndex] : 0;
}
Uint GetAttributeBindingIndex(Uint attribIndex) const {
return attribIndex < m_attributeBindingIndex.size() ? m_attributeBindingIndex[attribIndex]
: attribIndex;
}
const VertexBufferBindingPoint& GetBindingPoint(Uint bindingIndex) const {
static const VertexBufferBindingPoint kEmpty{};
return bindingIndex < m_bindingPoints.size() ? m_bindingPoints[bindingIndex] : kEmpty;
}
const VertexAttributeVersion& GetAttributeVersion(Uint index) const;
const Array<VertexAttributeVersion, MAX_VERTEX_ATTRIBS>& GetAllAttributeVersions() const;