From fe7a5ee1b2b2905d91b995d1d70b37ce1b58bf54 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Thu, 16 Jul 2026 02:23:18 -0400 Subject: [PATCH] [Fix] (MG_Impl/GLImpl, MG_State, MG_Backend/DirectGLES): eliminate packed_pixels SIGTRAPs - complete TexImage format/type/internalformat validation matrix (depth-stencil family, integer-ness, packed-type pairing, 3D depth rejection), fix inverted UpdateSubData assert with clamped copy, demote unimplemented readback asserts to logged no-ops --- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 49 +++--- .../MG_Impl/GLImpl/Texture/GL_Texture.cpp | 42 +++-- .../MG_Impl/GLImpl/Texture/Validators.cpp | 145 +++++++++++++----- .../GLState/TextureState/MipmapStorage.cpp | 5 +- MobileGL/MG_Test/Texture/TextureTest.cpp | 88 +++++++++++ 5 files changed, 258 insertions(+), 71 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index e53a2ea6..7b926059 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -3266,17 +3266,20 @@ namespace MobileGL::MG_Backend::DirectGLES { MGLOG_D("ReadPixels: x=%d y=%d w=%d h=%d format=%s type=%s pixels=%p", x, y, width, height, MG_Util::ConvertGLEnumToString(format).c_str(), MG_Util::ConvertGLEnumToString(type).c_str(), pixels); - MOBILEGL_ASSERT(format == GL_RGBA || format == GL_RGBA_INTEGER || format == GL_RED || - format == GL_RED_INTEGER || format == GL_DEPTH_COMPONENT || format == GL_STENCIL_INDEX, - "Only GL_RGBA, GL_RGBA_INTEGER, GL_RED, GL_RED_INTEGER, GL_DEPTH_COMPONENT and " - "GL_STENCIL_INDEX are supported currently, " - "while requested %s.", - MG_Util::ConvertGLEnumToString(format).c_str()); - MOBILEGL_ASSERT(type == GL_UNSIGNED_BYTE || type == GL_UNSIGNED_INT || type == GL_UNSIGNED_INT_2_10_10_10_REV || - type == GL_INT || type == GL_FLOAT, - "Only GL_UNSIGNED_BYTE, GL_UNSIGNED_INT, GL_UNSIGNED_INT_2_10_10_10_REV, " - "GL_INT and GL_FLOAT are supported currently, while requested %s.", - MG_Util::ConvertGLEnumToString(type).c_str()); + // Unimplemented readback formats degrade to a logged no-op instead of killing the process; + // spec-invalid combinations are already rejected with GL errors at the state layer. + if (format != GL_RGBA && format != GL_RGBA_INTEGER && format != GL_RED && format != GL_RED_INTEGER && + format != GL_DEPTH_COMPONENT && format != GL_STENCIL_INDEX) { + MGLOG_E("ReadPixels: format %s is not implemented yet, skipping readback", + MG_Util::ConvertGLEnumToString(format).c_str()); + return; + } + if (type != GL_UNSIGNED_BYTE && type != GL_UNSIGNED_INT && type != GL_UNSIGNED_INT_2_10_10_10_REV && + type != GL_INT && type != GL_FLOAT) { + MGLOG_E("ReadPixels: type %s is not implemented yet, skipping readback", + MG_Util::ConvertGLEnumToString(type).c_str()); + return; + } MGLOG_D("ReadPixels: SyncNeccessaryTextures()"); TextureImpl::SyncNeccessaryTextures(); @@ -3362,16 +3365,20 @@ namespace MobileGL::MG_Backend::DirectGLES { MG_Util::ConvertGLEnumToString(target).c_str(), level, MG_Util::ConvertGLEnumToString(format).c_str(), MG_Util::ConvertGLEnumToString(type).c_str(), pixels); - MOBILEGL_ASSERT(format == GL_RGBA || format == GL_RGBA_INTEGER || format == GL_BGRA, - "Only GL_RGBA, GL_RGBA_INTEGER and GL_BGRA are supported currently, while requested %s.", - MG_Util::ConvertGLEnumToString(format).c_str()); - MOBILEGL_ASSERT(type == GL_UNSIGNED_BYTE || type == GL_UNSIGNED_INT || type == GL_UNSIGNED_INT_2_10_10_10_REV || - type == GL_INT || type == GL_FLOAT || type == GL_UNSIGNED_INT_8_8_8_8 || - type == GL_UNSIGNED_INT_8_8_8_8_REV || type == GL_HALF_FLOAT, - "Only GL_UNSIGNED_BYTE, GL_UNSIGNED_INT, GL_UNSIGNED_INT_2_10_10_10_REV, " - "GL_INT, GL_FLOAT, GL_HALF_FLOAT, GL_UNSIGNED_INT_8_8_8_8 and GL_UNSIGNED_INT_8_8_8_8_REV " - "are supported currently, while requested %s.", - MG_Util::ConvertGLEnumToString(type).c_str()); + // Unimplemented readback formats degrade to a logged no-op instead of killing the process; + // spec-invalid combinations are already rejected with GL errors at the state layer. + if (format != GL_RGBA && format != GL_RGBA_INTEGER && format != GL_BGRA) { + MGLOG_E("GetTexImage: format %s is not implemented yet, skipping readback", + MG_Util::ConvertGLEnumToString(format).c_str()); + return; + } + if (type != GL_UNSIGNED_BYTE && type != GL_UNSIGNED_INT && type != GL_UNSIGNED_INT_2_10_10_10_REV && + type != GL_INT && type != GL_FLOAT && type != GL_UNSIGNED_INT_8_8_8_8 && + type != GL_UNSIGNED_INT_8_8_8_8_REV && type != GL_HALF_FLOAT) { + MGLOG_E("GetTexImage: type %s is not implemented yet, skipping readback", + MG_Util::ConvertGLEnumToString(type).c_str()); + return; + } GLenum esFormat = format, esType = type; if (esFormat == GL_BGRA) esFormat = GL_RGBA; diff --git a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp index 0e790ad4..88201752 100644 --- a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp +++ b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp @@ -1357,6 +1357,18 @@ namespace MobileGL::MG_Impl::GLImpl { return; if (!TextureImpl::ValidateTextureLevelWithUploadTarget(textureUploadTarget, level)) return; + // Depth and depth-stencil formats are not three-dimensional in core GL (2D-array targets are fine). + if ((textureUploadTarget == TextureUploadTarget::Texture3D || + textureUploadTarget == TextureUploadTarget::ProxyTexture3D) && + (textureInputFormat == TextureInputFormat::DepthComponent || + textureInputFormat == TextureInputFormat::DepthStencil)) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, + "Depth 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 @@ -2648,17 +2660,25 @@ namespace MobileGL::MG_Impl::GLImpl { } } - // Special case for depth/stencil - if (textureInputFormat == TextureInputFormat::StencilIndex) { - if (textureObject->GetFormat() != TextureInternalFormat::DepthStencil && - textureObject->GetFormat() != TextureInternalFormat::Depth24Stencil8 && - textureObject->GetFormat() != TextureInternalFormat::Depth32FStencil8) { - MG_State::pGLContext->RecordError( - ErrorCode::InvalidOperation, - MakeUnique("MG_Impl/GLImpl", "GetTexImage_State", - "No stencil buffer for stencil index format")); - return false; - } + // Shared format/type/internal-format matrix (packed-type pairing, depth-vs-color mismatch, + // integer-ness). Also rejects STENCIL_INDEX readback, which needs GL_ARB_texture_stencil8 + // (not advertised by MobileGL). + if (!TextureImpl::ValidateTextureInternalFormatCompatibleWithInput( + textureInputFormat, textureObject->GetFormat(), texturePixelDataType)) { + return false; + } + + // GetTexImage-specific: DEPTH_STENCIL readback needs a depth-stencil texture (a depth-only + // texture has no stencil data to return). + if (textureInputFormat == TextureInputFormat::DepthStencil && + textureObject->GetFormat() != TextureInternalFormat::DepthStencil && + textureObject->GetFormat() != TextureInternalFormat::Depth24Stencil8 && + textureObject->GetFormat() != TextureInternalFormat::Depth32FStencil8) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", "GetTexImage_State", + "DEPTH_STENCIL readback requires a depth-stencil texture")); + return false; } return true; diff --git a/MobileGL/MG_Impl/GLImpl/Texture/Validators.cpp b/MobileGL/MG_Impl/GLImpl/Texture/Validators.cpp index 96b7dfaa..bb060efe 100644 --- a/MobileGL/MG_Impl/GLImpl/Texture/Validators.cpp +++ b/MobileGL/MG_Impl/GLImpl/Texture/Validators.cpp @@ -175,61 +175,132 @@ namespace MobileGL::MG_Impl::GLImpl::TextureImpl { return true; } + static Bool IsIntegerColorInputFormat(TextureInputFormat format) { + return format == TextureInputFormat::RInteger || format == TextureInputFormat::RGInteger || + format == TextureInputFormat::RGBInteger || format == TextureInputFormat::BGRInteger || + format == TextureInputFormat::RGBAInteger || format == TextureInputFormat::BGRAInteger; + } + + static Bool IsIntegerColorInternalFormat(TextureInternalFormat internalFormat) { + switch (internalFormat) { + case TextureInternalFormat::R8I: + case TextureInternalFormat::R8UI: + case TextureInternalFormat::R16I: + case TextureInternalFormat::R16UI: + case TextureInternalFormat::R32I: + case TextureInternalFormat::R32UI: + case TextureInternalFormat::RG8I: + case TextureInternalFormat::RG8UI: + case TextureInternalFormat::RG16I: + case TextureInternalFormat::RG16UI: + case TextureInternalFormat::RG32I: + case TextureInternalFormat::RG32UI: + case TextureInternalFormat::RGB8I: + case TextureInternalFormat::RGB8UI: + case TextureInternalFormat::RGB16I: + case TextureInternalFormat::RGB16UI: + case TextureInternalFormat::RGB32I: + case TextureInternalFormat::RGB32UI: + case TextureInternalFormat::RGBA8I: + case TextureInternalFormat::RGBA8UI: + case TextureInternalFormat::RGBA16I: + case TextureInternalFormat::RGBA16UI: + case TextureInternalFormat::RGBA32I: + case TextureInternalFormat::RGBA32UI: + case TextureInternalFormat::RGB10A2UI: + return true; + default: + return false; + } + } + + static Bool IsDepthLikeInternalFormat(TextureInternalFormat internalFormat) { + switch (internalFormat) { + case TextureInternalFormat::DepthComponent: + case TextureInternalFormat::DepthComponent16: + case TextureInternalFormat::DepthComponent24: + case TextureInternalFormat::DepthComponent32: // not core, kept for Minecraft 1.21.5+ + case TextureInternalFormat::DepthComponent32F: + case TextureInternalFormat::Depth24Stencil8: + case TextureInternalFormat::Depth32FStencil8: + case TextureInternalFormat::DepthStencil: + return true; + default: + return false; + } + } + + static Bool IsDepthLikeInputFormat(TextureInputFormat format) { + return format == TextureInputFormat::DepthComponent || format == TextureInputFormat::DepthStencil || + format == TextureInputFormat::StencilIndex; + } + + // Mirrors the desktop-GL validity matrix used by GL CTS packed_pixels (glcPackedPixelsTests + // isFormatValid, INPUT_TEXIMAGE): packed-type/format pairing, depth-vs-color mismatch, and + // integer-ness matching all raise GL_INVALID_OPERATION instead of reaching the upload path. Bool ValidateTextureInternalFormatCompatibleWithInput(TextureInputFormat format, TextureInternalFormat internalFormat, TexturePixelDataType type) { + const auto recordInvalidOperation = [](const char* message) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", "ValidateTextureInternalFormatCompatibleWithInput", + message)); + return false; + }; + if (type == TexturePixelDataType::UnsignedByte332 || type == TexturePixelDataType::UnsignedByte233Rev || - type == TexturePixelDataType::UnsignedShort565 || type == TexturePixelDataType::UnsignedShort565Rev || - type == TexturePixelDataType::UnsignedInt101111Rev) { + type == TexturePixelDataType::UnsignedShort565 || type == TexturePixelDataType::UnsignedShort565Rev) { + if (format != TextureInputFormat::RGB && format != TextureInputFormat::RGBInteger) { + return recordInvalidOperation("Packed RGB type requires RGB or RGB_INTEGER format"); + } + } + + if (type == TexturePixelDataType::UnsignedInt101111Rev || type == TexturePixelDataType::UnsignedInt5999Rev) { if (format != TextureInputFormat::RGB) { - MG_State::pGLContext->RecordError( - ErrorCode::InvalidOperation, - MakeUnique("MG_Impl/GLImpl", "ValidateTextureInternalFormatCompatibleWithInput", - "Invalid format for the given type")); - return false; + return recordInvalidOperation("Packed float RGB type requires RGB format"); } } if (type == TexturePixelDataType::UnsignedShort4444 || type == TexturePixelDataType::UnsignedShort4444Rev || type == TexturePixelDataType::UnsignedShort5551 || type == TexturePixelDataType::UnsignedShort1555Rev || type == TexturePixelDataType::UnsignedInt8888 || type == TexturePixelDataType::UnsignedInt8888Rev || - type == TexturePixelDataType::UnsignedInt1010102 || type == TexturePixelDataType::UnsignedInt2101010Rev || - type == TexturePixelDataType::UnsignedInt5999Rev) { - if (format != TextureInputFormat::RGBA && format != TextureInputFormat::BGRA) { - MG_State::pGLContext->RecordError( - ErrorCode::InvalidOperation, - MakeUnique("MG_Impl/GLImpl", "ValidateTextureInternalFormatCompatibleWithInput", - "Invalid format for the given type")); - return false; + type == TexturePixelDataType::UnsignedInt1010102 || type == TexturePixelDataType::UnsignedInt2101010Rev) { + if (format != TextureInputFormat::RGBA && format != TextureInputFormat::BGRA && + format != TextureInputFormat::RGBAInteger && format != TextureInputFormat::BGRAInteger) { + return recordInvalidOperation("Packed RGBA type requires RGBA/BGRA (integer) format"); } } - if (internalFormat == TextureInternalFormat::DepthComponent || - internalFormat == TextureInternalFormat::DepthComponent16 || - internalFormat == TextureInternalFormat::DepthComponent24 || - internalFormat == TextureInternalFormat::DepthComponent32F) { - if (format != TextureInputFormat::DepthComponent) { - MG_State::pGLContext->RecordError( - ErrorCode::InvalidOperation, - MakeUnique("MG_Impl/GLImpl", "ValidateTextureInternalFormatCompatibleWithInput", - "Invalid format for depth component internal format")); - return false; + if (type == TexturePixelDataType::UnsignedInt248 || type == TexturePixelDataType::Float32UnsignedInt248Rev) { + if (format != TextureInputFormat::DepthStencil) { + return recordInvalidOperation("Packed depth-stencil type requires DEPTH_STENCIL format"); } } - if (format == TextureInputFormat::DepthComponent && - (internalFormat != TextureInternalFormat::DepthComponent && - internalFormat != TextureInternalFormat::DepthComponent16 && - internalFormat != TextureInternalFormat::DepthComponent24 && - internalFormat != TextureInternalFormat::DepthComponent32F && - internalFormat != TextureInternalFormat::DepthComponent32 // workaround for Minecraft 1.21.5+ - )) { - MG_State::pGLContext->RecordError( - ErrorCode::InvalidOperation, - MakeUnique("MG_Impl/GLImpl", "ValidateTextureInternalFormatCompatibleWithInput", - "Invalid internal format for depth component format")); - return false; + if (format == TextureInputFormat::DepthStencil && type != TexturePixelDataType::UnsignedInt248 && + type != TexturePixelDataType::Float32UnsignedInt248Rev) { + return recordInvalidOperation("DEPTH_STENCIL format requires a packed depth-stencil type"); } + + if (IsIntegerColorInputFormat(format) && + (type == TexturePixelDataType::Float || type == TexturePixelDataType::HalfFloat)) { + return recordInvalidOperation("Integer format cannot be used with a floating-point type"); + } + + // TexImage in core 3.3 has no stencil-only upload path (that arrived with GL 4.4). + if (format == TextureInputFormat::StencilIndex) { + return recordInvalidOperation("STENCIL_INDEX is not a valid texture upload format"); + } + + if (IsDepthLikeInputFormat(format) != IsDepthLikeInternalFormat(internalFormat)) { + return recordInvalidOperation("Depth/stencil-ness of format and internal format must match"); + } + + if (IsIntegerColorInputFormat(format) != IsIntegerColorInternalFormat(internalFormat)) { + return recordInvalidOperation("Integer-ness of format and internal format must match"); + } + return true; } diff --git a/MobileGL/MG_State/GLState/TextureState/MipmapStorage.cpp b/MobileGL/MG_State/GLState/TextureState/MipmapStorage.cpp index 3c227c31..e4127398 100644 --- a/MobileGL/MG_State/GLState/TextureState/MipmapStorage.cpp +++ b/MobileGL/MG_State/GLState/TextureState/MipmapStorage.cpp @@ -31,11 +31,12 @@ namespace MobileGL { auto& targetData = m_data; MOBILEGL_ASSERT(level < targetData.size(), "UpdateSubData: level out of range"); auto& levelData = targetData[level]; - MOBILEGL_ASSERT(levelData.size() <= input.size, "UpdateSubData: input data larger than allocated"); + MOBILEGL_ASSERT(input.size <= levelData.size(), "UpdateSubData: input data larger than allocated"); if (input.data && input.size > 0) { const Uint8* src = static_cast(input.data); - Memcpy(levelData.data(), src, input.size); + // Clamp so a size mismatch can never write past the allocation. + Memcpy(levelData.data(), src, std::min(input.size, levelData.size())); m_isDirty[level] = true; } } diff --git a/MobileGL/MG_Test/Texture/TextureTest.cpp b/MobileGL/MG_Test/Texture/TextureTest.cpp index a2fc634c..462e30c9 100644 --- a/MobileGL/MG_Test/Texture/TextureTest.cpp +++ b/MobileGL/MG_Test/Texture/TextureTest.cpp @@ -257,6 +257,94 @@ TEST_F(TextureTest, BoundTexImage2DUnpacksPackedBgra8888ToRgba8WithPixelStoreSki EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); } +// GL CTS packed_pixels feeds every format/type/internalformat combination to TexImage and expects +// GL_INVALID_OPERATION for the invalid ones; these used to slip through validation and SIGTRAP in +// the shadow-storage upload path. +TEST_F(TextureTest, TexImage2DRejectsMismatchedFormatCombinations) { + GLuint texture = 0; + MG_Impl::GLImpl::GenTextures(1, &texture); + MG_Impl::GLImpl::BindTexture(GL_TEXTURE_2D, texture); + + // Depth-stencil internal format with a color format. + MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, 2, 2, 0, GL_BGR, GL_UNSIGNED_BYTE, nullptr); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_INVALID_OPERATION); + + // Color internal format with a depth format. + MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 2, 2, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_INVALID_OPERATION); + + // Stencil-only uploads do not exist in core 3.3. + MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, 2, 2, 0, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, + nullptr); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_INVALID_OPERATION); + + // Packed depth-stencil type with a color format. + MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 2, 2, 0, GL_RGBA, GL_UNSIGNED_INT_24_8, nullptr); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_INVALID_OPERATION); + + // DEPTH_STENCIL format requires one of the two packed depth-stencil types. + MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, 2, 2, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_BYTE, + nullptr); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_INVALID_OPERATION); + + // Integer-ness of format and internal format must match (both directions). + MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 2, 2, 0, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, nullptr); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_INVALID_OPERATION); + MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8UI, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_INVALID_OPERATION); + + // Integer formats cannot be paired with floating-point types. + MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32I, 2, 2, 0, GL_RGBA_INTEGER, GL_FLOAT, nullptr); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_INVALID_OPERATION); + + // UNSIGNED_INT_5_9_9_9_REV pairs with RGB only. + MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 0, GL_RGB9_E5, 2, 2, 0, GL_RGBA, GL_UNSIGNED_INT_5_9_9_9_REV, nullptr); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_INVALID_OPERATION); +} + +TEST_F(TextureTest, TexImage2DAcceptsSpecCompliantFormatCombinations) { + GLuint texture = 0; + MG_Impl::GLImpl::GenTextures(1, &texture); + MG_Impl::GLImpl::BindTexture(GL_TEXTURE_2D, texture); + + MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, 2, 2, 0, GL_DEPTH_STENCIL, + GL_UNSIGNED_INT_24_8, nullptr); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); + + // Depth-component internal format accepts DEPTH_STENCIL input (stencil bits are dropped). + MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, 2, 2, 0, GL_DEPTH_STENCIL, + GL_UNSIGNED_INT_24_8, nullptr); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); + + MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8UI, 2, 2, 0, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, nullptr); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); + + // Packed RGB types allow the integer variant of the RGB format. + MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 0, GL_RGB8UI, 2, 2, 0, GL_RGB_INTEGER, GL_UNSIGNED_BYTE_3_3_2, + nullptr); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); + + MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 0, GL_RGB9_E5, 2, 2, 0, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV, nullptr); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + +TEST_F(TextureTest, TexImage3DRejectsDepthFormatsForThreeDimensionalTarget) { + 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, GL_DEPTH24_STENCIL8, 2, 2, 2, 0, GL_DEPTH_STENCIL, + GL_UNSIGNED_INT_24_8, nullptr); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_INVALID_OPERATION); + + // 2D-array targets remain valid for depth formats. + GLuint arrayTexture = 0; + MG_Impl::GLImpl::GenTextures(1, &arrayTexture); + MG_Impl::GLImpl::BindTexture(GL_TEXTURE_2D_ARRAY, arrayTexture); + MG_Impl::GLImpl::TexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH24_STENCIL8, 2, 2, 2, 0, GL_DEPTH_STENCIL, + GL_UNSIGNED_INT_24_8, nullptr); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + TEST_F(TextureTest, BoundTexSubImage2DUnpacksPackedBgra8888RevToRgba8) { GLuint texture = 0; MG_Impl::GLImpl::GenTextures(1, &texture);