//
// Copyright 2015 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.
//
// ReadPixelsTest:
//   Tests calls related to glReadPixels.
//

#include "test_utils/ANGLETest.h"

#include <array>

#include "test_utils/gl_raii.h"
#include "util/random_utils.h"

using namespace angle;

namespace
{

class ReadPixelsTest : public ANGLETest<>
{
  protected:
    ReadPixelsTest()
    {
        setWindowWidth(32);
        setWindowHeight(32);
        setConfigRedBits(8);
        setConfigGreenBits(8);
        setConfigBlueBits(8);
        setConfigAlphaBits(8);
    }
};

// Test out of bounds framebuffer reads.
TEST_P(ReadPixelsTest, OutOfBounds)
{
    // TODO: re-enable once root cause of http://anglebug.com/42260408 is fixed
    ANGLE_SKIP_TEST_IF(IsAndroid() && IsAdreno() && IsOpenGLES());

    glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    EXPECT_GL_NO_ERROR();

    GLsizei pixelsWidth  = 32;
    GLsizei pixelsHeight = 32;
    GLint offset         = 16;
    std::vector<GLColor> pixels((pixelsWidth + offset) * (pixelsHeight + offset));

    glReadPixels(-offset, -offset, pixelsWidth + offset, pixelsHeight + offset, GL_RGBA,
                 GL_UNSIGNED_BYTE, &pixels[0]);
    EXPECT_GL_NO_ERROR();

    // Expect that all pixels which fell within the framebuffer are red
    for (int y = pixelsHeight / 2; y < pixelsHeight; y++)
    {
        for (int x = pixelsWidth / 2; x < pixelsWidth; x++)
        {
            EXPECT_EQ(GLColor::red, pixels[y * (pixelsWidth + offset) + x]);
        }
    }
}

class ReadPixelsPBONVTest : public ReadPixelsTest
{
  protected:
    ReadPixelsPBONVTest() : mPBO(0), mTexture(0), mFBO(0) {}

    void testSetUp() override
    {
        glGenBuffers(1, &mPBO);
        glGenFramebuffers(1, &mFBO);

        reset(4 * getWindowWidth() * getWindowHeight(), 4, 4);
    }

    virtual void reset(GLuint bufferSize, GLuint fboWidth, GLuint fboHeight)
    {
        ANGLE_SKIP_TEST_IF(!hasPBOExts());

        mPBOBufferSize = bufferSize;
        glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
        glBufferData(GL_PIXEL_PACK_BUFFER, mPBOBufferSize, nullptr, GL_STATIC_DRAW);
        glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);

        glDeleteTextures(1, &mTexture);
        glGenTextures(1, &mTexture);
        glBindTexture(GL_TEXTURE_2D, mTexture);
        glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, fboWidth, fboHeight);
        mFBOWidth  = fboWidth;
        mFBOHeight = fboHeight;

        glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);

        ASSERT_GL_NO_ERROR();
    }

    void testTearDown() override
    {
        glDeleteBuffers(1, &mPBO);
        glDeleteTextures(1, &mTexture);
        glDeleteFramebuffers(1, &mFBO);
    }

    bool hasPBOExts() const
    {
        return IsGLExtensionEnabled("GL_NV_pixel_buffer_object") &&
               IsGLExtensionEnabled("GL_EXT_texture_storage");
    }

    GLuint mPBO           = 0;
    GLuint mTexture       = 0;
    GLuint mFBO           = 0;
    GLuint mFBOWidth      = 0;
    GLuint mFBOHeight     = 0;
    GLuint mPBOBufferSize = 0;
};

// Test basic usage of PBOs.
TEST_P(ReadPixelsPBONVTest, Basic)
{
    ANGLE_SKIP_TEST_IF(!hasPBOExts() || !IsGLExtensionEnabled("GL_EXT_map_buffer_range") ||
                       !IsGLExtensionEnabled("GL_OES_mapbuffer"));

    // http://anglebug.com/42263593
    ANGLE_SKIP_TEST_IF(IsWindows() && IsDesktopOpenGL());
    // http://anglebug.com/42263926
    ANGLE_SKIP_TEST_IF(IsLinux() && IsAMD() && IsDesktopOpenGL());

    glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    // Clear last pixel to green
    glScissor(15, 15, 1, 1);
    glEnable(GL_SCISSOR_TEST);
    glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    EXPECT_GL_NO_ERROR();

    glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
    glReadPixels(0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, 0);

    void *mappedPtr = glMapBufferRangeEXT(GL_PIXEL_PACK_BUFFER, 0, mPBOBufferSize, GL_MAP_READ_BIT);
    GLColor *dataColor = static_cast<GLColor *>(mappedPtr);
    EXPECT_GL_NO_ERROR();

    EXPECT_EQ(GLColor::red, dataColor[0]);
    EXPECT_EQ(GLColor::red, dataColor[16 * 16 - 2]);
    EXPECT_EQ(GLColor::green, dataColor[16 * 16 - 1]);

    glUnmapBufferOES(GL_PIXEL_PACK_BUFFER);
    EXPECT_GL_NO_ERROR();
}

// Test that calling SubData preserves PBO data.
TEST_P(ReadPixelsPBONVTest, SubDataPreservesContents)
{
    ANGLE_SKIP_TEST_IF(!hasPBOExts() || !IsGLExtensionEnabled("GL_EXT_map_buffer_range") ||
                       !IsGLExtensionEnabled("GL_OES_mapbuffer"));

    // anglebug.com/40096466
    ANGLE_SKIP_TEST_IF(IsMac() && IsNVIDIA() && IsDesktopOpenGL());

    glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    EXPECT_GL_NO_ERROR();

    glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
    glReadPixels(0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, 0);

    unsigned char data[4] = {1, 2, 3, 4};

    glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
    glBindBuffer(GL_ARRAY_BUFFER, mPBO);
    glBufferSubData(GL_ARRAY_BUFFER, 0, 4, data);

    void *mappedPtr    = glMapBufferRangeEXT(GL_ARRAY_BUFFER, 0, 32, GL_MAP_READ_BIT);
    GLColor *dataColor = static_cast<GLColor *>(mappedPtr);
    EXPECT_GL_NO_ERROR();

    EXPECT_EQ(GLColor(1, 2, 3, 4), dataColor[0]);
    EXPECT_EQ(GLColor::red, dataColor[1]);

    glUnmapBufferOES(GL_ARRAY_BUFFER);
    EXPECT_GL_NO_ERROR();
}

// Test that calling ReadPixels with GL_DYNAMIC_DRAW buffer works
TEST_P(ReadPixelsPBONVTest, DynamicPBO)
{
    ANGLE_SKIP_TEST_IF(!hasPBOExts() || !IsGLExtensionEnabled("GL_EXT_map_buffer_range") ||
                       !IsGLExtensionEnabled("GL_OES_mapbuffer"));

    // anglebug.com/40096466
    ANGLE_SKIP_TEST_IF(IsMac() && IsNVIDIA() && IsDesktopOpenGL());

    glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
    glBufferData(GL_PIXEL_PACK_BUFFER, 4 * getWindowWidth() * getWindowHeight(), nullptr,
                 GL_DYNAMIC_DRAW);

    glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    EXPECT_GL_NO_ERROR();

    glReadPixels(0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, 0);

    unsigned char data[4] = {1, 2, 3, 4};

    glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
    glBindBuffer(GL_ARRAY_BUFFER, mPBO);
    glBufferSubData(GL_ARRAY_BUFFER, 0, 4, data);

    void *mappedPtr    = glMapBufferRangeEXT(GL_ARRAY_BUFFER, 0, 32, GL_MAP_READ_BIT);
    GLColor *dataColor = static_cast<GLColor *>(mappedPtr);
    EXPECT_GL_NO_ERROR();

    EXPECT_EQ(GLColor(1, 2, 3, 4), dataColor[0]);
    EXPECT_EQ(GLColor::red, dataColor[1]);

    glUnmapBufferOES(GL_ARRAY_BUFFER);
    EXPECT_GL_NO_ERROR();
}

TEST_P(ReadPixelsPBONVTest, ReadFromFBO)
{
    ANGLE_SKIP_TEST_IF(!hasPBOExts() || !IsGLExtensionEnabled("GL_EXT_map_buffer_range") ||
                       !IsGLExtensionEnabled("GL_OES_mapbuffer"));

    glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
    glViewport(0, 0, mFBOWidth, mFBOHeight);
    glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    // Clear last pixel to green
    glScissor(mFBOWidth - 1, mFBOHeight - 1, 1, 1);
    glEnable(GL_SCISSOR_TEST);
    glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    EXPECT_GL_NO_ERROR();

    glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
    glReadPixels(0, 0, mFBOWidth, mFBOHeight, GL_RGBA, GL_UNSIGNED_BYTE, 0);

    void *mappedPtr =
        glMapBufferRangeEXT(GL_PIXEL_PACK_BUFFER, 0, 4 * mFBOWidth * mFBOHeight, GL_MAP_READ_BIT);
    GLColor *dataColor = static_cast<GLColor *>(mappedPtr);
    EXPECT_GL_NO_ERROR();

    EXPECT_EQ(GLColor::red, dataColor[0]);
    EXPECT_EQ(GLColor::red, dataColor[mFBOWidth * mFBOHeight - 2]);
    EXPECT_EQ(GLColor::green, dataColor[mFBOWidth * mFBOHeight - 1]);

    glUnmapBufferOES(GL_PIXEL_PACK_BUFFER);
    EXPECT_GL_NO_ERROR();
}

// Test calling ReadPixels with a non-zero "data" param into a PBO
TEST_P(ReadPixelsPBONVTest, ReadFromFBOWithDataOffset)
{
    ANGLE_SKIP_TEST_IF(!hasPBOExts() || !IsGLExtensionEnabled("GL_EXT_map_buffer_range") ||
                       !IsGLExtensionEnabled("GL_OES_mapbuffer"));

    glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
    glViewport(0, 0, mFBOWidth, mFBOHeight);
    glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    // Clear first pixel to green
    glScissor(0, 0, 1, 1);
    glEnable(GL_SCISSOR_TEST);
    glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    EXPECT_GL_NO_ERROR();

    glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);

    // Read (height - 1) rows offset by width * 4.
    glReadPixels(0, 0, mFBOWidth, mFBOHeight - 1, GL_RGBA, GL_UNSIGNED_BYTE,
                 reinterpret_cast<void *>(mFBOWidth * static_cast<uintptr_t>(4)));

    void *mappedPtr =
        glMapBufferRangeEXT(GL_PIXEL_PACK_BUFFER, 0, 4 * mFBOWidth * mFBOHeight, GL_MAP_READ_BIT);
    GLColor *dataColor = static_cast<GLColor *>(mappedPtr);
    EXPECT_GL_NO_ERROR();

    EXPECT_EQ(GLColor::green, dataColor[mFBOWidth]);
    EXPECT_EQ(GLColor::red, dataColor[mFBOWidth + 1]);
    EXPECT_EQ(GLColor::red, dataColor[mFBOWidth * mFBOHeight - 1]);

    glUnmapBufferOES(GL_PIXEL_PACK_BUFFER);
    EXPECT_GL_NO_ERROR();
}

class ReadPixelsPBOTest : public ReadPixelsPBONVTest
{
  protected:
    ReadPixelsPBOTest() : ReadPixelsPBONVTest() {}

    void testSetUp() override
    {
        glGenBuffers(1, &mPBO);
        glGenFramebuffers(1, &mFBO);

        reset(4 * getWindowWidth() * getWindowHeight(), 4, 1);
    }

    void reset(GLuint bufferSize, GLuint fboWidth, GLuint fboHeight) override
    {
        glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
        glBufferData(GL_PIXEL_PACK_BUFFER, bufferSize, nullptr, GL_STATIC_DRAW);
        glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);

        glDeleteTextures(1, &mTexture);
        glGenTextures(1, &mTexture);
        glBindTexture(GL_TEXTURE_2D, mTexture);
        glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, fboWidth, fboHeight);

        glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);

        mFBOWidth  = fboWidth;
        mFBOHeight = fboHeight;

        mPBOBufferSize = bufferSize;

        ASSERT_GL_NO_ERROR();
    }
};

// Test basic usage of PBOs.
TEST_P(ReadPixelsPBOTest, Basic)
{
    glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    EXPECT_GL_NO_ERROR();

    glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
    glReadPixels(0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, 0);

    void *mappedPtr    = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, 32, GL_MAP_READ_BIT);
    GLColor *dataColor = static_cast<GLColor *>(mappedPtr);
    EXPECT_GL_NO_ERROR();

    EXPECT_EQ(GLColor::red, dataColor[0]);

    glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
    EXPECT_GL_NO_ERROR();
}

// Test copy to snorm
TEST_P(ReadPixelsPBOTest, Snorm)
{
    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_render_snorm"));

    constexpr GLsizei kSize = 6;

    GLRenderbuffer rbo;
    glBindRenderbuffer(GL_RENDERBUFFER, rbo);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_SNORM, kSize, kSize);
    ASSERT_GL_NO_ERROR();

    GLFramebuffer fbo;
    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo);
    ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    ASSERT_GL_NO_ERROR();

    glEnable(GL_SCISSOR_TEST);
    glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
    glScissor(0, 0, kSize / 2, kSize / 2);
    glClear(GL_COLOR_BUFFER_BIT);

    glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
    glScissor(kSize / 2, 0, kSize / 2, kSize / 2);
    glClear(GL_COLOR_BUFFER_BIT);

    glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
    glScissor(0, kSize / 2, kSize / 2, kSize / 2);
    glClear(GL_COLOR_BUFFER_BIT);

    glClearColor(1.0f, 1.0f, 0.0f, 1.0f);
    glScissor(kSize / 2, kSize / 2, kSize / 2, kSize / 2);
    glClear(GL_COLOR_BUFFER_BIT);
    EXPECT_GL_NO_ERROR();

    glDisable(GL_SCISSOR_TEST);

    glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
    glReadPixels(0, 0, kSize, kSize, GL_RGBA, GL_BYTE, 0);

    std::vector<GLColor> result(kSize * kSize);
    void *mappedPtr = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, kSize * kSize * 4, GL_MAP_READ_BIT);
    memcpy(result.data(), mappedPtr, kSize * kSize * 4);
    glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
    EXPECT_GL_NO_ERROR();

    auto verify = [&](const GLColor expect[4]) {
        for (size_t i = 0; i < kSize; ++i)
        {
            for (size_t j = 0; j < kSize; ++j)
            {
                uint32_t index = (i < kSize / 2 ? 0 : 1) << 1 | (j < kSize / 2 ? 0 : 1);
                EXPECT_EQ(result[i * kSize + j], expect[index]) << i << " " << j;
            }
        }
    };

    // The image should have the following colors
    //
    //     +---+---+
    //     | R | G |
    //     +---+---+
    //     | B | Y |
    //     +---+---+
    //
    const GLColor kColors[4] = {
        GLColor(127, 0, 0, 127),
        GLColor(0, 127, 0, 127),
        GLColor(0, 0, 127, 127),
        GLColor(127, 127, 0, 127),
    };
    verify(kColors);

    // Test again, but this time with reverse order
    if (EnsureGLExtensionEnabled("GL_ANGLE_pack_reverse_row_order"))
    {
        glPixelStorei(GL_PACK_REVERSE_ROW_ORDER_ANGLE, GL_TRUE);
        glReadPixels(0, 0, kSize, kSize, GL_RGBA, GL_BYTE, 0);

        mappedPtr = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, kSize * kSize * 4, GL_MAP_READ_BIT);
        memcpy(result.data(), mappedPtr, kSize * kSize * 4);
        glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
        EXPECT_GL_NO_ERROR();

        const GLColor kReversedColors[4] = {
            GLColor(0, 0, 127, 127),
            GLColor(127, 127, 0, 127),
            GLColor(127, 0, 0, 127),
            GLColor(0, 127, 0, 127),
        };
        verify(kReversedColors);
    }
}

// Test read pixel to PBO of an sRGB unorm renderbuffer
TEST_P(ReadPixelsPBOTest, SrgbUnorm)
{
    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_pack_reverse_row_order"));

    constexpr GLsizei kSize = 1;
    constexpr angle::GLColor clearColor(64, 0, 0, 255);
    constexpr angle::GLColor encodedToSrgbColor(136, 0, 0, 255);

    GLRenderbuffer rbo;
    glBindRenderbuffer(GL_RENDERBUFFER, rbo);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8_ALPHA8, kSize, kSize);
    ASSERT_GL_NO_ERROR();

    GLFramebuffer fbo;
    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo);
    ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    ASSERT_GL_NO_ERROR();

    glClearColor(clearColor[0] / 255.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
    glPixelStorei(GL_PACK_REVERSE_ROW_ORDER_ANGLE, GL_TRUE);
    glReadPixels(0, 0, kSize, kSize, GL_RGBA, GL_UNSIGNED_BYTE, 0);

    GLColor result;
    void *mappedPtr = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, kSize * kSize * 4, GL_MAP_READ_BIT);
    memcpy(result.data(), mappedPtr, kSize * kSize * 4);
    glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
    EXPECT_GL_NO_ERROR();

    EXPECT_NEAR(result[0], encodedToSrgbColor[0], 1);
}

// Test an error is generated when the PBO is too small.
TEST_P(ReadPixelsPBOTest, PBOTooSmall)
{
    reset(4 * 16 * 16 - 1, 16, 16);

    glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    EXPECT_GL_NO_ERROR();

    glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
    glReadPixels(0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, 0);

    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}

// Test an error is generated when the PBO is mapped.
TEST_P(ReadPixelsPBOTest, PBOMapped)
{
    glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    EXPECT_GL_NO_ERROR();

    glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
    glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, 32, GL_MAP_READ_BIT);
    glReadPixels(0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, 0);

    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}

// Test that binding a PBO to ARRAY_BUFFER works as expected.
TEST_P(ReadPixelsPBOTest, ArrayBufferTarget)
{
    glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    EXPECT_GL_NO_ERROR();

    glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
    glReadPixels(0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, 0);

    glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
    glBindBuffer(GL_ARRAY_BUFFER, mPBO);

    void *mappedPtr    = glMapBufferRange(GL_ARRAY_BUFFER, 0, 32, GL_MAP_READ_BIT);
    GLColor *dataColor = static_cast<GLColor *>(mappedPtr);
    EXPECT_GL_NO_ERROR();

    EXPECT_EQ(GLColor::red, dataColor[0]);

    glUnmapBuffer(GL_ARRAY_BUFFER);
    EXPECT_GL_NO_ERROR();
}

// Test that using a PBO does not overwrite existing data.
TEST_P(ReadPixelsPBOTest, ExistingDataPreserved)
{
    // Clear backbuffer to red
    glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    EXPECT_GL_NO_ERROR();

    // Read 16x16 region from red backbuffer to PBO
    glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
    glReadPixels(0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, 0);

    // Clear backbuffer to green
    glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    EXPECT_GL_NO_ERROR();

    // Read 16x16 region from green backbuffer to PBO at offset 16
    glReadPixels(0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, reinterpret_cast<void *>(16));
    void *mappedPtr =
        glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, 17 * sizeof(GLColor), GL_MAP_READ_BIT);
    GLColor *dataColor = static_cast<GLColor *>(mappedPtr);
    EXPECT_GL_NO_ERROR();

    // Test pixel 0 is red (existing data)
    EXPECT_EQ(GLColor::red, dataColor[0]);

    // Test pixel 16 is green (new data)
    EXPECT_EQ(GLColor::green, dataColor[16]);

    glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
    EXPECT_GL_NO_ERROR();
}

// Test that calling SubData preserves PBO data.
TEST_P(ReadPixelsPBOTest, SubDataPreservesContents)
{
    // anglebug.com/40096466
    ANGLE_SKIP_TEST_IF(IsMac() && IsNVIDIA() && IsDesktopOpenGL());

    glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    EXPECT_GL_NO_ERROR();

    glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
    glReadPixels(0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, 0);

    unsigned char data[4] = {1, 2, 3, 4};

    glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
    glBindBuffer(GL_ARRAY_BUFFER, mPBO);
    glBufferSubData(GL_ARRAY_BUFFER, 0, 4, data);

    void *mappedPtr    = glMapBufferRange(GL_ARRAY_BUFFER, 0, 32, GL_MAP_READ_BIT);
    GLColor *dataColor = static_cast<GLColor *>(mappedPtr);
    EXPECT_GL_NO_ERROR();

    EXPECT_EQ(GLColor(1, 2, 3, 4), dataColor[0]);

    glUnmapBuffer(GL_ARRAY_BUFFER);
    EXPECT_GL_NO_ERROR();
}

// Same as the prior test, but with an offset.
TEST_P(ReadPixelsPBOTest, SubDataOffsetPreservesContents)
{
    // anglebug.com/42260410
    ANGLE_SKIP_TEST_IF(IsNexus5X() && IsAdreno() && IsOpenGLES());
    // anglebug.com/40096466
    ANGLE_SKIP_TEST_IF(IsMac() && IsNVIDIA() && IsDesktopOpenGL());

    glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    EXPECT_GL_NO_ERROR();

    glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
    glReadPixels(0, 0, 16, 16, GL_RGBA, GL_UNSIGNED_BYTE, 0);

    unsigned char data[4] = {1, 2, 3, 4};

    glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
    glBindBuffer(GL_ARRAY_BUFFER, mPBO);
    glBufferSubData(GL_ARRAY_BUFFER, 16, 4, data);

    void *mappedPtr    = glMapBufferRange(GL_ARRAY_BUFFER, 0, 32, GL_MAP_READ_BIT);
    GLColor *dataColor = static_cast<GLColor *>(mappedPtr);
    EXPECT_GL_NO_ERROR();

    EXPECT_EQ(GLColor::red, dataColor[0]);
    EXPECT_EQ(GLColor(1, 2, 3, 4), dataColor[4]);

    glUnmapBuffer(GL_ARRAY_BUFFER);
    EXPECT_GL_NO_ERROR();
}

// Test that uploading data to buffer that's in use then writing to it as PBO works.
TEST_P(ReadPixelsPBOTest, UseAsUBOThenUpdateThenReadFromFBO)
{
    glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
    glViewport(0, 0, mFBOWidth, mFBOHeight);

    const std::array<GLColor, 4> kInitialData = {GLColor::red, GLColor::red, GLColor::red,
                                                 GLColor::red};
    const std::array<GLColor, 4> kUpdateData  = {GLColor::white, GLColor::white, GLColor::white,
                                                 GLColor::white};

    GLBuffer buffer;
    glBindBuffer(GL_UNIFORM_BUFFER, buffer);
    glBufferData(GL_UNIFORM_BUFFER, sizeof(kInitialData), kInitialData.data(), GL_DYNAMIC_COPY);
    glBindBufferBase(GL_UNIFORM_BUFFER, 0, buffer);
    EXPECT_GL_NO_ERROR();

    constexpr char kVerifyUBO[] = R"(#version 300 es
precision mediump float;
uniform block {
    uvec4 data;
} ubo;
out vec4 colorOut;
void main()
{
    if (all(equal(ubo.data, uvec4(0xFF0000FFu))))
        colorOut = vec4(0, 1.0, 0, 1.0);
    else
        colorOut = vec4(1.0, 0, 0, 1.0);
})";

    ANGLE_GL_PROGRAM(verifyUbo, essl3_shaders::vs::Simple(), kVerifyUBO);
    drawQuad(verifyUbo, essl3_shaders::PositionAttrib(), 0.5);
    EXPECT_GL_NO_ERROR();

    // Update buffer data
    glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(kInitialData), kUpdateData.data());
    EXPECT_GL_NO_ERROR();

    // Clear first pixel to blue
    glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
    glScissor(0, 0, 1, 1);
    glEnable(GL_SCISSOR_TEST);
    glClear(GL_COLOR_BUFFER_BIT);
    EXPECT_GL_NO_ERROR();

    glBindBuffer(GL_PIXEL_PACK_BUFFER, buffer);

    // Read the framebuffer pixels
    glReadPixels(0, 0, mFBOWidth, mFBOHeight, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);

    void *mappedPtr =
        glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, sizeof(kInitialData), GL_MAP_READ_BIT);
    GLColor *dataColor = static_cast<GLColor *>(mappedPtr);
    EXPECT_GL_NO_ERROR();

    EXPECT_EQ(GLColor::blue, dataColor[0]);
    EXPECT_EQ(GLColor::green, dataColor[1]);
    EXPECT_EQ(GLColor::green, dataColor[2]);
    EXPECT_EQ(GLColor::green, dataColor[3]);

    glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
    EXPECT_GL_NO_ERROR();

    glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
    EXPECT_PIXEL_COLOR_EQ(1, 0, GLColor::green);
    EXPECT_PIXEL_COLOR_EQ(2, 0, GLColor::green);
    EXPECT_PIXEL_COLOR_EQ(3, 0, GLColor::green);
}

// Test PBO readback with row length smaller than area width.
TEST_P(ReadPixelsPBOTest, SmallRowLength)
{
    constexpr int kSize = 2;
    reset(kSize * kSize * 4, kSize, kSize);
    std::vector<GLColor> texData(kSize * kSize);
    texData[0] = GLColor::red;
    texData[1] = GLColor::green;
    texData[2] = GLColor::blue;
    texData[3] = GLColor::white;
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kSize, kSize, GL_RGBA, GL_UNSIGNED_BYTE,
                    texData.data());
    ASSERT_GL_NO_ERROR();

    glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
    ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);

    glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
    std::vector<GLColor> bufData(kSize * kSize, GLColor::black);
    glBufferData(GL_PIXEL_PACK_BUFFER, mPBOBufferSize, bufData.data(), GL_STATIC_DRAW);

    glPixelStorei(GL_PACK_ROW_LENGTH, 1);
    glReadPixels(0, 0, kSize, kSize, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    ASSERT_GL_NO_ERROR();

    void *mappedPtr = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, mPBOBufferSize, GL_MAP_READ_BIT);
    ASSERT_NE(nullptr, mappedPtr);
    ASSERT_GL_NO_ERROR();

    // TODO(anglebug.com/354005999)
    // Metal compute path may produce flaky results
    // Suppressed until a fallback is implemented
    if (!IsMetal())
    {
        GLColor *colorPtr = static_cast<GLColor *>(mappedPtr);
        EXPECT_EQ(colorPtr[0], GLColor::red);
        EXPECT_EQ(colorPtr[1], GLColor::blue);
        EXPECT_EQ(colorPtr[2], GLColor::white);
        EXPECT_EQ(colorPtr[3], GLColor::black);
    }
    ASSERT_TRUE(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
    ASSERT_GL_NO_ERROR();
}

class ReadPixelsPBODrawTest : public ReadPixelsPBOTest
{
  protected:
    ReadPixelsPBODrawTest() : mProgram(0), mPositionVBO(0) {}

    void testSetUp() override
    {
        ReadPixelsPBOTest::testSetUp();

        constexpr char kVS[] =
            "attribute vec4 aTest; attribute vec2 aPosition; varying vec4 vTest;\n"
            "void main()\n"
            "{\n"
            "    vTest        = aTest;\n"
            "    gl_Position  = vec4(aPosition, 0.0, 1.0);\n"
            "    gl_PointSize = 1.0;\n"
            "}";

        constexpr char kFS[] =
            "precision mediump float; varying vec4 vTest;\n"
            "void main()\n"
            "{\n"
            "    gl_FragColor = vTest;\n"
            "}";

        mProgram = CompileProgram(kVS, kFS);
        ASSERT_NE(0u, mProgram);

        glGenBuffers(1, &mPositionVBO);
        glBindBuffer(GL_ARRAY_BUFFER, mPositionVBO);
        glBufferData(GL_ARRAY_BUFFER, 128, nullptr, GL_DYNAMIC_DRAW);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
    }

    void testTearDown() override
    {
        glDeleteProgram(mProgram);
        glDeleteBuffers(1, &mPositionVBO);
        ReadPixelsPBOTest::testTearDown();
    }

    GLuint mProgram;
    GLuint mPositionVBO;
};

// Test that we can draw with PBO data.
TEST_P(ReadPixelsPBODrawTest, DrawWithPBO)
{
    GLColor color(1, 2, 3, 4);
    glBindTexture(GL_TEXTURE_2D, mTexture);
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &color);
    EXPECT_GL_NO_ERROR();

    glBindFramebuffer(GL_READ_FRAMEBUFFER, mFBO);
    EXPECT_GL_NO_ERROR();

    glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
    glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, 0);
    glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
    EXPECT_GL_NO_ERROR();

    float positionData[] = {0.5f, 0.5f};

    glUseProgram(mProgram);
    glViewport(0, 0, 1, 1);
    glBindBuffer(GL_ARRAY_BUFFER, mPositionVBO);
    glBufferSubData(GL_ARRAY_BUFFER, 0, 1 * 2 * 4, positionData);
    EXPECT_GL_NO_ERROR();

    GLint positionLocation = glGetAttribLocation(mProgram, "aPosition");
    EXPECT_NE(-1, positionLocation);

    GLint testLocation = glGetAttribLocation(mProgram, "aTest");
    EXPECT_NE(-1, testLocation);

    glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(positionLocation);
    EXPECT_GL_NO_ERROR();

    glBindBuffer(GL_ARRAY_BUFFER, mPBO);
    glVertexAttribPointer(testLocation, 4, GL_UNSIGNED_BYTE, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(testLocation);
    EXPECT_GL_NO_ERROR();

    glDrawArrays(GL_POINTS, 0, 1);
    EXPECT_GL_NO_ERROR();

    color = GLColor(0, 0, 0, 0);
    glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &color);
    EXPECT_GL_NO_ERROR();

    EXPECT_EQ(GLColor(1, 2, 3, 4), color);
}

// Test that we can correctly update a buffer bound to the vertex stage with PBO.
TEST_P(ReadPixelsPBODrawTest, UpdateVertexArrayWithPixelPack)
{
    glUseProgram(mProgram);
    glViewport(0, 0, 1, 1);
    glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
    ASSERT_GL_NO_ERROR();

    // First draw with pre-defined data.
    std::array<float, 2> positionData = {0.5f, 0.5f};

    glBindBuffer(GL_ARRAY_BUFFER, mPositionVBO);
    glBufferSubData(GL_ARRAY_BUFFER, 0, positionData.size() * sizeof(positionData[0]),
                    positionData.data());
    ASSERT_GL_NO_ERROR();

    GLint positionLocation = glGetAttribLocation(mProgram, "aPosition");
    EXPECT_NE(-1, positionLocation);

    GLint testLocation = glGetAttribLocation(mProgram, "aTest");
    EXPECT_NE(-1, testLocation);

    glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(positionLocation);
    ASSERT_GL_NO_ERROR();

    glBindBuffer(GL_ARRAY_BUFFER, mPBO);
    glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLColor), &GLColor::red);
    glVertexAttribPointer(testLocation, 4, GL_UNSIGNED_BYTE, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(testLocation);
    ASSERT_GL_NO_ERROR();

    glDrawArrays(GL_POINTS, 0, 1);
    ASSERT_GL_NO_ERROR();

    // Update the buffer bound to the VAO with a PBO.
    glBindTexture(GL_TEXTURE_2D, mTexture);
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &GLColor::green);
    ASSERT_GL_NO_ERROR();

    glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
    glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, 0);
    glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
    ASSERT_GL_NO_ERROR();

    // Draw again and verify the VAO has the updated data.
    glDrawArrays(GL_POINTS, 0, 1);

    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}

class ReadPixelsTextureNorm16PBOTest : public ReadPixelsTest
{
  protected:
    void testSetUp() override
    {
        glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
        glBindTexture(GL_TEXTURE_2D, mTex);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTex, 0);
        glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
        ASSERT_GL_NO_ERROR();
    }

    template <typename T>
    void test(GLenum format, GLenum internalFormat, GLenum readFormat)
    {
        const bool isSigned = std::is_same<T, GLshort>::value;
        const GLenum type   = isSigned ? GL_SHORT : GL_UNSIGNED_SHORT;

        T data[4] = {};
        data[0]   = isSigned ? -32767 : 32767;
        data[1]   = isSigned ? -16383 : 16383;
        data[2]   = isSigned ? -8191 : 8191;
        data[3]   = isSigned ? -4095 : 4095;

        glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, 1, 1, 0, format, type, data);
        ASSERT_GL_NO_ERROR();
        ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);

        bool supportedCombination = true;
        if (readFormat != GL_RGBA)
        {
            GLenum implementationFormat, implementationType;
            glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT,
                          reinterpret_cast<GLint *>(&implementationFormat));
            glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE,
                          reinterpret_cast<GLint *>(&implementationType));
            ASSERT_GL_NO_ERROR();

            supportedCombination = implementationFormat == readFormat && implementationType == type;
        }

        glBufferData(GL_PIXEL_PACK_BUFFER, 12, nullptr, GL_STATIC_COPY);
        ASSERT_GL_NO_ERROR();

        // Use non-zero offset for better code coverage
        constexpr GLint offset = 4;
        glReadPixels(0, 0, 1, 1, readFormat, type, reinterpret_cast<void *>(offset));
        if (supportedCombination)
        {
            ASSERT_GL_NO_ERROR();
        }
        else
        {
            EXPECT_GL_ERROR(GL_INVALID_OPERATION);
            ANGLE_SKIP_TEST_IF(!supportedCombination);
        }

        T *dataRead =
            static_cast<T *>(glMapBufferRange(GL_PIXEL_PACK_BUFFER, offset, 8, GL_MAP_READ_BIT));
        ASSERT_GL_NO_ERROR();

        EXPECT_EQ(dataRead[0], data[0]);
        if (readFormat == GL_RGBA || readFormat == GL_RG)
        {
            EXPECT_EQ(dataRead[1], format != GL_RED ? data[1] : 0);
        }
        if (readFormat == GL_RGBA)
        {
            EXPECT_EQ(dataRead[2], format == GL_RGBA ? data[2] : 0);
            EXPECT_EQ(dataRead[3], format == GL_RGBA ? data[3] : (isSigned ? 32767 : 65535));
        }
        glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
    }

    void testUnsigned(GLenum format, GLenum internalFormat, GLenum readFormat)
    {
        ASSERT(internalFormat == GL_RGBA16_EXT || internalFormat == GL_RG16_EXT ||
               internalFormat == GL_R16_EXT);
        test<GLushort>(format, internalFormat, readFormat);
    }

    void testSigned(GLenum format, GLenum internalFormat, GLenum readFormat)
    {
        ASSERT(internalFormat == GL_RGBA16_SNORM_EXT || internalFormat == GL_RG16_SNORM_EXT ||
               internalFormat == GL_R16_SNORM_EXT);
        test<GLshort>(format, internalFormat, readFormat);
    }

    GLFramebuffer mFBO;
    GLTexture mTex;
    GLBuffer mPBO;
};

// Test PBO RGBA readback for RGBA16 color buffer.
TEST_P(ReadPixelsTextureNorm16PBOTest, RGBA16_RGBA)
{
    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_norm16"));
    testUnsigned(GL_RGBA, GL_RGBA16_EXT, GL_RGBA);
}

// Test PBO RGBA readback for RG16 color buffer.
TEST_P(ReadPixelsTextureNorm16PBOTest, RG16_RGBA)
{
    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_norm16"));
    testUnsigned(GL_RG, GL_RG16_EXT, GL_RGBA);
}

// Test PBO RG readback for RG16 color buffer.
TEST_P(ReadPixelsTextureNorm16PBOTest, RG16_RG)
{
    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_norm16"));
    testUnsigned(GL_RG, GL_RG16_EXT, GL_RG);
}

// Test PBO RGBA readback for R16 color buffer.
TEST_P(ReadPixelsTextureNorm16PBOTest, R16_RGBA)
{
    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_norm16"));
    testUnsigned(GL_RED, GL_R16_EXT, GL_RGBA);
}

// Test PBO RED readback for R16 color buffer.
TEST_P(ReadPixelsTextureNorm16PBOTest, R16_RED)
{
    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_norm16"));
    testUnsigned(GL_RED, GL_R16_EXT, GL_RED);
}

// Test PBO RGBA readback for RGBA16_SNORM color buffer.
TEST_P(ReadPixelsTextureNorm16PBOTest, RGBA16_SNORM_RGBA)
{
    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_render_snorm"));
    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_norm16"));
    testSigned(GL_RGBA, GL_RGBA16_SNORM_EXT, GL_RGBA);
}

// Test PBO RGBA readback for RG16_SNORM color buffer.
TEST_P(ReadPixelsTextureNorm16PBOTest, RG16_SNORM_RGBA)
{
    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_render_snorm"));
    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_norm16"));
    testSigned(GL_RG, GL_RG16_SNORM_EXT, GL_RGBA);
}

// Test PBO RG readback for RG16_SNORM color buffer.
TEST_P(ReadPixelsTextureNorm16PBOTest, RG16_SNORM_RG)
{
    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_render_snorm"));
    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_norm16"));
    testSigned(GL_RG, GL_RG16_SNORM_EXT, GL_RG);
}

// Test PBO RGBA readback for R16_SNORM color buffer.
TEST_P(ReadPixelsTextureNorm16PBOTest, R16_SNORM_RGBA)
{
    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_render_snorm"));
    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_norm16"));
    testSigned(GL_RED, GL_R16_SNORM_EXT, GL_RGBA);
}

// Test PBO RED readback for R16_SNORM color buffer.
TEST_P(ReadPixelsTextureNorm16PBOTest, R16_SNORM_RED)
{
    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_render_snorm"));
    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_norm16"));
    testSigned(GL_RED, GL_R16_SNORM_EXT, GL_RED);
}

class ReadPixelsMultisampleTest : public ReadPixelsTest
{
  protected:
    ReadPixelsMultisampleTest() : mFBO(0), mRBO(0), mPBO(0)
    {
        setSamples(4);
        setMultisampleEnabled(true);
    }

    void testSetUp() override
    {
        glGenFramebuffers(1, &mFBO);
        glBindFramebuffer(GL_FRAMEBUFFER, mFBO);

        glGenRenderbuffers(1, &mRBO);
        glBindRenderbuffer(GL_RENDERBUFFER, mRBO);

        glGenBuffers(1, &mPBO);
        glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
        glBufferData(GL_PIXEL_PACK_BUFFER, 4 * getWindowWidth() * getWindowHeight(), nullptr,
                     GL_STATIC_DRAW);
        glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);

        ASSERT_GL_NO_ERROR();
    }

    void testTearDown() override
    {
        glDeleteFramebuffers(1, &mFBO);
        glDeleteRenderbuffers(1, &mRBO);
        glDeleteBuffers(1, &mPBO);
    }

    GLuint mFBO;
    GLuint mRBO;
    GLuint mPBO;
};

// Test ReadPixels from a multisampled framebuffer.
TEST_P(ReadPixelsMultisampleTest, BasicClear)
{
    if (getClientMajorVersion() < 3 && !IsGLExtensionEnabled("GL_ANGLE_framebuffer_multisample"))
    {
        std::cout
            << "Test skipped because ES3 or GL_ANGLE_framebuffer_multisample is not available."
            << std::endl;
        return;
    }

    if (IsGLExtensionEnabled("GL_ANGLE_framebuffer_multisample"))
    {
        glRenderbufferStorageMultisampleANGLE(GL_RENDERBUFFER, 2, GL_RGBA8, 4, 4);
    }
    else
    {
        glRenderbufferStorageMultisample(GL_RENDERBUFFER, 2, GL_RGBA8, 4, 4);
    }

    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, mRBO);
    ASSERT_GL_NO_ERROR();

    glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);
    EXPECT_GL_NO_ERROR();

    glReadPixels(0, 0, 1, 1, GL_RGBA8, GL_UNSIGNED_BYTE, nullptr);
    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}

// Test ReadPixels from a multisampled swapchain.
TEST_P(ReadPixelsMultisampleTest, DefaultFramebuffer)
{
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    EXPECT_PIXEL_RECT_EQ(0, 0, getWindowWidth(), getWindowHeight(), GLColor::red);
    EXPECT_GL_NO_ERROR();
}

// Test ReadPixels from a multisampled swapchain into a PBO.
TEST_P(ReadPixelsMultisampleTest, DefaultFramebufferPBO)
{
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    glBindBuffer(GL_PIXEL_PACK_BUFFER, mPBO);

    const int w = getWindowWidth();
    const int h = getWindowHeight();
    glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    EXPECT_GL_NO_ERROR();

    const std::vector<angle::GLColor> expectedColor(w * h, GLColor::red);
    std::vector<angle::GLColor> actualColor(w * h);
    const void *mapPointer =
        glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, sizeof(angle::GLColor) * w * h, GL_MAP_READ_BIT);
    ASSERT_NE(nullptr, mapPointer);
    memcpy(actualColor.data(), mapPointer, sizeof(angle::GLColor) * w * h);
    glUnmapBuffer(GL_PIXEL_PACK_BUFFER);

    EXPECT_EQ(expectedColor, actualColor);
}

class ReadPixelsTextureTest : public ANGLETest<>
{
  public:
    ReadPixelsTextureTest() : mFBO(0), mTextureRGBA(0), mTextureBGRA(0)
    {
        setWindowWidth(32);
        setWindowHeight(32);
        setConfigRedBits(8);
        setConfigGreenBits(8);
        setConfigBlueBits(8);
        setConfigAlphaBits(8);
    }

    void testSetUp() override
    {
        glGenTextures(1, &mTextureRGBA);
        glGenTextures(1, &mTextureBGRA);
        glGenFramebuffers(1, &mFBO);
        glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
    }

    void testTearDown() override
    {
        glDeleteFramebuffers(1, &mFBO);
        glDeleteTextures(1, &mTextureRGBA);
        glDeleteTextures(1, &mTextureBGRA);
    }

    void initTextureRGBA(GLenum textureTarget,
                         GLint levels,
                         GLint attachmentLevel,
                         GLint attachmentLayer)
    {
        glBindTexture(textureTarget, mTextureRGBA);
        glTexStorage3D(textureTarget, levels, GL_RGBA8, kSize, kSize, kSize);
        glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTextureRGBA,
                                  attachmentLevel, attachmentLayer);
        ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
        ASSERT_GL_NO_ERROR();
        initializeTextureData(textureTarget, levels, GL_RGBA);
    }

    void initTextureBGRA(GLenum textureTarget,
                         GLint levels,
                         GLint attachmentLevel,
                         GLint attachmentLayer)
    {
        glBindTexture(textureTarget, mTextureBGRA);
        for (GLint level = 0; level < levels; ++level)
        {
            glTexImage3D(textureTarget, level, GL_BGRA_EXT, kSize >> level, kSize >> level,
                         textureTarget == GL_TEXTURE_3D ? kSize >> level : kSize, 0, GL_BGRA_EXT,
                         GL_UNSIGNED_BYTE, nullptr);
        }
        glTexParameteri(textureTarget, GL_TEXTURE_BASE_LEVEL, 0);
        glTexParameteri(textureTarget, GL_TEXTURE_MAX_LEVEL, levels - 1);
        glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTextureBGRA,
                                  attachmentLevel, attachmentLayer);
        ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
        ASSERT_GL_NO_ERROR();
        initializeTextureData(textureTarget, levels, GL_BGRA_EXT);
    }

    void testRead(GLenum textureTarget, GLint levels, GLint attachmentLevel, GLint attachmentLayer)
    {
        initTextureRGBA(textureTarget, levels, attachmentLevel, attachmentLayer);
        verifyColor(attachmentLevel, attachmentLayer);

        // Skip BGRA test on GL/Nvidia, leading to internal incomplete framebuffer error.
        // http://anglebug.com/42266676
        ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsOpenGL());

        if (IsGLExtensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            initTextureBGRA(textureTarget, levels, attachmentLevel, attachmentLayer);
            verifyColor(attachmentLevel, attachmentLayer);
        }
    }

    void initPBO()
    {
        // Create a buffer big enough to hold mip 0 + allow some offset during readback.
        glGenBuffers(1, &mBuffer);
        glBindBuffer(GL_PIXEL_PACK_BUFFER, mBuffer);
        glBufferData(GL_PIXEL_PACK_BUFFER, sizeof(angle::GLColor) * 16 * 2, nullptr,
                     GL_STREAM_COPY);
        ASSERT_GL_NO_ERROR();
    }

    void testPBORead(GLenum textureTarget,
                     GLint levels,
                     GLint attachmentLevel,
                     GLint attachmentLayer)
    {
        initPBO();
        initTextureRGBA(textureTarget, levels, attachmentLevel, attachmentLayer);
        verifyPBO(attachmentLevel, attachmentLayer);

        // Skip BGRA test on GL/Nvidia, leading to internal incomplete framebuffer error.
        // http://anglebug.com/42266676
        ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsOpenGL());

        if (IsGLExtensionEnabled("GL_EXT_texture_format_BGRA8888"))
        {
            initTextureBGRA(textureTarget, levels, attachmentLevel, attachmentLayer);
            verifyPBO(attachmentLevel, attachmentLayer);
        }
    }

    // Give each {level,layer} pair a (probably) unique color via random.
    GLuint getColorValue(GLint level, GLint layer)
    {
        mRNG.reseed(level + layer * 32);
        return mRNG.randomUInt();
    }

    void verifyColor(GLint level, GLint layer)
    {
        const angle::GLColor colorValue(getColorValue(level, layer));
        const GLint size = kSize >> level;
        EXPECT_PIXEL_RECT_EQ(0, 0, size, size, colorValue);
    }

    void verifyPBO(GLint level, GLint layer)
    {
        const GLint size     = kSize >> level;
        const GLsizei offset = kSize * (level + layer);
        glReadPixels(0, 0, size, size, GL_RGBA, GL_UNSIGNED_BYTE, reinterpret_cast<void *>(offset));

        const std::vector<angle::GLColor> expectedColor(size * size, getColorValue(level, layer));
        std::vector<angle::GLColor> actualColor(size * size);

        void *mapPointer = glMapBufferRange(GL_PIXEL_PACK_BUFFER, offset,
                                            sizeof(angle::GLColor) * size * size, GL_MAP_READ_BIT);
        ASSERT_NE(nullptr, mapPointer);
        memcpy(actualColor.data(), mapPointer, sizeof(angle::GLColor) * size * size);
        glUnmapBuffer(GL_PIXEL_PACK_BUFFER);

        ASSERT_GL_NO_ERROR();
        EXPECT_EQ(expectedColor, actualColor);
    }

    void initializeTextureData(GLenum textureTarget, GLint levels, GLenum format)
    {
        for (GLint level = 0; level < levels; ++level)
        {
            GLint mipSize = kSize >> level;
            GLint layers  = (textureTarget == GL_TEXTURE_3D ? mipSize : kSize);

            size_t layerSize = mipSize * mipSize;
            std::vector<GLuint> textureData(layers * layerSize);

            for (GLint layer = 0; layer < layers; ++layer)
            {
                GLuint colorValue = getColorValue(level, layer);
                size_t offset     = (layer * layerSize);

                if (format == GL_BGRA_EXT)
                {
                    const GLuint rb = colorValue & 0x00FF00FF;
                    const GLuint br = (rb & 0xFF) << 16 | rb >> 16;
                    const GLuint ga = colorValue & 0xFF00FF00;
                    colorValue      = ga | br;
                }

                std::fill(textureData.begin() + offset, textureData.begin() + offset + layerSize,
                          colorValue);
            }

            glTexSubImage3D(textureTarget, level, 0, 0, 0, mipSize, mipSize, layers, format,
                            GL_UNSIGNED_BYTE, textureData.data());
        }
    }

    static constexpr GLint kSize = 4;

    angle::RNG mRNG;
    GLuint mFBO;
    GLuint mTextureRGBA;
    GLuint mTextureBGRA;
    GLuint mBuffer;
};

// Test 3D attachment readback.
TEST_P(ReadPixelsTextureTest, BasicAttachment3D)
{
    testRead(GL_TEXTURE_3D, 1, 0, 0);
}

// Test 3D attachment readback, non-zero mip.
TEST_P(ReadPixelsTextureTest, MipAttachment3D)
{
    testRead(GL_TEXTURE_3D, 2, 1, 0);
}

// Test 3D attachment readback, non-zero layer.
TEST_P(ReadPixelsTextureTest, LayerAttachment3D)
{
    testRead(GL_TEXTURE_3D, 1, 0, 1);
}

// Test 3D attachment readback, non-zero mip and layer.
TEST_P(ReadPixelsTextureTest, MipLayerAttachment3D)
{
    testRead(GL_TEXTURE_3D, 2, 1, 1);
}

// Test 2D array attachment readback.
TEST_P(ReadPixelsTextureTest, BasicAttachment2DArray)
{
    testRead(GL_TEXTURE_2D_ARRAY, 1, 0, 0);
}

// Test 3D attachment readback, non-zero mip.
TEST_P(ReadPixelsTextureTest, MipAttachment2DArray)
{
    testRead(GL_TEXTURE_2D_ARRAY, 2, 1, 0);
}

// Test 3D attachment readback, non-zero layer.
TEST_P(ReadPixelsTextureTest, LayerAttachment2DArray)
{
    testRead(GL_TEXTURE_2D_ARRAY, 1, 0, 1);
}

// Test 3D attachment readback, non-zero mip and layer.
TEST_P(ReadPixelsTextureTest, MipLayerAttachment2DArray)
{
    testRead(GL_TEXTURE_2D_ARRAY, 2, 1, 1);
}

// Test 3D attachment PBO readback.
TEST_P(ReadPixelsTextureTest, BasicAttachment3DPBO)
{
    testPBORead(GL_TEXTURE_3D, 1, 0, 0);
}

// Test 3D attachment readback, non-zero mip.
TEST_P(ReadPixelsTextureTest, MipAttachment3DPBO)
{
    testPBORead(GL_TEXTURE_3D, 2, 1, 0);
}

// Test 3D attachment readback, non-zero layer.
TEST_P(ReadPixelsTextureTest, LayerAttachment3DPBO)
{
    // http://anglebug.com/40644770
    ANGLE_SKIP_TEST_IF(IsMac() && IsIntelUHD630Mobile() && IsDesktopOpenGL());

    testPBORead(GL_TEXTURE_3D, 1, 0, 1);
}

// Test 3D attachment readback, non-zero mip and layer.
TEST_P(ReadPixelsTextureTest, MipLayerAttachment3DPBO)
{
    // http://anglebug.com/40644770
    ANGLE_SKIP_TEST_IF(IsMac() && IsIntelUHD630Mobile() && IsDesktopOpenGL());

    testPBORead(GL_TEXTURE_3D, 2, 1, 1);
}

// Test 2D array attachment readback.
TEST_P(ReadPixelsTextureTest, BasicAttachment2DArrayPBO)
{
    testPBORead(GL_TEXTURE_2D_ARRAY, 1, 0, 0);
}

// Test 3D attachment readback, non-zero mip.
TEST_P(ReadPixelsTextureTest, MipAttachment2DArrayPBO)
{
    testPBORead(GL_TEXTURE_2D_ARRAY, 2, 1, 0);
}

// Test 3D attachment readback, non-zero layer.
TEST_P(ReadPixelsTextureTest, LayerAttachment2DArrayPBO)
{
    // http://anglebug.com/40644770
    ANGLE_SKIP_TEST_IF(IsMac() && IsIntelUHD630Mobile() && IsDesktopOpenGL());

    testPBORead(GL_TEXTURE_2D_ARRAY, 1, 0, 1);
}

// Test 3D attachment readback, non-zero mip and layer.
TEST_P(ReadPixelsTextureTest, MipLayerAttachment2DArrayPBO)
{
    // http://anglebug.com/40644770
    ANGLE_SKIP_TEST_IF(IsMac() && IsIntelUHD630Mobile() && IsDesktopOpenGL());

    testPBORead(GL_TEXTURE_2D_ARRAY, 2, 1, 1);
}

// a test class to be used for error checking of glReadPixels
class ReadPixelsErrorTest : public ReadPixelsTest
{
  protected:
    ReadPixelsErrorTest() : mTexture(0), mFBO(0) {}

    void testSetUp() override
    {
        glGenTextures(1, &mTexture);
        glBindTexture(GL_TEXTURE_2D, mTexture);
        glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 4, 1);

        glGenFramebuffers(1, &mFBO);
        glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);

        ASSERT_GL_NO_ERROR();
    }

    void testTearDown() override
    {
        glDeleteTextures(1, &mTexture);
        glDeleteFramebuffers(1, &mFBO);
    }

    void testUnsupportedTypeConversions(std::vector<GLenum> internalFormats,
                                        std::vector<GLenum> unsupportedTypes)
    {
        glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
        for (GLenum internalFormat : internalFormats)
        {
            GLRenderbuffer rbo;
            glBindRenderbuffer(GL_RENDERBUFFER, rbo);
            glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, 1, 1);
            ASSERT_GL_NO_ERROR();

            glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo);
            ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);

            GLenum implementationFormat, implementationType;
            glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT,
                          reinterpret_cast<GLint *>(&implementationFormat));
            ASSERT_GL_NO_ERROR();
            glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE,
                          reinterpret_cast<GLint *>(&implementationType));
            ASSERT_GL_NO_ERROR();

            for (GLenum type : unsupportedTypes)
            {
                uint8_t pixel[8] = {};
                if (implementationFormat != GL_RGBA || implementationType != type)
                {
                    glReadPixels(0, 0, 1, 1, GL_RGBA, type, pixel);
                    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
                }
            }
        }
    }

    GLuint mTexture;
    GLuint mFBO;
};

//  The test verifies that glReadPixels generates a GL_INVALID_OPERATION error
//  when the read buffer is GL_NONE.
//  Reference: GLES 3.0.4, Section 4.3.2 Reading Pixels
TEST_P(ReadPixelsErrorTest, ReadBufferIsNone)
{
    glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
    glReadBuffer(GL_NONE);
    std::vector<GLubyte> pixels(4);
    EXPECT_GL_NO_ERROR();
    glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}

// The test verifies that glReadPixels generates a GL_INVALID_OPERATION
// error when reading signed 8-bit color buffers using incompatible types.
TEST_P(ReadPixelsErrorTest, ColorBufferSnorm8)
{
    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_render_snorm"));

    testUnsupportedTypeConversions({GL_R8_SNORM, GL_RG8_SNORM, GL_RGBA8_SNORM},
                                   {GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT});
}

// The test verifies that glReadPixels generates a GL_INVALID_OPERATION
// error when reading signed 16-bit color buffers using incompatible types.
TEST_P(ReadPixelsErrorTest, ColorBufferSnorm16)
{
    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_render_snorm"));
    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_norm16"));

    testUnsupportedTypeConversions({GL_R16_SNORM_EXT, GL_RG16_SNORM_EXT, GL_RGBA16_SNORM_EXT},
                                   {GL_BYTE, GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT});
}

// a test class to be used for error checking of glReadPixels with WebGLCompatibility
class ReadPixelsWebGLErrorTest : public ReadPixelsTest
{
  protected:
    ReadPixelsWebGLErrorTest() : mTexture(0), mFBO(0) { setWebGLCompatibilityEnabled(true); }

    void testSetUp() override
    {
        glGenTextures(1, &mTexture);
        glBindTexture(GL_TEXTURE_2D, mTexture);
        glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 4, 1);

        glGenFramebuffers(1, &mFBO);
        glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0);
        ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);

        ASSERT_GL_NO_ERROR();
    }

    void testTearDown() override
    {
        glDeleteTextures(1, &mTexture);
        glDeleteFramebuffers(1, &mFBO);
    }

    GLuint mTexture;
    GLuint mFBO;
};

// Test that WebGL context readpixels generates an error when reading GL_UNSIGNED_INT_24_8 type.
TEST_P(ReadPixelsWebGLErrorTest, TypeIsUnsignedInt24_8)
{
    glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0);
    glReadBuffer(GL_COLOR_ATTACHMENT0);
    std::vector<GLuint> pixels(4);
    EXPECT_GL_NO_ERROR();
    glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_INT_24_8, pixels.data());
    EXPECT_GL_ERROR(GL_INVALID_ENUM);
}

// Test that WebGL context readpixels generates an error when reading GL_DEPTH_COMPONENT format.
TEST_P(ReadPixelsWebGLErrorTest, FormatIsDepthComponent)
{
    glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0);
    glReadBuffer(GL_COLOR_ATTACHMENT0);
    std::vector<GLubyte> pixels(4);
    EXPECT_GL_NO_ERROR();
    glReadPixels(0, 0, 1, 1, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, pixels.data());
    EXPECT_GL_ERROR(GL_INVALID_ENUM);
}

}  // anonymous namespace

// Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against.
ANGLE_INSTANTIATE_TEST_ES2(ReadPixelsTest);
ANGLE_INSTANTIATE_TEST_ES2(ReadPixelsPBONVTest);

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ReadPixelsPBOTest);
ANGLE_INSTANTIATE_TEST_ES3(ReadPixelsPBOTest);

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ReadPixelsPBODrawTest);
ANGLE_INSTANTIATE_TEST_ES3_AND(ReadPixelsPBODrawTest,
                               ES3_VULKAN().enable(Feature::ForceFallbackFormat));

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ReadPixelsTextureNorm16PBOTest);
ANGLE_INSTANTIATE_TEST_ES3(ReadPixelsTextureNorm16PBOTest);

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ReadPixelsMultisampleTest);
ANGLE_INSTANTIATE_TEST_ES3(ReadPixelsMultisampleTest);

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ReadPixelsTextureTest);
ANGLE_INSTANTIATE_TEST_ES3(ReadPixelsTextureTest);

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ReadPixelsErrorTest);
ANGLE_INSTANTIATE_TEST_ES3(ReadPixelsErrorTest);

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ReadPixelsWebGLErrorTest);
ANGLE_INSTANTIATE_TEST_ES3(ReadPixelsWebGLErrorTest);
