mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 21:58:31 +09:00
[Fix, Test] (MG_State, MG_Impl): ARB_vertex_attrib_binding state model - spec stride default, legacy stride/pointer shadows, divisor re-binds, core-profile VAO-0 rejection
This commit is contained in:
@@ -29,6 +29,8 @@ namespace MobileGL::MG_State::GLState {
|
||||
attr.Normalized = false;
|
||||
attr.Stride = 0;
|
||||
attr.Offset = 0;
|
||||
attr.LegacyStride = 0;
|
||||
attr.LegacyPointer = 0;
|
||||
attr.Buffer = nullptr;
|
||||
|
||||
BumpAttributeFormatVersion(index);
|
||||
@@ -61,10 +63,19 @@ namespace MobileGL::MG_State::GLState {
|
||||
void VertexArrayObject::SetAttributeFormat(Uint index, int size, DataType type, Bool normalized, int stride,
|
||||
SizeT offset, Bool isInteger, Bool isBgra) {
|
||||
if (index >= MAX_VERTEX_ATTRIBS) return;
|
||||
if (size < 1 || size > 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The classic pointer-style API takes back full ownership of the resolved fields.
|
||||
m_attributeUsesBindingModel[index] = false;
|
||||
|
||||
// The legacy query shadows: written here and nowhere else, so a later binding-model
|
||||
// mutation cannot leak into VERTEX_ATTRIB_ARRAY_STRIDE / _POINTER. They are pure
|
||||
// query state, so they carry no version bump of their own.
|
||||
m_attributes[index].LegacyStride = stride;
|
||||
m_attributes[index].LegacyPointer = offset;
|
||||
|
||||
if (m_attributes[index].Size == size && m_attributes[index].Type == type &&
|
||||
m_attributes[index].Normalized == normalized && m_attributes[index].Stride == stride &&
|
||||
m_attributes[index].Offset == offset && m_attributes[index].IsInteger == isInteger &&
|
||||
@@ -72,10 +83,6 @@ namespace MobileGL::MG_State::GLState {
|
||||
return;
|
||||
}
|
||||
|
||||
if (size < 1 || size > 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& attr = m_attributes[index];
|
||||
attr.Size = size;
|
||||
attr.Type = type;
|
||||
@@ -111,6 +118,12 @@ namespace MobileGL::MG_State::GLState {
|
||||
binding.Offset = offset;
|
||||
binding.Stride = effectiveStride;
|
||||
binding.Divisor = m_attributes[index].Divisor;
|
||||
|
||||
// Other attributes may already be pointed at this binding point through
|
||||
// glVertexAttribBinding; they see the new buffer/offset/stride too (basic-state3
|
||||
// checks exactly that after a glVertexAttribPointer). They are not adopted into the
|
||||
// binding model here - only the ones already in it re-resolve.
|
||||
ResolveAttributesForBinding(index, /*adopt: */ false);
|
||||
}
|
||||
|
||||
void VertexArrayObject::BindAttributeBuffer(Uint index, const SharedPtr<BufferObject>& buffer) {
|
||||
@@ -147,10 +160,24 @@ 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) {
|
||||
// GL 4.6 core 10.3.2 defines VertexAttribDivisor(i, d) as
|
||||
// VertexAttribBinding(i, i); VertexBindingDivisor(i, d)
|
||||
// - the binding is RE-POINTED at i, it is not merely written through when it already
|
||||
// happens to be i. Guarding the write on "binding == index" (which is what this did)
|
||||
// left an attribute that glVertexAttribBinding had moved elsewhere pointing at the old
|
||||
// binding, so the next resolve restored that binding's divisor and the new one was
|
||||
// lost (KHR-GL4x.vertex_attrib_binding.basic-state4).
|
||||
//
|
||||
// What is deliberately NOT copied from VertexAttribBinding is the adoption into the
|
||||
// binding model: an attribute configured the classic way keeps its pointer-resolved
|
||||
// stride/offset, exactly as before. The binding point mirrors that state already
|
||||
// (MirrorPointerIntoBinding), so nothing observable differs - and adopting it here
|
||||
// would silently swap the raw pointer stride for the effective one under every
|
||||
// application that calls glVertexAttribDivisor after glVertexAttribPointer.
|
||||
if (index < MAX_VERTEX_ATTRIB_BINDINGS) {
|
||||
m_attributeBindingIndex[index] = index;
|
||||
m_bindingPoints[index].Divisor = divisor;
|
||||
ResolveAttributesForBinding(index, /*adopt: */ false);
|
||||
}
|
||||
if (m_attributes[index].Divisor == divisor) return;
|
||||
m_attributes[index].Divisor = divisor;
|
||||
@@ -164,7 +191,6 @@ namespace MobileGL::MG_State::GLState {
|
||||
|
||||
void VertexArrayObject::ResolveAttributeFromBinding(Uint attribIndex) {
|
||||
if (attribIndex >= MAX_VERTEX_ATTRIBS) return;
|
||||
if (!m_attributeUsesBindingModel[attribIndex]) return;
|
||||
|
||||
const Uint bindingIndex = m_attributeBindingIndex[attribIndex];
|
||||
if (bindingIndex >= MAX_VERTEX_ATTRIB_BINDINGS) return;
|
||||
@@ -172,11 +198,24 @@ namespace MobileGL::MG_State::GLState {
|
||||
|
||||
auto& attr = m_attributes[attribIndex];
|
||||
|
||||
// VERTEX_ATTRIB_ARRAY_DIVISOR is not independent per-attribute state: it IS the divisor
|
||||
// of the binding point the attribute is attached to (GL 4.6 core 10.3.2), whichever API
|
||||
// configured the attribute. glVertexBindingDivisor therefore has to reach a classic
|
||||
// pointer-configured attribute as well - basic-state4 alternates the two spellings on
|
||||
// the same attribute and expects each to win in turn.
|
||||
if (attr.Divisor != binding.Divisor) {
|
||||
attr.Divisor = binding.Divisor;
|
||||
BumpAttributeFormatVersion(attribIndex);
|
||||
}
|
||||
|
||||
// Everything else stays owned by whichever API configured the attribute: a classic
|
||||
// glVertexAttrib*Pointer attribute keeps its pointer-resolved stride and offset.
|
||||
if (!m_attributeUsesBindingModel[attribIndex]) return;
|
||||
|
||||
const SizeT resolvedOffset = binding.Offset + m_attributeRelativeOffset[attribIndex];
|
||||
if (attr.Stride != binding.Stride || attr.Offset != resolvedOffset || attr.Divisor != binding.Divisor) {
|
||||
if (attr.Stride != binding.Stride || attr.Offset != resolvedOffset) {
|
||||
attr.Stride = binding.Stride;
|
||||
attr.Offset = resolvedOffset;
|
||||
attr.Divisor = binding.Divisor;
|
||||
BumpAttributeFormatVersion(attribIndex);
|
||||
}
|
||||
|
||||
@@ -186,6 +225,14 @@ namespace MobileGL::MG_State::GLState {
|
||||
}
|
||||
}
|
||||
|
||||
void VertexArrayObject::ResolveAttributesForBinding(Uint bindingIndex, Bool adopt) {
|
||||
for (Uint attribIndex = 0; attribIndex < MAX_VERTEX_ATTRIBS; ++attribIndex) {
|
||||
if (m_attributeBindingIndex[attribIndex] != bindingIndex) continue;
|
||||
if (adopt) m_attributeUsesBindingModel[attribIndex] = true;
|
||||
ResolveAttributeFromBinding(attribIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void VertexArrayObject::SetBindingBuffer(Uint bindingIndex, const SharedPtr<BufferObject>& buffer, SizeT offset,
|
||||
int stride) {
|
||||
if (bindingIndex >= MAX_VERTEX_ATTRIB_BINDINGS) return;
|
||||
@@ -195,15 +242,10 @@ namespace MobileGL::MG_State::GLState {
|
||||
binding.Offset = offset;
|
||||
binding.Stride = stride;
|
||||
|
||||
for (Uint attribIndex = 0; attribIndex < MAX_VERTEX_ATTRIBS; ++attribIndex) {
|
||||
if (m_attributeBindingIndex[attribIndex] == bindingIndex) {
|
||||
// Binding a vertex buffer to a binding point adopts every attribute currently
|
||||
// mapped to that binding point into the binding model (the default mapping is
|
||||
// attribute i -> binding i, which matches the GL 4.3 rules for state mixing).
|
||||
m_attributeUsesBindingModel[attribIndex] = true;
|
||||
ResolveAttributeFromBinding(attribIndex);
|
||||
}
|
||||
}
|
||||
// Binding a vertex buffer to a binding point adopts every attribute currently mapped to
|
||||
// that binding point into the binding model (the default mapping is attribute i ->
|
||||
// binding i, which matches the GL 4.3 rules for state mixing).
|
||||
ResolveAttributesForBinding(bindingIndex, /*adopt: */ true);
|
||||
}
|
||||
|
||||
void VertexArrayObject::SetBindingDivisor(Uint bindingIndex, Uint divisor) {
|
||||
@@ -211,11 +253,7 @@ namespace MobileGL::MG_State::GLState {
|
||||
|
||||
m_bindingPoints[bindingIndex].Divisor = divisor;
|
||||
|
||||
for (Uint attribIndex = 0; attribIndex < MAX_VERTEX_ATTRIBS; ++attribIndex) {
|
||||
if (m_attributeBindingIndex[attribIndex] == bindingIndex && m_attributeUsesBindingModel[attribIndex]) {
|
||||
ResolveAttributeFromBinding(attribIndex);
|
||||
}
|
||||
}
|
||||
ResolveAttributesForBinding(bindingIndex, /*adopt: */ false);
|
||||
}
|
||||
|
||||
void VertexArrayObject::SetAttributeBinding(Uint attribIndex, Uint bindingIndex) {
|
||||
|
||||
@@ -32,6 +32,16 @@ namespace MobileGL {
|
||||
Bool IsBgra = false;
|
||||
Uint Divisor = 0;
|
||||
SharedPtr<BufferObject> Buffer;
|
||||
|
||||
// GL 4.6 core table 23.3: VERTEX_ATTRIB_ARRAY_STRIDE and _POINTER are the
|
||||
// arguments of the last glVertexAttrib*Pointer call on this attribute,
|
||||
// reported verbatim, and NOTHING else writes them - not glVertexAttribFormat,
|
||||
// not glBindVertexBuffer. Stride/Offset above are the *resolved* draw inputs
|
||||
// and the binding model does overwrite those, so the two views have to be
|
||||
// stored apart or the binding-model sequence reports a legacy state it never
|
||||
// set (KHR-GL4x.vertex_attrib_binding.basic-state3).
|
||||
int LegacyStride = 0;
|
||||
SizeT LegacyPointer = 0;
|
||||
};
|
||||
|
||||
// ARB_vertex_attrib_binding separate binding point. Attributes configured through the
|
||||
@@ -40,7 +50,8 @@ namespace MobileGL {
|
||||
struct VertexBufferBindingPoint {
|
||||
SharedPtr<BufferObject> Buffer;
|
||||
SizeT Offset = 0;
|
||||
int Stride = 0;
|
||||
// GL 4.6 core table 23.4: the initial VERTEX_BINDING_STRIDE is 16, not 0.
|
||||
int Stride = 16;
|
||||
Uint Divisor = 0;
|
||||
};
|
||||
|
||||
@@ -185,6 +196,10 @@ namespace MobileGL {
|
||||
void BumpAttributeBufferVersion(Uint index);
|
||||
void BumpAttributeSwitchVersion(Uint index);
|
||||
void ResolveAttributeFromBinding(Uint attribIndex);
|
||||
// Re-resolve every attribute currently pointed at `bindingIndex`. `adopt` turns
|
||||
// the ones that are not in the binding model yet into binding-model attributes
|
||||
// first (what glBindVertexBuffer does, GL 4.3 rules for state mixing).
|
||||
void ResolveAttributesForBinding(Uint bindingIndex, Bool adopt);
|
||||
|
||||
// The default mapping is attribute i -> binding point i. Keep it an iota over
|
||||
// MAX_VERTEX_ATTRIBS rather than a literal list: a literal list silently leaves the
|
||||
|
||||
Reference in New Issue
Block a user