diff --git a/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp b/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp index 4e111de5..cc66abbb 100644 --- a/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp +++ b/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp @@ -996,7 +996,7 @@ DECLARE_GL_FUNCTION_HEAD(void, MultiDrawElementsIndirect, GLenum mode, GLenum ty DECLARE_GL_FUNCTION_HEAD(GLint, GetProgramResourceLocationIndex, GLuint program, GLenum programInterface, const GLchar* name) DECLARE_GL_FUNCTION_END(GLint, GetProgramResourceLocationIndex, program, programInterface, name) DECLARE_GL_FUNCTION_HEAD(void, ShaderStorageBlockBinding, GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ShaderStorageBlockBinding, program, storageBlockIndex, storageBlockBinding) DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureView, GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureView, texture, target, origtexture, internalformat, minlevel, numlevels, minlayer, numlayers) -DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttribLFormat, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttribLFormat, attribindex, size, type, relativeoffset) +DECLARE_GL_FUNCTION_HEAD(void, VertexAttribLFormat, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttribLFormat, attribindex, size, type, relativeoffset) DECLARE_GL_FUNCTION_HEAD(void, BufferStorage, GLenum target, GLsizeiptr size, const void* data, GLbitfield flags) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BufferStorage, target, size, data, flags) DECLARE_GL_FUNCTION_HEAD(void, ClearTexImage, GLuint texture, GLint level, GLenum format, GLenum type, const void* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearTexImage, texture, level, format, type, data) DECLARE_GL_FUNCTION_HEAD(void, ClearTexSubImage, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearTexSubImage, texture, level, xoffset, yoffset, zoffset, width, height, depth, format, type, data) @@ -1090,7 +1090,7 @@ DECLARE_GL_FUNCTION_HEAD(void, VertexArrayVertexBuffers, GLuint vaobj, GLuint fi DECLARE_GL_FUNCTION_HEAD(void, VertexArrayAttribBinding, GLuint vaobj, GLuint attribindex, GLuint bindingindex) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexArrayAttribBinding, vaobj, attribindex, bindingindex) DECLARE_GL_FUNCTION_HEAD(void, VertexArrayAttribFormat, GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexArrayAttribFormat, vaobj, attribindex, size, type, normalized, relativeoffset) 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, VertexArrayAttribLFormat, GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) DECLARE_GL_FUNCTION_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_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) diff --git a/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp b/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp index c88f89e0..64cabd04 100644 --- a/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp +++ b/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp @@ -671,6 +671,40 @@ namespace MobileGL::MG_Impl::GLImpl { } switch (target) { + // The vertex buffer binding points of the vertex array object that is bound. Indexed by + // binding point, not by attribute (GL 4.6 core 10.3.1). + case GL_VERTEX_BINDING_BUFFER: + case GL_VERTEX_BINDING_DIVISOR: + case GL_VERTEX_BINDING_OFFSET: + case GL_VERTEX_BINDING_STRIDE: { + if (index >= VertexArrayImpl::GetMaxVertexAttribBindings()) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, + "Vertex buffer binding index is out of range.")); + return; + } + const auto& vao = MG_State::pGLContext->GetBoundVertexArray(); + if (!vao) { + *data = 0; + return; + } + const auto& binding = vao->GetBindingPoint(index); + switch (target) { + case GL_VERTEX_BINDING_BUFFER: + *data = binding.Buffer ? static_cast(binding.Buffer->GetExternalIndex()) : 0; + return; + case GL_VERTEX_BINDING_DIVISOR: + *data = static_cast(binding.Divisor); + return; + case GL_VERTEX_BINDING_OFFSET: + *data = static_cast(binding.Offset); + return; + default: + *data = static_cast(binding.Stride); + return; + } + } case GL_IMAGE_BINDING_NAME: case GL_IMAGE_BINDING_LEVEL: case GL_IMAGE_BINDING_LAYERED: @@ -1738,20 +1772,22 @@ namespace MobileGL::MG_Impl::GLImpl { *params = vao ? static_cast(vao->GetExternalIndex()) : 0; return; } + // The vertex buffer binding points are per-binding-index state, so the non-indexed getter + // has nothing to answer with (GL 4.6 core table 23.4). + case GL_VERTEX_BINDING_BUFFER: case GL_VERTEX_BINDING_DIVISOR: - *params = 0; // vertex-binding entrypoints are stubbed - return; case GL_VERTEX_BINDING_OFFSET: - *params = 0; // vertex-binding entrypoints are stubbed - return; case GL_VERTEX_BINDING_STRIDE: - *params = 0; // vertex-binding entrypoints are stubbed + RecordIndexedOnlyGetterError(__func__, pname); return; case GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET: - *params = 0; // vertex-binding entrypoints are stubbed + *params = static_cast(VertexArrayImpl::GetMaxVertexAttribRelativeOffset()); return; case GL_MAX_VERTEX_ATTRIB_BINDINGS: - *params = 0; // vertex-binding entrypoints are stubbed + *params = static_cast(VertexArrayImpl::GetMaxVertexAttribBindings()); + return; + case GL_MAX_VERTEX_ATTRIB_STRIDE: + *params = static_cast(VertexArrayImpl::GetMaxVertexAttribStride()); return; case GL_VIEWPORT: { const auto& vp = MG_State::pGLContext->GetViewport(); diff --git a/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.cpp b/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.cpp index 70035562..5778cc0a 100644 --- a/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.cpp +++ b/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.cpp @@ -105,12 +105,24 @@ namespace MobileGL::MG_Impl::GLImpl { return pname == GL_CURRENT_VERTEX_ATTRIB; } + // The stride a pointer-style call gives its binding point: the argument when it is non-zero, + // otherwise the tightly packed element size (GL 4.6 core 10.3.2). A packed 2_10_10_10 or + // 10F_11F_11F attribute is one 32-bit word regardless of its component count. + static int EffectiveVertexStride(GLsizei stride, GLint size, GLenum type) { + if (stride != 0) return static_cast(stride); + switch (type) { + case GL_INT_2_10_10_10_REV: + case GL_UNSIGNED_INT_2_10_10_10_REV: + case GL_UNSIGNED_INT_10F_11F_11F_REV: + return 4; + default: + break; + } + return static_cast(size * MG_Util::GetGLTypeSize(type)); + } + static bool ValidateVertexBindingIndex(GLuint bindingindex, const char* funcName) { - // Bound by the same dynamic limit as attribute indices: the default attribute -> binding - // mapping is the identity, so a binding point the backend cannot address as an attribute - // would resolve into an attribute the backend must then reject on every draw. Real drivers - // likewise report MAX_VERTEX_ATTRIB_BINDINGS == MAX_VERTEX_ATTRIBS. - if (bindingindex >= VertexArrayImpl::GetMaxVertexAttribs()) { + if (bindingindex >= VertexArrayImpl::GetMaxVertexAttribBindings()) { MG_State::pGLContext->RecordError( ErrorCode::InvalidValue, MakeUnique("MG_Impl/GLImpl", funcName, @@ -155,6 +167,17 @@ namespace MobileGL::MG_Impl::GLImpl { SharedPtr GetNamedVertexArrayObject_State(GLuint vaobj, const char* caller) { + // Name zero is not a vertex array object in a core profile: it names the default vertex + // array, which the by-name (direct state access) entry points never accept. MobileGL keeps a + // real object at index 0 for the compatibility paths, so the generic name validation below + // would otherwise let it through (GL 4.6 core 10.3.1). + if (vaobj == 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", caller, + "Vertex array name 0 is not a vertex array object.")); + return nullptr; + } if (!VertexArrayImpl::ValidateVertexArrayName(vaobj)) return nullptr; if (!VertexArrayImpl::ValidateVertexArrayObject(vaobj)) return nullptr; return MG_State::pGLContext->GetVertexArrayObject(vaobj); @@ -210,7 +233,7 @@ namespace MobileGL::MG_Impl::GLImpl { DataType dataType = MG_Util::ConvertGLEnumToDataType(type); // Integer path: never normalized, never BGRA/packed (the validator rejects those). - if (!VertexArrayImpl::ValidateVertexAttribFormat(index, size, dataType, false, stride, true)) return; + if (!VertexArrayImpl::ValidateVertexAttribFormat(index, size, type, dataType, false, stride, true)) return; auto& vao = MG_State::pGLContext->GetBoundVertexArray(); if (!vao) { @@ -227,6 +250,7 @@ namespace MobileGL::MG_Impl::GLImpl { vao->SetAttributeFormat(index, size, dataType, false, stride, offset, true, false); vao->BindAttributeBuffer(index, vbo); + vao->MirrorPointerIntoBinding(index, vbo, offset, EffectiveVertexStride(stride, size, type)); } void VertexAttribPointer_State(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, @@ -234,7 +258,7 @@ namespace MobileGL::MG_Impl::GLImpl { if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return; DataType dataType = MG_Util::ConvertGLEnumToDataType(type); - if (!VertexArrayImpl::ValidateVertexAttribFormat(index, size, dataType, normalized == GL_TRUE, stride, false)) + if (!VertexArrayImpl::ValidateVertexAttribFormat(index, size, type, dataType, normalized == GL_TRUE, stride, false)) return; auto& vao = MG_State::pGLContext->GetBoundVertexArray(); @@ -256,6 +280,7 @@ namespace MobileGL::MG_Impl::GLImpl { const int effectiveSize = isBgra ? 4 : size; vao->SetAttributeFormat(index, effectiveSize, dataType, normalized, stride, offset, false, isBgra); vao->BindAttributeBuffer(index, vbo); + vao->MirrorPointerIntoBinding(index, vbo, offset, EffectiveVertexStride(stride, effectiveSize, type)); } void BindVertexArray_State(GLuint array) { @@ -359,6 +384,13 @@ namespace MobileGL::MG_Impl::GLImpl { MakeUnique("MG_Impl/GLImpl", caller, "offset and stride must be non-negative.")); return; } + if (static_cast(stride) > VertexArrayImpl::GetMaxVertexAttribStride()) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", caller, + "stride exceeds GL_MAX_VERTEX_ATTRIB_STRIDE.")); + return; + } auto bufferObject = GetVertexArrayBufferObject_State(buffer, caller); if (buffer != 0 && !bufferObject) return; @@ -389,12 +421,36 @@ namespace MobileGL::MG_Impl::GLImpl { static void VertexAttribFormatSeparate_State(const SharedPtr& vao, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset, Bool isInteger, const char* caller) { + static_cast(caller); if (!VertexArrayImpl::ValidateVertexAttributeIndex(attribindex)) return; DataType dataType = MG_Util::ConvertGLEnumToDataType(type); - if (!VertexArrayImpl::ValidateVertexAttribPointerParams(attribindex, size, dataType, 0)) return; + // The separate-format entry points take the same size/type rules as the pointer ones, + // GL_BGRA included, so they need the full format validation rather than the pointer-only + // subset - that one reports GL_BGRA as an out-of-range size. + if (!VertexArrayImpl::ValidateVertexAttribFormat(attribindex, size, type, dataType, normalized == GL_TRUE, 0, + isInteger)) + return; + if (!VertexArrayImpl::ValidateVertexAttribRelativeOffset(relativeoffset)) return; - vao->SetAttributeFormatSeparate(attribindex, size, dataType, normalized, isInteger, relativeoffset); + const Bool isBgra = (size == static_cast(GL_BGRA)); + vao->SetAttributeFormatSeparate(attribindex, isBgra ? 4 : size, dataType, normalized, isInteger, + relativeoffset, isBgra); + } + + // The long (64-bit) attribute format. MobileGL has no 64-bit vertex attributes, so nothing is + // recorded; what the entry point owes the application is the parameter validation, which is + // observable through glGetError regardless of whether the format could be used in a draw. + static void VertexAttribLFormatSeparate_State(GLuint attribindex, GLint size, GLenum type, + GLuint relativeoffset) { + if (!VertexArrayImpl::ValidateVertexAttributeIndex(attribindex)) return; + if (!VertexArrayImpl::ValidateVertexAttribLFormat(attribindex, size, type)) return; + if (!VertexArrayImpl::ValidateVertexAttribRelativeOffset(relativeoffset)) return; + + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", "VertexAttribLFormat", + "64-bit vertex attributes are not supported.")); } void VertexArrayAttribFormat_State(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, @@ -1177,6 +1233,18 @@ namespace MobileGL::MG_Impl::GLImpl { "VertexAttribIFormat"); } + void VertexAttribLFormat(GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) { + auto vao = GetBoundVertexArrayOrError("VertexAttribLFormat"); + if (!vao) return; + VertexAttribLFormatSeparate_State(attribindex, size, type, relativeoffset); + } + + void VertexArrayAttribLFormat(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) { + auto vao = GetNamedVertexArrayObject_State(vaobj, "VertexArrayAttribLFormat"); + if (!vao) return; + VertexAttribLFormatSeparate_State(attribindex, size, type, relativeoffset); + } + void VertexAttribBinding(GLuint attribindex, GLuint bindingindex) { auto vao = GetBoundVertexArrayOrError("VertexAttribBinding"); if (!vao) return; diff --git a/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.h b/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.h index 55376749..3f8c5693 100644 --- a/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.h +++ b/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.h @@ -98,6 +98,7 @@ namespace MobileGL::MG_Impl::GLImpl { 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); + void VertexArrayAttribLFormat(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); void VertexArrayAttribBinding(GLuint vaobj, GLuint attribindex, GLuint bindingindex); void VertexArrayBindingDivisor(GLuint vaobj, GLuint bindingindex, GLuint divisor); void VertexArrayVertexBuffers(GLuint vaobj, GLuint first, GLsizei count, const GLuint* buffers, @@ -107,6 +108,7 @@ namespace MobileGL::MG_Impl::GLImpl { const GLsizei* strides); void VertexAttribFormat(GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); void VertexAttribIFormat(GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); + void VertexAttribLFormat(GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); void VertexAttribBinding(GLuint attribindex, GLuint bindingindex); void VertexBindingDivisor(GLuint bindingindex, GLuint divisor); void VertexAttribDivisor(GLuint index, GLuint divisor); diff --git a/MobileGL/MG_Impl/GLImpl/VertexArray/Validators.cpp b/MobileGL/MG_Impl/GLImpl/VertexArray/Validators.cpp index f06d4ffc..f4accd01 100644 --- a/MobileGL/MG_Impl/GLImpl/VertexArray/Validators.cpp +++ b/MobileGL/MG_Impl/GLImpl/VertexArray/Validators.cpp @@ -23,6 +23,18 @@ namespace MobileGL::MG_Impl::GLImpl::VertexArrayImpl { return std::min(static_cast(backendLimit), capacity); } + Uint GetMaxVertexAttribBindings() { + return GetMaxVertexAttribs(); + } + + Uint GetMaxVertexAttribRelativeOffset() { + return 2047; + } + + Uint GetMaxVertexAttribStride() { + return 2048; + } + Bool ValidateVertexArrayName(Uint index) { Bool isValid = MG_State::pGLContext->ValidateVertexArrayName(index); if (!isValid) { @@ -90,9 +102,31 @@ namespace MobileGL::MG_Impl::GLImpl::VertexArrayImpl { return true; } - Bool ValidateVertexAttribFormat(Uint index, GLint sizeRaw, DataType type, Bool normalized, Int stride, - Bool integerPath) { + Bool ValidateVertexAttribFormat(Uint index, GLint sizeRaw, GLenum glType, DataType type, Bool normalized, + Int stride, Bool integerPath) { constexpr const char* fn = "ValidateVertexAttribFormat"; + // GL_UNSIGNED_INT_10F_11F_11F_REV is a three-component float-path-only packing that has no + // DataType of its own, so it has to be recognised by name before the conversion below turns + // it into Unknown and reports the wrong error (GL 4.6 core 10.3.2). + if (glType == GL_UNSIGNED_INT_10F_11F_11F_REV) { + if (integerPath) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique( + "MG_Impl/GLImpl", fn, + std::format("GL_UNSIGNED_INT_10F_11F_11F_REV is not an integer-path type (attribute {}).", + index))); + return false; + } + if (sizeRaw != 3) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique( + "MG_Impl/GLImpl", fn, + std::format("GL_UNSIGNED_INT_10F_11F_11F_REV requires size 3 (attribute {}).", index))); + return false; + } + } if (type == DataType::Unknown) { MG_State::pGLContext->RecordError( ErrorCode::InvalidEnum, @@ -170,4 +204,40 @@ namespace MobileGL::MG_Impl::GLImpl::VertexArrayImpl { } return true; } + + Bool ValidateVertexAttribLFormat(Uint index, GLint size, GLenum type) { + constexpr const char* fn = "ValidateVertexAttribLFormat"; + if (size < 1 || size > 4) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique( + "MG_Impl/GLImpl", fn, + std::format("Invalid size {} for attribute {}. Must be 1-4.", size, index))); + return false; + } + // GL 4.6 core 10.3.2: the long form takes GL_DOUBLE and nothing else. + if (type != GL_DOUBLE) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique( + "MG_Impl/GLImpl", fn, + std::format("Type 0x{:X} is not GL_DOUBLE (attribute {}).", type, index))); + return false; + } + return true; + } + + Bool ValidateVertexAttribRelativeOffset(Uint relativeOffset) { + const Uint limit = GetMaxVertexAttribRelativeOffset(); + if (relativeOffset > limit) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique( + "MG_Impl/GLImpl", "ValidateVertexAttribRelativeOffset", + std::format("relativeoffset {} exceeds GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET ({}).", relativeOffset, + limit))); + return false; + } + return true; + } } // namespace MobileGL::MG_Impl::GLImpl::VertexArrayImpl diff --git a/MobileGL/MG_Impl/GLImpl/VertexArray/Validators.h b/MobileGL/MG_Impl/GLImpl/VertexArray/Validators.h index ca75201f..38f38cc6 100644 --- a/MobileGL/MG_Impl/GLImpl/VertexArray/Validators.h +++ b/MobileGL/MG_Impl/GLImpl/VertexArray/Validators.h @@ -15,6 +15,20 @@ namespace MobileGL::MG_Impl::GLImpl::VertexArrayImpl { // capacity). Falls back to the capacity when no backend is active (unit tests). Uint GetMaxVertexAttribs(); + // GL_MAX_VERTEX_ATTRIB_BINDINGS. The default attribute -> binding mapping is the identity, so a + // binding point that cannot also be an attribute index would resolve into an attribute the + // backend has to reject on every draw; real drivers report the two limits equal as well. + Uint GetMaxVertexAttribBindings(); + + // GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET. The relative offset is folded into the resolved + // attribute offset in the frontend and never reaches a backend limit, so this is the value the + // spec requires an implementation to support at minimum (GL 4.6 core table 23.63). + Uint GetMaxVertexAttribRelativeOffset(); + + // GL_MAX_VERTEX_ATTRIB_STRIDE. Like the relative offset above, the stride never reaches a + // backend limit of its own, so this is the spec minimum (GL 4.6 core table 23.63). + Uint GetMaxVertexAttribStride(); + Bool ValidateVertexArrayName(Uint index); Bool ValidateVertexArrayObject(Uint index); Bool ValidateVertexAttributeIndex(Uint index); @@ -22,6 +36,13 @@ namespace MobileGL::MG_Impl::GLImpl::VertexArrayImpl { // Full glVertexAttribPointer / glVertexAttribIPointer format validation, including the packed // 2_10_10_10 types and GL_BGRA size. sizeRaw is the untranslated GL size (possibly GL_BGRA); // integerPath selects the glVertexAttribIPointer rules. - Bool ValidateVertexAttribFormat(Uint index, GLint sizeRaw, DataType type, Bool normalized, Int stride, - Bool integerPath); + Bool ValidateVertexAttribFormat(Uint index, GLint sizeRaw, GLenum glType, DataType type, Bool normalized, + Int stride, Bool integerPath); + // glVertexAttribLFormat / glVertexArrayAttribLFormat: the only accepted type is GL_DOUBLE and + // the size range is 1-4 (GL_BGRA is a float-path size). Separate from the function above + // because the long path shares none of its type or size rules. + Bool ValidateVertexAttribLFormat(Uint index, GLint size, GLenum type); + // Shared by every *Format entry point: INVALID_VALUE once relativeoffset leaves the range the + // implementation advertises. + Bool ValidateVertexAttribRelativeOffset(Uint relativeOffset); } // namespace MobileGL::MG_Impl::GLImpl::VertexArrayImpl diff --git a/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.cpp b/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.cpp index 0a10dba4..e8a9c731 100644 --- a/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.cpp +++ b/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.cpp @@ -77,6 +77,26 @@ namespace MobileGL::MG_State::GLState { BumpAttributeFormatVersion(index); } + void VertexArrayObject::MirrorPointerIntoBinding(Uint index, const SharedPtr& 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& 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); } diff --git a/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.h b/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.h index 15c8c03f..8c3b6f78 100644 --- a/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.h +++ b/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.h @@ -64,6 +64,12 @@ namespace MobileGL { void BindAttributeBuffer(Uint index, const SharedPtr& 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& buffer, SizeT offset, + int effectiveStride); + BindingSlot& GetIndexBufferBindingSlot(); const BindingSlot& 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