From f02a6e0c7f2d3f1a2cac464436f38c7d304dea7c Mon Sep 17 00:00:00 2001 From: Geoff Lang Date: Mon, 26 Oct 2020 14:35:14 -0400 Subject: [PATCH] Work around Mac glGenerateMipmap with missing levels bug. Some Mac GL drivers fail to generate mipmaps if level zero is not set before the texture is first used, all mipmap data is black. To work around this, whenever a texture level is allocated, ensure that level zero is also allocated with at least a 1x1 image. Bug: angleproject:5223 Change-Id: If1a728e017dec600c77a54f7ae185b719aaaae84 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2497569 Reviewed-by: Jamie Madill Reviewed-by: Kenneth Russell Commit-Queue: Geoff Lang --- include/platform/FeaturesGL.h | 6 ++++ src/libANGLE/renderer/gl/TextureGL.cpp | 25 +++++++++++++-- src/libANGLE/renderer/gl/renderergl_utils.cpp | 2 ++ src/tests/gl_tests/MipmapTest.cpp | 32 +++++++++++++++++++ 4 files changed, 63 insertions(+), 2 deletions(-) diff --git a/include/platform/FeaturesGL.h b/include/platform/FeaturesGL.h index 42b5e871f..c28e56e8e 100644 --- a/include/platform/FeaturesGL.h +++ b/include/platform/FeaturesGL.h @@ -499,6 +499,12 @@ struct FeaturesGL : FeatureSetBase "keep_buffer_shadow_copy", FeatureCategory::OpenGLWorkarounds, "Maintain a shadow copy of buffer data when the GL API does not permit reading data back.", &members}; + + // glGenerateMipmap fails if the zero texture level is not set on some Mac drivers + Feature setZeroLevelBeforeGenerateMipmap = { + "set_zero_level_before_generating_mipmap", FeatureCategory::OpenGLWorkarounds, + "glGenerateMipmap fails if the zero texture level is not set on some Mac drivers.", + &members}; }; inline FeaturesGL::FeaturesGL() = default; diff --git a/src/libANGLE/renderer/gl/TextureGL.cpp b/src/libANGLE/renderer/gl/TextureGL.cpp index fc1856dfd..37bb3527d 100644 --- a/src/libANGLE/renderer/gl/TextureGL.cpp +++ b/src/libANGLE/renderer/gl/TextureGL.cpp @@ -265,8 +265,29 @@ angle::Result TextureGL::setImageHelper(const gl::Context *context, texImageFormat.type, pixels)); } - setLevelInfo(context, target, level, 1, - GetLevelInfo(features, internalFormat, texImageFormat.internalFormat)); + LevelInfoGL levelInfo = GetLevelInfo(features, internalFormat, texImageFormat.internalFormat); + setLevelInfo(context, target, level, 1, levelInfo); + + if (features.setZeroLevelBeforeGenerateMipmap.enabled && getType() == gl::TextureType::_2D && + level != 0 && mLevelInfo[0].nativeInternalFormat == GL_NONE) + { + // Only fill level zero if it's possible that mipmaps can be generated with this format + const gl::InternalFormat &internalFormatInfo = + gl::GetInternalFormatInfo(internalFormat, type); + if (!internalFormatInfo.sized || + (internalFormatInfo.filterSupport(context->getClientVersion(), + context->getExtensions()) && + internalFormatInfo.textureAttachmentSupport(context->getClientVersion(), + context->getExtensions()))) + { + ANGLE_GL_TRY_ALWAYS_CHECK( + context, + functions->texImage2D(nativegl::GetTextureBindingTarget(target), 0, + texImageFormat.internalFormat, 1, 1, 0, texImageFormat.format, + texImageFormat.type, nullptr)); + setLevelInfo(context, target, 0, 1, levelInfo); + } + } return angle::Result::Continue; } diff --git a/src/libANGLE/renderer/gl/renderergl_utils.cpp b/src/libANGLE/renderer/gl/renderergl_utils.cpp index 17e22d463..030523119 100644 --- a/src/libANGLE/renderer/gl/renderergl_utils.cpp +++ b/src/libANGLE/renderer/gl/renderergl_utils.cpp @@ -1837,6 +1837,8 @@ void InitializeFeatures(const FunctionsGL *functions, angle::FeaturesGL *feature ANGLE_FEATURE_CONDITION(features, disableSyncControlSupport, false); ANGLE_FEATURE_CONDITION(features, keepBufferShadowCopy, !CanMapBufferForRead(functions)); + + ANGLE_FEATURE_CONDITION(features, setZeroLevelBeforeGenerateMipmap, IsApple()); } void InitializeFrontendFeatures(const FunctionsGL *functions, angle::FrontendFeatures *features) diff --git a/src/tests/gl_tests/MipmapTest.cpp b/src/tests/gl_tests/MipmapTest.cpp index e7f88951c..64caf94d7 100644 --- a/src/tests/gl_tests/MipmapTest.cpp +++ b/src/tests/gl_tests/MipmapTest.cpp @@ -605,6 +605,38 @@ TEST_P(MipmapTest, DISABLED_ThreeLevelsInitData) EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::red); } +// Test generating mipmaps with base level and max level set. Ported from part of the +// conformance2/textures/misc/tex-mipmap-levels WebGL2 test. +TEST_P(MipmapTestES3, GenerateMipmapPartialLevels) +{ + const std::vector kRedData(64, GLColor::red); + const std::vector kGreenData(16, GLColor::green); + const std::vector kBlueData(4, GLColor::blue); + + // Initialize mips 2 to 4 + GLTexture texture; + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, kRedData.data()); + glTexImage2D(GL_TEXTURE_2D, 3, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, kGreenData.data()); + glTexImage2D(GL_TEXTURE_2D, 4, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, kBlueData.data()); + + // Set base and max levels + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 2); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 4); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + + // Verify the data + clearAndDrawQuad(m2DProgram, 2, 2); + EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); + + // Test that generateMipmap works with partial levels. + glGenerateMipmap(GL_TEXTURE_2D); + clearAndDrawQuad(m2DProgram, 2, 2); + EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); +} + // This test generates mipmaps for a 1x1 texture, which should be a no-op. TEST_P(MipmapTestES3, GenerateMipmap1x1Texture) {