[Fix, Test] (MG_Impl): multi-bind name rejection is per element, and only transform feedback constrains the range size to a multiple of four

This commit is contained in:
2026-08-11 09:17:51 -04:00
parent 6f64ec0f51
commit 16c010985f
3 changed files with 50 additions and 47 deletions
+34 -27
View File
@@ -1527,19 +1527,27 @@ namespace MobileGL::MG_Impl::GLImpl {
return false;
}
}
// A transform feedback capture binding is addressed in 32-bit components, so BOTH the
// offset and the size must be multiples of 4. An atomic counter binding is addressed in
// 32-bit counters and constrains its offset the same way (GL 4.6 core 6.1.1) - that one
// has no queryable alignment pname, which is why it was missing here.
const Bool isFourByteAddressed =
target == GL_TRANSFORM_FEEDBACK_BUFFER || target == GL_ATOMIC_COUNTER_BUFFER;
if (isFourByteAddressed && ((offset % 4) != 0 || (hasBuffer && (size % 4) != 0))) {
// GL 4.6 core 6.1.1 constrains the OFFSET to a multiple of four for both
// TRANSFORM_FEEDBACK_BUFFER and ATOMIC_COUNTER_BUFFER (the atomic-counter one has no
// queryable alignment pname, which is why it was missing here), and the SIZE only for
// transform feedback, whose capture is written in whole 32-bit components. Extending the
// size rule to atomic counters as well breaks a legal bind: the conformance suite splits
// MAX_ATOMIC_COUNTER_BUFFER_SIZE evenly across the binding points and that quotient is
// not required to land on four.
if ((target == GL_TRANSFORM_FEEDBACK_BUFFER || target == GL_ATOMIC_COUNTER_BUFFER) && (offset % 4) != 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", funcName,
std::format("offset ({}) must be a multiple of 4 for {}.", offset,
MG_Util::ConvertGLEnumToString(target))));
return false;
}
if (target == GL_TRANSFORM_FEEDBACK_BUFFER && hasBuffer && (size % 4) != 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>(
"MG_Impl/GLImpl", funcName,
std::format("offset ({}) and size ({}) must both be multiples of 4 for {}.", offset, size,
MG_Util::ConvertGLEnumToString(target))));
std::format("size ({}) must be a multiple of 4 for GL_TRANSFORM_FEEDBACK_BUFFER.", size)));
return false;
}
return true;
@@ -1742,29 +1750,28 @@ namespace MobileGL::MG_Impl::GLImpl {
// ARB_multi_bind states the equivalence to a loop of single binds "except that ... buffers
// will not be created if they do not exist": glBindBuffer instantiates a name glGenBuffers
// merely reserved, glBindBuffers* must refuse it instead. That is INVALID_OPERATION, and it
// is an all-or-nothing check - one bad name leaves every binding point in the range alone
// merely reserved, glBindBuffers* must refuse it and raise INVALID_OPERATION instead
// (KHR-GL44.multi_bind.errors_bind_buffers).
static Bool ValidateMultiBindBufferNames(const GLuint* buffers, GLsizei count, const char* funcName) {
if (buffers == nullptr) return true;
for (GLsizei i = 0; i < count; ++i) {
if (buffers[i] == 0) continue;
if (MG_State::pGLContext->ValidateBufferObject(buffers[i])) continue;
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>(
"MG_Impl/GLImpl", funcName,
std::format("buffers[{}] ({}) is not the name of an existing buffer object.", i, buffers[i])));
return false;
}
return true;
//
// Deliberately PER ELEMENT, not all-or-nothing: the equivalence the extension defines is a
// loop, so a bad entry costs its own binding point and nothing else. Rejecting the whole
// call instead cost multi_bind.functional_bind_buffers_base its bindings.
static Bool IsExistingBufferForMultiBind(GLuint buffer, GLsizei index, const char* funcName) {
if (buffer == 0 || MG_State::pGLContext->ValidateBufferObject(buffer)) return true;
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>(
"MG_Impl/GLImpl", funcName,
std::format("buffers[{}] ({}) is not the name of an existing buffer object.", index, buffer)));
return false;
}
void BindBuffersBase(GLenum target, GLuint first, GLsizei count, const GLuint* buffers) {
if (!ValidateMultiBindBufferRange(target, first, count, __func__)) return;
if (!ValidateMultiBindBufferNames(buffers, count, __func__)) return;
for (GLsizei i = 0; i < count; ++i) {
BindBufferBase_State(target, first + i, buffers ? buffers[i] : 0);
const GLuint buffer = buffers ? buffers[i] : 0;
if (!IsExistingBufferForMultiBind(buffer, i, __func__)) continue;
BindBufferBase_State(target, first + i, buffer);
}
}
@@ -1777,8 +1784,8 @@ namespace MobileGL::MG_Impl::GLImpl {
void BindBuffersRange(GLenum target, GLuint first, GLsizei count, const GLuint* buffers, const GLintptr* offsets,
const GLsizeiptr* sizes) {
if (!ValidateMultiBindBufferRange(target, first, count, __func__)) return;
if (!ValidateMultiBindBufferNames(buffers, count, __func__)) return;
for (GLsizei i = 0; i < count; ++i) {
if (buffers && !IsExistingBufferForMultiBind(buffers[i], i, __func__)) continue;
if (!buffers || buffers[i] == 0) {
BindBufferBase_State(target, first + i, 0);
} else {