From 6eb5ff51c5927d80258b56198ebe779ab90004b6 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Mon, 20 Jul 2026 07:47:08 -0400 Subject: [PATCH] [Fix] (MG_Impl/GLImpl): reject the RGTC internal formats on 3D texture targets - RGTC compresses 4x4 blocks of a 2D image and has no 3D form, and the check must run on the raw enum because RGTC now resolves to plain R8/RG8 storage --- .../MG_Impl/GLImpl/Texture/GL_Texture.cpp | 15 +++++++++++++ MobileGL/MG_Test/Texture/TextureTest.cpp | 22 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp index 8442938a..753005a3 100644 --- a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp +++ b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp @@ -1724,6 +1724,21 @@ namespace MobileGL::MG_Impl::GLImpl { return; } + // RGTC is a 2D-only compression scheme, so a 3D target rejects it. This has to be tested on + // the raw enum: the RGTC formats resolve to plain R8/RG8/SNORM storage on the way in (see + // GLToMG's TextureEnumConverter), so once the internal format is converted there is nothing + // left to distinguish them from an ordinary one- or two-channel upload. + if ((textureUploadTarget == TextureUploadTarget::Texture3D || + textureUploadTarget == TextureUploadTarget::ProxyTexture3D) && + (internalformat == GL_COMPRESSED_RED_RGTC1 || internalformat == GL_COMPRESSED_SIGNED_RED_RGTC1 || + internalformat == GL_COMPRESSED_RG_RGTC2 || internalformat == GL_COMPRESSED_SIGNED_RG_RGTC2)) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, + "RGTC compressed formats are invalid for 3D texture targets")); + return; + } + // TODO: GL_INVALID_OPERATION is generated if a non-zero buffer object name is bound to the // GL_PIXEL_UNPACK_BUFFER target and the buffer object's data store is currently mapped. // GL_INVALID_OPERATION is generated if a non-zero buffer object name is bound to the GL_PIXEL_UNPACK_BUFFER diff --git a/MobileGL/MG_Test/Texture/TextureTest.cpp b/MobileGL/MG_Test/Texture/TextureTest.cpp index 5c9e9baa..a23fd8ea 100644 --- a/MobileGL/MG_Test/Texture/TextureTest.cpp +++ b/MobileGL/MG_Test/Texture/TextureTest.cpp @@ -1557,6 +1557,28 @@ TEST_F(TextureTest, CompressedInternalFormatsResolveToTheirUncompressedStorage) } } +// RGTC compresses 4x4 blocks of a 2D image and has no 3D form, so glTexImage3D must reject it even +// though the same enum is accepted on a 2D target. The generic compressed formats carry no such +// restriction and stay legal in 3D. +TEST_F(TextureTest, RgtcInternalFormatsAreRejectedOnThreeDimensionalTargets) { + const GLenum rgtc[] = {GL_COMPRESSED_RED_RGTC1, GL_COMPRESSED_SIGNED_RED_RGTC1, GL_COMPRESSED_RG_RGTC2, + GL_COMPRESSED_SIGNED_RG_RGTC2}; + for (const GLenum internalFormat : rgtc) { + GLuint texture = 0; + MG_Impl::GLImpl::GenTextures(1, &texture); + MG_Impl::GLImpl::BindTexture(GL_TEXTURE_3D, texture); + MG_Impl::GLImpl::TexImage3D(GL_TEXTURE_3D, 0, internalFormat, 4, 4, 4, 0, GL_RED, GL_UNSIGNED_BYTE, nullptr); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_INVALID_OPERATION) + << "internalFormat 0x" << std::hex << internalFormat; + } + + GLuint generic = 0; + MG_Impl::GLImpl::GenTextures(1, &generic); + MG_Impl::GLImpl::BindTexture(GL_TEXTURE_3D, generic); + MG_Impl::GLImpl::TexImage3D(GL_TEXTURE_3D, 0, GL_COMPRESSED_RGBA, 4, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + TEST_F(TextureTest, TextureStorage3DAndSubImageModifyNamedObjectOnly) { GLuint texture = 0; MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_3D, 1, &texture);