[Fix] (MG_Impl): apply the buffer texture's own format and range rules

glTextureBuffer and glTextureBufferRange took any internal format the texture enum
converter recognised. A buffer texture accepts a much shorter list than a sampled or a
renderable texture does (GL 4.6 core table 8.16), and it cannot be inferred from either,
so a format like GL_RGB8 was accepted and produced a texture nothing could read.

Two error codes were wrong as well. A texture whose effective target is not
GL_TEXTURE_BUFFER is the wrong object rather than the wrong token, so it is
INVALID_OPERATION. And the range form never checked its range against the buffer it was
attaching, so a size past the end of the buffer was accepted and left the texture
addressing memory the buffer does not own.

Fixes direct_state_access.textures_buffer_errors and textures_buffer_range_errors on both
backends.
This commit is contained in:
BZLZHH
2026-08-05 02:29:25 +00:00
parent 534ec65dda
commit ebe4fe133f
+64 -1
View File
@@ -2039,11 +2039,63 @@ namespace MobileGL::MG_Impl::GLImpl {
// resolved - by binding for the target forms, by name for the DSA ones. `size` is
// kWholeBuffer for the non-Range entry points, which attach the buffer as it grows rather
// than freezing the size it happens to have now.
// The sized internal formats a buffer texture accepts (GL 4.6 core table 8.16). This is a much
// shorter list than the renderable or texturable formats, so it cannot be inferred from either.
static Bool IsBufferTextureInternalFormat(GLenum internalformat) {
switch (internalformat) {
case GL_R8:
case GL_R16:
case GL_R16F:
case GL_R32F:
case GL_R8I:
case GL_R16I:
case GL_R32I:
case GL_R8UI:
case GL_R16UI:
case GL_R32UI:
case GL_RG8:
case GL_RG16:
case GL_RG16F:
case GL_RG32F:
case GL_RG8I:
case GL_RG16I:
case GL_RG32I:
case GL_RG8UI:
case GL_RG16UI:
case GL_RG32UI:
case GL_RGB32F:
case GL_RGB32I:
case GL_RGB32UI:
case GL_RGBA8:
case GL_RGBA16:
case GL_RGBA16F:
case GL_RGBA32F:
case GL_RGBA8I:
case GL_RGBA16I:
case GL_RGBA32I:
case GL_RGBA8UI:
case GL_RGBA16UI:
case GL_RGBA32UI:
return true;
default:
return false;
}
}
static void AttachBufferToTexture(const SharedPtr<MG_State::GLState::ITextureObject>& textureObject,
GLenum internalformat, GLuint buffer, GLintptr offset, SizeT size,
const char* caller) {
using MG_State::GLState::TextureObjectBuffer;
TextureInternalFormat textureInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat);
if (!IsBufferTextureInternalFormat(internalformat)) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>(
"MG_Impl/GLImpl", caller,
std::format("internalformat 0x{:X} is not one of the sized formats a buffer texture accepts.",
internalformat)));
return;
}
if (!TextureImpl::ValidateTextureInternalFormat(textureInternalFormat)) return;
auto& bufferObject = MG_State::pGLContext->GetBufferObject(buffer);
@@ -2056,8 +2108,10 @@ namespace MobileGL::MG_Impl::GLImpl {
}
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
if (textureObject->GetStorageType() != TextureStorageType::Buffer) {
// A texture whose target is something else is a wrong object, not a wrong token
// (GL 4.6 core 8.9).
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller,
"The effective target of `texture` is not `GL_TEXTURE_BUFFER`."));
return;
@@ -2086,6 +2140,15 @@ namespace MobileGL::MG_Impl::GLImpl {
"offset is not a multiple of GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT."));
return;
}
// The range has to lie inside the buffer that is being attached. Detaching (buffer
// zero) carries no range to check.
if (bufferObject && static_cast<SizeT>(offset) + size > bufferObject->GetSize()) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller,
"offset + size is greater than the buffer object's GL_BUFFER_SIZE."));
return;
}
}
auto* texBufferObject = static_cast<TextureObjectBuffer*>(textureObject.get());