Edit

kc3-lang/angle/src/tests/egl_tests/EGLPreRotationTest.cpp

Branch :

  • Show log

    Commit

  • Author : Yuly Novikov
    Date : 2021-03-24 18:29:02
    Hash : 3d6cedb1
    Message : Leave Android devices in portrait orientation after test EGLPreRotationSurfaceTest.ChangeRotationWithDraw alternates between landscape and portrait orientations. The last one needs to be portrait, since this is the default state that other tests expect. Since changing orientation takes 1 second, it's more efficient to do it in this one place instead of before every test. Bug: angleproject:5781 Change-Id: I63c2eeb77663ea7de6679385c9deb089b16e74f3 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2785433 Reviewed-by: Ian Elliott <ianelliott@google.com> Reviewed-by: Yuly Novikov <ynovikov@chromium.org> Commit-Queue: Yuly Novikov <ynovikov@chromium.org>

  • src/tests/egl_tests/EGLPreRotationTest.cpp
  • //
    // Copyright 2020 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.
    //
    // EGLPreRotationTest:
    //   Tests pertaining to Android pre-rotation.
    //
    
    #include <gtest/gtest.h>
    
    #include <vector>
    
    #include "common/Color.h"
    #include "common/platform.h"
    #include "test_utils/ANGLETest.h"
    #include "test_utils/gl_raii.h"
    #include "util/EGLWindow.h"
    #include "util/OSWindow.h"
    #include "util/Timer.h"
    #include "util/test_utils.h"
    
    using namespace angle;
    
    namespace
    {
    
    using EGLPreRotationSurfaceTestParams = std::tuple<angle::PlatformParameters, bool>;
    
    std::string PrintToStringParamName(
        const ::testing::TestParamInfo<EGLPreRotationSurfaceTestParams> &info)
    {
        std::stringstream ss;
        ss << std::get<0>(info.param);
        if (std::get<1>(info.param))
        {
            ss << "__PreRotationEnabled";
        }
        else
        {
            ss << "__PreRotationDisabled";
        }
        return ss.str();
    }
    
    // A class to test various Android pre-rotation cases.  In order to make it easier to debug test
    // failures, the initial window size is 256x256, and each pixel will have a unique and predictable
    // value.  The red channel will increment with the x axis, and the green channel will increment
    // with the y axis.  The four corners will have the following values:
    //
    // Where                 GLES Render &  ReadPixels coords       Color    (in Hex)
    // Lower-left,  which is (-1.0,-1.0) & (  0,   0) in GLES will be black  (0x00, 0x00, 0x00, 0xFF)
    // Lower-right, which is ( 1.0,-1.0) & (256,   0) in GLES will be red    (0xFF, 0x00, 0x00, 0xFF)
    // Upper-left,  which is (-1.0, 1.0) & (  0, 256) in GLES will be green  (0x00, 0xFF, 0x00, 0xFF)
    // Upper-right, which is ( 1.0, 1.0) & (256, 256) in GLES will be yellow (0xFF, 0xFF, 0x00, 0xFF)
    class EGLPreRotationSurfaceTest : public ANGLETestWithParam<EGLPreRotationSurfaceTestParams>
    {
      protected:
        EGLPreRotationSurfaceTest()
            : mDisplay(EGL_NO_DISPLAY),
              mWindowSurface(EGL_NO_SURFACE),
              mContext(EGL_NO_CONTEXT),
              mOSWindow(nullptr),
              mSize(256)
        {}
    
        // Release any resources created in the test body
        void testTearDown() override
        {
            if (mDisplay != EGL_NO_DISPLAY)
            {
                eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    
                if (mWindowSurface != EGL_NO_SURFACE)
                {
                    eglDestroySurface(mDisplay, mWindowSurface);
                    mWindowSurface = EGL_NO_SURFACE;
                }
    
                if (mContext != EGL_NO_CONTEXT)
                {
                    eglDestroyContext(mDisplay, mContext);
                    mContext = EGL_NO_CONTEXT;
                }
    
                eglTerminate(mDisplay);
                mDisplay = EGL_NO_DISPLAY;
            }
    
            mOSWindow->destroy();
            OSWindow::Delete(&mOSWindow);
    
            ASSERT_TRUE(mWindowSurface == EGL_NO_SURFACE && mContext == EGL_NO_CONTEXT);
        }
    
        void testSetUp() override
        {
            mOSWindow = OSWindow::New();
            mOSWindow->initialize("EGLSurfaceTest", mSize, mSize);
        }
    
        void initializeDisplay()
        {
            const angle::PlatformParameters platform = ::testing::get<0>(GetParam());
            GLenum platformType                      = platform.getRenderer();
            GLenum deviceType                        = platform.getDeviceType();
    
            std::vector<const char *> enabledFeatures;
            std::vector<const char *> disabledFeatures;
            if (::testing::get<1>(GetParam()))
            {
                enabledFeatures.push_back("enablePreRotateSurfaces");
            }
            else
            {
                disabledFeatures.push_back("enablePreRotateSurfaces");
            }
            enabledFeatures.push_back(nullptr);
            disabledFeatures.push_back(nullptr);
    
            std::vector<EGLAttrib> displayAttributes;
            displayAttributes.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE);
            displayAttributes.push_back(platformType);
            displayAttributes.push_back(EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE);
            displayAttributes.push_back(EGL_DONT_CARE);
            displayAttributes.push_back(EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE);
            displayAttributes.push_back(EGL_DONT_CARE);
            displayAttributes.push_back(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE);
            displayAttributes.push_back(deviceType);
            displayAttributes.push_back(EGL_FEATURE_OVERRIDES_ENABLED_ANGLE);
            displayAttributes.push_back(reinterpret_cast<EGLAttrib>(enabledFeatures.data()));
            displayAttributes.push_back(EGL_FEATURE_OVERRIDES_DISABLED_ANGLE);
            displayAttributes.push_back(reinterpret_cast<EGLAttrib>(disabledFeatures.data()));
            displayAttributes.push_back(EGL_NONE);
    
            mDisplay = eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE,
                                             reinterpret_cast<void *>(mOSWindow->getNativeDisplay()),
                                             displayAttributes.data());
            ASSERT_TRUE(mDisplay != EGL_NO_DISPLAY);
    
            EGLint majorVersion, minorVersion;
            ASSERT_TRUE(eglInitialize(mDisplay, &majorVersion, &minorVersion) == EGL_TRUE);
    
            eglBindAPI(EGL_OPENGL_ES_API);
            ASSERT_EGL_SUCCESS();
        }
    
        void initializeContext()
        {
            EGLint contextAttibutes[] = {EGL_CONTEXT_CLIENT_VERSION,
                                         ::testing::get<0>(GetParam()).majorVersion, EGL_NONE};
    
            mContext = eglCreateContext(mDisplay, mConfig, nullptr, contextAttibutes);
            ASSERT_EGL_SUCCESS();
        }
    
        void initializeSurfaceWithRGBA8888Config()
        {
            const EGLint configAttributes[] = {
                EGL_RED_SIZE,   8, EGL_GREEN_SIZE,   8, EGL_BLUE_SIZE,      8, EGL_ALPHA_SIZE, 8,
                EGL_DEPTH_SIZE, 0, EGL_STENCIL_SIZE, 0, EGL_SAMPLE_BUFFERS, 0, EGL_NONE};
    
            EGLint configCount;
            EGLConfig config;
            ASSERT_TRUE(eglChooseConfig(mDisplay, configAttributes, &config, 1, &configCount) ||
                        (configCount != 1) == EGL_TRUE);
    
            mConfig = config;
    
            EGLint surfaceType = EGL_NONE;
            eglGetConfigAttrib(mDisplay, mConfig, EGL_SURFACE_TYPE, &surfaceType);
    
            std::vector<EGLint> windowAttributes;
            windowAttributes.push_back(EGL_NONE);
    
            if (surfaceType & EGL_WINDOW_BIT)
            {
                // Create first window surface
                mWindowSurface = eglCreateWindowSurface(mDisplay, mConfig, mOSWindow->getNativeWindow(),
                                                        windowAttributes.data());
                ASSERT_EGL_SUCCESS();
            }
    
            initializeContext();
        }
    
        void initializeSurfaceWithRGBA8888d24s8Config()
        {
            const EGLint configAttributes[] = {
                EGL_RED_SIZE,   8,  EGL_GREEN_SIZE,   8, EGL_BLUE_SIZE,      8, EGL_ALPHA_SIZE, 8,
                EGL_DEPTH_SIZE, 24, EGL_STENCIL_SIZE, 8, EGL_SAMPLE_BUFFERS, 0, EGL_NONE};
    
            EGLint configCount;
            EGLConfig config;
            ASSERT_TRUE(eglChooseConfig(mDisplay, configAttributes, &config, 1, &configCount) ||
                        (configCount != 1) == EGL_TRUE);
    
            mConfig = config;
    
            EGLint surfaceType = EGL_NONE;
            eglGetConfigAttrib(mDisplay, mConfig, EGL_SURFACE_TYPE, &surfaceType);
    
            std::vector<EGLint> windowAttributes;
            windowAttributes.push_back(EGL_NONE);
    
            if (surfaceType & EGL_WINDOW_BIT)
            {
                // Create first window surface
                mWindowSurface = eglCreateWindowSurface(mDisplay, mConfig, mOSWindow->getNativeWindow(),
                                                        windowAttributes.data());
                ASSERT_EGL_SUCCESS();
            }
    
            initializeContext();
        }
    
        void testDrawingAndReadPixels()
        {
            glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr);
            EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black);
            EXPECT_PIXEL_COLOR_EQ(0, mSize - 1, GLColor::green);
            EXPECT_PIXEL_COLOR_EQ(mSize - 1, 0, GLColor::red);
            EXPECT_PIXEL_COLOR_EQ(mSize - 1, mSize - 1, GLColor::yellow);
            ASSERT_GL_NO_ERROR();
    
            eglSwapBuffers(mDisplay, mWindowSurface);
            ASSERT_EGL_SUCCESS();
    
            glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr);
            EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black);
            EXPECT_PIXEL_COLOR_EQ(0, mSize - 1, GLColor::green);
            EXPECT_PIXEL_COLOR_EQ(mSize - 1, 0, GLColor::red);
            EXPECT_PIXEL_COLOR_EQ(mSize - 1, mSize - 1, GLColor::yellow);
            ASSERT_GL_NO_ERROR();
    
            {
                // Now, test a 4x4 area in the center of the window, which should tell us if a non-1x1
                // ReadPixels is oriented correctly for the device's orientation:
                GLint xOffset  = 126;
                GLint yOffset  = 126;
                GLsizei width  = 4;
                GLsizei height = 4;
                std::vector<GLColor> pixels(width * height);
                glReadPixels(xOffset, yOffset, width, height, GL_RGBA, GL_UNSIGNED_BYTE, &pixels[0]);
                EXPECT_GL_NO_ERROR();
                // Expect that all red values equate to x and green values equate to y
                for (int y = 0; y < height; y++)
                {
                    for (int x = 0; x < width; x++)
                    {
                        int index = (y * width) + x;
                        GLColor expectedPixel(xOffset + x, yOffset + y, 0, 255);
                        GLColor actualPixel = pixels[index];
                        EXPECT_EQ(expectedPixel, actualPixel);
                    }
                }
            }
    
            {
                // Now, test a 8x4 area off-the-center of the window, just to make sure that works too:
                GLint xOffset  = 13;
                GLint yOffset  = 26;
                GLsizei width  = 8;
                GLsizei height = 4;
                std::vector<GLColor> pixels2(width * height);
                glReadPixels(xOffset, yOffset, width, height, GL_RGBA, GL_UNSIGNED_BYTE, &pixels2[0]);
                EXPECT_GL_NO_ERROR();
                // Expect that all red values equate to x and green values equate to y
                for (int y = 0; y < height; y++)
                {
                    for (int x = 0; x < width; x++)
                    {
                        int index = (y * width) + x;
                        GLColor expectedPixel(xOffset + x, yOffset + y, 0, 255);
                        GLColor actualPixel = pixels2[index];
                        EXPECT_EQ(expectedPixel, actualPixel);
                    }
                }
            }
    
            eglSwapBuffers(mDisplay, mWindowSurface);
            ASSERT_EGL_SUCCESS();
        }
    
        EGLDisplay mDisplay;
        EGLSurface mWindowSurface;
        EGLContext mContext;
        EGLConfig mConfig;
        OSWindow *mOSWindow;
        int mSize;
    };
    
    // Provide a predictable pattern for testing pre-rotation
    TEST_P(EGLPreRotationSurfaceTest, OrientedWindowWithDraw)
    {
        // http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(isVulkanRenderer() && IsLinux() && IsIntel());
    
        // Flaky on Linux SwANGLE http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(IsLinux() && isSwiftshader());
    
        // To aid in debugging, we want this window visible
        setWindowVisible(mOSWindow, true);
    
        initializeDisplay();
        initializeSurfaceWithRGBA8888Config();
    
        eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
        ASSERT_EGL_SUCCESS();
    
        // Init program
        constexpr char kVS[] =
            "attribute vec2 position;\n"
            "attribute vec2 redGreen;\n"
            "varying vec2 v_data;\n"
            "void main() {\n"
            "  gl_Position = vec4(position, 0, 1);\n"
            "  v_data = redGreen;\n"
            "}";
    
        constexpr char kFS[] =
            "varying highp vec2 v_data;\n"
            "void main() {\n"
            "  gl_FragColor = vec4(v_data, 0, 1);\n"
            "}";
    
        ANGLE_GL_PROGRAM(program, kVS, kFS);
        glUseProgram(program);
    
        GLint positionLocation = glGetAttribLocation(program, "position");
        ASSERT_NE(-1, positionLocation);
    
        GLint redGreenLocation = glGetAttribLocation(program, "redGreen");
        ASSERT_NE(-1, redGreenLocation);
    
        GLBuffer indexBuffer;
        GLVertexArray vertexArray;
        GLBuffer vertexBuffers[2];
    
        glBindVertexArray(vertexArray);
    
        std::vector<GLushort> indices = {0, 1, 2, 2, 3, 0};
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * indices.size(), &indices[0],
                     GL_STATIC_DRAW);
    
        std::vector<GLfloat> positionData = {// quad vertices
                                             -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f};
    
        glBindBuffer(GL_ARRAY_BUFFER, vertexBuffers[0]);
        glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * positionData.size(), &positionData[0],
                     GL_STATIC_DRAW);
        glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, nullptr);
        glEnableVertexAttribArray(positionLocation);
    
        std::vector<GLfloat> redGreenData = {// green(0,1), black(0,0), red(1,0), yellow(1,1)
                                             0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f};
    
        glBindBuffer(GL_ARRAY_BUFFER, vertexBuffers[1]);
        glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * redGreenData.size(), &redGreenData[0],
                     GL_STATIC_DRAW);
        glVertexAttribPointer(redGreenLocation, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, nullptr);
        glEnableVertexAttribArray(redGreenLocation);
    
        ASSERT_GL_NO_ERROR();
    
        testDrawingAndReadPixels();
    }
    
    // Use dFdx() and dFdy() and still provide a predictable pattern for testing pre-rotation
    // In this case, the color values will be the following: (dFdx(v_data.x), dFdy(v_data.y), 0, 1).
    // To help make this meaningful for pre-rotation, the derivatives will vary in the four corners of
    // the window:
    //
    //  +------------+------------+      +--------+--------+
    //  | (  0, 219) | (239, 249) |      | Green  | Yellow |
    //  +------------+------------+  OR  +--------+--------+
    //  | (  0,   0) | (229,   0) |      | Black  |  Red   |
    //  +------------+------------+      +--------+--------+
    TEST_P(EGLPreRotationSurfaceTest, OrientedWindowWithDerivativeDraw)
    {
        // http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(isVulkanRenderer() && IsLinux() && IsIntel());
    
        // Flaky on Linux SwANGLE http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(IsLinux() && isSwiftshader());
    
        // To aid in debugging, we want this window visible
        setWindowVisible(mOSWindow, true);
    
        initializeDisplay();
        initializeSurfaceWithRGBA8888Config();
    
        eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
        ASSERT_EGL_SUCCESS();
    
        // Init program
        constexpr char kVS[] =
            "#version 300 es\n"
            "in highp vec2 position;\n"
            "in highp vec2 redGreen;\n"
            "out highp vec2 v_data;\n"
            "void main() {\n"
            "  gl_Position = vec4(position, 0, 1);\n"
            "  v_data = redGreen;\n"
            "}";
    
        constexpr char kFS[] =
            "#version 300 es\n"
            "in highp vec2 v_data;\n"
            "out highp vec4 FragColor;\n"
            "void main() {\n"
            "  FragColor = vec4(dFdx(v_data.x), dFdy(v_data.y), 0, 1);\n"
            "}";
    
        ANGLE_GL_PROGRAM(program, kVS, kFS);
        glUseProgram(program);
    
        GLint positionLocation = glGetAttribLocation(program, "position");
        ASSERT_NE(-1, positionLocation);
    
        GLint redGreenLocation = glGetAttribLocation(program, "redGreen");
        ASSERT_NE(-1, redGreenLocation);
    
        GLBuffer indexBuffer;
        GLVertexArray vertexArray;
        GLBuffer vertexBuffers[2];
    
        glBindVertexArray(vertexArray);
    
        std::vector<GLushort> indices = {// 4 squares each made up of 6 vertices:
                                         // 1st square, in the upper-left part of window
                                         0, 1, 2, 2, 3, 0,
                                         // 2nd square, in the upper-right part of window
                                         4, 5, 6, 6, 7, 4,
                                         // 3rd square, in the lower-left part of window
                                         8, 9, 10, 10, 11, 8,
                                         // 4th square, in the lower-right part of window
                                         12, 13, 14, 14, 15, 12};
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * indices.size(), &indices[0],
                     GL_STATIC_DRAW);
    
        std::vector<GLfloat> positionData = {// 4 squares each made up of quad vertices
                                             // 1st square, in the upper-left part of window
                                             -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
                                             // 2nd square, in the upper-right part of window
                                             0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,
                                             // 3rd square, in the lower-left part of window
                                             -1.0f, 0.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f,
                                             // 4th square, in the lower-right part of window
                                             0.0f, 0.0f, 0.0f, -1.0f, 1.0f, -1.0f, 1.0f, 0.0f};
    
        glBindBuffer(GL_ARRAY_BUFFER, vertexBuffers[0]);
        glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * positionData.size(), &positionData[0],
                     GL_STATIC_DRAW);
        glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, nullptr);
        glEnableVertexAttribArray(positionLocation);
    
        std::vector<GLfloat> redGreenData = {// green(0,110), black(0,0), red(115,0), yellow(120,125)
                                             // 4 squares each made up of 4 pairs of half-color values:
                                             // 1st square, in the upper-left part of window
                                             0.0f, 110.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 110.0f,
                                             // 2nd square, in the upper-right part of window
                                             0.0f, 125.0f, 0.0f, 0.0f, 120.0f, 0.0f, 120.0f, 125.0f,
                                             // 3rd square, in the lower-left part of window
                                             0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
                                             // 4th square, in the lower-right part of window
                                             0.0f, 0.0f, 0.0f, 0.0f, 115.0f, 0.0f, 115.0f, 0.0f};
    
        glBindBuffer(GL_ARRAY_BUFFER, vertexBuffers[1]);
        glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * redGreenData.size(), &redGreenData[0],
                     GL_STATIC_DRAW);
        glVertexAttribPointer(redGreenLocation, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, nullptr);
        glEnableVertexAttribArray(redGreenLocation);
    
        ASSERT_GL_NO_ERROR();
    
        // Draw and check the 4 corner pixels, to ensure we're getting the expected "colors"
        glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_SHORT, nullptr);
        GLColor expectedPixelLowerLeft(0, 0, 0, 255);
        GLColor expectedPixelLowerRight(229, 0, 0, 255);
        GLColor expectedPixelUpperLeft(0, 219, 0, 255);
        GLColor expectedPixelUpperRight(239, 249, 0, 255);
        EXPECT_PIXEL_COLOR_EQ(0, 0, expectedPixelLowerLeft);
        EXPECT_PIXEL_COLOR_EQ(mSize - 1, 0, expectedPixelLowerRight);
        EXPECT_PIXEL_COLOR_EQ(0, mSize - 1, expectedPixelUpperLeft);
        EXPECT_PIXEL_COLOR_EQ(mSize - 1, mSize - 1, expectedPixelUpperRight);
        ASSERT_GL_NO_ERROR();
    
        // Make the image visible
        eglSwapBuffers(mDisplay, mWindowSurface);
        ASSERT_EGL_SUCCESS();
    
        // Draw again and check the 4 center pixels, to ensure we're getting the expected "colors"
        glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_SHORT, nullptr);
        EXPECT_PIXEL_COLOR_EQ((mSize / 2) - 1, (mSize / 2) - 1, expectedPixelLowerLeft);
        EXPECT_PIXEL_COLOR_EQ((mSize / 2) - 1, (mSize / 2), expectedPixelUpperLeft);
        EXPECT_PIXEL_COLOR_EQ((mSize / 2), (mSize / 2) - 1, expectedPixelLowerRight);
        EXPECT_PIXEL_COLOR_EQ((mSize / 2), (mSize / 2), expectedPixelUpperRight);
        ASSERT_GL_NO_ERROR();
    }
    
    // Android-specific test that changes a window's rotation, which requires ContextVk::syncState() to
    // handle the new rotation
    TEST_P(EGLPreRotationSurfaceTest, ChangeRotationWithDraw)
    {
        // This test uses functionality that is only available on Android
        ANGLE_SKIP_TEST_IF(isVulkanRenderer() && !IsAndroid());
    
        // To aid in debugging, we want this window visible
        setWindowVisible(mOSWindow, true);
    
        initializeDisplay();
        initializeSurfaceWithRGBA8888Config();
    
        eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
        ASSERT_EGL_SUCCESS();
    
        // Init program
        constexpr char kVS[] =
            "attribute vec2 position;\n"
            "attribute vec2 redGreen;\n"
            "varying vec2 v_data;\n"
            "void main() {\n"
            "  gl_Position = vec4(position, 0, 1);\n"
            "  v_data = redGreen;\n"
            "}";
    
        constexpr char kFS[] =
            "varying highp vec2 v_data;\n"
            "void main() {\n"
            "  gl_FragColor = vec4(v_data, 0, 1);\n"
            "}";
    
        ANGLE_GL_PROGRAM(program, kVS, kFS);
        glUseProgram(program);
    
        GLint positionLocation = glGetAttribLocation(program, "position");
        ASSERT_NE(-1, positionLocation);
    
        GLint redGreenLocation = glGetAttribLocation(program, "redGreen");
        ASSERT_NE(-1, redGreenLocation);
    
        GLBuffer indexBuffer;
        GLVertexArray vertexArray;
        GLBuffer vertexBuffers[2];
    
        glBindVertexArray(vertexArray);
    
        std::vector<GLushort> indices = {0, 1, 2, 2, 3, 0};
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * indices.size(), &indices[0],
                     GL_STATIC_DRAW);
    
        std::vector<GLfloat> positionData = {// quad vertices
                                             -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f};
    
        glBindBuffer(GL_ARRAY_BUFFER, vertexBuffers[0]);
        glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * positionData.size(), &positionData[0],
                     GL_STATIC_DRAW);
        glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, nullptr);
        glEnableVertexAttribArray(positionLocation);
    
        std::vector<GLfloat> redGreenData = {// green(0,1), black(0,0), red(1,0), yellow(1,1)
                                             0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f};
    
        glBindBuffer(GL_ARRAY_BUFFER, vertexBuffers[1]);
        glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * redGreenData.size(), &redGreenData[0],
                     GL_STATIC_DRAW);
        glVertexAttribPointer(redGreenLocation, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, nullptr);
        glEnableVertexAttribArray(redGreenLocation);
    
        ASSERT_GL_NO_ERROR();
    
        // Change the rotation back and forth between landscape and portrait, and make sure that the
        // drawing and reading happen consistently with the desired rotation.
        // Last rotation needs to be portrait, since other tests expect it to be the default.
        for (int i = 0; i < 4; i++)
        {
            bool landscape;
            EGLint actualWidth   = 0;
            EGLint actualHeight  = 0;
            EGLint desiredWidth  = 0;
            EGLint desiredHeight = 0;
            if ((i % 2) == 0)
            {
                landscape     = true;
                desiredWidth  = 300;
                desiredHeight = 200;
            }
            else
            {
                landscape     = false;
                desiredWidth  = 200;
                desiredHeight = 300;
            }
            mOSWindow->resize(desiredWidth, desiredHeight);
            // setOrientation() uses a reverse-JNI call, which sends data to other parts of Android.
            // Sometime later (i.e. asynchronously), the window is updated.  Sleep a little here, and
            // then allow for multiple eglSwapBuffers calls to eventually see the new rotation.
            mOSWindow->setOrientation(desiredWidth, desiredHeight);
            angle::Sleep(1000);
            eglSwapBuffers(mDisplay, mWindowSurface);
            ASSERT_EGL_SUCCESS();
    
            while ((actualWidth != desiredWidth) && (actualHeight != desiredHeight))
            {
                glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr);
                EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black);
                if (landscape)
                {
                    EXPECT_PIXEL_COLOR_EQ(mSize - 1, 0, GLColor::red);
                }
                else
                {
                    EXPECT_PIXEL_COLOR_EQ(0, mSize - 1, GLColor::green);
                }
                ASSERT_GL_NO_ERROR();
    
                eglSwapBuffers(mDisplay, mWindowSurface);
                ASSERT_EGL_SUCCESS();
    
                eglQuerySurface(mDisplay, mWindowSurface, EGL_HEIGHT, &actualHeight);
                eglQuerySurface(mDisplay, mWindowSurface, EGL_WIDTH, &actualWidth);
            }
        }
    }
    
    // A slight variation of EGLPreRotationSurfaceTest, where the initial window size is 400x300, yet
    // the drawing is still 256x256.  In addition, gl_FragCoord is used in a "clever" way, as the color
    // of the 256x256 drawing area, which reproduces an interesting pre-rotation case from the
    // following dEQP tests:
    //
    // - dEQP.GLES31/functional_texture_multisample_samples_*_sample_position
    //
    // This will test the rotation of gl_FragCoord, as well as the viewport, scissor, and rendering
    // area calculations, especially when the Android device is rotated.
    class EGLPreRotationLargeSurfaceTest : public EGLPreRotationSurfaceTest
    {
      protected:
        EGLPreRotationLargeSurfaceTest() : mSize(256) {}
    
        void testSetUp() override
        {
            mOSWindow = OSWindow::New();
            mOSWindow->initialize("EGLSurfaceTest", 400, 300);
        }
    
        int mSize;
    };
    
    // Provide a predictable pattern for testing pre-rotation
    TEST_P(EGLPreRotationLargeSurfaceTest, OrientedWindowWithFragCoordDraw)
    {
        // http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(isVulkanRenderer() && IsLinux() && IsIntel());
    
        // Flaky on Linux SwANGLE http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(IsLinux() && isSwiftshader());
    
        // To aid in debugging, we want this window visible
        setWindowVisible(mOSWindow, true);
    
        initializeDisplay();
        initializeSurfaceWithRGBA8888Config();
    
        eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
        ASSERT_EGL_SUCCESS();
    
        // Init program
        constexpr char kVS[] =
            "attribute vec2 position;\n"
            "void main() {\n"
            "  gl_Position = vec4(position, 0, 1);\n"
            "}";
    
        constexpr char kFS[] =
            "void main() {\n"
            "  gl_FragColor = vec4(gl_FragCoord.x / 256.0, gl_FragCoord.y / 256.0, 0.0, 1.0);\n"
            "}";
    
        ANGLE_GL_PROGRAM(program, kVS, kFS);
        glUseProgram(program);
    
        GLint positionLocation = glGetAttribLocation(program, "position");
        ASSERT_NE(-1, positionLocation);
    
        GLBuffer indexBuffer;
        GLVertexArray vertexArray;
        GLBuffer vertexBuffer;
    
        glBindVertexArray(vertexArray);
    
        std::vector<GLushort> indices = {0, 1, 2, 2, 3, 0};
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * indices.size(), &indices[0],
                     GL_STATIC_DRAW);
    
        std::vector<GLfloat> positionData = {// quad vertices
                                             -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f};
    
        glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
        glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * positionData.size(), &positionData[0],
                     GL_STATIC_DRAW);
        glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, nullptr);
        glEnableVertexAttribArray(positionLocation);
    
        ASSERT_GL_NO_ERROR();
    
        glViewport(0, 0, mSize, mSize);
    
        testDrawingAndReadPixels();
    }
    
    // Pre-rotation tests for glBlitFramebuffer.  A slight variation of EGLPreRotationLargeSurfaceTest,
    // where the initial window size is still 400x300, and the drawing is still 256x256.  In addition,
    // glBlitFramebuffer is tested in a variety of ways.  Separate tests are used to make debugging
    // simpler, but they all share common setup.  These tests reproduce interesting pre-rotation cases
    // from dEQP tests such as the following:
    //
    // - dEQP.GLES3/functional_fbo_blit_default_framebuffer_*
    // - dEQP.GLES3/functional_fbo_invalidate_*
    constexpr GLuint kCoordMidWayShort       = 127;
    constexpr GLuint kCoordMidWayLong        = 128;
    constexpr GLColor kColorMidWayShortShort = GLColor(127, 127, 0, 255);
    constexpr GLColor kColorMidWayShortLong  = GLColor(127, 128, 0, 255);
    constexpr GLColor kColorMidWayLongShort  = GLColor(128, 127, 0, 255);
    constexpr GLColor kColorMidWayLongLong   = GLColor(128, 128, 0, 255);
    // When scaling horizontally, the "black" and "green" colors have a 1 in the red component
    constexpr GLColor kColorScaleHorizBlack = GLColor(1, 0, 0, 255);
    constexpr GLColor kColorScaleHorizGreen = GLColor(1, 255, 0, 255);
    // When scaling vertically, the "black" and "red" colors have a 1 in the green component
    constexpr GLColor kColorScaleVertBlack = GLColor(0, 1, 0, 255);
    constexpr GLColor kColorScaleVertRed   = GLColor(255, 1, 0, 255);
    
    class EGLPreRotationBlitFramebufferTest : public EGLPreRotationLargeSurfaceTest
    {
      protected:
        EGLPreRotationBlitFramebufferTest() {}
    
        GLuint createProgram()
        {
            constexpr char kVS[] =
                "attribute vec2 position;\n"
                "attribute vec2 redGreen;\n"
                "varying vec2 v_data;\n"
                "void main() {\n"
                "  gl_Position = vec4(position, 0, 1);\n"
                "  v_data = redGreen;\n"
                "}";
    
            constexpr char kFS[] =
                "varying highp vec2 v_data;\n"
                "void main() {\n"
                "  gl_FragColor = vec4(v_data, 0, 1);\n"
                "}";
    
            return CompileProgram(kVS, kFS);
        }
    
        void initializeGeometry(GLuint program,
                                GLBuffer *indexBuffer,
                                GLVertexArray *vertexArray,
                                GLBuffer *vertexBuffers)
        {
            GLint positionLocation = glGetAttribLocation(program, "position");
            ASSERT_NE(-1, positionLocation);
    
            GLint redGreenLocation = glGetAttribLocation(program, "redGreen");
            ASSERT_NE(-1, redGreenLocation);
    
            glBindVertexArray(*vertexArray);
    
            std::vector<GLushort> indices = {0, 1, 2, 2, 3, 0};
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, *indexBuffer);
            glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * indices.size(), &indices[0],
                         GL_STATIC_DRAW);
    
            std::vector<GLfloat> positionData = {// quad vertices
                                                 -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f};
    
            glBindBuffer(GL_ARRAY_BUFFER, vertexBuffers[0]);
            glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * positionData.size(), &positionData[0],
                         GL_STATIC_DRAW);
            glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2,
                                  nullptr);
            glEnableVertexAttribArray(positionLocation);
    
            std::vector<GLfloat> redGreenData = {// green(0,1), black(0,0), red(1,0), yellow(1,1)
                                                 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f};
    
            glBindBuffer(GL_ARRAY_BUFFER, vertexBuffers[1]);
            glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * redGreenData.size(), &redGreenData[0],
                         GL_STATIC_DRAW);
            glVertexAttribPointer(redGreenLocation, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2,
                                  nullptr);
            glEnableVertexAttribArray(redGreenLocation);
        }
    
        void initializeFBO(GLFramebuffer *framebuffer, GLTexture *texture)
        {
            glBindTexture(GL_TEXTURE_2D, *texture);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, mSize, mSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                         nullptr);
    
            glBindFramebuffer(GL_FRAMEBUFFER, *framebuffer);
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *texture, 0);
        }
    
        // Ensures that the correct colors are where they should be when the entire 256x256 pattern has
        // been rendered or blitted to a location relative to an x and y offset.
        void test256x256PredictablePattern(GLint xOffset, GLint yOffset)
        {
            EXPECT_PIXEL_COLOR_EQ(xOffset + 0, yOffset + 0, GLColor::black);
            EXPECT_PIXEL_COLOR_EQ(xOffset + 0, yOffset + mSize - 1, GLColor::green);
            EXPECT_PIXEL_COLOR_EQ(xOffset + mSize - 1, yOffset + 0, GLColor::red);
            EXPECT_PIXEL_COLOR_EQ(xOffset + mSize - 1, yOffset + mSize - 1, GLColor::yellow);
            EXPECT_PIXEL_COLOR_EQ(xOffset + kCoordMidWayShort, yOffset + kCoordMidWayShort,
                                  kColorMidWayShortShort);
            EXPECT_PIXEL_COLOR_EQ(xOffset + kCoordMidWayShort, yOffset + kCoordMidWayLong,
                                  kColorMidWayShortLong);
            EXPECT_PIXEL_COLOR_EQ(xOffset + kCoordMidWayLong, yOffset + kCoordMidWayShort,
                                  kColorMidWayLongShort);
            EXPECT_PIXEL_COLOR_EQ(xOffset + kCoordMidWayLong, yOffset + kCoordMidWayLong,
                                  kColorMidWayLongLong);
        }
    };
    
    // Draw a predictable pattern (for testing pre-rotation) into an FBO, and then use glBlitFramebuffer
    // to blit that pattern into various places within the 400x300 window
    TEST_P(EGLPreRotationBlitFramebufferTest, BasicBlitFramebuffer)
    {
        // http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(isVulkanRenderer() && IsLinux() && IsIntel());
    
        // Flaky on Linux SwANGLE http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(IsLinux() && isSwiftshader());
    
        // To aid in debugging, we want this window visible
        setWindowVisible(mOSWindow, true);
    
        initializeDisplay();
        initializeSurfaceWithRGBA8888Config();
    
        eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
        ASSERT_EGL_SUCCESS();
    
        // Init program
        GLuint program = createProgram();
        ASSERT_NE(0u, program);
        glUseProgram(program);
    
        GLBuffer indexBuffer;
        GLVertexArray vertexArray;
        GLBuffer vertexBuffers[2];
        initializeGeometry(program, &indexBuffer, &vertexArray, vertexBuffers);
        ASSERT_GL_NO_ERROR();
    
        // Create a texture-backed FBO and render the predictable pattern to it
        GLFramebuffer fbo;
        GLTexture texture;
        initializeFBO(&fbo, &texture);
        ASSERT_GL_NO_ERROR();
    
        glViewport(0, 0, mSize, mSize);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr);
        ASSERT_GL_NO_ERROR();
    
        // Ensure the predictable pattern seems correct in the FBO
        test256x256PredictablePattern(0, 0);
        ASSERT_GL_NO_ERROR();
    
        //
        // Test blitting the entire FBO image to a 256x256 part of the default framebuffer (no scaling)
        //
    
        // Blit from the FBO to the default framebuffer (i.e. the swapchain)
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        glBlitFramebuffer(0, 0, mSize, mSize, 0, 0, mSize, mSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
        ASSERT_GL_NO_ERROR();
    
        // Swap buffers to put the image in the window (so the test can be visually checked)
        eglSwapBuffers(mDisplay, mWindowSurface);
        ASSERT_GL_NO_ERROR();
    
        // Blit again to check the colors in the back buffer
        glClear(GL_COLOR_BUFFER_BIT);
        glBlitFramebuffer(0, 0, mSize, mSize, 0, 0, mSize, mSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
        test256x256PredictablePattern(0, 0);
        ASSERT_GL_NO_ERROR();
    
        // Clear to black and blit to a different part of the window
        glClear(GL_COLOR_BUFFER_BIT);
        GLint xOffset = 40;
        GLint yOffset = 30;
        glViewport(xOffset, yOffset, mSize, mSize);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
        glBlitFramebuffer(0, 0, mSize, mSize, xOffset, yOffset, xOffset + mSize, yOffset + mSize,
                          GL_COLOR_BUFFER_BIT, GL_NEAREST);
    
        // Swap buffers to put the image in the window (so the test can be visually checked)
        eglSwapBuffers(mDisplay, mWindowSurface);
        ASSERT_GL_NO_ERROR();
    
        // Blit again to check the colors in the back buffer
        glClear(GL_COLOR_BUFFER_BIT);
        glBlitFramebuffer(0, 0, mSize, mSize, xOffset, yOffset, xOffset + mSize, yOffset + mSize,
                          GL_COLOR_BUFFER_BIT, GL_NEAREST);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
        test256x256PredictablePattern(xOffset, yOffset);
        ASSERT_GL_NO_ERROR();
    
        ASSERT_EGL_SUCCESS();
    }
    
    // Blit the ms0 stencil buffer to the default framebuffer with rotation on android.
    TEST_P(EGLPreRotationBlitFramebufferTest, BlitStencilWithRotation)
    {
        // http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(isVulkanRenderer() && IsLinux() && IsIntel());
    
        // Flaky on Linux SwANGLE http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(IsLinux() && isSwiftshader());
    
        setWindowVisible(mOSWindow, true);
    
        initializeDisplay();
        initializeSurfaceWithRGBA8888d24s8Config();
    
        eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
        ASSERT_EGL_SUCCESS();
    
        mOSWindow->setOrientation(300, 400);
        angle::Sleep(1000);
        eglSwapBuffers(mDisplay, mWindowSurface);
        ASSERT_EGL_SUCCESS();
    
        GLRenderbuffer colorbuf;
        glBindRenderbuffer(GL_RENDERBUFFER, colorbuf.get());
        glRenderbufferStorageMultisample(GL_RENDERBUFFER, 0, GL_RGBA8, 64, 128);
    
        GLRenderbuffer depthstencilbuf;
        glBindRenderbuffer(GL_RENDERBUFFER, depthstencilbuf.get());
        glRenderbufferStorageMultisample(GL_RENDERBUFFER, 0, GL_DEPTH24_STENCIL8, 64, 128);
    
        GLFramebuffer framebuffer;
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorbuf);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
                                  depthstencilbuf);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
                                  depthstencilbuf);
        glCheckFramebufferStatus(GL_FRAMEBUFFER);
    
        glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
        // Replace stencil to 1.
        ANGLE_GL_PROGRAM(drawRed, essl3_shaders::vs::Simple(), essl3_shaders::fs::Red());
        glEnable(GL_STENCIL_TEST);
        glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
        glStencilFunc(GL_ALWAYS, 1, 255);
        drawQuad(drawRed.get(), essl3_shaders::PositionAttrib(), 0.8f);
    
        // Blit stencil buffer to default frambuffer.
        GLenum attachments1[] = {GL_COLOR_ATTACHMENT0};
        glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, attachments1);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
        glBlitFramebuffer(0, 0, 64, 128, 0, 0, 64, 128, GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT,
                          GL_NEAREST);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
    
        ANGLE_GL_PROGRAM(drawGreen, essl3_shaders::vs::Simple(), essl3_shaders::fs::Green());
        glDisable(GL_STENCIL_TEST);
        drawQuad(drawGreen.get(), essl3_shaders::PositionAttrib(), 0.5f);
    
        // Draw blue color if the stencil is equal to 1.
        // If the blit finished successfully, the stencil test should all pass.
        ANGLE_GL_PROGRAM(drawBlue, essl3_shaders::vs::Simple(), essl3_shaders::fs::Blue());
        glEnable(GL_STENCIL_TEST);
        glStencilFunc(GL_EQUAL, 1, 255);
        drawQuad(drawBlue.get(), essl3_shaders::PositionAttrib(), 0.3f);
    
        // Check the result, especially the boundaries.
        EXPECT_PIXEL_COLOR_EQ(0, 127, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(32, 127, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(32, 0, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(63, 0, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(63, 64, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(32, 64, GLColor::blue);
    
        // Some pixels around x=0/63 (related to the pre-rotation degree) still fail on android.
        // From the image in the window, the failures near one of the image's edge look like "aliasing".
        // We need to fix blit with pre-rotation. http://anglebug.com/5044
        // EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
        // EXPECT_PIXEL_COLOR_EQ(0, 64, GLColor::blue);
        // EXPECT_PIXEL_COLOR_EQ(63, 1, GLColor::blue);
        // EXPECT_PIXEL_COLOR_EQ(63, 127, GLColor::blue);
    
        eglSwapBuffers(mDisplay, mWindowSurface);
    
        ASSERT_GL_NO_ERROR();
    }
    
    // Blit the multisample stencil buffer to the default framebuffer with rotation on android.
    TEST_P(EGLPreRotationBlitFramebufferTest, BlitMultisampleStencilWithRotation)
    {
        // http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(isVulkanRenderer() && IsLinux() && IsIntel());
    
        // Flaky on Linux SwANGLE http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(IsLinux() && isSwiftshader());
    
        setWindowVisible(mOSWindow, true);
    
        initializeDisplay();
        initializeSurfaceWithRGBA8888d24s8Config();
    
        eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
        ASSERT_EGL_SUCCESS();
    
        mOSWindow->setOrientation(300, 400);
        angle::Sleep(1000);
        eglSwapBuffers(mDisplay, mWindowSurface);
        ASSERT_EGL_SUCCESS();
    
        GLRenderbuffer colorbuf;
        glBindRenderbuffer(GL_RENDERBUFFER, colorbuf.get());
        glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGBA8, 128, 64);
    
        GLRenderbuffer depthstencilbuf;
        glBindRenderbuffer(GL_RENDERBUFFER, depthstencilbuf.get());
        glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, 128, 64);
    
        GLFramebuffer framebuffer;
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorbuf);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
                                  depthstencilbuf);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
                                  depthstencilbuf);
        glCheckFramebufferStatus(GL_FRAMEBUFFER);
    
        glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
        // Replace stencil to 1.
        ANGLE_GL_PROGRAM(drawRed, essl3_shaders::vs::Simple(), essl3_shaders::fs::Red());
        glEnable(GL_STENCIL_TEST);
        glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
        glStencilFunc(GL_ALWAYS, 1, 255);
        drawQuad(drawRed.get(), essl3_shaders::PositionAttrib(), 0.8f);
    
        // Blit multisample stencil buffer to default frambuffer.
        GLenum attachments1[] = {GL_COLOR_ATTACHMENT0};
        glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, attachments1);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
        glBlitFramebuffer(0, 0, 128, 64, 0, 0, 128, 64, GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT,
                          GL_NEAREST);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
    
        ANGLE_GL_PROGRAM(drawGreen, essl3_shaders::vs::Simple(), essl3_shaders::fs::Green());
        glDisable(GL_STENCIL_TEST);
        drawQuad(drawGreen.get(), essl3_shaders::PositionAttrib(), 0.5f);
    
        // Draw blue color if the stencil is equal to 1.
        // If the blit finished successfully, the stencil test should all pass.
        ANGLE_GL_PROGRAM(drawBlue, essl3_shaders::vs::Simple(), essl3_shaders::fs::Blue());
        glEnable(GL_STENCIL_TEST);
        glStencilFunc(GL_EQUAL, 1, 255);
        drawQuad(drawBlue.get(), essl3_shaders::PositionAttrib(), 0.3f);
    
        // Check the result, especially the boundaries.
        EXPECT_PIXEL_COLOR_EQ(127, 32, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(64, 32, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(0, 63, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(64, 63, GLColor::blue);
    
        // Some pixels around x=0/127 or y=0 (related to the pre-rotation degree)still fail on android.
        // We need to fix blit with pre-rotation. http://anglebug.com/5044
        // Failures of Rotated90Degrees.
        // EXPECT_PIXEL_COLOR_EQ(127, 1, GLColor::blue);
        // EXPECT_PIXEL_COLOR_EQ(127, 63, GLColor::blue);
        // Failures of Rotated180Degrees.
        // EXPECT_PIXEL_COLOR_EQ(64, 0, GLColor::blue);
        // EXPECT_PIXEL_COLOR_EQ(127, 0, GLColor::blue);
        // Failures of Rotated270Degrees.
        // EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
        // EXPECT_PIXEL_COLOR_EQ(0, 32, GLColor::blue);
    
        eglSwapBuffers(mDisplay, mWindowSurface);
    
        ASSERT_GL_NO_ERROR();
    }
    
    // Blit stencil to default framebuffer with flip and prerotation.
    TEST_P(EGLPreRotationBlitFramebufferTest, BlitStencilWithFlip)
    {
        // http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(isVulkanRenderer() && IsLinux() && IsIntel());
    
        // Flaky on Linux SwANGLE http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(IsLinux() && isSwiftshader());
    
        // We need to fix blit with pre-rotation. http://anglebug.com/5044
        ANGLE_SKIP_TEST_IF(IsAndroid() || IsWindows());
    
        // To aid in debugging, we want this window visible
        setWindowVisible(mOSWindow, true);
    
        initializeDisplay();
        initializeSurfaceWithRGBA8888d24s8Config();
    
        eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
        ASSERT_EGL_SUCCESS();
    
        mOSWindow->setOrientation(300, 400);
        angle::Sleep(1000);
        eglSwapBuffers(mDisplay, mWindowSurface);
        ASSERT_EGL_SUCCESS();
    
        constexpr int kSize = 128;
        glViewport(0, 0, kSize, kSize);
    
        GLRenderbuffer colorbuf;
        glBindRenderbuffer(GL_RENDERBUFFER, colorbuf.get());
        glRenderbufferStorageMultisample(GL_RENDERBUFFER, 0, GL_RGBA8, kSize, kSize);
    
        GLRenderbuffer depthstencilbuf;
        glBindRenderbuffer(GL_RENDERBUFFER, depthstencilbuf.get());
        glRenderbufferStorageMultisample(GL_RENDERBUFFER, 0, GL_DEPTH24_STENCIL8, kSize, kSize);
    
        GLFramebuffer framebuffer;
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorbuf);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
                                  depthstencilbuf);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
                                  depthstencilbuf);
        glCheckFramebufferStatus(GL_FRAMEBUFFER);
    
        glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
        // Replace stencil to 1.
        ANGLE_GL_PROGRAM(drawRed, essl3_shaders::vs::Simple(), essl3_shaders::fs::Red());
        glEnable(GL_STENCIL_TEST);
        glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
        glStencilFunc(GL_ALWAYS, 1, 255);
        drawQuad(drawRed.get(), essl3_shaders::PositionAttrib(), 0.8f);
    
        // Blit stencil buffer to default frambuffer with X-flip.
        GLenum attachments1[] = {GL_COLOR_ATTACHMENT0};
        glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, attachments1);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
        glBlitFramebuffer(0, 0, kSize, kSize, kSize, 0, 0, kSize,
                          GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
    
        ANGLE_GL_PROGRAM(drawGreen, essl3_shaders::vs::Simple(), essl3_shaders::fs::Green());
        glDisable(GL_STENCIL_TEST);
        drawQuad(drawGreen.get(), essl3_shaders::PositionAttrib(), 0.5f);
    
        // Draw blue color if the stencil is equal to 1.
        // If the blit finished successfully, the stencil test should all pass.
        glEnable(GL_STENCIL_TEST);
        glStencilFunc(GL_EQUAL, 1, 255);
        ANGLE_GL_PROGRAM(gradientProgram, essl31_shaders::vs::Passthrough(),
                         essl31_shaders::fs::RedGreenGradient());
        drawQuad(gradientProgram, essl31_shaders::PositionAttrib(), 0.5f, 1.0f, true);
    
        // Check the result, especially the boundaries.
        EXPECT_PIXEL_NEAR(0, 0, 0, 0, 0, 255, 1.0);                      // Black
        EXPECT_PIXEL_NEAR(kSize - 1, 0, 253, 0, 0, 255, 1.0);            // Red
        EXPECT_PIXEL_NEAR(0, kSize - 1, 0, 253, 0, 255, 1.0);            // Green
        EXPECT_PIXEL_NEAR(kSize - 1, kSize - 1, 253, 253, 0, 255, 1.0);  // Yellow
    
        eglSwapBuffers(mDisplay, mWindowSurface);
    
        ASSERT_GL_NO_ERROR();
    }
    
    // Blit color buffer to default framebuffer with Y-flip/X-flip.
    TEST_P(EGLPreRotationBlitFramebufferTest, BlitColorToDefault)
    {
        // This test uses functionality that is only available on Android
        ANGLE_SKIP_TEST_IF(isVulkanRenderer() && !IsAndroid());
    
        // To aid in debugging, we want this window visible
        setWindowVisible(mOSWindow, true);
    
        initializeDisplay();
        initializeSurfaceWithRGBA8888Config();
    
        eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
        ASSERT_EGL_SUCCESS();
    
        constexpr int kSize = 128;
        glViewport(0, 0, kSize, kSize);
    
        GLRenderbuffer colorbuf;
        glBindRenderbuffer(GL_RENDERBUFFER, colorbuf.get());
        glRenderbufferStorageMultisample(GL_RENDERBUFFER, 0, GL_RGBA8, kSize, kSize);
    
        GLFramebuffer framebuffer;
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorbuf);
    
        glCheckFramebufferStatus(GL_FRAMEBUFFER);
    
        glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
    
        ANGLE_GL_PROGRAM(gradientProgram, essl31_shaders::vs::Passthrough(),
                         essl31_shaders::fs::RedGreenGradient());
    
        EGLint desiredWidth  = 300;
        EGLint desiredHeight = 400;
        mOSWindow->resize(desiredWidth, desiredHeight);
        mOSWindow->setOrientation(desiredWidth, desiredHeight);
        angle::Sleep(1000);
        eglSwapBuffers(mDisplay, mWindowSurface);
        ASSERT_EGL_SUCCESS();
    
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.get());
        drawQuad(gradientProgram, essl31_shaders::PositionAttrib(), 0.5f, 1.0f, true);
    
        // Blit color buffer to default frambuffer without flip.
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
        glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
    
        // Check the result, especially the boundaries.
        EXPECT_PIXEL_NEAR(0, 0, 0, 0, 0, 255, 1.0);                      // Balck
        EXPECT_PIXEL_NEAR(kSize - 1, 0, 253, 0, 0, 255, 1.0);            // Red
        EXPECT_PIXEL_NEAR(0, kSize - 1, 0, 253, 0, 255, 1.0);            // Green
        EXPECT_PIXEL_NEAR(kSize - 1, kSize - 1, 253, 253, 0, 255, 1.0);  // Yellow
    
        // Blit color buffer to default frambuffer with Y-flip.
        glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer.get());
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
        glBlitFramebuffer(0, 0, kSize, kSize, 0, kSize, kSize, 0, GL_COLOR_BUFFER_BIT, GL_NEAREST);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
    
        EXPECT_PIXEL_NEAR(0, 0, 0, 253, 0, 255, 1.0);                  // Green
        EXPECT_PIXEL_NEAR(kSize - 1, 0, 253, 253, 0, 255, 1.0);        // Yellow
        EXPECT_PIXEL_NEAR(0, kSize - 1, 0, 0, 0, 255, 1.0);            // Balck
        EXPECT_PIXEL_NEAR(kSize - 1, kSize - 1, 253, 0, 0, 255, 1.0);  // Red
    
        // Blit color buffer to default frambuffer with X-flip.
        glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer.get());
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
        glBlitFramebuffer(0, 0, kSize, kSize, kSize, 0, 0, kSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
    
        EXPECT_PIXEL_NEAR(0, 0, 253, 0, 0, 255, 1.0);                  // Red
        EXPECT_PIXEL_NEAR(kSize - 1, 0, 0, 0, 0, 255, 1.0);            // Balck
        EXPECT_PIXEL_NEAR(0, kSize - 1, 253, 253, 0, 255, 1.0);        // Yellow
        EXPECT_PIXEL_NEAR(kSize - 1, kSize - 1, 0, 253, 0, 255, 1.0);  // Green
    
        ASSERT_GL_NO_ERROR();
    }
    
    // Blit color buffer from default framebuffer with Y-flip/X-flip.
    TEST_P(EGLPreRotationBlitFramebufferTest, BlitColorFromDefault)
    {
        // This test uses functionality that is only available on Android
        ANGLE_SKIP_TEST_IF(isVulkanRenderer() && !IsAndroid());
    
        // To aid in debugging, we want this window visible
        setWindowVisible(mOSWindow, true);
    
        initializeDisplay();
        initializeSurfaceWithRGBA8888Config();
    
        eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
        ASSERT_EGL_SUCCESS();
    
        constexpr int kSize = 128;
        glViewport(0, 0, kSize, kSize);
    
        GLRenderbuffer colorbuf;
        glBindRenderbuffer(GL_RENDERBUFFER, colorbuf.get());
        glRenderbufferStorageMultisample(GL_RENDERBUFFER, 0, GL_RGBA8, kSize, kSize);
    
        GLFramebuffer framebuffer;
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorbuf);
        glCheckFramebufferStatus(GL_FRAMEBUFFER);
    
        glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
    
        ANGLE_GL_PROGRAM(gradientProgram, essl31_shaders::vs::Passthrough(),
                         essl31_shaders::fs::RedGreenGradient());
    
        EGLint desiredWidth  = 300;
        EGLint desiredHeight = 400;
        mOSWindow->resize(desiredWidth, desiredHeight);
        mOSWindow->setOrientation(desiredWidth, desiredHeight);
        angle::Sleep(1000);
        eglSwapBuffers(mDisplay, mWindowSurface);
        ASSERT_EGL_SUCCESS();
    
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        drawQuad(gradientProgram, essl31_shaders::PositionAttrib(), 0.5f, 1.0f, true);
    
        // Blit color buffer from default frambuffer without flip.
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.get());
        glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer.get());
    
        // Check the result, especially the boundaries.
        EXPECT_PIXEL_NEAR(0, 0, 0, 0, 0, 255, 1.0);                      // Balck
        EXPECT_PIXEL_NEAR(kSize - 1, 0, 253, 0, 0, 255, 1.0);            // Red
        EXPECT_PIXEL_NEAR(0, kSize - 1, 0, 253, 0, 255, 1.0);            // Green
        EXPECT_PIXEL_NEAR(kSize - 1, kSize - 1, 253, 253, 0, 255, 1.0);  // Yellow
    
        // Blit color buffer from default frambuffer with Y-flip.
        glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.get());
        glBlitFramebuffer(0, 0, kSize, kSize, 0, kSize, kSize, 0, GL_COLOR_BUFFER_BIT, GL_NEAREST);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer.get());
    
        EXPECT_PIXEL_NEAR(0, 0, 0, 253, 0, 255, 1.0);                  // Green
        EXPECT_PIXEL_NEAR(kSize - 1, 0, 253, 253, 0, 255, 1.0);        // Yellow
        EXPECT_PIXEL_NEAR(0, kSize - 1, 0, 0, 0, 255, 1.0);            // Balck
        EXPECT_PIXEL_NEAR(kSize - 1, kSize - 1, 253, 0, 0, 255, 1.0);  // Red
    
        // Blit color buffer from default frambuffer with X-flip.
        glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.get());
        glBlitFramebuffer(0, 0, kSize, kSize, kSize, 0, 0, kSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer.get());
    
        EXPECT_PIXEL_NEAR(0, 0, 253, 0, 0, 255, 1.0);                  // Red
        EXPECT_PIXEL_NEAR(kSize - 1, 0, 0, 0, 0, 255, 1.0);            // Balck
        EXPECT_PIXEL_NEAR(0, kSize - 1, 253, 253, 0, 255, 1.0);        // Yellow
        EXPECT_PIXEL_NEAR(kSize - 1, kSize - 1, 0, 253, 0, 255, 1.0);  // Green
    
        ASSERT_GL_NO_ERROR();
    }
    
    // Blit multisample color buffer to resolved framebuffer.
    TEST_P(EGLPreRotationBlitFramebufferTest, BlitMultisampleColorToResolved)
    {
        // This test uses functionality that is only available on Android
        ANGLE_SKIP_TEST_IF(isVulkanRenderer() && !IsAndroid());
    
        // To aid in debugging, we want this window visible
        setWindowVisible(mOSWindow, true);
    
        initializeDisplay();
        initializeSurfaceWithRGBA8888Config();
    
        eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
        ASSERT_EGL_SUCCESS();
    
        constexpr int kSize = 128;
        glViewport(0, 0, kSize, kSize);
    
        GLRenderbuffer colorMS;
        glBindRenderbuffer(GL_RENDERBUFFER, colorMS.get());
        glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGBA8, kSize, kSize);
    
        GLRenderbuffer colorResolved;
        glBindRenderbuffer(GL_RENDERBUFFER, colorResolved.get());
        glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kSize, kSize);
    
        GLFramebuffer framebufferMS;
        glBindFramebuffer(GL_FRAMEBUFFER, framebufferMS.get());
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorMS);
    
        glCheckFramebufferStatus(GL_FRAMEBUFFER);
    
        glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
    
        ANGLE_GL_PROGRAM(gradientProgram, essl31_shaders::vs::Passthrough(),
                         essl31_shaders::fs::RedGreenGradient());
        drawQuad(gradientProgram, essl31_shaders::PositionAttrib(), 0.5f, 1.0f, true);
    
        GLFramebuffer framebufferResolved;
        glBindFramebuffer(GL_FRAMEBUFFER, framebufferResolved.get());
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
                                  colorResolved.get());
    
        EGLint desiredWidth  = 300;
        EGLint desiredHeight = 400;
        mOSWindow->resize(desiredWidth, desiredHeight);
        mOSWindow->setOrientation(desiredWidth, desiredHeight);
        angle::Sleep(1000);
        eglSwapBuffers(mDisplay, mWindowSurface);
        ASSERT_EGL_SUCCESS();
    
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebufferMS.get());
        drawQuad(gradientProgram, essl31_shaders::PositionAttrib(), 0.5f, 1.0f, true);
    
        glBindFramebuffer(GL_READ_FRAMEBUFFER, framebufferMS.get());
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebufferResolved.get());
        glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, framebufferResolved.get());
    
        // Check the result, especially the boundaries.
        EXPECT_PIXEL_NEAR(0, 0, 0, 0, 0, 255, 1.0);                      // Balck
        EXPECT_PIXEL_NEAR(kSize - 1, 0, 253, 0, 0, 255, 1.0);            // Red
        EXPECT_PIXEL_NEAR(0, kSize - 1, 0, 253, 0, 255, 1.0);            // Green
        EXPECT_PIXEL_NEAR(kSize - 1, kSize - 1, 253, 253, 0, 255, 1.0);  // Yellow
    
        ASSERT_GL_NO_ERROR();
    }
    
    // Blit color buffer to default framebuffer with linear filter.
    TEST_P(EGLPreRotationBlitFramebufferTest, BlitColorWithLinearFilter)
    {
        // http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(isVulkanRenderer() && IsLinux() && IsIntel());
    
        // Flaky on Linux SwANGLE http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(IsLinux() && isSwiftshader());
    
        setWindowVisible(mOSWindow, true);
    
        initializeDisplay();
        initializeSurfaceWithRGBA8888Config();
    
        eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
        ASSERT_EGL_SUCCESS();
    
        mOSWindow->setOrientation(300, 400);
        angle::Sleep(1000);
        eglSwapBuffers(mDisplay, mWindowSurface);
        ASSERT_EGL_SUCCESS();
    
        constexpr int kSize = 128;
        glViewport(0, 0, kSize, kSize);
    
        GLRenderbuffer colorbuf;
        glBindRenderbuffer(GL_RENDERBUFFER, colorbuf.get());
        glRenderbufferStorageMultisample(GL_RENDERBUFFER, 0, GL_RGBA8, kSize, kSize);
    
        GLFramebuffer framebuffer;
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorbuf);
    
        glCheckFramebufferStatus(GL_FRAMEBUFFER);
    
        glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
    
        ANGLE_GL_PROGRAM(gradientProgram, essl31_shaders::vs::Passthrough(),
                         essl31_shaders::fs::RedGreenGradient());
        drawQuad(gradientProgram, essl31_shaders::PositionAttrib(), 0.5f, 1.0f, true);
    
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
        glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize, GL_COLOR_BUFFER_BIT, GL_LINEAR);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
    
        // Check the result, especially the boundaries.
        EXPECT_PIXEL_NEAR(0, 0, 0, 0, 0, 255, 1.0);                      // Black
        EXPECT_PIXEL_NEAR(kSize - 1, 0, 253, 0, 0, 255, 1.0);            // Red
        EXPECT_PIXEL_NEAR(0, kSize - 1, 0, 253, 0, 255, 1.0);            // Green
        EXPECT_PIXEL_NEAR(kSize - 1, kSize - 1, 253, 253, 0, 255, 1.0);  // Yellow
    
        eglSwapBuffers(mDisplay, mWindowSurface);
    
        ASSERT_GL_NO_ERROR();
    }
    
    // Draw a predictable pattern (for testing pre-rotation) into an FBO, and then use glBlitFramebuffer
    // to blit the left and right halves of that pattern into various places within the 400x300 window
    TEST_P(EGLPreRotationBlitFramebufferTest, LeftAndRightBlitFramebuffer)
    {
        // http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(isVulkanRenderer() && IsLinux() && IsIntel());
    
        // Flaky on Linux SwANGLE http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(IsLinux() && isSwiftshader());
    
        // To aid in debugging, we want this window visible
        setWindowVisible(mOSWindow, true);
    
        initializeDisplay();
        initializeSurfaceWithRGBA8888Config();
    
        eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
        ASSERT_EGL_SUCCESS();
    
        // Init program
        GLuint program = createProgram();
        ASSERT_NE(0u, program);
        glUseProgram(program);
    
        GLBuffer indexBuffer;
        GLVertexArray vertexArray;
        GLBuffer vertexBuffers[2];
        initializeGeometry(program, &indexBuffer, &vertexArray, vertexBuffers);
        ASSERT_GL_NO_ERROR();
    
        // Create a texture-backed FBO and render the predictable pattern to it
        GLFramebuffer fbo;
        GLTexture texture;
        initializeFBO(&fbo, &texture);
        ASSERT_GL_NO_ERROR();
    
        glViewport(0, 0, mSize, mSize);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr);
        ASSERT_GL_NO_ERROR();
    
        // Ensure the predictable pattern seems correct in the FBO
        test256x256PredictablePattern(0, 0);
        ASSERT_GL_NO_ERROR();
    
        // Prepare to blit to the default framebuffer and read from the FBO
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    
        // Blit to an offset part of the 400x300 window
        GLint xOffset = 40;
        GLint yOffset = 30;
    
        //
        // Test blitting half of the FBO image to a 128x256 or 256x128 part of the default framebuffer
        // (no scaling)
        //
    
        // 1st) Clear to black and blit the left and right halves of the texture to the left and right
        // halves of that different part of the window
        glClear(GL_COLOR_BUFFER_BIT);
        glViewport(xOffset, yOffset, mSize, mSize);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
        glBlitFramebuffer(0, 0, mSize / 2, mSize, xOffset, yOffset, xOffset + (mSize / 2),
                          yOffset + mSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
        glBlitFramebuffer(mSize / 2, 0, mSize, mSize, xOffset + (mSize / 2), yOffset, xOffset + mSize,
                          yOffset + mSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
    
        // Swap buffers to put the image in the window (so the test can be visually checked)
        eglSwapBuffers(mDisplay, mWindowSurface);
        ASSERT_GL_NO_ERROR();
    
        // Blit again to check the colors in the back buffer
        glClear(GL_COLOR_BUFFER_BIT);
        glBlitFramebuffer(0, 0, mSize / 2, mSize, xOffset, yOffset, xOffset + (mSize / 2),
                          yOffset + mSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
        glBlitFramebuffer(mSize / 2, 0, mSize, mSize, xOffset + (mSize / 2), yOffset, xOffset + mSize,
                          yOffset + mSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
        test256x256PredictablePattern(xOffset, yOffset);
        ASSERT_GL_NO_ERROR();
    
        // 2nd) Clear to black and this time blit the left half of the source texture to the right half
        // of the destination window, and then blit the right half of the source texture to the left
        // half of the destination window
        glClear(GL_COLOR_BUFFER_BIT);
        glViewport(xOffset, yOffset, mSize, mSize);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
        glBlitFramebuffer(mSize / 2, 0, mSize, mSize, xOffset, yOffset, xOffset + (mSize / 2),
                          yOffset + mSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
        glBlitFramebuffer(0, 0, mSize / 2, mSize, xOffset + (mSize / 2), yOffset, xOffset + mSize,
                          yOffset + mSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
    
        // Swap buffers to put the image in the window (so the test can be visually checked)
        eglSwapBuffers(mDisplay, mWindowSurface);
        ASSERT_GL_NO_ERROR();
    
        // Blit again to check the colors in the back buffer
        glClear(GL_COLOR_BUFFER_BIT);
        glBlitFramebuffer(mSize / 2, 0, mSize, mSize, xOffset, yOffset, xOffset + (mSize / 2),
                          yOffset + mSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
        glBlitFramebuffer(0, 0, mSize / 2, mSize, xOffset + (mSize / 2), yOffset, xOffset + mSize,
                          yOffset + mSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
        EXPECT_PIXEL_COLOR_EQ(xOffset + kCoordMidWayShort + 1, yOffset + 0, GLColor::black);
        EXPECT_PIXEL_COLOR_EQ(xOffset + kCoordMidWayShort + 1, yOffset + mSize - 1, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(xOffset + kCoordMidWayShort, yOffset + 0, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(xOffset + kCoordMidWayShort, yOffset + mSize - 1, GLColor::yellow);
        EXPECT_PIXEL_COLOR_EQ(xOffset + mSize - 1, yOffset + kCoordMidWayShort, kColorMidWayShortShort);
        EXPECT_PIXEL_COLOR_EQ(xOffset + mSize - 1, yOffset + kCoordMidWayLong, kColorMidWayShortLong);
        EXPECT_PIXEL_COLOR_EQ(xOffset + 0, yOffset + kCoordMidWayShort, kColorMidWayLongShort);
        EXPECT_PIXEL_COLOR_EQ(xOffset + 0, yOffset + kCoordMidWayLong, kColorMidWayLongLong);
        ASSERT_GL_NO_ERROR();
    
        ASSERT_EGL_SUCCESS();
    }
    
    // Draw a predictable pattern (for testing pre-rotation) into an FBO, and then use glBlitFramebuffer
    // to blit the top and bottom halves of that pattern into various places within the 400x300 window
    TEST_P(EGLPreRotationBlitFramebufferTest, TopAndBottomBlitFramebuffer)
    {
        // http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(isVulkanRenderer() && IsLinux() && IsIntel());
    
        // Flaky on Linux SwANGLE http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(IsLinux() && isSwiftshader());
    
        // To aid in debugging, we want this window visible
        setWindowVisible(mOSWindow, true);
    
        initializeDisplay();
        initializeSurfaceWithRGBA8888Config();
    
        eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
        ASSERT_EGL_SUCCESS();
    
        // Init program
        GLuint program = createProgram();
        ASSERT_NE(0u, program);
        glUseProgram(program);
    
        GLBuffer indexBuffer;
        GLVertexArray vertexArray;
        GLBuffer vertexBuffers[2];
        initializeGeometry(program, &indexBuffer, &vertexArray, vertexBuffers);
        ASSERT_GL_NO_ERROR();
    
        // Create a texture-backed FBO and render the predictable pattern to it
        GLFramebuffer fbo;
        GLTexture texture;
        initializeFBO(&fbo, &texture);
        ASSERT_GL_NO_ERROR();
    
        glViewport(0, 0, mSize, mSize);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr);
        ASSERT_GL_NO_ERROR();
    
        // Ensure the predictable pattern seems correct in the FBO
        test256x256PredictablePattern(0, 0);
        ASSERT_GL_NO_ERROR();
    
        // Prepare to blit to the default framebuffer and read from the FBO
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    
        // Blit to an offset part of the 400x300 window
        GLint xOffset = 40;
        GLint yOffset = 30;
    
        //
        // Test blitting half of the FBO image to a 128x256 or 256x128 part of the default framebuffer
        // (no scaling)
        //
    
        // 1st) Clear to black and blit the top and bottom halves of the texture to the top and bottom
        // halves of that different part of the window
        glClear(GL_COLOR_BUFFER_BIT);
        glViewport(xOffset, yOffset, mSize, mSize);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
        glBlitFramebuffer(0, 0, mSize, mSize / 2, xOffset, yOffset, xOffset + mSize,
                          yOffset + (mSize / 2), GL_COLOR_BUFFER_BIT, GL_NEAREST);
        glBlitFramebuffer(0, mSize / 2, mSize, mSize, xOffset, yOffset + (mSize / 2), xOffset + mSize,
                          yOffset + mSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
    
        // Swap buffers to put the image in the window (so the test can be visually checked)
        eglSwapBuffers(mDisplay, mWindowSurface);
        ASSERT_GL_NO_ERROR();
    
        // Blit again to check the colors in the back buffer
        glClear(GL_COLOR_BUFFER_BIT);
        glBlitFramebuffer(0, 0, mSize, mSize / 2, xOffset, yOffset, xOffset + mSize,
                          yOffset + (mSize / 2), GL_COLOR_BUFFER_BIT, GL_NEAREST);
        glBlitFramebuffer(0, mSize / 2, mSize, mSize, xOffset, yOffset + (mSize / 2), xOffset + mSize,
                          yOffset + mSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
        test256x256PredictablePattern(xOffset, yOffset);
        ASSERT_GL_NO_ERROR();
    
        // 2nd) Clear to black and this time blit the top half of the source texture to the bottom half
        // of the destination window, and then blit the bottom half of the source texture to the top
        // half of the destination window
        glClear(GL_COLOR_BUFFER_BIT);
        glViewport(xOffset, yOffset, mSize, mSize);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
        glBlitFramebuffer(0, 0, mSize, mSize / 2, xOffset, yOffset + (mSize / 2), xOffset + mSize,
                          yOffset + mSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
        glBlitFramebuffer(0, mSize / 2, mSize, mSize, xOffset, yOffset, xOffset + mSize,
                          yOffset + (mSize / 2), GL_COLOR_BUFFER_BIT, GL_NEAREST);
    
        // Swap buffers to put the image in the window (so the test can be visually checked)
        eglSwapBuffers(mDisplay, mWindowSurface);
        ASSERT_GL_NO_ERROR();
    
        // Blit again to check the colors in the back buffer
        glClear(GL_COLOR_BUFFER_BIT);
        glBlitFramebuffer(0, 0, mSize, mSize / 2, xOffset, yOffset + (mSize / 2), xOffset + mSize,
                          yOffset + mSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
        glBlitFramebuffer(0, mSize / 2, mSize, mSize, xOffset, yOffset, xOffset + mSize,
                          yOffset + (mSize / 2), GL_COLOR_BUFFER_BIT, GL_NEAREST);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
        EXPECT_PIXEL_COLOR_EQ(xOffset + 0, yOffset + kCoordMidWayShort + 1, GLColor::black);
        EXPECT_PIXEL_COLOR_EQ(xOffset + 0, yOffset + kCoordMidWayShort, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(xOffset + mSize - 1, yOffset + kCoordMidWayShort + 1, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(xOffset + mSize - 1, yOffset + kCoordMidWayShort, GLColor::yellow);
        EXPECT_PIXEL_COLOR_EQ(xOffset + kCoordMidWayShort, yOffset + mSize - 1, kColorMidWayShortShort);
        EXPECT_PIXEL_COLOR_EQ(xOffset + kCoordMidWayShort, yOffset + 0, kColorMidWayShortLong);
        EXPECT_PIXEL_COLOR_EQ(xOffset + kCoordMidWayLong, yOffset + mSize - 1, kColorMidWayLongShort);
        EXPECT_PIXEL_COLOR_EQ(xOffset + kCoordMidWayLong, yOffset + 0, kColorMidWayLongLong);
        ASSERT_GL_NO_ERROR();
    
        ASSERT_EGL_SUCCESS();
    }
    
    // Draw a predictable pattern (for testing pre-rotation) into an FBO, and then use glBlitFramebuffer
    // to blit that pattern into various places within the 400x300 window, but being scaled to one-half
    // size
    TEST_P(EGLPreRotationBlitFramebufferTest, ScaledBlitFramebuffer)
    {
        // http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(isVulkanRenderer() && IsLinux() && IsIntel());
    
        // Flaky on Linux SwANGLE http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(IsLinux() && isSwiftshader());
    
        // To aid in debugging, we want this window visible
        setWindowVisible(mOSWindow, true);
    
        initializeDisplay();
        initializeSurfaceWithRGBA8888Config();
    
        eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
        ASSERT_EGL_SUCCESS();
    
        // Init program
        GLuint program = createProgram();
        ASSERT_NE(0u, program);
        glUseProgram(program);
    
        GLBuffer indexBuffer;
        GLVertexArray vertexArray;
        GLBuffer vertexBuffers[2];
        initializeGeometry(program, &indexBuffer, &vertexArray, vertexBuffers);
        ASSERT_GL_NO_ERROR();
    
        // Create a texture-backed FBO and render the predictable pattern to it
        GLFramebuffer fbo;
        GLTexture texture;
        initializeFBO(&fbo, &texture);
        ASSERT_GL_NO_ERROR();
    
        glViewport(0, 0, mSize, mSize);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr);
        ASSERT_GL_NO_ERROR();
    
        // Ensure the predictable pattern seems correct in the FBO
        test256x256PredictablePattern(0, 0);
        ASSERT_GL_NO_ERROR();
    
        // Prepare to blit to the default framebuffer and read from the FBO
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    
        // Blit to an offset part of the 400x300 window
        GLint xOffset = 40;
        GLint yOffset = 30;
    
        //
        // Test blitting the entire FBO image to a 128x256 or 256x128 part of the default framebuffer
        // (requires scaling)
        //
    
        // 1st) Clear to black and blit the FBO to the left and right halves of that different part of
        // the window
        glClear(GL_COLOR_BUFFER_BIT);
        glViewport(xOffset, yOffset, mSize, mSize);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
        glBlitFramebuffer(0, 0, mSize, mSize, xOffset, yOffset, xOffset + (mSize / 2), yOffset + mSize,
                          GL_COLOR_BUFFER_BIT, GL_NEAREST);
        glBlitFramebuffer(0, 0, mSize, mSize, xOffset + (mSize / 2), yOffset, xOffset + mSize,
                          yOffset + mSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
    
        // Swap buffers to put the image in the window (so the test can be visually checked)
        eglSwapBuffers(mDisplay, mWindowSurface);
        ASSERT_GL_NO_ERROR();
    
        // Blit again to check the colors in the back buffer
        glClear(GL_COLOR_BUFFER_BIT);
        glBlitFramebuffer(0, 0, mSize, mSize, xOffset, yOffset, xOffset + (mSize / 2), yOffset + mSize,
                          GL_COLOR_BUFFER_BIT, GL_NEAREST);
        glBlitFramebuffer(0, 0, mSize, mSize, xOffset + (mSize / 2), yOffset, xOffset + mSize,
                          yOffset + mSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
        EXPECT_PIXEL_COLOR_EQ(xOffset + 0, yOffset + 0, kColorScaleHorizBlack);
        EXPECT_PIXEL_COLOR_EQ(xOffset + 0, yOffset + mSize - 1, kColorScaleHorizGreen);
        EXPECT_PIXEL_COLOR_EQ(xOffset + kCoordMidWayShort, yOffset + 0, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(xOffset + kCoordMidWayShort, yOffset + mSize - 1, GLColor::yellow);
        EXPECT_PIXEL_COLOR_EQ(xOffset + kCoordMidWayLong, yOffset + 0, kColorScaleHorizBlack);
        EXPECT_PIXEL_COLOR_EQ(xOffset + kCoordMidWayLong, yOffset + mSize - 1, kColorScaleHorizGreen);
        EXPECT_PIXEL_COLOR_EQ(xOffset + mSize - 1, yOffset + 0, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(xOffset + mSize - 1, yOffset + mSize - 1, GLColor::yellow);
    
        // 2nd) Clear to black and blit the FBO to the top and bottom halves of that different part of
        // the window
        glClear(GL_COLOR_BUFFER_BIT);
        glViewport(xOffset, yOffset, mSize, mSize);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
        glBlitFramebuffer(0, 0, mSize, mSize, xOffset, yOffset, xOffset + mSize, yOffset + (mSize / 2),
                          GL_COLOR_BUFFER_BIT, GL_NEAREST);
        glBlitFramebuffer(0, 0, mSize, mSize, xOffset, yOffset + (mSize / 2), xOffset + mSize,
                          yOffset + mSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
    
        // Swap buffers to put the image in the window (so the test can be visually checked)
        eglSwapBuffers(mDisplay, mWindowSurface);
        ASSERT_GL_NO_ERROR();
    
        // Blit again to check the colors in the back buffer
        glClear(GL_COLOR_BUFFER_BIT);
        glBlitFramebuffer(0, 0, mSize, mSize, xOffset, yOffset, xOffset + mSize, yOffset + (mSize / 2),
                          GL_COLOR_BUFFER_BIT, GL_NEAREST);
        glBlitFramebuffer(0, 0, mSize, mSize, xOffset, yOffset + (mSize / 2), xOffset + mSize,
                          yOffset + mSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
        EXPECT_PIXEL_COLOR_EQ(xOffset + 0, yOffset + 0, kColorScaleVertBlack);
        EXPECT_PIXEL_COLOR_EQ(xOffset + mSize - 1, yOffset + 0, kColorScaleVertRed);
        EXPECT_PIXEL_COLOR_EQ(xOffset + 0, yOffset + kCoordMidWayShort, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(xOffset + mSize - 1, yOffset + kCoordMidWayShort, GLColor::yellow);
        EXPECT_PIXEL_COLOR_EQ(xOffset + 0, yOffset + kCoordMidWayLong, kColorScaleVertBlack);
        EXPECT_PIXEL_COLOR_EQ(xOffset + mSize - 1, yOffset + kCoordMidWayLong, kColorScaleVertRed);
        EXPECT_PIXEL_COLOR_EQ(xOffset + 0, yOffset + mSize - 1, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(xOffset + mSize - 1, yOffset + mSize - 1, GLColor::yellow);
        ASSERT_GL_NO_ERROR();
    
        ASSERT_EGL_SUCCESS();
    }
    
    // Draw a predictable pattern (for testing pre-rotation) into a 256x256 portion of the 400x300
    // window, and then use glBlitFramebuffer to blit that pattern into an FBO
    TEST_P(EGLPreRotationBlitFramebufferTest, FboDestBlitFramebuffer)
    {
        // http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(isVulkanRenderer() && IsLinux() && IsIntel());
    
        // Flaky on Linux SwANGLE http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(IsLinux() && isSwiftshader());
    
        // To aid in debugging, we want this window visible
        setWindowVisible(mOSWindow, true);
    
        initializeDisplay();
        initializeSurfaceWithRGBA8888Config();
    
        eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
        ASSERT_EGL_SUCCESS();
    
        // Init program
        GLuint program = createProgram();
        ASSERT_NE(0u, program);
        glUseProgram(program);
    
        GLBuffer indexBuffer;
        GLVertexArray vertexArray;
        GLBuffer vertexBuffers[2];
        initializeGeometry(program, &indexBuffer, &vertexArray, vertexBuffers);
        ASSERT_GL_NO_ERROR();
    
        // Create a texture-backed FBO and render the predictable pattern to it
        GLFramebuffer fbo;
        GLTexture texture;
        initializeFBO(&fbo, &texture);
        ASSERT_GL_NO_ERROR();
    
        glViewport(0, 0, mSize, mSize);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr);
        ASSERT_GL_NO_ERROR();
    
        // Ensure the predictable pattern seems correct in the FBO
        test256x256PredictablePattern(0, 0);
        ASSERT_GL_NO_ERROR();
    
        // Prepare to blit to the default framebuffer and read from the FBO
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    
        // Blit to an offset part of the 400x300 window
        GLint xOffset = 40;
        GLint yOffset = 30;
    
        //
        // Test blitting a 256x256 part of the default framebuffer to the entire FBO (no scaling)
        //
    
        // To get the entire predictable pattern into the default framebuffer at the desired offset,
        // blit it from the FBO
        glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
        glViewport(xOffset, yOffset, mSize, mSize);
        glClear(GL_COLOR_BUFFER_BIT);
        glBlitFramebuffer(0, 0, mSize, mSize, xOffset, yOffset, xOffset + mSize, yOffset + mSize,
                          GL_COLOR_BUFFER_BIT, GL_NEAREST);
        // Swap buffers to put the image in the window (so the test can be visually checked)
        eglSwapBuffers(mDisplay, mWindowSurface);
        ASSERT_GL_NO_ERROR();
        // Blit again to check the colors in the back buffer
        glClear(GL_COLOR_BUFFER_BIT);
        glBlitFramebuffer(0, 0, mSize, mSize, xOffset, yOffset, xOffset + mSize, yOffset + mSize,
                          GL_COLOR_BUFFER_BIT, GL_NEAREST);
    
        // Clear the FBO to black and blit from the window to the FBO
        glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
        glViewport(0, 0, mSize, mSize);
        glClear(GL_COLOR_BUFFER_BIT);
        glBlitFramebuffer(xOffset, yOffset, xOffset + mSize, yOffset + mSize, 0, 0, mSize, mSize,
                          GL_COLOR_BUFFER_BIT, GL_NEAREST);
    
        // Ensure the predictable pattern seems correct in the FBO
        glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
        test256x256PredictablePattern(0, 0);
        ASSERT_GL_NO_ERROR();
    
        ASSERT_EGL_SUCCESS();
    }
    
    // Draw a predictable pattern (for testing pre-rotation) into a 256x256 portion of the 400x300
    // window, and then use glBlitFramebuffer to blit that pattern into an FBO, but with coordinates
    // that are partially out-of-bounds of the source
    TEST_P(EGLPreRotationBlitFramebufferTest, FboDestOutOfBoundsSourceBlitFramebuffer)
    {
        // http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(isVulkanRenderer() && IsLinux() && IsIntel());
    
        // Flaky on Linux SwANGLE http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(IsLinux() && isSwiftshader());
    
        // To aid in debugging, we want this window visible
        setWindowVisible(mOSWindow, true);
    
        initializeDisplay();
        initializeSurfaceWithRGBA8888Config();
    
        eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
        ASSERT_EGL_SUCCESS();
    
        // Init program
        GLuint program = createProgram();
        ASSERT_NE(0u, program);
        glUseProgram(program);
    
        GLBuffer indexBuffer;
        GLVertexArray vertexArray;
        GLBuffer vertexBuffers[2];
        initializeGeometry(program, &indexBuffer, &vertexArray, vertexBuffers);
        ASSERT_GL_NO_ERROR();
    
        // Create a texture-backed FBO and render the predictable pattern to it
        GLFramebuffer fbo;
        GLTexture texture;
        initializeFBO(&fbo, &texture);
        ASSERT_GL_NO_ERROR();
    
        glViewport(0, 0, mSize, mSize);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr);
        ASSERT_GL_NO_ERROR();
    
        // Ensure the predictable pattern seems correct in the FBO
        test256x256PredictablePattern(0, 0);
        ASSERT_GL_NO_ERROR();
    
        // Prepare to blit to the default framebuffer and read from the FBO
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    
        // Blit to the origin of the 400x300 window
        GLint xOffset = 0;
        GLint yOffset = 0;
    
        //
        // Test blitting a 256x256 part of the default framebuffer to the entire FBO (no scaling)
        //
    
        // To get the entire predictable pattern into the default framebuffer at the desired offset,
        // blit it from the FBO
        glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
        glViewport(xOffset, yOffset, mSize, mSize);
        glClear(GL_COLOR_BUFFER_BIT);
        glBlitFramebuffer(0, 0, mSize, mSize, xOffset, yOffset, xOffset + mSize, yOffset + mSize,
                          GL_COLOR_BUFFER_BIT, GL_NEAREST);
        // Swap buffers to put the image in the window (so the test can be visually checked)
        eglSwapBuffers(mDisplay, mWindowSurface);
        ASSERT_GL_NO_ERROR();
        // Blit again to check the colors in the back buffer
        glClear(GL_COLOR_BUFFER_BIT);
        glBlitFramebuffer(0, 0, mSize, mSize, xOffset, yOffset, xOffset + mSize, yOffset + mSize,
                          GL_COLOR_BUFFER_BIT, GL_NEAREST);
    
        // Clear the FBO to black and blit from the window to the FBO, but give source coordinates that
        // are partially outside of the window
        xOffset = -10;
        yOffset = -15;
        glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
        glViewport(0, 0, mSize, mSize);
        glClear(GL_COLOR_BUFFER_BIT);
        glBlitFramebuffer(xOffset, yOffset, xOffset + mSize, yOffset + mSize, 0, 0, mSize, mSize,
                          GL_COLOR_BUFFER_BIT, GL_LINEAR);
    
        // Ensure the predictable pattern seems correct in the FBO
        glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
        // NOTE: There is a strip of black on the left and bottom edges of the PBO, corresponding to
        // the source coordinates that were outside of the source.  The strip of black is xOffset
        // pixels wide on the left side, and yOffset pixels tall on the bottom side.
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black);
        EXPECT_PIXEL_COLOR_EQ(0, 255, GLColor::black);
        EXPECT_PIXEL_COLOR_EQ(-xOffset - 1, 0, GLColor::black);
        EXPECT_PIXEL_COLOR_EQ(-xOffset - 1, 255, GLColor::black);
        EXPECT_PIXEL_COLOR_EQ(0, -yOffset - 1, GLColor::black);
        EXPECT_PIXEL_COLOR_EQ(255, -yOffset - 1, GLColor::black);
        EXPECT_PIXEL_COLOR_EQ(255 + xOffset, 0, GLColor::black);
        EXPECT_PIXEL_COLOR_EQ(255 + xOffset, -yOffset - 1, GLColor::black);
        EXPECT_PIXEL_COLOR_EQ(0, 255 + yOffset, GLColor::black);
        EXPECT_PIXEL_COLOR_EQ(-xOffset - 1, 255 + yOffset, GLColor::black);
    
        // FBO coordinate (-xOffset, -yOffset) (or (10, 15)) has the values from the bottom-left corner
        // of the source (which happens to be black).  Thus, the following two tests are equivalent:
        EXPECT_PIXEL_COLOR_EQ(-xOffset, -yOffset, GLColor::black);
        EXPECT_PIXEL_COLOR_EQ(10, 15, GLColor::black);
    
        // Note: the following is equivalent to (0, 0):
        EXPECT_PIXEL_COLOR_EQ(10 + xOffset, 15 + yOffset, GLColor::black);
    
        EXPECT_PIXEL_COLOR_EQ(-xOffset + 1, -yOffset + 1, GLColor(1, 1, 0, 255));
        EXPECT_PIXEL_COLOR_EQ(-xOffset + 10, -yOffset + 10, GLColor(10, 10, 0, 255));
        EXPECT_PIXEL_COLOR_EQ(-xOffset + 20, -yOffset + 20, GLColor(20, 20, 0, 255));
        EXPECT_PIXEL_COLOR_EQ(-xOffset + 100, -yOffset + 100, GLColor(100, 100, 0, 255));
        EXPECT_PIXEL_COLOR_EQ(-xOffset + 200, -yOffset + 200, GLColor(200, 200, 0, 255));
        EXPECT_PIXEL_COLOR_EQ(-xOffset + 230, -yOffset + 230, GLColor(230, 230, 0, 255));
        // Note how the offset works differently when added to the same coordinate value as above.  The
        // black strip causes the value to be 2X less the offset in each direction.  Thus, coordinate
        // (230+xOffset, 230+yOffset) yields actual coordinate (220, 215) and red-green values
        // (230+(2*xOffset), 230+(2*yOffset)) or (210, 200).  The following two tests are equivalent:
        EXPECT_PIXEL_COLOR_EQ(230 + xOffset, 230 + yOffset,
                              GLColor(230 + (2 * xOffset), 230 + (2 * yOffset), 0, 255));
        EXPECT_PIXEL_COLOR_EQ(220, 215, GLColor(210, 200, 0, 255));
        // FBO coordinate (245, 240) has the highest pixel values from the source.  The value of the
        // FBO pixel at (245, 240) is smaller than the same coordinate in the source because of the
        // blit's offsets.  That is, the value is (245-xOffset, 240-yOffset) or (235, 225).  Thus, the
        // following two tests are the same:
        EXPECT_PIXEL_COLOR_EQ(255 + xOffset, 255 + yOffset,
                              GLColor(255 + (2 * xOffset), 255 + (2 * yOffset), 0, 255));
        EXPECT_PIXEL_COLOR_EQ(245, 240, GLColor(235, 225, 0, 255));
    
        // Again, the "mid-way" coordinates will get values that aren't truly mid-way:
        EXPECT_PIXEL_COLOR_EQ(
            xOffset + kCoordMidWayShort, yOffset + kCoordMidWayShort,
            GLColor(kCoordMidWayShort + (2 * xOffset), kCoordMidWayShort + (2 * yOffset), 0, 255));
        EXPECT_PIXEL_COLOR_EQ(
            xOffset + kCoordMidWayShort, yOffset + kCoordMidWayLong,
            GLColor(kCoordMidWayShort + (2 * xOffset), kCoordMidWayLong + (2 * yOffset), 0, 255));
        EXPECT_PIXEL_COLOR_EQ(
            xOffset + kCoordMidWayLong, yOffset + kCoordMidWayShort,
            GLColor(kCoordMidWayLong + (2 * xOffset), kCoordMidWayShort + (2 * yOffset), 0, 255));
        EXPECT_PIXEL_COLOR_EQ(
            xOffset + kCoordMidWayLong, yOffset + kCoordMidWayLong,
            GLColor(kCoordMidWayLong + (2 * xOffset), kCoordMidWayLong + (2 * yOffset), 0, 255));
    
        // Almost Red
        EXPECT_PIXEL_COLOR_EQ(255, -yOffset, GLColor(255 + xOffset, 0, 0, 255));
        // Almost Green
        EXPECT_PIXEL_COLOR_EQ(-xOffset, 255, GLColor(0, 255 + yOffset, 0, 255));
        // Almost Yellow
        EXPECT_PIXEL_COLOR_EQ(255, 255, GLColor(255 + xOffset, 255 + yOffset, 0, 255));
    
        ASSERT_GL_NO_ERROR();
    
        ASSERT_EGL_SUCCESS();
    }
    
    // Draw a predictable pattern (for testing pre-rotation) into a 256x256 portion of the 400x300
    // window, and then use glBlitFramebuffer to blit that pattern into an FBO, but with coordinates
    // that are partially out-of-bounds of the source, and cause a "stretch" to occur
    TEST_P(EGLPreRotationBlitFramebufferTest, FboDestOutOfBoundsSourceWithStretchBlitFramebuffer)
    {
        // http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(isVulkanRenderer() && IsLinux() && IsIntel());
    
        // Flaky on Linux SwANGLE http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(IsLinux() && isSwiftshader());
    
        // To aid in debugging, we want this window visible
        setWindowVisible(mOSWindow, true);
    
        initializeDisplay();
        initializeSurfaceWithRGBA8888Config();
    
        eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
        ASSERT_EGL_SUCCESS();
    
        // Init program
        GLuint program = createProgram();
        ASSERT_NE(0u, program);
        glUseProgram(program);
    
        GLBuffer indexBuffer;
        GLVertexArray vertexArray;
        GLBuffer vertexBuffers[2];
        initializeGeometry(program, &indexBuffer, &vertexArray, vertexBuffers);
        ASSERT_GL_NO_ERROR();
    
        // Create a texture-backed FBO and render the predictable pattern to it
        GLFramebuffer fbo;
        GLTexture texture;
        initializeFBO(&fbo, &texture);
        ASSERT_GL_NO_ERROR();
    
        glViewport(0, 0, mSize, mSize);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr);
        ASSERT_GL_NO_ERROR();
    
        // Ensure the predictable pattern seems correct in the FBO
        test256x256PredictablePattern(0, 0);
        ASSERT_GL_NO_ERROR();
    
        // Prepare to blit to the default framebuffer and read from the FBO
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    
        // Blit to the origin of the 400x300 window
        GLint xOffset = 0;
        GLint yOffset = 0;
    
        //
        // Test blitting a 256x256 part of the default framebuffer to the entire FBO (no scaling)
        //
    
        // To get the entire predictable pattern into the default framebuffer at the desired offset,
        // blit it from the FBO
        glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
        glViewport(xOffset, yOffset, mSize, mSize);
        glClear(GL_COLOR_BUFFER_BIT);
        glBlitFramebuffer(0, 0, mSize, mSize, xOffset, yOffset, xOffset + mSize, yOffset + mSize,
                          GL_COLOR_BUFFER_BIT, GL_NEAREST);
        // Swap buffers to put the image in the window (so the test can be visually checked)
        eglSwapBuffers(mDisplay, mWindowSurface);
        ASSERT_GL_NO_ERROR();
        // Blit again to check the colors in the back buffer
        glClear(GL_COLOR_BUFFER_BIT);
        glBlitFramebuffer(0, 0, mSize, mSize, xOffset, yOffset, xOffset + mSize, yOffset + mSize,
                          GL_COLOR_BUFFER_BIT, GL_NEAREST);
    
        // Clear the FBO to black and blit from the window to the FBO, but give source coordinates that
        // are partially outside of the window, but "stretch" the result by 0.5 (i.e. 2X shrink in x)
        xOffset = -10;
        yOffset = -15;
        glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
        glViewport(0, 0, mSize, mSize);
        glClear(GL_COLOR_BUFFER_BIT);
        glBlitFramebuffer(xOffset, yOffset, xOffset + mSize, yOffset + mSize, 0, 0, mSize / 2, mSize,
                          GL_COLOR_BUFFER_BIT, GL_LINEAR);
    
        // Ensure the predictable pattern seems correct in the FBO
        glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
        // NOTE: There is a strip of black on the left and bottom edges of the PBO, corresponding to
        // the source coordinates that were outside of the source.  The strip of black is xOffset/2
        // pixels wide on the left side, and yOffset pixels tall on the bottom side.
        EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor::black, 1);
        EXPECT_PIXEL_COLOR_NEAR(0, 255, GLColor::black, 1);
        EXPECT_PIXEL_COLOR_NEAR((-xOffset / 2) - 1, 0, GLColor::black, 1);
        EXPECT_PIXEL_COLOR_NEAR((-xOffset / 2) - 1, 255, GLColor::black, 1);
        EXPECT_PIXEL_COLOR_NEAR(0, -yOffset - 1, GLColor::black, 1);
        EXPECT_PIXEL_COLOR_NEAR(255 / 2, -yOffset - 1, GLColor::black, 1);
        EXPECT_PIXEL_COLOR_NEAR((255 + xOffset) / 2, 0, GLColor::black, 1);
        EXPECT_PIXEL_COLOR_NEAR((255 + xOffset) / 2, -yOffset - 1, GLColor::black, 1);
        EXPECT_PIXEL_COLOR_NEAR(0, 255 + yOffset, GLColor::black, 1);
        EXPECT_PIXEL_COLOR_NEAR((-xOffset / 2) - 1, 255 + yOffset, GLColor::black, 1);
    
        // FBO coordinate (-xOffset, -yOffset) (or (10, 15)) has the values from the bottom-left corner
        // of the source (which happens to be black).  Thus, the following two tests are equivalent:
        EXPECT_PIXEL_COLOR_NEAR(-xOffset / 2, -yOffset, GLColor::black, 1);
        EXPECT_PIXEL_COLOR_NEAR(10 + xOffset, 15 + yOffset, GLColor::black, 1);
        EXPECT_PIXEL_COLOR_NEAR(220 / 2, 215, GLColor(210, 200, 0, 255), 1);
    
        EXPECT_PIXEL_COLOR_NEAR((254 + xOffset) / 2, 255 + yOffset,
                                GLColor(254 + (2 * xOffset), 255 + (2 * yOffset), 0, 255), 1);
        EXPECT_PIXEL_COLOR_NEAR(254 / 2, 240, GLColor(244, 225, 0, 255), 1);
    
        // Almost Red
        EXPECT_PIXEL_COLOR_NEAR(254 / 2, -yOffset, GLColor(254 + xOffset, 0, 0, 255), 1);
        // Almost Green
        EXPECT_PIXEL_COLOR_NEAR(-xOffset / 2, 255, GLColor(0, 255 + yOffset, 0, 255), 1);
        // Almost Yellow
        EXPECT_PIXEL_COLOR_NEAR(254 / 2, 255, GLColor(254 + xOffset, 255 + yOffset, 0, 255), 1);
    
        ASSERT_GL_NO_ERROR();
    
        ASSERT_EGL_SUCCESS();
    }
    
    // Draw a predictable pattern (for testing pre-rotation) into a 256x256 portion of the 400x300
    // window, and then use glBlitFramebuffer to blit that pattern into an FBO, but with source and FBO
    // coordinates that are partially out-of-bounds of the source
    TEST_P(EGLPreRotationBlitFramebufferTest, FboDestOutOfBoundsSourceAndDestBlitFramebuffer)
    {
        // http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(isVulkanRenderer() && IsLinux() && IsIntel());
    
        // Flaky on Linux SwANGLE http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(IsLinux() && isSwiftshader());
    
        // To aid in debugging, we want this window visible
        setWindowVisible(mOSWindow, true);
    
        initializeDisplay();
        initializeSurfaceWithRGBA8888Config();
    
        eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
        ASSERT_EGL_SUCCESS();
    
        // Init program
        GLuint program = createProgram();
        ASSERT_NE(0u, program);
        glUseProgram(program);
    
        GLBuffer indexBuffer;
        GLVertexArray vertexArray;
        GLBuffer vertexBuffers[2];
        initializeGeometry(program, &indexBuffer, &vertexArray, vertexBuffers);
        ASSERT_GL_NO_ERROR();
    
        // Create a texture-backed FBO and render the predictable pattern to it
        GLFramebuffer fbo;
        GLTexture texture;
        initializeFBO(&fbo, &texture);
        ASSERT_GL_NO_ERROR();
    
        glViewport(0, 0, mSize, mSize);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr);
        ASSERT_GL_NO_ERROR();
    
        // Ensure the predictable pattern seems correct in the FBO
        test256x256PredictablePattern(0, 0);
        ASSERT_GL_NO_ERROR();
    
        // Prepare to blit to the default framebuffer and read from the FBO
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
        glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    
        // Blit to the origin of the 400x300 window
        GLint xOffset = 0;
        GLint yOffset = 0;
    
        //
        // Test blitting a 256x256 part of the default framebuffer to the entire FBO (no scaling)
        //
    
        // To get the entire predictable pattern into the default framebuffer at the desired offset,
        // blit it from the FBO
        glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
        glViewport(xOffset, yOffset, mSize, mSize);
        glClear(GL_COLOR_BUFFER_BIT);
        glBlitFramebuffer(0, 0, mSize, mSize, xOffset, yOffset, xOffset + mSize, yOffset + mSize,
                          GL_COLOR_BUFFER_BIT, GL_NEAREST);
        // Swap buffers to put the image in the window (so the test can be visually checked)
        eglSwapBuffers(mDisplay, mWindowSurface);
        ASSERT_GL_NO_ERROR();
        // Blit again to check the colors in the back buffer
        glClear(GL_COLOR_BUFFER_BIT);
        glBlitFramebuffer(0, 0, mSize, mSize, xOffset, yOffset, xOffset + mSize, yOffset + mSize,
                          GL_COLOR_BUFFER_BIT, GL_NEAREST);
    
        // Clear the FBO to black and blit from the window to the FBO, but give source coordinates that
        // are partially outside of the window, and give destination coordinates that are partially
        // outside of the FBO
        xOffset = -10;
        yOffset = -15;
        glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
        glViewport(0, 0, mSize, mSize);
        glClear(GL_COLOR_BUFFER_BIT);
        glBlitFramebuffer(xOffset, yOffset, (2 * xOffset) + mSize, (2 * yOffset) + mSize, -xOffset,
                          -yOffset, mSize, mSize, GL_COLOR_BUFFER_BIT, GL_LINEAR);
    
        // Ensure the predictable pattern seems correct in the FBO
        glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
        // NOTE: There is a strip of black on the left and bottom edges of the PBO, corresponding to
        // the source coordinates that were outside of the source.  The strip of black is xOffset*2
        // pixels wide on the left side, and yOffset*2 pixels tall on the bottom side.
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black);
        EXPECT_PIXEL_COLOR_EQ(0, 255, GLColor::black);
        EXPECT_PIXEL_COLOR_EQ((-xOffset * 2) - 1, 0, GLColor::black);
        EXPECT_PIXEL_COLOR_EQ((-xOffset * 2) - 1, 255, GLColor::black);
        EXPECT_PIXEL_COLOR_EQ(0, (-yOffset * 2) - 1, GLColor::black);
        EXPECT_PIXEL_COLOR_EQ(255, (-yOffset * 2) - 1, GLColor::black);
        EXPECT_PIXEL_COLOR_EQ(255 + xOffset, 0, GLColor::black);
        EXPECT_PIXEL_COLOR_EQ(255 + xOffset, (-yOffset * 2) - 1, GLColor::black);
        EXPECT_PIXEL_COLOR_EQ(0, 255 + yOffset, GLColor::black);
        EXPECT_PIXEL_COLOR_EQ((-xOffset * 2) - 1, 255 + yOffset, GLColor::black);
    
        // FBO coordinate (-xOffset*2, -yOffset*2) (or (20, 30)) has the values from the bottom-left
        // corner of the source (which happens to be black).  Thus, the following two tests are
        // equivalent:
        EXPECT_PIXEL_COLOR_EQ((-xOffset * 2), (-yOffset * 2), GLColor::black);
        EXPECT_PIXEL_COLOR_EQ(20, 30, GLColor::black);
    
        // Note: the following is equivalent to (0, 0):
        EXPECT_PIXEL_COLOR_EQ(20 + (xOffset * 2), 30 + (yOffset * 2), GLColor::black);
    
        EXPECT_PIXEL_COLOR_EQ((-xOffset * 2) + 1, (-yOffset * 2) + 1, GLColor(1, 1, 0, 255));
        EXPECT_PIXEL_COLOR_EQ((-xOffset * 2) + 10, (-yOffset * 2) + 10, GLColor(10, 10, 0, 255));
        EXPECT_PIXEL_COLOR_EQ((-xOffset * 2) + 20, (-yOffset * 2) + 20, GLColor(20, 20, 0, 255));
        EXPECT_PIXEL_COLOR_EQ((-xOffset * 2) + 100, (-yOffset * 2) + 100, GLColor(100, 100, 0, 255));
        EXPECT_PIXEL_COLOR_EQ((-xOffset * 2) + 200, (-yOffset * 2) + 200, GLColor(200, 200, 0, 255));
        EXPECT_PIXEL_COLOR_EQ((-xOffset * 2) + 230, (-yOffset * 2) + 225, GLColor(230, 225, 0, 255));
    
        // Almost Red
        EXPECT_PIXEL_COLOR_EQ(255, -yOffset * 2, GLColor(255 + (xOffset * 2), 0, 0, 255));
        // Almost Green
        EXPECT_PIXEL_COLOR_EQ(-xOffset * 2, 255, GLColor(0, 255 + (yOffset * 2), 0, 255));
        // Almost Yellow
        EXPECT_PIXEL_COLOR_EQ(255, 255, GLColor(255 + (xOffset * 2), 255 + (yOffset * 2), 0, 255));
    
        ASSERT_GL_NO_ERROR();
    
        ASSERT_EGL_SUCCESS();
    }
    
    class EGLPreRotationInterpolateAtOffsetTest : public EGLPreRotationSurfaceTest
    {
      protected:
        EGLPreRotationInterpolateAtOffsetTest() {}
    
        GLuint createProgram()
        {
            // Init program
            constexpr char kVS[] =
                "#version 310 es\n"
                "#extension GL_OES_shader_multisample_interpolation : require\n"
                "in highp vec2 position;\n"
                "uniform float screen_width;\n"
                "uniform float screen_height;\n"
                "out highp vec2 v_screenPosition;\n"
                "out highp vec2 v_offset;\n"
                "void main (void)\n"
                "{\n"
                "   gl_Position = vec4(position, 0, 1);\n"
                "   v_screenPosition = (position.xy + vec2(1.0, 1.0)) / 2.0 * vec2(screen_width, "
                "screen_height);\n"
                "   v_offset = position.xy * 0.5f;\n"
                "}";
    
            constexpr char kFS[] =
                "#version 310 es\n"
                "#extension GL_OES_shader_multisample_interpolation : require\n"
                "in highp vec2 v_screenPosition;\n"
                "in highp vec2 v_offset;\n"
                "layout(location = 0) out mediump vec4 FragColor;\n"
                "void main() {\n"
                "   const highp float threshold = 0.15625; // 4 subpixel bits. Assume 3 accurate bits "
                "+ 0.03125 for other errors\n"
                "\n"
                "   highp vec2 pixelCenter = floor(v_screenPosition) + vec2(0.5, 0.5);\n"
                "   highp vec2 offsetValue = interpolateAtOffset(v_screenPosition, v_offset);\n"
                "   highp vec2 refValue = pixelCenter + v_offset;\n"
                "\n"
                "   bool valuesEqual = all(lessThan(abs(offsetValue - refValue), vec2(threshold)));\n"
                "   if (valuesEqual)\n"
                "       FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n"
                "   else\n"
                "       FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
                "}";
    
            return CompileProgram(kVS, kFS);
        }
        void initializeGeometry(GLuint program,
                                GLBuffer *indexBuffer,
                                GLVertexArray *vertexArray,
                                GLBuffer *vertexBuffers)
        {
            GLint positionLocation = glGetAttribLocation(program, "position");
            ASSERT_NE(-1, positionLocation);
    
            GLuint screenWidthId  = glGetUniformLocation(program, "screen_width");
            GLuint screenHeightId = glGetUniformLocation(program, "screen_height");
    
            glUniform1f(screenWidthId, (GLfloat)mSize);
            glUniform1f(screenHeightId, (GLfloat)mSize);
    
            glBindVertexArray(*vertexArray);
    
            std::vector<GLushort> indices = {0, 1, 2, 2, 3, 0};
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, *indexBuffer);
            glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * indices.size(), &indices[0],
                         GL_STATIC_DRAW);
    
            std::vector<GLfloat> positionData = {// quad vertices
                                                 -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f};
    
            glBindBuffer(GL_ARRAY_BUFFER, vertexBuffers[0]);
            glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * positionData.size(), &positionData[0],
                         GL_STATIC_DRAW);
            glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2,
                                  nullptr);
            glEnableVertexAttribArray(positionLocation);
        }
    };
    
    // Draw with interpolateAtOffset() builtin function to pre-rotated default FBO
    TEST_P(EGLPreRotationInterpolateAtOffsetTest, InterpolateAtOffsetWithDefaultFBO)
    {
        // http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(isVulkanRenderer() && IsLinux() && IsIntel());
    
        // Flaky on Linux SwANGLE http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(IsLinux() && isSwiftshader());
    
        // To aid in debugging, we want this window visible
        setWindowVisible(mOSWindow, true);
    
        initializeDisplay();
        initializeSurfaceWithRGBA8888Config();
    
        eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
        ASSERT_EGL_SUCCESS();
    
        ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_shader_multisample_interpolation"));
    
        // Init program
        GLuint program = createProgram();
        ASSERT_NE(0u, program);
        glUseProgram(program);
    
        GLBuffer indexBuffer;
        GLVertexArray vertexArray;
        GLBuffer vertexBuffers;
        initializeGeometry(program, &indexBuffer, &vertexArray, &vertexBuffers);
        ASSERT_GL_NO_ERROR();
    
        glViewport(0, 0, mSize, mSize);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor(0, 255, 0, 255));
        EXPECT_PIXEL_COLOR_EQ(mSize - 1, 0, GLColor(0, 255, 0, 255));
        EXPECT_PIXEL_COLOR_EQ(0, mSize - 1, GLColor(0, 255, 0, 255));
        EXPECT_PIXEL_COLOR_EQ(mSize - 1, mSize - 1, GLColor(0, 255, 0, 255));
        ASSERT_GL_NO_ERROR();
    
        // Make the image visible
        eglSwapBuffers(mDisplay, mWindowSurface);
        ASSERT_EGL_SUCCESS();
    }
    
    // Draw with interpolateAtOffset() builtin function to pre-rotated custom FBO
    TEST_P(EGLPreRotationInterpolateAtOffsetTest, InterpolateAtOffsetWithCustomFBO)
    {
        // http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(isVulkanRenderer() && IsLinux() && IsIntel());
    
        // Flaky on Linux SwANGLE http://anglebug.com/4453
        ANGLE_SKIP_TEST_IF(IsLinux() && isSwiftshader());
    
        // To aid in debugging, we want this window visible
        setWindowVisible(mOSWindow, true);
    
        initializeDisplay();
        initializeSurfaceWithRGBA8888Config();
    
        eglMakeCurrent(mDisplay, mWindowSurface, mWindowSurface, mContext);
        ASSERT_EGL_SUCCESS();
    
        ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_shader_multisample_interpolation"));
    
        // Init program
        GLuint program = createProgram();
        ASSERT_NE(0u, program);
        glUseProgram(program);
    
        GLBuffer indexBuffer;
        GLVertexArray vertexArray;
        GLBuffer vertexBuffers;
        initializeGeometry(program, &indexBuffer, &vertexArray, &vertexBuffers);
        ASSERT_GL_NO_ERROR();
    
        // Create a texture-backed FBO
        GLFramebuffer fbo;
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, mSize, mSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
        ASSERT_GL_NO_ERROR();
    
        glViewport(0, 0, mSize, mSize);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor(0, 255, 0, 255));
        EXPECT_PIXEL_COLOR_EQ(mSize - 1, 0, GLColor(0, 255, 0, 255));
        EXPECT_PIXEL_COLOR_EQ(0, mSize - 1, GLColor(0, 255, 0, 255));
        EXPECT_PIXEL_COLOR_EQ(mSize - 1, mSize - 1, GLColor(0, 255, 0, 255));
        ASSERT_GL_NO_ERROR();
    }
    
    }  // anonymous namespace
    
    #ifdef Bool
    // X11 ridiculousness.
    #    undef Bool
    #endif
    
    GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EGLPreRotationInterpolateAtOffsetTest);
    ANGLE_INSTANTIATE_TEST_COMBINE_1(EGLPreRotationInterpolateAtOffsetTest,
                                     PrintToStringParamName,
                                     testing::Bool(),
                                     WithNoFixture(ES31_VULKAN()));
    
    GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EGLPreRotationSurfaceTest);
    ANGLE_INSTANTIATE_TEST_COMBINE_1(EGLPreRotationSurfaceTest,
                                     PrintToStringParamName,
                                     testing::Bool(),
                                     WithNoFixture(ES2_VULKAN()),
                                     WithNoFixture(ES3_VULKAN()),
                                     WithNoFixture(ES3_VULKAN_SWIFTSHADER()));
    
    GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EGLPreRotationLargeSurfaceTest);
    ANGLE_INSTANTIATE_TEST_COMBINE_1(EGLPreRotationLargeSurfaceTest,
                                     PrintToStringParamName,
                                     testing::Bool(),
                                     WithNoFixture(ES2_VULKAN()),
                                     WithNoFixture(ES3_VULKAN()),
                                     WithNoFixture(ES3_VULKAN_SWIFTSHADER()));
    
    GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EGLPreRotationBlitFramebufferTest);
    ANGLE_INSTANTIATE_TEST_COMBINE_1(EGLPreRotationBlitFramebufferTest,
                                     PrintToStringParamName,
                                     testing::Bool(),
                                     WithNoFixture(ES2_VULKAN()),
                                     WithNoFixture(ES3_VULKAN()),
                                     WithNoFixture(ES3_VULKAN_SWIFTSHADER()));