diff --git a/MobileGL/MG_Impl/GLImpl/Framebuffer/Validators.cpp b/MobileGL/MG_Impl/GLImpl/Framebuffer/Validators.cpp index 0ae604b6..fbfa6bab 100644 --- a/MobileGL/MG_Impl/GLImpl/Framebuffer/Validators.cpp +++ b/MobileGL/MG_Impl/GLImpl/Framebuffer/Validators.cpp @@ -76,7 +76,13 @@ namespace MobileGL::MG_Impl::GLImpl::FramebufferImpl { } Bool ValidateRenderbufferName(Uint index, Bool allowZero) { - if (index == 0 && !allowZero) { + if (index == 0) { + // Zero is never a GenRenderbuffers name, so it must not reach the name-table lookup + // below: where it is allowed (glBindRenderbuffer / FramebufferRenderbuffer detach) it + // means "unbind", and looking it up would record a bogus INVALID_OPERATION - GL CTS's + // per-case state reset calls glBindRenderbuffer(GL_RENDERBUFFER, 0) after every case. + if (allowZero) return true; + MG_State::pGLContext->RecordError( ErrorCode::InvalidValue, MakeUnique("MG_Impl/GLImpl/FramebufferImpl", "ValidateRenderbufferName", diff --git a/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp b/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp index 6ebe9a6a..5dbdb5df 100644 --- a/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp +++ b/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp @@ -1894,7 +1894,15 @@ namespace MobileGL::MG_Impl::GLImpl { *params = dynamicParameters.MaxTextureSize; break; case GL_MAX_UNIFORM_BUFFER_BINDINGS: - *params = std::max(dynamicParameters.MaxUniformBufferBindings, kFrontendMinUniformBufferBindings); + // Never advertise more indexed binding points than the state layer's fixed per-target + // array can store (BufferBindingPointCount): glBindBufferBase rejects indices past + // that capacity, and GL CTS's per-case state reset walks every advertised binding + // (gluStateReset), so an over-advertised value aborts whole test batches. The floor + // equals the GL 3.3 core minimum (36), so the clamp never under-advertises. + *params = static_cast(std::min( + static_cast(std::max(dynamicParameters.MaxUniformBufferBindings, + kFrontendMinUniformBufferBindings)), + MG_State::GLState::BufferBindingPointCount)); break; case GL_MAX_UNIFORM_BLOCK_SIZE: *params = dynamicParameters.MaxUniformBlockSize; diff --git a/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.cpp b/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.cpp index 221cb974..18a723a6 100644 --- a/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.cpp +++ b/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.cpp @@ -78,15 +78,12 @@ namespace MobileGL::MG_Impl::GLImpl { } static bool ValidateCurrentVertexAttribIndex(GLuint index, const char* funcName) { - if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return false; - if (index == 0) { - MG_State::pGLContext->RecordError( - ErrorCode::InvalidOperation, - MakeUnique("MG_Impl/GLImpl", funcName, - "Generic vertex attribute 0 current value cannot be modified.")); - return false; - } - return true; + // GL 3.3 core 2.7: VertexAttrib* sets the current value of ANY generic attribute, + // including index 0 - only an out-of-range index is an error (INVALID_VALUE). + // "Attribute 0 is immutable" was legacy immediate-mode lore; rejecting it broke GL + // CTS's per-case state reset, which writes vertexAttrib4f(0, 0,0,0,1) after every case. + (void)funcName; + return VertexArrayImpl::ValidateVertexAttributeIndex(index); } static bool TryGetVertexAttribute(GLuint index, const MG_State::GLState::VertexAttribute** outAttr) { diff --git a/MobileGL/MG_Test/VertexArray/VertexArrayTest.cpp b/MobileGL/MG_Test/VertexArray/VertexArrayTest.cpp index 45fd94ba..736a3f6a 100644 --- a/MobileGL/MG_Test/VertexArray/VertexArrayTest.cpp +++ b/MobileGL/MG_Test/VertexArray/VertexArrayTest.cpp @@ -1173,9 +1173,17 @@ TEST_F(GeneralVertexArrayTest, CurrentAttrib_PackedValidation) { GetVertexAttribfv(1, GL_CURRENT_VERTEX_ATTRIB, out); EXPECT_FLOAT_EQ(out[0], 42.0f); // unchanged by the failed call - // Attribute 0 is rejected by MobileGL policy (GL_INVALID_OPERATION). + // GL 3.3 core 2.7: attribute 0's current value is writable like any other generic + // attribute - no error. (An earlier MobileGL policy rejected index 0 with + // GL_INVALID_OPERATION, which broke GL CTS's per-case state reset: gluStateReset writes + // vertexAttrib4f(0, 0,0,0,1) for every attribute after every case.) VertexAttribP4ui(0, GL_UNSIGNED_INT_2_10_10_10_REV, GL_FALSE, 1u); - EXPECT_EQ(GetError(), GL_INVALID_OPERATION); + EXPECT_EQ(GetError(), GL_NO_ERROR); + GetVertexAttribfv(0, GL_CURRENT_VERTEX_ATTRIB, out); + EXPECT_EQ(GetError(), GL_NO_ERROR); + EXPECT_FLOAT_EQ(out[0], 1.0f); // x field of the packed word + VertexAttrib4f(0, 0.0f, 0.0f, 0.0f, 1.0f); // restore the initial current value + EXPECT_EQ(GetError(), GL_NO_ERROR); // Out-of-range index -> GL_INVALID_VALUE. VertexAttribP4ui(VertexArrayImpl::GetMaxVertexAttribs(), GL_UNSIGNED_INT_2_10_10_10_REV, GL_FALSE, 1u);