[Fix] (MG_Impl, MG_State): give the vertex buffer binding points a real state view

The binding-point half of ARB_vertex_attrib_binding was implemented, but nothing
outside it could see the result. glGetIntegerv answered GL_MAX_VERTEX_ATTRIB_BINDINGS,
GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET and GL_MAX_VERTEX_ATTRIB_STRIDE with a hardcoded
0 and a comment saying the entry points were stubs, which they no longer are. An
application that sizes its loops off those limits therefore saw none, and every
"bindingindex must be less than MAX_VERTEX_ATTRIB_BINDINGS" check silently accepted
everything because the limit it validated against was not the one it reported.

The indexed getters answer GL_VERTEX_BINDING_{BUFFER,DIVISOR,OFFSET,STRIDE} from the
bound vertex array now, and the non-indexed getter reports them as indexed-only rather
than returning a fabricated 0.

glVertexAttribPointer is defined in terms of the binding model: it also points the
attribute at its own binding point and gives that point the buffer, the pointer as the
offset and the effective (never zero) stride. MobileGL resolved the pointer form
straight into the flat attribute view and left the binding point untouched, so
GL_VERTEX_BINDING_OFFSET read back 0 for every attribute set up the classic way. The
flat view keeps the raw stride, because GL_VERTEX_ATTRIB_ARRAY_STRIDE reports that
argument verbatim, so the binding point is recorded alongside it rather than resolved
from it. glVertexAttribDivisor likewise now moves the binding point's divisor.

The by-name entry points reject vertex array 0. MobileGL keeps a real object at index 0
for the compatibility paths, so the name validation used to let the default vertex array
through a direct-state-access call that has no such thing.

glVertexAttribFormat and friends validated with the pointer-only subset, which reports
GL_BGRA as an out-of-range size instead of applying the BGRA rules, and never saw
relativeoffset at all. They share the full format validation now, which also grew the
GL_UNSIGNED_INT_10F_11F_11F_REV rules - that type has no DataType of its own, so it has
to be recognised before the conversion turns it into Unknown and reports the wrong error.

glVertexAttribLFormat and glVertexArrayAttribLFormat were stubs. They validate their
arguments now and then report that 64-bit vertex attributes are unsupported, which is
honest; silently accepting a format that can never be used is not.

Takes direct_state_access.vertex_arrays_* from 12 to 17 of 19 on both backends.
This commit is contained in:
BZLZHH
2026-08-05 02:16:32 +00:00
parent 6359b0002b
commit 42fd02d82f
8 changed files with 254 additions and 26 deletions
@@ -77,6 +77,26 @@ namespace MobileGL::MG_State::GLState {
BumpAttributeFormatVersion(index);
}
void VertexArrayObject::MirrorPointerIntoBinding(Uint index, const SharedPtr<BufferObject>& buffer, SizeT offset,
int effectiveStride) {
if (index >= MAX_VERTEX_ATTRIBS || index >= MAX_VERTEX_ATTRIB_BINDINGS) return;
// glVertexAttribPointer is defined in terms of the binding model (GL 4.6 core 10.3.2): it
// also sets binding point `index` to the buffer, the pointer as the offset, and the
// *effective* stride, and points the attribute at that binding point with relative offset 0.
// The flat attribute view keeps the raw stride, because VERTEX_ATTRIB_ARRAY_STRIDE reports
// that argument verbatim, so the binding point is recorded alongside the resolved attribute
// rather than being resolved into it.
m_attributeBindingIndex[index] = index;
m_attributeRelativeOffset[index] = 0;
auto& binding = m_bindingPoints[index];
binding.Buffer = buffer;
binding.Offset = offset;
binding.Stride = effectiveStride;
binding.Divisor = m_attributes[index].Divisor;
}
void VertexArrayObject::BindAttributeBuffer(Uint index, const SharedPtr<BufferObject>& buffer) {
if (index >= MAX_VERTEX_ATTRIBS) return;
@@ -111,6 +131,11 @@ namespace MobileGL::MG_State::GLState {
void VertexArrayObject::SetAttributeDivisor(Uint index, Uint divisor) {
if (index >= MAX_VERTEX_ATTRIBS) return;
// glVertexAttribDivisor is VertexBindingDivisor on the attribute's own binding point
// (GL 4.6 core 10.3.2), so the binding-point view has to follow the resolved attribute.
if (index < MAX_VERTEX_ATTRIB_BINDINGS && m_attributeBindingIndex[index] == index) {
m_bindingPoints[index].Divisor = divisor;
}
if (m_attributes[index].Divisor == divisor) return;
m_attributes[index].Divisor = divisor;
BumpAttributeFormatVersion(index);
@@ -187,18 +212,18 @@ namespace MobileGL::MG_State::GLState {
}
void VertexArrayObject::SetAttributeFormatSeparate(Uint attribIndex, int size, DataType type, Bool normalized,
Bool isInteger, Uint relativeOffset) {
Bool isInteger, Uint relativeOffset, Bool isBgra) {
if (attribIndex >= MAX_VERTEX_ATTRIBS) return;
if (size < 1 || size > 4) return;
auto& attr = m_attributes[attribIndex];
if (attr.Size != size || attr.Type != type || attr.Normalized != normalized || attr.IsInteger != isInteger ||
attr.IsBgra || m_attributeRelativeOffset[attribIndex] != relativeOffset) {
attr.IsBgra != isBgra || m_attributeRelativeOffset[attribIndex] != relativeOffset) {
attr.Size = size;
attr.Type = type;
attr.Normalized = normalized;
attr.IsInteger = isInteger;
attr.IsBgra = false; // the binding-format path (glVertexAttribFormat) does not carry BGRA
attr.IsBgra = isBgra;
m_attributeRelativeOffset[attribIndex] = relativeOffset;
BumpAttributeFormatVersion(attribIndex);
}
@@ -64,6 +64,12 @@ namespace MobileGL {
void BindAttributeBuffer(Uint index, const SharedPtr<BufferObject>& buffer);
// Record what the pointer-style API implies for the binding-point view: attribute
// `index` bound to binding point `index` with relative offset 0, and that binding
// point carrying the buffer, the pointer offset and the effective stride.
void MirrorPointerIntoBinding(Uint index, const SharedPtr<BufferObject>& buffer, SizeT offset,
int effectiveStride);
BindingSlot<BufferObject>& GetIndexBufferBindingSlot();
const BindingSlot<BufferObject>& GetIndexBufferBindingSlot() const;
@@ -82,7 +88,7 @@ namespace MobileGL {
void SetBindingDivisor(Uint bindingIndex, Uint divisor);
void SetAttributeBinding(Uint attribIndex, Uint bindingIndex);
void SetAttributeFormatSeparate(Uint attribIndex, int size, DataType type, Bool normalized,
Bool isInteger, Uint relativeOffset);
Bool isInteger, Uint relativeOffset, Bool isBgra = false);
// The binding-point view the attributes were resolved from. Kept queryable
// because glGetVertexArrayIndexed[64]iv reports it verbatim, and the resolved