Edit

kc3-lang/angle/src/tests/gl_tests/SRGBFramebufferTest.cpp

Branch :

  • Show log

    Commit

  • Author : Lubosz Sarnecki
    Date : 2021-08-26 14:44:03
    Hash : f162e4bc
    Message : SRGBFramebufferTest: Add test that used to fail on Vulkan. This commit adds a test that reproduces a Vulkan validation error found in several suites of the Chromium CI. The error was caused by enabling the VK_KHR_image_format_list extension on SwiftShader and thus exposing the GL_EXT_sRGB_write_control GL extension. Commit bd19620fcd599947a2fada3e02061d02d690d9eb reverted the change and disabled the extension for all platforms besides AMD and ARM. The issue was fixed in angle by either of the following commits: cf090996c7850074d76ce8408af4661a3c91e5e2 e70a1444af85c77a39e82a69569f723098c8d37c To reproduce the Vulkan validation error, both commits need to be reverted. Bug: angleproject:5281 Bug: angleproject:6244 Change-Id: Idd7d8d955004506a5f4b1b822ad568c727e19f1f Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3122645 Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: Jamie Madill <jmadill@chromium.org> Reviewed-by: Cody Northrop <cnorthrop@google.com> Commit-Queue: Lubosz Sarnecki <lubosz.sarnecki@collabora.com>

  • src/tests/gl_tests/SRGBFramebufferTest.cpp
  • //
    // Copyright 2016 The ANGLE Project Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    //
    
    // SRGBFramebufferTest.cpp: Tests of sRGB framebuffer functionality.
    
    #include "test_utils/ANGLETest.h"
    #include "test_utils/gl_raii.h"
    
    namespace
    {
    constexpr angle::GLColor linearColor(64, 127, 191, 255);
    constexpr angle::GLColor srgbColor(13, 54, 133, 255);
    }  // namespace
    
    namespace angle
    {
    
    class SRGBFramebufferTest : public ANGLETest
    {
      protected:
        SRGBFramebufferTest()
        {
            setWindowWidth(128);
            setWindowHeight(128);
            setConfigRedBits(8);
            setConfigGreenBits(8);
            setConfigBlueBits(8);
            setConfigAlphaBits(8);
        }
    
        void testSetUp() override
        {
            mProgram = CompileProgram(essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
            ASSERT_NE(0u, mProgram);
    
            mColorLocation = glGetUniformLocation(mProgram, essl1_shaders::ColorUniform());
            ASSERT_NE(-1, mColorLocation);
        }
    
        void testTearDown() override { glDeleteProgram(mProgram); }
    
        GLuint mProgram      = 0;
        GLint mColorLocation = -1;
    };
    
    class SRGBFramebufferTestES3 : public SRGBFramebufferTest
    {};
    
    // Test basic validation of GL_EXT_sRGB_write_control
    TEST_P(SRGBFramebufferTest, Validation)
    {
        GLenum expectedError =
            IsGLExtensionEnabled("GL_EXT_sRGB_write_control") ? GL_NO_ERROR : GL_INVALID_ENUM;
    
        GLboolean value = GL_FALSE;
        glEnable(GL_FRAMEBUFFER_SRGB_EXT);
        EXPECT_GL_ERROR(expectedError);
    
        glGetBooleanv(GL_FRAMEBUFFER_SRGB_EXT, &value);
        EXPECT_GL_ERROR(expectedError);
        if (expectedError == GL_NO_ERROR)
        {
            EXPECT_GL_TRUE(value);
        }
    
        glDisable(GL_FRAMEBUFFER_SRGB_EXT);
        EXPECT_GL_ERROR(expectedError);
    
        glGetBooleanv(GL_FRAMEBUFFER_SRGB_EXT, &value);
        EXPECT_GL_ERROR(expectedError);
        if (expectedError == GL_NO_ERROR)
        {
            EXPECT_GL_FALSE(value);
        }
    }
    
    // Test basic functionality of GL_EXT_sRGB_write_control
    TEST_P(SRGBFramebufferTest, BasicUsage)
    {
        if (!IsGLExtensionEnabled("GL_EXT_sRGB_write_control") ||
            (!IsGLExtensionEnabled("GL_EXT_sRGB") && getClientMajorVersion() < 3))
        {
            std::cout
                << "Test skipped because GL_EXT_sRGB_write_control and GL_EXT_sRGB are not available."
                << std::endl;
            return;
        }
    
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture.get());
        glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB_ALPHA_EXT, 1, 1, 0, GL_SRGB_ALPHA_EXT, GL_UNSIGNED_BYTE,
                     nullptr);
    
        GLFramebuffer framebuffer;
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.get(), 0);
    
        glUseProgram(mProgram);
        glUniform4fv(mColorLocation, 1, srgbColor.toNormalizedVector().data());
    
        glEnable(GL_FRAMEBUFFER_SRGB_EXT);
        drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f);
        EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0);
    
        glDisable(GL_FRAMEBUFFER_SRGB_EXT);
        drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f);
        EXPECT_PIXEL_COLOR_NEAR(0, 0, srgbColor, 1.0);
    }
    
    // Test that GL_EXT_sRGB_write_control state applies to all framebuffers if multiple are used
    // 1. disable srgb
    // 2. draw to both framebuffers
    // 3. enable srgb
    // 4. draw to both framebuffers
    TEST_P(SRGBFramebufferTest, MultipleFramebuffers)
    {
        if (!IsGLExtensionEnabled("GL_EXT_sRGB_write_control") ||
            (!IsGLExtensionEnabled("GL_EXT_sRGB") && getClientMajorVersion() < 3))
        {
            std::cout
                << "Test skipped because GL_EXT_sRGB_write_control and GL_EXT_sRGB are not available."
                << std::endl;
            return;
        }
    
        // NVIDIA failures on older drivers
        // http://anglebug.com/5641
        ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsOpenGLES());
    
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture.get());
        glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB_ALPHA_EXT, 1, 1, 0, GL_SRGB_ALPHA_EXT, GL_UNSIGNED_BYTE,
                     nullptr);
    
        GLFramebuffer framebuffer1;
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer1.get());
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.get(), 0);
    
        glUseProgram(mProgram);
        glUniform4fv(mColorLocation, 1, srgbColor.toNormalizedVector().data());
    
        glDisable(GL_FRAMEBUFFER_SRGB_EXT);
        drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f);
        EXPECT_PIXEL_COLOR_NEAR(0, 0, srgbColor, 1.0);
    
        GLFramebuffer framebuffer2;
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer2.get());
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.get(), 0);
        drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f);
        EXPECT_PIXEL_COLOR_NEAR(0, 0, srgbColor, 1.0);
    
        glEnable(GL_FRAMEBUFFER_SRGB_EXT);
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer1.get());
        drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f);
        EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0);
    
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer2.get());
        drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f);
        EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0);
    }
    
    // Test that we behave correctly when we toggle FRAMEBUFFER_SRGB_EXT on a framebuffer that has an
    // attachment in linear colorspace
    TEST_P(SRGBFramebufferTest, NegativeAlreadyLinear)
    {
        if (!IsGLExtensionEnabled("GL_EXT_sRGB_write_control") ||
            (!IsGLExtensionEnabled("GL_EXT_sRGB") && getClientMajorVersion() < 3))
        {
            std::cout
                << "Test skipped because GL_EXT_sRGB_write_control and GL_EXT_sRGB are not available."
                << std::endl;
            return;
        }
    
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture.get());
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        GLFramebuffer framebuffer;
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.get(), 0);
    
        glUseProgram(mProgram);
        glUniform4fv(mColorLocation, 1, linearColor.toNormalizedVector().data());
    
        glEnable(GL_FRAMEBUFFER_SRGB_EXT);
        drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f);
        EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0);
    
        glDisable(GL_FRAMEBUFFER_SRGB_EXT);
        drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f);
        EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0);
    }
    
    // Test that lifetimes of internal resources are tracked correctly by deleting a texture and then
    // attempting to use it. This is expected to produce a non-fatal error.
    TEST_P(SRGBFramebufferTest, NegativeLifetimeTracking)
    {
        if (!IsGLExtensionEnabled("GL_EXT_sRGB_write_control") ||
            (!IsGLExtensionEnabled("GL_EXT_sRGB") && getClientMajorVersion() < 3))
        {
            std::cout
                << "Test skipped because GL_EXT_sRGB_write_control and GL_EXT_sRGB are not available."
                << std::endl;
            return;
        }
    
        // NVIDIA failures
        // http://anglebug.com/5641
        ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsOpenGLES());
    
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture.get());
        glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB_ALPHA_EXT, 1, 1, 0, GL_SRGB_ALPHA_EXT, GL_UNSIGNED_BYTE,
                     nullptr);
    
        GLFramebuffer framebuffer;
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.get(), 0);
    
        glUseProgram(mProgram);
        glUniform4fv(mColorLocation, 1, srgbColor.toNormalizedVector().data());
    
        glDisable(GL_FRAMEBUFFER_SRGB_EXT);
        drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f);
        EXPECT_PIXEL_COLOR_NEAR(0, 0, srgbColor, 1.0);
    
        // Delete the texture
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
        texture.reset();
    
        drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f);
        EXPECT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION);
    
        GLColor throwaway_color;
        glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &throwaway_color);
        EXPECT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION);
    }
    
    // Test that glBlitFramebuffer correctly converts colorspaces
    TEST_P(SRGBFramebufferTestES3, BlitFramebuffer)
    {
        // http://anglebug.com/5790
        ANGLE_SKIP_TEST_IF(!IsVulkan());
    
        if (!IsGLExtensionEnabled("GL_EXT_sRGB_write_control") ||
            (!IsGLExtensionEnabled("GL_EXT_sRGB") && getClientMajorVersion() < 3))
        {
            std::cout
                << "Test skipped because GL_EXT_sRGB_write_control and GL_EXT_sRGB are not available."
                << std::endl;
            return;
        }
    
        GLTexture dstTexture;
        glBindTexture(GL_TEXTURE_2D, dstTexture.get());
        glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB_ALPHA_EXT, 1, 1, 0, GL_SRGB_ALPHA_EXT, GL_UNSIGNED_BYTE,
                     nullptr);
        GLFramebuffer dstFramebuffer;
        glBindFramebuffer(GL_FRAMEBUFFER, dstFramebuffer.get());
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dstTexture.get(),
                               0);
    
        GLTexture srcTexture;
        glBindTexture(GL_TEXTURE_2D, srcTexture.get());
        glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB_ALPHA_EXT, 1, 1, 0, GL_SRGB_ALPHA_EXT, GL_UNSIGNED_BYTE,
                     nullptr);
    
        GLFramebuffer srcFramebuffer;
        glBindFramebuffer(GL_FRAMEBUFFER, srcFramebuffer.get());
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, srcTexture.get(),
                               0);
    
        glUseProgram(mProgram);
        glUniform4fv(mColorLocation, 1, srgbColor.toNormalizedVector().data());
    
        // Draw onto the framebuffer normally
        glEnable(GL_FRAMEBUFFER_SRGB_EXT);
        drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f);
        EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0);
    
        // Blit the framebuffer normally
        glEnable(GL_FRAMEBUFFER_SRGB_EXT);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFramebuffer);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFramebuffer);
        glBlitFramebuffer(0, 0, 1, 1, 0, 0, 1, 1, GL_COLOR_BUFFER_BIT, GL_NEAREST);
    
        glBindFramebuffer(GL_FRAMEBUFFER, dstFramebuffer);
        EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0);
    
        // Blit the framebuffer with forced linear colorspace
        glDisable(GL_FRAMEBUFFER_SRGB_EXT);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFramebuffer);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFramebuffer);
        glBlitFramebuffer(0, 0, 1, 1, 0, 0, 1, 1, GL_COLOR_BUFFER_BIT, GL_NEAREST);
    
        glBindFramebuffer(GL_FRAMEBUFFER, dstFramebuffer);
        EXPECT_PIXEL_COLOR_NEAR(0, 0, srgbColor, 1.0);
    }
    
    // This test reproduces an issue in the Vulkan backend found in the Chromium CI that
    // was caused by enabling the VK_KHR_image_format_list extension on SwiftShader
    // which exposed GL_EXT_sRGB_write_control.
    TEST_P(SRGBFramebufferTest, DrawToSmallFBOClearLargeFBO)
    {
        ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_sRGB_write_control") ||
                           (!IsGLExtensionEnabled("GL_EXT_sRGB") && getClientMajorVersion() < 3));
    
        // Disabling GL_FRAMEBUFFER_SRGB_EXT caused the issue
        glDisable(GL_FRAMEBUFFER_SRGB_EXT);
    
        // The issue involved framebuffers of two different sizes.
        // The smaller needed to be drawn to, while the larger one could be just cleared
        // to reproduce the issue. These are the smallest tested sizes that generated
        // the validation error.
        constexpr GLsizei kDimensionsSmall[] = {1, 1};
        constexpr GLsizei kDimensionsLarge[] = {2, 2};
        {
            GLTexture texture;
            glBindTexture(GL_TEXTURE_2D, texture.get());
            glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, kDimensionsSmall[0], kDimensionsSmall[1]);
            glBindTexture(GL_TEXTURE_2D, 0);
    
            GLFramebuffer framebuffer;
            glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.get(),
                                   0);
    
            unsigned char vertexData[] = {0};
            GLBuffer vertexBuffer;
            glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer.get());
            glBufferData(GL_ARRAY_BUFFER, sizeof(char), vertexData, GL_STATIC_DRAW);
    
            unsigned int indexData[] = {0};
            GLBuffer indexBuffer;
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer.get());
            glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int), indexData, GL_STATIC_DRAW);
    
            glUseProgram(mProgram);
    
            glDrawElements(GL_POINTS, 1, GL_UNSIGNED_INT, nullptr);
    
            EXPECT_GL_NO_ERROR();
        }
        {
            GLTexture texture;
            glBindTexture(GL_TEXTURE_2D, texture.get());
            glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, kDimensionsLarge[0], kDimensionsLarge[1]);
            glBindTexture(GL_TEXTURE_2D, 0);
    
            GLFramebuffer framebuffer;
            glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.get(),
                                   0);
    
            // Vulkan validation happened to fail here with:
            // "Cannot execute a render pass with renderArea not within the bound of the framebuffer"
            glClear(GL_COLOR_BUFFER_BIT);
    
            EXPECT_GL_NO_ERROR();
        }
    }
    
    // Use this to select which configurations (e.g. which renderer, which GLES major version) these
    // tests should be run against.
    ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(SRGBFramebufferTest);
    ANGLE_INSTANTIATE_TEST_ES3(SRGBFramebufferTestES3);
    
    }  // namespace angle