mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-13 06:38:31 +09:00
[Feat] (MG_State/MG_Impl): ARB_vertex_attrib_binding + ARB_multi_bind state model
Add a separate binding-point model to VertexArrayObject with eager resolution into the flat per-attribute view backends already consume. Implements glBindVertexBuffer(s), glVertexAttrib(I)Format, glVertexAttribBinding, glVertexBindingDivisor and the DSA variants (glVertexArrayAttribBinding, glVertexArrayBindingDivisor, glVertexArrayVertexBuffers), fixing glVertexArrayVertexBuffer which previously conflated binding index with attribute index. Multi-bind (glBindBuffersBase/Range) loops over the single-bind entry points. Needed by Flywheel's indirect backend (GlVertexArrayDSA setup path). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -51,6 +51,9 @@ namespace MobileGL::MG_State::GLState {
|
||||
SizeT offset, Bool isInteger) {
|
||||
if (index >= MAX_VERTEX_ATTRIBS) return;
|
||||
|
||||
// The classic pointer-style API takes back full ownership of the resolved fields.
|
||||
m_attributeUsesBindingModel[index] = false;
|
||||
|
||||
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) {
|
||||
@@ -116,6 +119,91 @@ namespace MobileGL::MG_State::GLState {
|
||||
return m_attributes[index].Divisor;
|
||||
}
|
||||
|
||||
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;
|
||||
const auto& binding = m_bindingPoints[bindingIndex];
|
||||
|
||||
auto& attr = m_attributes[attribIndex];
|
||||
|
||||
const SizeT resolvedOffset = binding.Offset + m_attributeRelativeOffset[attribIndex];
|
||||
if (attr.Stride != binding.Stride || attr.Offset != resolvedOffset || attr.Divisor != binding.Divisor) {
|
||||
attr.Stride = binding.Stride;
|
||||
attr.Offset = resolvedOffset;
|
||||
attr.Divisor = binding.Divisor;
|
||||
BumpAttributeFormatVersion(attribIndex);
|
||||
}
|
||||
|
||||
if (attr.Buffer != binding.Buffer) {
|
||||
attr.Buffer = binding.Buffer;
|
||||
BumpAttributeBufferVersion(attribIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void VertexArrayObject::SetBindingBuffer(Uint bindingIndex, const SharedPtr<BufferObject>& buffer, SizeT offset,
|
||||
int stride) {
|
||||
if (bindingIndex >= MAX_VERTEX_ATTRIB_BINDINGS) return;
|
||||
|
||||
auto& binding = m_bindingPoints[bindingIndex];
|
||||
binding.Buffer = buffer;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VertexArrayObject::SetBindingDivisor(Uint bindingIndex, Uint divisor) {
|
||||
if (bindingIndex >= MAX_VERTEX_ATTRIB_BINDINGS) return;
|
||||
|
||||
m_bindingPoints[bindingIndex].Divisor = divisor;
|
||||
|
||||
for (Uint attribIndex = 0; attribIndex < MAX_VERTEX_ATTRIBS; ++attribIndex) {
|
||||
if (m_attributeBindingIndex[attribIndex] == bindingIndex && m_attributeUsesBindingModel[attribIndex]) {
|
||||
ResolveAttributeFromBinding(attribIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VertexArrayObject::SetAttributeBinding(Uint attribIndex, Uint bindingIndex) {
|
||||
if (attribIndex >= MAX_VERTEX_ATTRIBS) return;
|
||||
if (bindingIndex >= MAX_VERTEX_ATTRIB_BINDINGS) return;
|
||||
|
||||
m_attributeBindingIndex[attribIndex] = bindingIndex;
|
||||
m_attributeUsesBindingModel[attribIndex] = true;
|
||||
ResolveAttributeFromBinding(attribIndex);
|
||||
}
|
||||
|
||||
void VertexArrayObject::SetAttributeFormatSeparate(Uint attribIndex, int size, DataType type, Bool normalized,
|
||||
Bool isInteger, Uint relativeOffset) {
|
||||
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 ||
|
||||
m_attributeRelativeOffset[attribIndex] != relativeOffset) {
|
||||
attr.Size = size;
|
||||
attr.Type = type;
|
||||
attr.Normalized = normalized;
|
||||
attr.IsInteger = isInteger;
|
||||
m_attributeRelativeOffset[attribIndex] = relativeOffset;
|
||||
BumpAttributeFormatVersion(attribIndex);
|
||||
}
|
||||
|
||||
m_attributeUsesBindingModel[attribIndex] = true;
|
||||
ResolveAttributeFromBinding(attribIndex);
|
||||
}
|
||||
|
||||
void VertexArrayObject::BumpAttributeFormatVersion(Uint index) {
|
||||
if (index >= MAX_VERTEX_ATTRIBS) return;
|
||||
++m_attributeVersions[index].FormatVersion;
|
||||
|
||||
@@ -26,6 +26,16 @@ namespace MobileGL {
|
||||
SharedPtr<BufferObject> Buffer;
|
||||
};
|
||||
|
||||
// ARB_vertex_attrib_binding separate binding point. Attributes configured through the
|
||||
// binding-point API are resolved eagerly into the flat VertexAttribute view above, so
|
||||
// backends keep consuming resolved attributes and never see binding points.
|
||||
struct VertexBufferBindingPoint {
|
||||
SharedPtr<BufferObject> Buffer;
|
||||
SizeT Offset = 0;
|
||||
int Stride = 0;
|
||||
Uint Divisor = 0;
|
||||
};
|
||||
|
||||
struct VertexAttributeVersion {
|
||||
Uint16 FormatVersion = 0;
|
||||
Uint16 BufferVersion = 0;
|
||||
@@ -35,6 +45,7 @@ namespace MobileGL {
|
||||
class VertexArrayObject {
|
||||
public:
|
||||
static constexpr int MAX_VERTEX_ATTRIBS = 16;
|
||||
static constexpr int MAX_VERTEX_ATTRIB_BINDINGS = 16;
|
||||
|
||||
VertexArrayObject(Uint externIndex);
|
||||
|
||||
@@ -58,6 +69,15 @@ namespace MobileGL {
|
||||
void SetAttributeDivisor(Uint index, Uint divisor);
|
||||
Uint GetAttributeDivisor(Uint index) const;
|
||||
|
||||
// ARB_vertex_attrib_binding style state. Each mutation re-resolves the affected
|
||||
// attributes into the flat VertexAttribute view.
|
||||
void SetBindingBuffer(Uint bindingIndex, const SharedPtr<BufferObject>& buffer, SizeT offset,
|
||||
int stride);
|
||||
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);
|
||||
|
||||
const VertexAttributeVersion& GetAttributeVersion(Uint index) const;
|
||||
const Array<VertexAttributeVersion, MAX_VERTEX_ATTRIBS>& GetAllAttributeVersions() const;
|
||||
|
||||
@@ -65,11 +85,21 @@ namespace MobileGL {
|
||||
void BumpAttributeFormatVersion(Uint index);
|
||||
void BumpAttributeBufferVersion(Uint index);
|
||||
void BumpAttributeSwitchVersion(Uint index);
|
||||
void ResolveAttributeFromBinding(Uint attribIndex);
|
||||
|
||||
const Uint m_externalIndex = 0;
|
||||
Array<VertexAttribute, MAX_VERTEX_ATTRIBS> m_attributes;
|
||||
Array<VertexAttributeVersion, MAX_VERTEX_ATTRIBS> m_attributeVersions;
|
||||
BindingSlot<BufferObject> m_indexBufferBindingSlot;
|
||||
|
||||
Array<VertexBufferBindingPoint, MAX_VERTEX_ATTRIB_BINDINGS> m_bindingPoints;
|
||||
Array<Uint, MAX_VERTEX_ATTRIBS> m_attributeBindingIndex = {0, 1, 2, 3, 4, 5, 6, 7,
|
||||
8, 9, 10, 11, 12, 13, 14, 15};
|
||||
Array<Uint, MAX_VERTEX_ATTRIBS> m_attributeRelativeOffset = {};
|
||||
// Set once an attribute (or its binding point) is touched through the
|
||||
// ARB_vertex_attrib_binding API; only such attributes are re-resolved, so the
|
||||
// classic glVertexAttribPointer path keeps its exact historical behavior.
|
||||
Array<Bool, MAX_VERTEX_ATTRIBS> m_attributeUsesBindingModel = {};
|
||||
};
|
||||
} // namespace GLState
|
||||
} // namespace MG_State
|
||||
|
||||
Reference in New Issue
Block a user