Edit

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

Branch :

  • Show log

    Commit

  • Author : Tim Van Patten
    Date : 2021-07-16 14:57:13
    Hash : 9ddaa686
    Message : Call getNearestSamples() in Framebuffer::setAttachment() The app jp.garud.ssimulator uses the same sample count of '2' for both glRenderbufferStorageMultisampleEXT() and glFramebufferTexture2DMultisampleEXT(). However, when glRenderbufferStorageMultisampleEXT() is called, Renderbuffer::setStorageMultisample() calls TextureCaps::getNearestSamples() which rounds up the sample count from '2' to '4'. Later, when the app tries to draw with the framebuffer, an error is generated by ANGLE: Framebuffer is incomplete: Attachments have different sample counts. The fix is to also call TextureCaps::getNearestSamples() in Framebuffer::setAttachment() to make sure the sample count passed into glFramebufferTexture2DMultisampleEXT() is also rounded up to match the renderbuffer. Bug: angleproject:6183 Test: MultisampledRenderToTextureTest.FramebufferCompletenessSmallSampleCount Change-Id: I58be9986077257f4767f2e528c2f87e496d9d774 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3036254 Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: Jamie Madill <jmadill@chromium.org> Commit-Queue: Tim Van Patten <timvp@google.com>

  • src/tests/gl_tests/MultisampledRenderToTextureTest.cpp
  • //
    // Copyright 2019 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.
    //
    
    // MultisampledRenderToTextureTest: Tests of EXT_multisampled_render_to_texture extension
    
    #include "test_utils/ANGLETest.h"
    #include "test_utils/gl_raii.h"
    
    using namespace angle;
    
    namespace
    {
    class MultisampledRenderToTextureTest : public ANGLETest
    {
      protected:
        MultisampledRenderToTextureTest()
        {
            setWindowWidth(64);
            setWindowHeight(64);
            setConfigRedBits(8);
            setConfigGreenBits(8);
            setConfigBlueBits(8);
            setConfigAlphaBits(8);
        }
    
        void testSetUp() override
        {
            if (getClientMajorVersion() >= 3 && getClientMinorVersion() >= 1)
            {
                glGetIntegerv(GL_MAX_INTEGER_SAMPLES, &mMaxIntegerSamples);
            }
        }
    
        void testTearDown() override {}
    
        void assertErrorIfNotMSRTT2(GLenum error)
        {
            if (EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture2"))
            {
                ASSERT_GL_NO_ERROR();
            }
            else
            {
                ASSERT_GL_ERROR(error);
            }
        }
    
        void setupCopyTexProgram()
        {
            mCopyTextureProgram.makeRaster(essl1_shaders::vs::Texture2D(),
                                           essl1_shaders::fs::Texture2D());
            ASSERT_GL_TRUE(mCopyTextureProgram.valid());
    
            mCopyTextureUniformLocation =
                glGetUniformLocation(mCopyTextureProgram, essl1_shaders::Texture2DUniform());
    
            ASSERT_GL_NO_ERROR();
        }
    
        void setupUniformColorProgramMultiRenderTarget(const bool bufferEnabled[8], GLuint *programOut)
        {
            std::stringstream fs;
    
            fs << "#extension GL_EXT_draw_buffers : enable\n"
                  "precision highp float;\n"
                  "uniform mediump vec4 "
               << essl1_shaders::ColorUniform()
               << ";\n"
                  "void main()\n"
                  "{\n";
    
            for (unsigned int index = 0; index < 8; index++)
            {
                if (bufferEnabled[index])
                {
                    fs << "    gl_FragData[" << index << "] = " << essl1_shaders::ColorUniform()
                       << ";\n";
                }
            }
    
            fs << "}\n";
    
            *programOut = CompileProgram(essl1_shaders::vs::Simple(), fs.str().c_str());
            ASSERT_NE(*programOut, 0u);
        }
    
        void verifyResults(GLuint texture,
                           const GLColor expected,
                           GLint fboSize,
                           GLint xs,
                           GLint ys,
                           GLint xe,
                           GLint ye)
        {
            glViewport(0, 0, fboSize, fboSize);
    
            glBindFramebuffer(GL_FRAMEBUFFER, 0);
    
            // Draw a quad with the target texture
            glUseProgram(mCopyTextureProgram);
            glBindTexture(GL_TEXTURE_2D, texture);
            glUniform1i(mCopyTextureUniformLocation, 0);
    
            glDisable(GL_DEPTH_TEST);
            glDisable(GL_STENCIL_TEST);
            glDisable(GL_BLEND);
            glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
            drawQuad(mCopyTextureProgram, essl1_shaders::PositionAttrib(), 0.5f);
    
            // Expect that the rendered quad has the same color as the source texture
            EXPECT_PIXEL_COLOR_NEAR(xs, ys, expected, 1.0);
            EXPECT_PIXEL_COLOR_NEAR(xs, ye - 1, expected, 1.0);
            EXPECT_PIXEL_COLOR_NEAR(xe - 1, ys, expected, 1.0);
            EXPECT_PIXEL_COLOR_NEAR(xe - 1, ye - 1, expected, 1.0);
            EXPECT_PIXEL_COLOR_NEAR((xs + xe) / 2, (ys + ye) / 2, expected, 1.0);
        }
    
        void clearAndDrawQuad(GLuint program, GLsizei viewportWidth, GLsizei viewportHeight)
        {
            glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
            glClear(GL_COLOR_BUFFER_BIT);
            glViewport(0, 0, viewportWidth, viewportHeight);
            ASSERT_GL_NO_ERROR();
    
            drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f);
            ASSERT_GL_NO_ERROR();
        }
    
        struct GLType
        {
            GLenum internalFormat;
            GLenum format;
            GLenum type;
        };
    
        void createAndAttachColorAttachment(bool useRenderbuffer,
                                            GLsizei size,
                                            GLenum renderbufferTarget,
                                            const GLType *glType,
                                            GLint samples,
                                            GLTexture *textureOut,
                                            GLRenderbuffer *renderbufferOut);
        void createAndAttachDepthStencilAttachment(bool useRenderbuffer,
                                                   GLsizei size,
                                                   GLTexture *textureOut,
                                                   GLRenderbuffer *renderbufferOut);
        void colorAttachmentMultisampleDrawTestCommon(bool useRenderbuffer);
        void copyTexImageTestCommon(bool useRenderbuffer);
        void copyTexSubImageTestCommon(bool useRenderbuffer);
        void drawCopyThenBlendCommon(bool useRenderbuffer);
        void clearDrawCopyThenBlendSameProgramCommon(bool useRenderbuffer);
        void drawCopyDrawThenMaskedClearCommon(bool useRenderbuffer);
        void clearThenBlendCommon(bool useRenderbuffer);
    
        GLProgram mCopyTextureProgram;
        GLint mCopyTextureUniformLocation = -1;
    
        const GLint mTestSampleCount = 4;
        GLint mMaxIntegerSamples     = 0;
    };
    
    class MultisampledRenderToTextureES3Test : public MultisampledRenderToTextureTest
    {
      protected:
        void readPixelsTestCommon(bool useRenderbuffer);
        void blitFramebufferTestCommon(bool useRenderbuffer);
        void drawCopyDrawAttachInvalidatedThenDrawCommon(bool useRenderbuffer);
        void drawCopyDrawAttachDepthStencilClearThenDrawCommon(bool useRenderbuffer);
        void depthStencilClearThenDrawCommon(bool useRenderbuffer);
        void colorAttachment1Common(bool useRenderbuffer);
        void colorAttachments0And3Common(bool useRenderbuffer);
        void blitFramebufferMixedColorAndDepthCommon(bool useRenderbuffer);
        void renderbufferUnresolveColorAndDepthStencilThenTwoColors(bool withDepth, bool withStencil);
    };
    
    class MultisampledRenderToTextureES31Test : public MultisampledRenderToTextureTest
    {
      protected:
        void blitFramebufferAttachment1Common(bool useRenderbuffer);
        void drawCopyThenBlendAllAttachmentsMixed(bool useRenderbuffer);
    };
    
    // Checking against invalid parameters for RenderbufferStorageMultisampleEXT.
    TEST_P(MultisampledRenderToTextureTest, RenderbufferParameterCheck)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
    
        // Linux Intel Vulkan returns 0 for GL_MAX_INTEGER_SAMPLES http://anglebug.com/5988
        ANGLE_SKIP_TEST_IF(IsLinux() && IsIntel() && IsVulkan());
    
        GLRenderbuffer renderbuffer;
        glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
    
        // Positive test case.  Formats required by the spec (GLES2.0 Table 4.5)
        glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT16, 64, 64);
        ASSERT_GL_NO_ERROR();
        glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_STENCIL_INDEX8, 64, 64);
        ASSERT_GL_NO_ERROR();
        glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_RGBA4, 64, 64);
        ASSERT_GL_NO_ERROR();
        glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_RGB5_A1, 64, 64);
        ASSERT_GL_NO_ERROR();
        glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_RGB565, 64, 64);
        ASSERT_GL_NO_ERROR();
    
        // Positive test case.  A few of the ES3 formats
        if (getClientMajorVersion() >= 3)
        {
            glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, 64, 64);
            ASSERT_GL_NO_ERROR();
            glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_RGBA8, 64, 64);
            ASSERT_GL_NO_ERROR();
    
            if (getClientMinorVersion() >= 1)
            {
                glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, mMaxIntegerSamples, GL_RGBA32I, 64,
                                                    64);
                ASSERT_GL_NO_ERROR();
                glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, mMaxIntegerSamples, GL_RGBA32UI,
                                                    64, 64);
                ASSERT_GL_NO_ERROR();
            }
        }
    
        GLint samples;
        glGetIntegerv(GL_MAX_SAMPLES_EXT, &samples);
        ASSERT_GL_NO_ERROR();
        EXPECT_GE(samples, 1);
    
        // Samples too large
        glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, samples + 1, GL_DEPTH_COMPONENT16, 64, 64);
        ASSERT_GL_ERROR(GL_INVALID_VALUE);
    
        // Renderbuffer size too large
        GLint maxSize;
        glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &maxSize);
        glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 2, GL_RGBA4, maxSize + 1, maxSize);
        ASSERT_GL_ERROR(GL_INVALID_VALUE);
        glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 2, GL_DEPTH_COMPONENT16, maxSize,
                                            maxSize + 1);
        ASSERT_GL_ERROR(GL_INVALID_VALUE);
    
        // Retrieving samples
        glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT16, 64, 64);
        GLint param = 0;
        glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES_EXT, &param);
        // GE because samples may vary base on implementation. Spec says "the resulting value for
        // RENDERBUFFER_SAMPLES_EXT is guaranteed to be greater than or equal to samples and no more
        // than the next larger sample count supported by the implementation"
        EXPECT_GE(param, 4);
    }
    
    // Checking against invalid parameters for FramebufferTexture2DMultisampleEXT.
    TEST_P(MultisampledRenderToTextureTest, Texture2DParameterCheck)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
        bool isES3 = getClientMajorVersion() >= 3;
    
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        ASSERT_GL_NO_ERROR();
    
        GLTexture depthTexture;
        if (isES3)
        {
            glBindTexture(GL_TEXTURE_2D, depthTexture);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, 64, 64, 0, GL_DEPTH_STENCIL,
                         GL_UNSIGNED_INT_24_8_OES, nullptr);
        }
    
        GLFramebuffer fbo;
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        // Positive test case
        glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                                             texture, 0, 4);
        ASSERT_GL_NO_ERROR();
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        if (EnsureGLExtensionEnabled("GL_EXT_draw_buffers") || isES3)
        {
            // Attachment not COLOR_ATTACHMENT0.  Allowed only in EXT_multisampled_render_to_texture2
            glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D,
                                                 texture, 0, 4);
            assertErrorIfNotMSRTT2(GL_INVALID_ENUM);
        }
    
        // Depth/stencil attachment.  Allowed only in EXT_multisampled_render_to_texture2
        if (isES3)
        {
            glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
                                                 depthTexture, 0, 4);
            assertErrorIfNotMSRTT2(GL_INVALID_ENUM);
    
            glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
                                                 depthTexture, 0, 4);
            assertErrorIfNotMSRTT2(GL_INVALID_ENUM);
    
            glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
                                                 GL_TEXTURE_2D, depthTexture, 0, 4);
            assertErrorIfNotMSRTT2(GL_INVALID_ENUM);
    
            glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D,
                                                 depthTexture, 0, 4);
            assertErrorIfNotMSRTT2(GL_INVALID_ENUM);
        }
    
        // Target not framebuffer
        glFramebufferTexture2DMultisampleEXT(GL_RENDERBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                                             texture, 0, 4);
        ASSERT_GL_ERROR(GL_INVALID_ENUM);
    
        GLint samples;
        glGetIntegerv(GL_MAX_SAMPLES_EXT, &samples);
        ASSERT_GL_NO_ERROR();
        EXPECT_GE(samples, 1);
    
        // Samples too large
        glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                                             texture, 0, samples + 1);
        ASSERT_GL_ERROR(GL_INVALID_VALUE);
    
        // Retrieving samples
        glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                                             texture, 0, 4);
        GLint param = 0;
        glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                              GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_SAMPLES_EXT, &param);
        // GE because samples may vary base on implementation. Spec says "the resulting value for
        // TEXTURE_SAMPLES_EXT is guaranteed to be greater than or equal to samples and no more than the
        // next larger sample count supported by the implementation"
        EXPECT_GE(param, 4);
    }
    
    // Checking against invalid parameters for FramebufferTexture2DMultisampleEXT (cubemap).
    TEST_P(MultisampledRenderToTextureTest, TextureCubeMapParameterCheck)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
        bool isES3 = getClientMajorVersion() >= 3;
    
        GLTexture texture;
        glBindTexture(GL_TEXTURE_CUBE_MAP, texture);
        for (GLenum face = 0; face < 6; face++)
        {
            glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, GL_RGBA, 64, 64, 0, GL_RGBA,
                         GL_UNSIGNED_BYTE, nullptr);
            ASSERT_GL_NO_ERROR();
        }
    
        GLint samples;
        glGetIntegerv(GL_MAX_SAMPLES_EXT, &samples);
        ASSERT_GL_NO_ERROR();
        EXPECT_GE(samples, 1);
    
        GLFramebuffer FBO;
        glBindFramebuffer(GL_FRAMEBUFFER, FBO);
        for (GLenum face = 0; face < 6; face++)
        {
            // Positive test case
            glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                                 GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, texture, 0, 4);
            ASSERT_GL_NO_ERROR();
            EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
            if (EnsureGLExtensionEnabled("GL_EXT_draw_buffers") || isES3)
            {
                // Attachment not COLOR_ATTACHMENT0.  Allowed only in
                // EXT_multisampled_render_to_texture2
                glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1,
                                                     GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, texture, 0,
                                                     4);
                assertErrorIfNotMSRTT2(GL_INVALID_ENUM);
            }
    
            // Target not framebuffer
            glFramebufferTexture2DMultisampleEXT(GL_RENDERBUFFER, GL_COLOR_ATTACHMENT0,
                                                 GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, texture, 0, 4);
            ASSERT_GL_ERROR(GL_INVALID_ENUM);
    
            // Samples too large
            glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                                 GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, texture, 0,
                                                 samples + 1);
            ASSERT_GL_ERROR(GL_INVALID_VALUE);
    
            // Retrieving samples
            glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                                 GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, texture, 0, 4);
            GLint param = 0;
            glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                                  GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_SAMPLES_EXT,
                                                  &param);
            // GE because samples may vary base on implementation. Spec says "the resulting value for
            // TEXTURE_SAMPLES_EXT is guaranteed to be greater than or equal to samples and no more than
            // the next larger sample count supported by the implementation"
            EXPECT_GE(param, 4);
        }
    }
    
    // Checking for framebuffer completeness using extension methods.
    TEST_P(MultisampledRenderToTextureTest, FramebufferCompleteness)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
    
        // Test failure introduced by Apple's changes (anglebug.com/5505)
        ANGLE_SKIP_TEST_IF(IsMetal() && IsAMD());
    
        // Checking that Renderbuffer and texture2d having different number of samples results
        // in a FRAMEBUFFER_INCOMPLETE_MULTISAMPLE
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        ASSERT_GL_NO_ERROR();
    
        // Texture attachment for color attachment 0.  Framebuffer should be complete.
        GLFramebuffer FBO;
        glBindFramebuffer(GL_FRAMEBUFFER, FBO);
        glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                                             texture, 0, 4);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        GLsizei maxSamples = 0;
        glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
    
        // Depth/stencil renderbuffer, potentially with a different sample count.
        GLRenderbuffer dsRenderbuffer;
        glBindRenderbuffer(GL_RENDERBUFFER, dsRenderbuffer);
        glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, maxSamples, GL_DEPTH_COMPONENT16, 64, 64);
        ASSERT_GL_NO_ERROR();
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, dsRenderbuffer);
    
        if (maxSamples > 4)
        {
            EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,
                             glCheckFramebufferStatus(GL_FRAMEBUFFER));
        }
        else
        {
            EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
        }
    
        // Color renderbuffer for color attachment 0.
        GLRenderbuffer colorRenderbuffer;
        glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
        glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, maxSamples, GL_RGBA4, 64, 64);
        ASSERT_GL_NO_ERROR();
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
                                  colorRenderbuffer);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // D3D backend doesn't implement multisampled render to texture renderbuffers correctly.
        // http://anglebug.com/3107
        ANGLE_SKIP_TEST_IF(IsD3D());
    
        if (getClientMajorVersion() >= 3)
        {
            // Texture attachment for color attachment 1.
            GLTexture texture2;
            glBindTexture(GL_TEXTURE_2D, texture2);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
            ASSERT_GL_NO_ERROR();
    
            // Attach with a potentially different number of samples.
            glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D,
                                                 texture, 0, 4);
    
            if (maxSamples > 4)
            {
                EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,
                                 glCheckFramebufferStatus(GL_FRAMEBUFFER));
            }
            else
            {
                EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
            }
    
            // Attach with same number of samples.
            glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D,
                                                 texture, 0, maxSamples);
            EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
        }
    }
    
    // Checking for framebuffer completeness using extension methods.
    TEST_P(MultisampledRenderToTextureTest, FramebufferCompletenessSmallSampleCount)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
    
        // Test failure introduced by Apple's changes (anglebug.com/5505)
        ANGLE_SKIP_TEST_IF(IsMetal() && IsAMD());
    
        // A sample count of '2' can be rounded up to '4' on some systems (e.g., ARM+Android).
        GLsizei samples = 2;
    
        // Checking that Renderbuffer and texture2d having different number of samples results
        // in a FRAMEBUFFER_INCOMPLETE_MULTISAMPLE
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        ASSERT_GL_NO_ERROR();
    
        // Texture attachment for color attachment 0.  Framebuffer should be complete.
        GLFramebuffer FBO;
        glBindFramebuffer(GL_FRAMEBUFFER, FBO);
        glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                                             texture, 0, samples);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Depth/stencil renderbuffer, potentially with a different sample count.
        GLRenderbuffer dsRenderbuffer;
        glBindRenderbuffer(GL_RENDERBUFFER, dsRenderbuffer);
        glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, samples, GL_DEPTH_COMPONENT16, 64, 64);
        ASSERT_GL_NO_ERROR();
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, dsRenderbuffer);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Color renderbuffer for color attachment 0.
        GLRenderbuffer colorRenderbuffer;
        glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
        glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, samples, GL_RGBA4, 64, 64);
        ASSERT_GL_NO_ERROR();
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
                                  colorRenderbuffer);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    }
    
    void MultisampledRenderToTextureTest::createAndAttachColorAttachment(
        bool useRenderbuffer,
        GLsizei size,
        GLenum renderbufferTarget,
        const GLType *glType,
        GLint samples,
        GLTexture *textureOut,
        GLRenderbuffer *renderbufferOut)
    {
        GLenum internalFormat = glType ? glType->internalFormat : GL_RGBA;
        GLenum format         = glType ? glType->format : GL_RGBA;
        GLenum type           = glType ? glType->type : GL_UNSIGNED_BYTE;
    
        if (useRenderbuffer)
        {
            if (internalFormat == GL_RGBA)
            {
                internalFormat = GL_RGBA8;
            }
            glBindRenderbuffer(GL_RENDERBUFFER, *renderbufferOut);
            glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, samples, internalFormat, size, size);
            glFramebufferRenderbuffer(GL_FRAMEBUFFER, renderbufferTarget, GL_RENDERBUFFER,
                                      *renderbufferOut);
        }
        else
        {
            glBindTexture(GL_TEXTURE_2D, *textureOut);
            glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, size, size, 0, format, type, nullptr);
            glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, renderbufferTarget, GL_TEXTURE_2D,
                                                 *textureOut, 0, samples);
        }
        ASSERT_GL_NO_ERROR();
    }
    
    void MultisampledRenderToTextureTest::createAndAttachDepthStencilAttachment(
        bool useRenderbuffer,
        GLsizei size,
        GLTexture *textureOut,
        GLRenderbuffer *renderbufferOut)
    {
        if (useRenderbuffer)
        {
            glBindRenderbuffer(GL_RENDERBUFFER, *renderbufferOut);
            glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, mTestSampleCount, GL_DEPTH24_STENCIL8,
                                                size, size);
            glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
                                      *renderbufferOut);
        }
        else
        {
            glBindTexture(GL_TEXTURE_2D, *textureOut);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, size, size, 0, GL_DEPTH_STENCIL,
                         GL_UNSIGNED_INT_24_8_OES, nullptr);
            glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
                                                 GL_TEXTURE_2D, *textureOut, 0, mTestSampleCount);
        }
        ASSERT_GL_NO_ERROR();
    }
    
    void MultisampledRenderToTextureTest::colorAttachmentMultisampleDrawTestCommon(bool useRenderbuffer)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
    
        GLFramebuffer FBO;
        glBindFramebuffer(GL_FRAMEBUFFER, FBO);
    
        // Set up color attachment and bind to FBO
        constexpr GLsizei kSize = 6;
        GLTexture texture;
        GLRenderbuffer renderbuffer;
        createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, nullptr,
                                       mTestSampleCount, &texture, &renderbuffer);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Set viewport and clear to black
        glViewport(0, 0, kSize, kSize);
        glClearColor(0.0, 0.0, 0.0, 1.0);
        glClear(GL_COLOR_BUFFER_BIT);
    
        // Set up Green square program
        ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
        glUseProgram(program);
        GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
        ASSERT_NE(-1, positionLocation);
    
        setupQuadVertexBuffer(0.5f, 0.5f);
        glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
        glEnableVertexAttribArray(positionLocation);
    
        // Draw green square
        glDrawArrays(GL_TRIANGLES, 0, 6);
        ASSERT_GL_NO_ERROR();
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::green);
    
        // Set up Red square program
        ANGLE_GL_PROGRAM(program2, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
        glUseProgram(program2);
        GLint positionLocation2 = glGetAttribLocation(program2, essl1_shaders::PositionAttrib());
        ASSERT_NE(-1, positionLocation2);
    
        setupQuadVertexBuffer(0.5f, 0.75f);
        glVertexAttribPointer(positionLocation2, 3, GL_FLOAT, GL_FALSE, 0, 0);
    
        // Draw red square
        glDrawArrays(GL_TRIANGLES, 0, 6);
        ASSERT_GL_NO_ERROR();
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::red);
    
        glDisableVertexAttribArray(0);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
    }
    
    // Draw test with color attachment only.
    TEST_P(MultisampledRenderToTextureTest, 2DColorAttachmentMultisampleDrawTest)
    {
        colorAttachmentMultisampleDrawTestCommon(false);
    }
    
    // Draw test with renderbuffer color attachment only
    TEST_P(MultisampledRenderToTextureTest, RenderbufferColorAttachmentMultisampleDrawTest)
    {
        colorAttachmentMultisampleDrawTestCommon(true);
    }
    
    // Test draw with a scissored region.
    TEST_P(MultisampledRenderToTextureTest, ScissoredDrawTest)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
    
        GLFramebuffer FBO;
        glBindFramebuffer(GL_FRAMEBUFFER, FBO);
    
        // Set up color attachment and bind to FBO
        constexpr GLsizei kSize = 1024;
        GLTexture texture;
        GLRenderbuffer renderbuffer;
        createAndAttachColorAttachment(false, kSize, GL_COLOR_ATTACHMENT0, nullptr, mTestSampleCount,
                                       &texture, &renderbuffer);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Set viewport and clear to black
        glViewport(0, 0, kSize, kSize);
        glClearColor(0.0, 0.0, 0.0, 1.0);
        glClear(GL_COLOR_BUFFER_BIT);
    
        // Set up Green square program
        ANGLE_GL_PROGRAM(drawGreen, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
        glUseProgram(drawGreen);
    
        // Draw green square
        drawQuad(drawGreen, essl1_shaders::PositionAttrib(), 0.0f);
        ASSERT_GL_NO_ERROR();
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::green);
    
        // Set up Red square program
        ANGLE_GL_PROGRAM(drawRed, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
        glUseProgram(drawRed);
    
        // Draw a scissored red square
    
        constexpr GLint kScissorStartX = 1;
        constexpr GLint kScissorStartY = 103;
        constexpr GLint kScissorEndX   = 285;
        constexpr GLint kScissorEndY   = 402;
    
        glEnable(GL_SCISSOR_TEST);
        glScissor(kScissorStartX, kScissorStartY, kScissorEndX - kScissorStartX + 1,
                  kScissorEndY - kScissorStartY + 1);
        drawQuad(drawRed, essl1_shaders::PositionAttrib(), 0.0f);
        ASSERT_GL_NO_ERROR();
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::green);
    
        EXPECT_PIXEL_COLOR_EQ(kScissorStartX, kScissorStartY, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kScissorEndX, kScissorStartY, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kScissorStartX, kScissorEndY, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kScissorEndX, kScissorEndY, GLColor::red);
    
        EXPECT_PIXEL_COLOR_EQ(kScissorStartX - 1, kScissorStartY, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(kScissorStartX, kScissorStartY - 1, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(kScissorEndX + 1, kScissorStartY, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(kScissorEndX, kScissorStartY - 1, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(kScissorStartX - 1, kScissorEndY, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(kScissorStartX, kScissorEndY + 1, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(kScissorEndX + 1, kScissorEndY, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(kScissorEndX, kScissorEndY + 1, GLColor::green);
    }
    
    // Test transform feedback with state change.  In the Vulkan backend, this results in an implicit
    // break of the render pass, and must work correctly with respect to the subpass index that's used.
    TEST_P(MultisampledRenderToTextureES3Test, TransformFeedbackTest)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
    
        GLFramebuffer FBO;
        glBindFramebuffer(GL_FRAMEBUFFER, FBO);
    
        // Set up color attachment and bind to FBO
        constexpr GLsizei kSize = 1024;
        GLTexture texture;
        GLRenderbuffer renderbuffer;
        createAndAttachColorAttachment(false, kSize, GL_COLOR_ATTACHMENT0, nullptr, mTestSampleCount,
                                       &texture, &renderbuffer);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Set up transform feedback.
        GLTransformFeedback xfb;
        glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, xfb);
    
        constexpr size_t kXfbBufferSize = 1024;  // arbitrary number
        GLBuffer xfbBuffer;
        glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, xfbBuffer);
        glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, kXfbBufferSize, nullptr, GL_STATIC_DRAW);
        glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, xfbBuffer);
    
        // Set up program with transform feedback
        std::vector<std::string> tfVaryings = {"gl_Position"};
        ANGLE_GL_PROGRAM_TRANSFORM_FEEDBACK(drawColor, essl1_shaders::vs::Simple(),
                                            essl1_shaders::fs::UniformColor(), tfVaryings,
                                            GL_INTERLEAVED_ATTRIBS);
        glUseProgram(drawColor);
        GLint colorUniformLocation =
            glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
        ASSERT_NE(colorUniformLocation, -1);
    
        // Start transform feedback
        glBeginTransformFeedback(GL_TRIANGLES);
    
        // Set viewport and clear to black
        glViewport(0, 0, kSize, kSize);
        glClearColor(0.0, 0.0, 0.0, 1.0);
        glClear(GL_COLOR_BUFFER_BIT);
    
        // Draw green.  There's no unresolve operation as the framebuffer has just been cleared.
        glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.0f);
        ASSERT_GL_NO_ERROR();
    
        // Incur a state change while transform feedback is active.  This will result in a pipeline
        // rebind in the Vulkan backend, which should necessarily break the render pass when
        // VK_EXT_transform_feedback is used.
        glEnable(GL_BLEND);
        glBlendFunc(GL_ONE, GL_ONE);
    
        // Draw red.  The implicit render pass break means that there's an unresolve operation.
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.0f);
    
        // End transform feedback
        glEndTransformFeedback();
    
        // Expect yellow.
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::yellow);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::yellow);
        EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::yellow);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::yellow);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::yellow);
    }
    
    // Draw test using both color and depth attachments.
    TEST_P(MultisampledRenderToTextureTest, 2DColorDepthMultisampleDrawTest)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
    
        // http://anglebug.com/5380
        ANGLE_SKIP_TEST_IF(IsLinux() && IsAMD() && IsVulkan());
    
        constexpr GLsizei kSize = 6;
        // create complete framebuffer with depth buffer
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        ASSERT_GL_NO_ERROR();
    
        GLFramebuffer FBO;
        glBindFramebuffer(GL_FRAMEBUFFER, FBO);
        glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                                             texture, 0, 4);
    
        GLRenderbuffer renderbuffer;
        glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
        glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT16, kSize, kSize);
        ASSERT_GL_NO_ERROR();
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderbuffer);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Set viewport and clear framebuffer
        glViewport(0, 0, kSize, kSize);
        glClearColor(0.0, 0.0, 0.0, 1.0);
        glClearDepthf(0.5f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
        // Draw first green square
        ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_GREATER);
        glUseProgram(program);
        GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
        ASSERT_NE(-1, positionLocation);
    
        setupQuadVertexBuffer(0.8f, 0.5f);
        glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
        glEnableVertexAttribArray(positionLocation);
    
        // Tests that TRIANGLES works.
        glDrawArrays(GL_TRIANGLES, 0, 6);
        ASSERT_GL_NO_ERROR();
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::green);
    
        // Draw red square behind green square
        ANGLE_GL_PROGRAM(program2, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
        glUseProgram(program2);
        GLint positionLocation2 = glGetAttribLocation(program2, essl1_shaders::PositionAttrib());
        ASSERT_NE(-1, positionLocation2);
    
        setupQuadVertexBuffer(0.7f, 1.0f);
        glVertexAttribPointer(positionLocation2, 3, GL_FLOAT, GL_FALSE, 0, 0);
    
        glDrawArrays(GL_TRIANGLES, 0, 6);
        ASSERT_GL_NO_ERROR();
        glDisable(GL_DEPTH_TEST);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::green);
    
        glDisableVertexAttribArray(0);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
    }
    
    void MultisampledRenderToTextureES3Test::readPixelsTestCommon(bool useRenderbuffer)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
    
        GLFramebuffer FBO;
        glBindFramebuffer(GL_FRAMEBUFFER, FBO);
    
        constexpr GLsizei kSize = 6;
        GLTexture texture;
        GLRenderbuffer renderbuffer;
        createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, nullptr,
                                       mTestSampleCount, &texture, &renderbuffer);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Set viewport and clear to red
        glViewport(0, 0, kSize, kSize);
        glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        ASSERT_GL_NO_ERROR();
    
        // Bind Pack Pixel Buffer and read to it
        GLBuffer PBO;
        glBindBuffer(GL_PIXEL_PACK_BUFFER, PBO);
        glBufferData(GL_PIXEL_PACK_BUFFER, 4 * kSize * kSize, nullptr, GL_STATIC_DRAW);
        glReadPixels(0, 0, kSize, kSize, GL_RGBA, GL_UNSIGNED_BYTE, 0);
        ASSERT_GL_NO_ERROR();
    
        // Retrieving pixel color
        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();
    }
    
    // Read pixels with pack buffer. ES3+.
    TEST_P(MultisampledRenderToTextureES3Test, ReadPixelsTest)
    {
        readPixelsTestCommon(false);
    }
    
    // Read pixels with pack buffer from renderbuffer. ES3+.
    TEST_P(MultisampledRenderToTextureES3Test, RenderbufferReadPixelsTest)
    {
        // D3D backend doesn't implement multisampled render to texture renderbuffers correctly.
        // http://anglebug.com/3107
        ANGLE_SKIP_TEST_IF(IsD3D());
    
        readPixelsTestCommon(true);
    }
    
    void MultisampledRenderToTextureTest::copyTexImageTestCommon(bool useRenderbuffer)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
        constexpr GLsizei kSize = 16;
    
        setupCopyTexProgram();
    
        GLFramebuffer FBO;
        glBindFramebuffer(GL_FRAMEBUFFER, FBO);
    
        GLTexture texture;
        GLRenderbuffer renderbuffer;
        createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, nullptr,
                                       mTestSampleCount, &texture, &renderbuffer);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Set color for framebuffer
        glClearColor(0.25f, 1.0f, 0.75f, 0.5f);
        glClear(GL_COLOR_BUFFER_BIT);
        ASSERT_GL_NO_ERROR();
    
        GLTexture copyToTex;
        glBindTexture(GL_TEXTURE_2D, copyToTex);
    
        // Disable mipmapping
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    
        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, kSize, kSize, 0);
        ASSERT_GL_NO_ERROR();
    
        verifyResults(copyToTex, {64, 255, 191, 255}, kSize, 0, 0, kSize, kSize);
    }
    
    // CopyTexImage from a multisampled texture functionality test.
    TEST_P(MultisampledRenderToTextureTest, CopyTexImageTest)
    {
        copyTexImageTestCommon(false);
    }
    
    // CopyTexImage from a multisampled texture functionality test using renderbuffer.
    TEST_P(MultisampledRenderToTextureTest, RenderbufferCopyTexImageTest)
    {
        copyTexImageTestCommon(true);
    }
    
    void MultisampledRenderToTextureTest::copyTexSubImageTestCommon(bool useRenderbuffer)
    {
        // Fails on Pixel 2. http://anglebug.com/4906
        ANGLE_SKIP_TEST_IF(IsAndroid());
    
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
        constexpr GLsizei kSize = 16;
    
        setupCopyTexProgram();
    
        GLFramebuffer copyFBO0;
        glBindFramebuffer(GL_FRAMEBUFFER, copyFBO0);
    
        // Create color attachment for copyFBO0
        GLTexture texture;
        GLRenderbuffer renderbuffer;
        createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, nullptr,
                                       mTestSampleCount, &texture, &renderbuffer);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        GLFramebuffer copyFBO1;
        glBindFramebuffer(GL_FRAMEBUFFER, copyFBO1);
    
        // Create color attachment for copyFBO1
        GLTexture texture1;
        GLRenderbuffer renderbuffer1;
        createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, nullptr,
                                       mTestSampleCount, &texture1, &renderbuffer1);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Set color for copyFBO0
        glBindFramebuffer(GL_FRAMEBUFFER, copyFBO0);
        glClearColor(0.25f, 1.0f, 0.75f, 0.5f);
        glClear(GL_COLOR_BUFFER_BIT);
        ASSERT_GL_NO_ERROR();
    
        // Set color for copyFBO1
        glBindFramebuffer(GL_FRAMEBUFFER, copyFBO1);
        glClearColor(1.0f, 0.75f, 0.5f, 0.25f);
        glClear(GL_COLOR_BUFFER_BIT);
        ASSERT_GL_NO_ERROR();
    
        GLTexture copyToTex;
        glBindTexture(GL_TEXTURE_2D, copyToTex);
    
        // Disable mipmapping
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    
        // copyFBO0 -> copyToTex
        // copyToTex should hold what was originally in copyFBO0 : (.25, 1, .75, .5)
        glBindFramebuffer(GL_FRAMEBUFFER, copyFBO0);
        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, kSize, kSize, 0);
        ASSERT_GL_NO_ERROR();
    
        const GLColor expected0(64, 255, 191, 255);
        verifyResults(copyToTex, expected0, kSize, 0, 0, kSize, kSize);
    
        // copyFBO[1] - copySubImage -> copyToTex
        // copyToTex should have subportion what was in copyFBO[1] : (1, .75, .5, .25)
        // The rest should still be untouched: (.25, 1, .75, .5)
        GLint half = kSize / 2;
        glBindFramebuffer(GL_FRAMEBUFFER, copyFBO1);
        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, half, half, half, half, half, half);
        ASSERT_GL_NO_ERROR();
    
        const GLColor expected1(255, 191, 127, 255);
        verifyResults(copyToTex, expected1, kSize, half, half, kSize, kSize);
    
        // Verify rest is untouched
        verifyResults(copyToTex, expected0, kSize, 0, 0, half, half);
        verifyResults(copyToTex, expected0, kSize, 0, half, half, kSize);
        verifyResults(copyToTex, expected0, kSize, half, 0, kSize, half);
    }
    
    // CopyTexSubImage from a multisampled texture functionality test.
    TEST_P(MultisampledRenderToTextureTest, CopyTexSubImageTest)
    {
        copyTexSubImageTestCommon(false);
    }
    
    // CopyTexSubImage from a multisampled texture functionality test with renderbuffers
    TEST_P(MultisampledRenderToTextureTest, RenderbufferCopyTexSubImageTest)
    {
        copyTexSubImageTestCommon(true);
    }
    
    void MultisampledRenderToTextureES3Test::blitFramebufferTestCommon(bool useRenderbuffer)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
    
        // Some draws are not executed before the blitframebuffer on Pixel2.
        // http://anglebug.com/2894
        ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGL() && IsPixel2());
    
        constexpr GLsizei kSize = 16;
    
        GLFramebuffer fboMS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
    
        // Create multisampled framebuffer to use as source.
        GLRenderbuffer depthMS;
        glBindRenderbuffer(GL_RENDERBUFFER, depthMS);
        glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT24, kSize, kSize);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthMS);
        ASSERT_GL_NO_ERROR();
    
        GLTexture textureMS;
        GLRenderbuffer renderbufferMS;
        createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, nullptr,
                                       mTestSampleCount, &textureMS, &renderbufferMS);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Clear depth to 0.5 and color to green.
        glClearDepthf(0.5f);
        glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
        glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
        glFlush();
        ASSERT_GL_NO_ERROR();
    
        // Draw red into the multisampled color buffer.
        ANGLE_GL_PROGRAM(drawRed, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_EQUAL);
        drawQuad(drawRed, essl1_shaders::PositionAttrib(), 0.0f);
        ASSERT_GL_NO_ERROR();
    
        // Create single sampled framebuffer to use as dest.
        GLFramebuffer fboSS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboSS);
        GLTexture colorSS;
        glBindTexture(GL_TEXTURE_2D, colorSS);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorSS, 0);
        ASSERT_GL_NO_ERROR();
    
        // Bind MS to READ as SS is already bound to DRAW.
        glBindFramebuffer(GL_READ_FRAMEBUFFER, fboMS);
        glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
        ASSERT_GL_NO_ERROR();
    
        // Bind SS to READ so we can readPixels from it
        glBindFramebuffer(GL_FRAMEBUFFER, fboSS);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::red);
        ASSERT_GL_NO_ERROR();
    }
    
    // BlitFramebuffer functionality test. ES3+.
    TEST_P(MultisampledRenderToTextureES3Test, BlitFramebufferTest)
    {
        blitFramebufferTestCommon(false);
    }
    
    // BlitFramebuffer functionality test with renderbuffer. ES3+.
    TEST_P(MultisampledRenderToTextureES3Test, RenderbufferBlitFramebufferTest)
    {
        blitFramebufferTestCommon(true);
    }
    
    // GenerateMipmap functionality test
    TEST_P(MultisampledRenderToTextureTest, GenerateMipmapTest)
    {
        // Fails on Pixel 2. http://anglebug.com/4906
        ANGLE_SKIP_TEST_IF(IsAndroid());
    
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
        constexpr GLsizei kSize = 64;
    
        setupCopyTexProgram();
        glUseProgram(mCopyTextureProgram);
    
        // Initialize texture with blue
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, kSize, kSize, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    
        GLFramebuffer FBO;
        glBindFramebuffer(GL_FRAMEBUFFER, FBO);
        glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                                             texture, 0, 4);
        ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
        glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        glViewport(0, 0, kSize, kSize);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        ASSERT_GL_NO_ERROR();
    
        // Generate mipmap
        glGenerateMipmap(GL_TEXTURE_2D);
        ASSERT_GL_NO_ERROR();
    
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
    
        // Now draw the texture to various different sized areas.
        clearAndDrawQuad(mCopyTextureProgram, kSize, kSize);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::blue);
    
        // Use mip level 1
        clearAndDrawQuad(mCopyTextureProgram, kSize / 2, kSize / 2);
        EXPECT_PIXEL_COLOR_EQ(kSize / 4, kSize / 4, GLColor::blue);
    
        // Use mip level 2
        clearAndDrawQuad(mCopyTextureProgram, kSize / 4, kSize / 4);
        EXPECT_PIXEL_COLOR_EQ(kSize / 8, kSize / 8, GLColor::blue);
    
        ASSERT_GL_NO_ERROR();
    }
    
    void MultisampledRenderToTextureTest::drawCopyThenBlendCommon(bool useRenderbuffer)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
        constexpr GLsizei kSize = 64;
    
        setupCopyTexProgram();
    
        GLFramebuffer fboMS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
    
        // Create multisampled framebuffer to draw into
        GLTexture textureMS;
        GLRenderbuffer renderbufferMS;
        createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, nullptr,
                                       mTestSampleCount, &textureMS, &renderbufferMS);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Draw red into the multisampled color buffer.
        ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
        glUseProgram(drawColor);
        GLint colorUniformLocation =
            glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
        ASSERT_NE(colorUniformLocation, -1);
    
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Create a texture and copy into it.
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, kSize, kSize, 0);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    
        // Draw again into the framebuffer, this time blending.  This tests that the framebuffer's data,
        // residing in the single-sampled texture, is available to the multisampled intermediate image
        // for blending.
    
        // Blend half-transparent green into the multisampled color buffer.
        glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 0.5f);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Verify that the texture is now yellow
        const GLColor kExpected(127, 127, 0, 191);
        EXPECT_PIXEL_COLOR_NEAR(0, 0, kExpected, 1);
        EXPECT_PIXEL_COLOR_NEAR(kSize - 1, 0, kExpected, 1);
        EXPECT_PIXEL_COLOR_NEAR(0, kSize - 1, kExpected, 1);
        EXPECT_PIXEL_COLOR_NEAR(kSize - 1, kSize - 1, kExpected, 1);
    
        // For completeness, verify that the texture used as copy target is red.
        ASSERT_GL_NO_ERROR();
        verifyResults(texture, GLColor::red, kSize, 0, 0, kSize, kSize);
    
        ASSERT_GL_NO_ERROR();
    }
    
    // Draw, copy, then blend.  The copy will make sure an implicit resolve happens.  Regardless, the
    // following draw should retain the data written by the first draw command.
    TEST_P(MultisampledRenderToTextureTest, DrawCopyThenBlend)
    {
        drawCopyThenBlendCommon(false);
    }
    
    // Draw, copy, then blend.  The copy will make sure an implicit resolve happens.  Regardless, the
    // following draw should retain the data written by the first draw command.  Uses renderbuffer.
    TEST_P(MultisampledRenderToTextureTest, RenderbufferDrawCopyThenBlend)
    {
        // Test failure introduced by Apple's changes (anglebug.com/5505)
        ANGLE_SKIP_TEST_IF(IsOSX() && IsARM64() && IsMetal());
    
        drawCopyThenBlendCommon(true);
    }
    
    void MultisampledRenderToTextureTest::clearDrawCopyThenBlendSameProgramCommon(bool useRenderbuffer)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
        constexpr GLsizei kSize = 64;
    
        setupCopyTexProgram();
    
        GLFramebuffer fboMS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
    
        // Create multisampled framebuffer to draw into
        GLTexture textureMS;
        GLRenderbuffer renderbufferMS;
        createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, nullptr,
                                       mTestSampleCount, &textureMS, &renderbufferMS);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Draw red into the multisampled color buffer.
        ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
        glUseProgram(drawColor);
        GLint colorUniformLocation =
            glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
        ASSERT_NE(colorUniformLocation, -1);
    
        // Clear the framebuffer.
        glClearColor(0.1f, 0.9f, 0.2f, 0.8f);
        glClear(GL_COLOR_BUFFER_BIT);
    
        // Then draw into it.
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Blend green into it.  This makes sure that the blend after the resolve doesn't have different
        // state from the one used here.
        glEnable(GL_BLEND);
        glBlendFunc(GL_ONE, GL_ONE);
        glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Create a texture and copy into it.
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, kSize, kSize, 0);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    
        // Draw again into the framebuffer, this time blending.  This tests that the framebuffer's data,
        // residing in the single-sampled texture, is available to the multisampled intermediate image
        // for blending.
    
        // Blend blue into the multisampled color buffer.
        glUniform4f(colorUniformLocation, 0.0f, 0.0f, 1.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Verify that the texture is now white
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::white);
        EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::white);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::white);
    
        // Once again, clear and draw so the program is used again in the way it was first used.
        glClear(GL_COLOR_BUFFER_BIT);
        glDisable(GL_BLEND);
        glUniform4f(colorUniformLocation, 0.0f, 0.0f, 1.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::blue);
    
        // For completeness, verify that the texture used as copy target is yellow.
        ASSERT_GL_NO_ERROR();
        verifyResults(texture, GLColor::yellow, kSize, 0, 0, kSize, kSize);
    
        ASSERT_GL_NO_ERROR();
    }
    
    // Clear&Draw, copy, then blend with same program.  The copy will make sure an implicit resolve
    // happens.  The second draw should retain the data written by the first draw command ("unresolve"
    // operation).  The same program is used for the first and second draw calls, and the fact that the
    // attachment is cleared or unresolved should not cause issues.  In the Vulkan backend, the program
    // will be used in different subpass indices, so two graphics pipelines should be created for it.
    TEST_P(MultisampledRenderToTextureTest, ClearDrawCopyThenBlendSameProgram)
    {
        clearDrawCopyThenBlendSameProgramCommon(false);
    }
    
    // Same as ClearDrawCopyThenBlendSameProgram but with renderbuffers
    TEST_P(MultisampledRenderToTextureTest, RenderbufferClearDrawCopyThenBlendSameProgram)
    {
        // Test failure introduced by Apple's changes (anglebug.com/5505)
        ANGLE_SKIP_TEST_IF(IsOSX() && IsARM64() && IsMetal());
    
        clearDrawCopyThenBlendSameProgramCommon(true);
    }
    
    // Similar to RenderbufferClearDrawCopyThenBlendSameProgram, but with the depth/stencil attachment
    // being unresolved only.
    TEST_P(MultisampledRenderToTextureES3Test,
           RenderbufferClearDrawCopyThenBlendWithDepthStencilSameProgram)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
    
        // http://anglebug.com/5380
        ANGLE_SKIP_TEST_IF(IsLinux() && IsAMD() && IsVulkan());
    
        // Test failure introduced by Apple's changes (anglebug.com/5505)
        ANGLE_SKIP_TEST_IF(IsOSX() && IsARM64() && IsMetal());
    
        constexpr GLsizei kSize = 64;
    
        setupCopyTexProgram();
    
        GLFramebuffer fboMS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
    
        // Create multisampled framebuffer to draw into
        GLTexture color;
        glBindTexture(GL_TEXTURE_2D, color);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color,
                                             0, 4);
    
        GLRenderbuffer depthStencil;
        glBindRenderbuffer(GL_RENDERBUFFER, depthStencil);
        glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, kSize, kSize);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
                                  depthStencil);
        ASSERT_GL_NO_ERROR();
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Draw red into the multisampled color buffer.
        ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
        glUseProgram(drawColor);
        GLint colorUniformLocation =
            glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
        ASSERT_NE(colorUniformLocation, -1);
    
        // Enable write to depth/stencil so the attachment has valid contents, but always pass the test.
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_ALWAYS);
    
        glEnable(GL_STENCIL_TEST);
        glStencilFunc(GL_ALWAYS, 0x55, 0xFF);
        glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
        glStencilMask(0xFF);
    
        // Clear the framebuffer.
        glClearColor(0.1f, 0.9f, 0.2f, 0.8f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
        // Then draw into it.
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 1.0f);
        ASSERT_GL_NO_ERROR();
    
        // Blend green into it.  This makes sure that the blend after the resolve doesn't have different
        // state from the one used here.  Additionally, test that the previous draw output the correct
        // depth/stencil data.  Again, this makes sure that the draw call after the resolve doesn't have
        // different has depth/stencil test state.
    
        // If depth is not set to 1, rendering would fail.
        glDepthFunc(GL_LESS);
    
        // If stencil is not set to 0x55, rendering would fail.
        glStencilFunc(GL_EQUAL, 0x55, 0xFF);
        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
    
        glEnable(GL_BLEND);
        glBlendFunc(GL_ONE, GL_ONE);
        glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.95f);
        ASSERT_GL_NO_ERROR();
    
        // Create a texture and copy into it.
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, kSize, kSize, 0);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    
        // Clear color (but not depth/stencil), and draw again into the framebuffer, this time blending.
        // Additionally, make sure the depth/stencil data are retained.
    
        // Clear color (to blue), but not depth/stencil.
        glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
    
        // Blend green into the multisampled color buffer.
        glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.9f);
        ASSERT_GL_NO_ERROR();
    
        // Verify that the texture is now cyan
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::cyan);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::cyan);
        EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::cyan);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::cyan);
    
        // Once again, clear and draw so the program is used again in the way it was first used.
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
        glDisable(GL_BLEND);
        glDepthFunc(GL_ALWAYS);
        glStencilFunc(GL_ALWAYS, 0x55, 0xFF);
        glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
        glUniform4f(colorUniformLocation, 0.0f, 0.0f, 1.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::blue);
    
        // For completeness, verify that the texture used as copy target is yellow.
        ASSERT_GL_NO_ERROR();
        verifyResults(texture, GLColor::yellow, kSize, 0, 0, kSize, kSize);
    
        ASSERT_GL_NO_ERROR();
    }
    
    void MultisampledRenderToTextureTest::drawCopyDrawThenMaskedClearCommon(bool useRenderbuffer)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
        constexpr GLsizei kSize = 64;
    
        setupCopyTexProgram();
    
        GLFramebuffer fboMS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
    
        // Create multisampled framebuffer to draw into
        GLTexture textureMS;
        GLRenderbuffer renderbufferMS;
        createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, nullptr,
                                       mTestSampleCount, &textureMS, &renderbufferMS);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Draw red into the multisampled color buffer.
        ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
        glUseProgram(drawColor);
        GLint colorUniformLocation =
            glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
        ASSERT_NE(colorUniformLocation, -1);
    
        // Draw into framebuffer.
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Create a texture and copy into it.
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, kSize, kSize, 0);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    
        // Draw again into the framebuffer, this time blending.  Afterwards, issue a masked clear.  This
        // ensures that previous resolved data is unresolved, and mid-render-pass clears work correctly.
    
        // Draw green into the multisampled color buffer.
        glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Issue a masked clear.
        glClearColor(0.1f, 0.9f, 1.0f, 0.8f);
        glColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_FALSE);
        glClear(GL_COLOR_BUFFER_BIT);
    
        // Verify that the texture is now cyan
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::cyan);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::cyan);
        EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::cyan);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::cyan);
    
        // For completeness, verify that the texture used as copy target is red.
        ASSERT_GL_NO_ERROR();
        verifyResults(texture, GLColor::red, kSize, 0, 0, kSize, kSize);
    
        ASSERT_GL_NO_ERROR();
    }
    
    // Draw, copy, draw then issue a masked clear.  The copy will make sure an implicit resolve
    // happens.  The second draw should retain the data written by the first draw command ("unresolve"
    // operation).  The final clear uses a draw call to perform the clear in the Vulkan backend, and it
    // should use the correct subpass index.
    TEST_P(MultisampledRenderToTextureTest, DrawCopyDrawThenMaskedClear)
    {
        drawCopyDrawThenMaskedClearCommon(false);
    }
    
    // Same as DrawCopyDrawThenMaskedClearCommon but with renderbuffers
    TEST_P(MultisampledRenderToTextureTest, RenderbufferDrawCopyDrawThenMaskedClear)
    {
        drawCopyDrawThenMaskedClearCommon(true);
    }
    
    void MultisampledRenderToTextureES3Test::drawCopyDrawAttachInvalidatedThenDrawCommon(
        bool useRenderbuffer)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
        constexpr GLsizei kSize = 64;
    
        setupCopyTexProgram();
    
        GLFramebuffer fboMS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
    
        // Create multisampled framebuffer to draw into
        GLTexture textureMS;
        GLRenderbuffer renderbufferMS;
        createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, nullptr,
                                       mTestSampleCount, &textureMS, &renderbufferMS);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Draw red into the multisampled color buffer.
        ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
        glUseProgram(drawColor);
        GLint colorUniformLocation =
            glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
        ASSERT_NE(colorUniformLocation, -1);
    
        // Clear and draw into framebuffer.
        glClear(GL_COLOR_BUFFER_BIT);
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Create a texture and copy into it.
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, kSize, kSize, 0);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    
        // Draw green into framebuffer.  This will unresolve color.
        glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Create multisampled framebuffer and invalidate its attachment.
        GLFramebuffer invalidateFboMS;
        glBindFramebuffer(GL_FRAMEBUFFER, invalidateFboMS);
    
        // Create multisampled framebuffer to draw into
        GLTexture invalidateTextureMS;
        GLRenderbuffer invalidateRenderbufferMS;
        createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, nullptr,
                                       mTestSampleCount, &invalidateTextureMS,
                                       &invalidateRenderbufferMS);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Invalidate the attachment.
        GLenum invalidateAttachments[] = {GL_COLOR_ATTACHMENT0};
        glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, invalidateAttachments);
    
        // Replace the original framebuffer's attachment with the invalidated one.
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
        if (useRenderbuffer)
        {
            glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
                                      invalidateRenderbufferMS);
        }
        else
        {
            glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                                                 invalidateTextureMS, 0, 4);
        }
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Draw blue into the multisampled color buffer.
        glUniform4f(colorUniformLocation, 0.0f, 0.0f, 1.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Verify that the texture is now blue
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::blue);
    
        // For completeness, verify that the texture used as copy target is red.
        ASSERT_GL_NO_ERROR();
        verifyResults(texture, GLColor::red, kSize, 0, 0, kSize, kSize);
    
        ASSERT_GL_NO_ERROR();
    }
    
    // Draw, copy, draw, attach an invalidated image then draw.  The second draw will need to unresolve
    // color.  Attaching an invalidated image changes the framebuffer, and the following draw doesn't
    // require an unresolve.  In the Vulkan backend, mismatches in unresolve state between framebuffer
    // and render pass will result in an ASSERT.
    TEST_P(MultisampledRenderToTextureES3Test, DrawCopyDrawAttachInvalidatedThenDraw)
    {
        drawCopyDrawAttachInvalidatedThenDrawCommon(false);
    }
    
    // Same as DrawCopyDrawAttachInvalidatedThenDraw but with renderbuffers
    TEST_P(MultisampledRenderToTextureES3Test, RenderbufferDrawCopyDrawAttachInvalidatedThenDraw)
    {
        drawCopyDrawAttachInvalidatedThenDrawCommon(true);
    }
    
    void MultisampledRenderToTextureES3Test::drawCopyDrawAttachDepthStencilClearThenDrawCommon(
        bool useRenderbuffer)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
        constexpr GLsizei kSize = 64;
    
        // http://anglebug.com/4935
        ANGLE_SKIP_TEST_IF(IsD3D11());
    
        setupCopyTexProgram();
    
        GLFramebuffer fboMS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
    
        // Create multisampled framebuffer to draw into
        GLTexture textureMS;
        GLRenderbuffer renderbufferMS;
        createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, nullptr,
                                       mTestSampleCount, &textureMS, &renderbufferMS);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Draw red into the multisampled color buffer.
        ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
        glUseProgram(drawColor);
        GLint colorUniformLocation =
            glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
        ASSERT_NE(colorUniformLocation, -1);
    
        // Clear and draw into framebuffer.  There is no unresolve due to clear.  The clear value is
        // irrelevant as the contents are immediately overdrawn with the draw call.
        glClear(GL_COLOR_BUFFER_BIT);
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Create a texture and copy into it.
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, kSize, kSize, 0);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    
        // Draw green into framebuffer.  This will unresolve color.
        glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Attach a depth/stencil attachment.
        GLTexture dsTextureMS;
        GLRenderbuffer dsRenderbufferMS;
        createAndAttachDepthStencilAttachment(useRenderbuffer, kSize, &dsTextureMS, &dsRenderbufferMS);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Clear all attachments, so no unresolve would be necessary.
        glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
        glClearDepthf(1);
        glClearStencil(0x55);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
        // If depth is not cleared to 1, rendering would fail.
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LESS);
    
        // If stencil is not cleared to 0x55, rendering would fail.
        glEnable(GL_STENCIL_TEST);
        glStencilFunc(GL_EQUAL, 0x55, 0xFF);
        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
        glStencilMask(0xFF);
    
        // Blend half-transparent green into the multisampled color buffer.
        glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 0.5f);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.95f);
        ASSERT_GL_NO_ERROR();
    
        // Verify that the texture is now cyan
        const GLColor kExpected2(0, 127, 127, 191);
        EXPECT_PIXEL_COLOR_NEAR(0, 0, kExpected2, 1);
        EXPECT_PIXEL_COLOR_NEAR(kSize - 1, 0, kExpected2, 1);
        EXPECT_PIXEL_COLOR_NEAR(0, kSize - 1, kExpected2, 1);
        EXPECT_PIXEL_COLOR_NEAR(kSize - 1, kSize - 1, kExpected2, 1);
    
        // For completeness, verify that the texture used as copy target is red.
        ASSERT_GL_NO_ERROR();
        verifyResults(texture, GLColor::red, kSize, 0, 0, kSize, kSize);
    
        ASSERT_GL_NO_ERROR();
    }
    
    // Draw, copy, draw, attach depth/stencil, clear then draw.  The second draw will need to unresolve
    // color.  Attaching depth/stencil changes the framebuffer, and the following clear ensures no
    // unresolve is necessary.  In the Vulkan backend, mismatches in unresolve state between framebuffer
    // and render pass will result in an ASSERT.
    TEST_P(MultisampledRenderToTextureES3Test, DrawCopyDrawAttachDepthStencilClearThenDraw)
    {
        // Test failure introduced by Apple's changes (anglebug.com/5505)
        ANGLE_SKIP_TEST_IF(IsMetal() && IsAMD());
    
        // Test failure introduced by Apple's changes (anglebug.com/5505)
        ANGLE_SKIP_TEST_IF(IsOSX() && IsARM64() && IsMetal());
    
        drawCopyDrawAttachDepthStencilClearThenDrawCommon(false);
    }
    
    // Same as DrawCopyDrawAttachDepthStencilClearThenDraw but with renderbuffers
    TEST_P(MultisampledRenderToTextureES3Test, RenderbufferDrawCopyDrawAttachDepthStencilClearThenDraw)
    {
        drawCopyDrawAttachDepthStencilClearThenDrawCommon(true);
    }
    
    // Draw, copy, redefine the color attachment with a different format, clear, copy then draw.  The
    // initial draw will need to unresolve color as the color attachment is preinitilized with data.
    // Redefining the color attachment forces framebuffer to recreate when the clear is called.  The
    // second copy resolves the clear and the final draw unresolves again.  In the Vulkan backend,
    // mismatches in unresolve state between framebuffer and render pass will result in an ASSERT and a
    // validation error (if ASSERT is removed).
    TEST_P(MultisampledRenderToTextureES3Test, DrawCopyRedefineClearCopyThenDraw)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
        constexpr GLsizei kSize = 64;
    
        setupCopyTexProgram();
    
        GLFramebuffer fboMS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
    
        std::vector<GLColor> initialColorData(kSize * kSize, GLColor::black);
    
        // Create multisampled framebuffer to draw into
        GLTexture colorMS;
        glBindTexture(GL_TEXTURE_2D, colorMS);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     initialColorData.data());
        glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                                             colorMS, 0, 4);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Draw red into the multisampled color buffer.
        ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
        glUseProgram(drawColor);
        GLint colorUniformLocation =
            glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
        ASSERT_NE(colorUniformLocation, -1);
    
        // Draw into framebuffer.  This will unresolve color.
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Create a texture and copy into it.
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, kSize, kSize, 0);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    
        // Incompatibly redefine the texture, forcing its image to be recreated.
        glBindTexture(GL_TEXTURE_2D, colorMS);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, kSize, kSize, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
        ASSERT_GL_NO_ERROR();
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
    
        // Create another texture and copy into it.
        GLTexture texture2;
        glBindTexture(GL_TEXTURE_2D, texture2);
        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, kSize, kSize, 0);
    
        // Clear to green and blend blue into the multisampled color buffer.
        glUseProgram(drawColor);
        glUniform4f(colorUniformLocation, 0.0f, 0.0f, 1.0f, 1.0f);
        glEnable(GL_BLEND);
        glBlendFunc(GL_ONE, GL_ONE);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Verify that the texture is now cyan
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::cyan);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::cyan);
        EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::cyan);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::cyan);
    
        // For completeness, verify that the texture used as copy target is red.
        ASSERT_GL_NO_ERROR();
        verifyResults(texture, GLColor::red, kSize, 0, 0, kSize, kSize);
    
        ASSERT_GL_NO_ERROR();
    }
    
    // Draw, copy, rebind the attachment, clear then draw.  The initial draw will need to unresolve
    // color.  The framebuffer attachment is temporary changed and then reset back to the original.
    // This causes the framebuffer to be recreated on the following clear and draw.  The clear prevents
    // the final draw from doing an unresolve.  In the Vulkan backend, mismatches in unresolve state
    // between framebuffer and render pass will result in an ASSERT and a validation error (if ASSERT is
    // removed).
    TEST_P(MultisampledRenderToTextureES3Test, DrawCopyRebindAttachmentClearThenDraw)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
        constexpr GLsizei kSize = 64;
    
        setupCopyTexProgram();
    
        GLFramebuffer fboMS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
    
        std::vector<GLColor> initialColorData(kSize * kSize, GLColor::black);
    
        // Create multisampled framebuffer to draw into
        GLTexture colorMS;
        glBindTexture(GL_TEXTURE_2D, colorMS);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                     initialColorData.data());
        glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                                             colorMS, 0, 4);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Draw red into the multisampled color buffer.
        ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
        glUseProgram(drawColor);
        GLint colorUniformLocation =
            glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
        ASSERT_NE(colorUniformLocation, -1);
    
        // Draw into framebuffer.  This will unresolve color.
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Create a texture and copy into it.
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, kSize, kSize, 0);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    
        // Bind the framebuffer to another texture.
        GLTexture color;
        glBindTexture(GL_TEXTURE_2D, color);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Do whatever.
        glClear(GL_COLOR_BUFFER_BIT);
    
        // Rebind the framebuffer back to the original texture.
        glBindTexture(GL_TEXTURE_2D, colorMS);
        glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                                             colorMS, 0, 4);
    
        // Clear to green and blend blue into the multisampled color buffer.
        glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        glUseProgram(drawColor);
        glUniform4f(colorUniformLocation, 0.0f, 0.0f, 1.0f, 1.0f);
        glEnable(GL_BLEND);
        glBlendFunc(GL_ONE, GL_ONE);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Verify that the texture is now cyan
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::cyan);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::cyan);
        EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::cyan);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::cyan);
    
        // For completeness, verify that the texture used as copy target is red.
        ASSERT_GL_NO_ERROR();
        verifyResults(texture, GLColor::red, kSize, 0, 0, kSize, kSize);
    
        ASSERT_GL_NO_ERROR();
    }
    
    void MultisampledRenderToTextureTest::clearThenBlendCommon(bool useRenderbuffer)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
        constexpr GLsizei kSize = 64;
    
        setupCopyTexProgram();
    
        GLFramebuffer fboMS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
    
        // Create multisampled framebuffer to draw into
        GLTexture textureMS;
        GLRenderbuffer renderbufferMS;
        createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, nullptr,
                                       mTestSampleCount, &textureMS, &renderbufferMS);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Clear the framebuffer.
        glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
    
        // Blend half-transparent green into the multisampled color buffer.
        ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
        glUseProgram(drawColor);
        GLint colorUniformLocation =
            glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
        ASSERT_NE(colorUniformLocation, -1);
    
        glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 0.5f);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Verify that the texture is now yellow
        const GLColor kExpected(127, 127, 0, 191);
        EXPECT_PIXEL_COLOR_NEAR(0, 0, kExpected, 1);
        EXPECT_PIXEL_COLOR_NEAR(kSize - 1, 0, kExpected, 1);
        EXPECT_PIXEL_COLOR_NEAR(0, kSize - 1, kExpected, 1);
        EXPECT_PIXEL_COLOR_NEAR(kSize - 1, kSize - 1, kExpected, 1);
    }
    
    // Clear then blend.  The clear should be applied correctly.
    TEST_P(MultisampledRenderToTextureTest, ClearThenBlend)
    {
        clearThenBlendCommon(false);
    }
    
    // Clear then blend.  The clear should be applied correctly.  Uses renderbuffer.
    TEST_P(MultisampledRenderToTextureTest, RenderbufferClearThenBlend)
    {
        clearThenBlendCommon(true);
    }
    
    void MultisampledRenderToTextureES3Test::depthStencilClearThenDrawCommon(bool useRenderbuffer)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
        if (!useRenderbuffer)
        {
            ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture2"));
        }
    
        // http://anglebug.com/5083
        ANGLE_SKIP_TEST_IF(IsWindows() && IsAMD() && IsVulkan());
    
        constexpr GLsizei kSize = 64;
    
        setupCopyTexProgram();
    
        GLFramebuffer fboMS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
    
        // Create framebuffer to draw into, with both color and depth attachments.
        GLTexture textureMS;
        GLRenderbuffer renderbufferMS;
        createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, nullptr,
                                       mTestSampleCount, &textureMS, &renderbufferMS);
    
        GLTexture dsTextureMS;
        GLRenderbuffer dsRenderbufferMS;
        createAndAttachDepthStencilAttachment(useRenderbuffer, kSize, &dsTextureMS, &dsRenderbufferMS);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Set viewport and clear depth/stencil
        glViewport(0, 0, kSize, kSize);
        glClearDepthf(1);
        glClearStencil(0x55);
        glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
        // If depth is not cleared to 1, rendering would fail.
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LESS);
    
        // If stencil is not cleared to 0x55, rendering would fail.
        glEnable(GL_STENCIL_TEST);
        glStencilFunc(GL_EQUAL, 0x55, 0xFF);
        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
        glStencilMask(0xFF);
    
        // Set up program
        ANGLE_GL_PROGRAM(drawRed, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
    
        // Draw red
        drawQuad(drawRed, essl1_shaders::PositionAttrib(), 0.0f);
        ASSERT_GL_NO_ERROR();
    
        // Create a texture and copy into it.
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, kSize, kSize, 0);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        ASSERT_GL_NO_ERROR();
    
        // Verify.
        verifyResults(texture, GLColor::red, kSize, 0, 0, kSize, kSize);
    }
    
    // Clear depth stencil, then draw.  The clear should be applied correctly.
    TEST_P(MultisampledRenderToTextureES3Test, DepthStencilClearThenDraw)
    {
        depthStencilClearThenDrawCommon(false);
    }
    
    // Clear depth stencil, then draw.  The clear should be applied correctly.  Uses renderbuffer.
    TEST_P(MultisampledRenderToTextureES3Test, RenderbufferDepthStencilClearThenDraw)
    {
        depthStencilClearThenDrawCommon(true);
    }
    
    // Clear&Draw, copy, then blend similarly to RenderbufferClearDrawCopyThenBlendSameProgram.  This
    // tests uses a depth/stencil buffer and makes sure the second draw (in the second render pass)
    // succeeds (i.e. depth/stencil data is not lost).  Note that this test doesn't apply to
    // depth/stencil textures as they are explicitly autoinvalidated between render passes.
    TEST_P(MultisampledRenderToTextureES3Test, RenderbufferDepthStencilClearDrawCopyThenBlend)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
    
        // http://anglebug.com/5083
        ANGLE_SKIP_TEST_IF(IsWindows() && IsAMD() && IsVulkan());
    
        // http://anglebug.com/5096
        ANGLE_SKIP_TEST_IF(IsLinux() && IsIntel() && IsVulkan());
    
        // http://anglebug.com/5380
        ANGLE_SKIP_TEST_IF(IsLinux() && IsAMD() && IsVulkan());
    
        // Test failure introduced by Apple's changes (anglebug.com/5505)
        ANGLE_SKIP_TEST_IF(IsOSX() && IsARM64() && IsMetal());
    
        constexpr GLsizei kSize = 64;
    
        setupCopyTexProgram();
    
        GLFramebuffer fbo;
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    
        // Create framebuffer to draw into, with both color and depth/stencil attachments.
        GLTexture color;
        glBindTexture(GL_TEXTURE_2D, color);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color,
                                             0, 4);
    
        GLRenderbuffer depthStencil;
        glBindRenderbuffer(GL_RENDERBUFFER, depthStencil);
        glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, kSize, kSize);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
                                  depthStencil);
        ASSERT_GL_NO_ERROR();
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Set viewport and clear depth/stencil
        glViewport(0, 0, kSize, kSize);
        glClearDepthf(1);
        glClearStencil(0x55);
        glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
        // If depth is not cleared to 1, rendering would fail.
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LESS);
    
        // If stencil is not cleared to 0x55, rendering would fail.
        glEnable(GL_STENCIL_TEST);
        glStencilFunc(GL_EQUAL, 0x55, 0xFF);
        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
        glStencilMask(0xFF);
    
        // Set up program
        ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
        glUseProgram(drawColor);
        GLint colorUniformLocation =
            glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
        ASSERT_NE(colorUniformLocation, -1);
    
        // Draw red
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.25f);
        ASSERT_GL_NO_ERROR();
    
        // Create a texture and copy into it.
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, kSize, kSize, 0);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        ASSERT_GL_NO_ERROR();
    
        // Draw again into the framebuffer, this time blending.  This tests that both the color and
        // depth/stencil data are preserved after the resolve incurred by the copy above.
        glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 0.5f);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.0f);
        ASSERT_GL_NO_ERROR();
    
        // Verify that the texture is now yellow
        const GLColor kExpected(127, 127, 0, 191);
        EXPECT_PIXEL_COLOR_NEAR(0, 0, kExpected, 1);
        EXPECT_PIXEL_COLOR_NEAR(kSize - 1, 0, kExpected, 1);
        EXPECT_PIXEL_COLOR_NEAR(0, kSize - 1, kExpected, 1);
        EXPECT_PIXEL_COLOR_NEAR(kSize - 1, kSize - 1, kExpected, 1);
    
        // For completeness, verify that the texture used as copy target is red.
        verifyResults(texture, GLColor::red, kSize, 0, 0, kSize, kSize);
    }
    
    // Draw, copy, then clear&blend.  This tests uses a depth/stencil buffer and makes sure the second
    // draw (in the second render pass) succeeds (i.e.  depth/stencil data is not lost).  The difference
    // with RenderbufferDepthStencilClearDrawCopyThenBlend is that color is cleared in the second render
    // pass, so only depth/stencil data is unresolved.  This test doesn't apply to depth/stencil
    // textures as they are explicitly autoinvalidated between render passes.
    TEST_P(MultisampledRenderToTextureES3Test, RenderbufferDepthStencilDrawCopyClearThenBlend)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
    
        // http://anglebug.com/5083
        ANGLE_SKIP_TEST_IF(IsWindows() && IsAMD() && IsVulkan());
    
        // http://anglebug.com/5096
        ANGLE_SKIP_TEST_IF(IsLinux() && IsIntel() && IsVulkan());
    
        // http://anglebug.com/5380
        ANGLE_SKIP_TEST_IF(IsLinux() && IsAMD() && IsVulkan());
    
        // Test failure introduced by Apple's changes (anglebug.com/5505)
        ANGLE_SKIP_TEST_IF(IsOSX() && IsARM64() && IsMetal());
    
        constexpr GLsizei kSize = 64;
    
        setupCopyTexProgram();
    
        GLFramebuffer fbo;
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    
        // Create framebuffer to draw into, with both color and depth/stencil attachments.
        GLTexture color;
        glBindTexture(GL_TEXTURE_2D, color);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color,
                                             0, 4);
    
        GLRenderbuffer depthStencil;
        glBindRenderbuffer(GL_RENDERBUFFER, depthStencil);
        glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, kSize, kSize);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
                                  depthStencil);
        ASSERT_GL_NO_ERROR();
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Set viewport and clear depth/stencil through draw
        glViewport(0, 0, kSize, kSize);
    
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_ALWAYS);
    
        glEnable(GL_STENCIL_TEST);
        glStencilFunc(GL_ALWAYS, 0x55, 0xFF);
        glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
        glStencilMask(0xFF);
    
        // Set up program
        ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
        glUseProgram(drawColor);
        GLint colorUniformLocation =
            glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
        ASSERT_NE(colorUniformLocation, -1);
    
        // Draw red
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 1.0f);
        ASSERT_GL_NO_ERROR();
    
        // Create a texture and copy into it.
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, kSize, kSize, 0);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        ASSERT_GL_NO_ERROR();
    
        // Clear color to blue
        glClearColor(0.0, 0.0, 1.0, 1.0);
        glClear(GL_COLOR_BUFFER_BIT);
    
        // If depth is not cleared to 1, rendering would fail.
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LESS);
    
        // If stencil is not cleared to 0x55, rendering would fail.
        glEnable(GL_STENCIL_TEST);
        glStencilFunc(GL_EQUAL, 0x55, 0xFF);
        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
        glStencilMask(0xFF);
    
        // Draw again into the framebuffer, this time blending.  This tests that depth/stencil data are
        // preserved after the resolve incurred by the copy above and color is cleared correctly.
        glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 0.5f);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.0f);
        ASSERT_GL_NO_ERROR();
    
        // Copy into the texture again.
        glBindTexture(GL_TEXTURE_2D, texture);
        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, kSize, kSize, 0);
        ASSERT_GL_NO_ERROR();
    
        // Verify that the texture is now cyan
        const GLColor kExpected(0, 127, 127, 191);
        EXPECT_PIXEL_COLOR_NEAR(0, 0, kExpected, 1);
        EXPECT_PIXEL_COLOR_NEAR(kSize - 1, 0, kExpected, 1);
        EXPECT_PIXEL_COLOR_NEAR(0, kSize - 1, kExpected, 1);
        EXPECT_PIXEL_COLOR_NEAR(kSize - 1, kSize - 1, kExpected, 1);
    
        // For completeness, verify that the texture used as copy target is also cyan.
        const GLColor expectedCopyResult(0, 127, 127, 191);
        verifyResults(texture, expectedCopyResult, kSize, 0, 0, kSize, kSize);
    }
    
    // Clear, then blit depth/stencil with renderbuffers.  This test makes sure depth/stencil blit uses
    // the correct image.  Note that this test doesn't apply to depth/stencil textures as they are
    // explicitly autoinvalidated between render passes.
    TEST_P(MultisampledRenderToTextureES3Test, RenderbufferClearThenBlitDepthStencil)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
    
        // D3D backend doesn't implement multisampled render to texture renderbuffers correctly.
        // http://anglebug.com/3107
        ANGLE_SKIP_TEST_IF(IsD3D());
    
        // The following trybot configurations don't support VK_KHR_depth_stencil_resolve.  ANGLE is not
        // conformant without this extension, but it's allowed as users commonly invalidate
        // depth/stencil.
        //
        //  - SwifthShader
        //  - Android
        //  - AMD
        //  - Nvidia on Windows7
        ANGLE_SKIP_TEST_IF(IsVulkan() &&
                           (isSwiftshader() || IsAndroid() || IsAMD() || (IsWindows7() && IsNVIDIA())));
    
        constexpr GLsizei kSize = 64;
    
        setupCopyTexProgram();
    
        GLFramebuffer fboMS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
    
        // Create framebuffer to draw into, with both color and depth/stencil attachments.
        GLTexture color;
        glBindTexture(GL_TEXTURE_2D, color);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color,
                                             0, 4);
    
        GLRenderbuffer depthStencilMS;
        glBindRenderbuffer(GL_RENDERBUFFER, depthStencilMS);
        glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, kSize, kSize);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
                                  depthStencilMS);
        ASSERT_GL_NO_ERROR();
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Clear depth/stencil
        glClearDepthf(1);
        glClearStencil(0x55);
        glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
        // Create framebuffer as blit target.
        GLFramebuffer fbo;
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
    
        glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0);
    
        GLRenderbuffer depthStencil;
        glBindRenderbuffer(GL_RENDERBUFFER, depthStencil);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, kSize, kSize);
        glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
                                  depthStencil);
        ASSERT_GL_NO_ERROR();
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_DRAW_FRAMEBUFFER);
    
        // Blit depth/stencil
        glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize,
                          GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
    
        // Draw into the framebuffer that was the destination of blit, verifying that depth and stencil
        // values are correct.
    
        // If depth is not 1, rendering would fail.
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LESS);
    
        // If stencil is not 0x55, rendering would fail.
        glEnable(GL_STENCIL_TEST);
        glStencilFunc(GL_EQUAL, 0x55, 0xFF);
        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
        glStencilMask(0xFF);
    
        // Set up program
        ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
        glUseProgram(drawColor);
        GLint colorUniformLocation =
            glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
        ASSERT_NE(colorUniformLocation, -1);
    
        // Draw red
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.0f);
        ASSERT_GL_NO_ERROR();
    
        // Verify that the texture is now red
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::red);
    
        // Clear depth/stencil to a different value, and blit again but this time flipped.
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboMS);
        glClearDepthf(0);
        glClearStencil(0x3E);
        glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
        // Blit
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
        glBlitFramebuffer(0, 0, kSize, kSize, kSize, kSize, 0, 0,
                          GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
    
        // Draw green
        glDepthFunc(GL_GREATER);
        glStencilFunc(GL_EQUAL, 0x3E, 0xFF);
        glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.0f);
        ASSERT_GL_NO_ERROR();
    
        // Verify that the texture is now green
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::green);
    }
    
    // Draw, then blit depth/stencil with renderbuffers.  This test makes sure depth/stencil resolve is
    // correctly implemented.  Note that this test doesn't apply to depth/stencil textures as they are
    // explicitly autoinvalidated between render passes.
    TEST_P(MultisampledRenderToTextureES3Test, RenderbufferDrawThenBlitDepthStencil)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
    
        // Skip on configurations that don't support VK_KHR_depth_stencil_resolve.  See comment
        // in RenderbufferClearThenBlitDepthStencil.
        ANGLE_SKIP_TEST_IF(IsVulkan() &&
                           (isSwiftshader() || IsAndroid() || IsAMD() || (IsWindows7() && IsNVIDIA())));
    
        // http://anglebug.com/5096
        ANGLE_SKIP_TEST_IF(IsLinux() && IsIntel() && IsVulkan());
    
        constexpr GLsizei kSize = 64;
    
        GLFramebuffer fboMS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
    
        // Create framebuffer to draw into, with both color and depth/stencil attachments.
        GLTexture color;
        glBindTexture(GL_TEXTURE_2D, color);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color,
                                             0, 4);
    
        GLRenderbuffer depthStencilMS;
        glBindRenderbuffer(GL_RENDERBUFFER, depthStencilMS);
        glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, kSize, kSize);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
                                  depthStencilMS);
        ASSERT_GL_NO_ERROR();
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Set up program
        ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
        glUseProgram(drawColor);
        GLint colorUniformLocation =
            glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
        ASSERT_NE(colorUniformLocation, -1);
    
        // Output depth/stencil through draw
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_ALWAYS);
    
        glEnable(GL_STENCIL_TEST);
        glStencilFunc(GL_ALWAYS, 0x55, 0xFF);
        glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
        glStencilMask(0xFF);
    
        // Draw blue
        glUniform4f(colorUniformLocation, 0.0f, 0.0f, 1.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 1.0f);
        ASSERT_GL_NO_ERROR();
    
        // Create framebuffer as blit target.
        GLFramebuffer fbo;
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
    
        glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0);
    
        GLRenderbuffer depthStencil;
        glBindRenderbuffer(GL_RENDERBUFFER, depthStencil);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, kSize, kSize);
        glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
                                  depthStencil);
        ASSERT_GL_NO_ERROR();
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_DRAW_FRAMEBUFFER);
    
        // Blit depth/stencil
        glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize,
                          GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
    
        // Draw into the framebuffer that was the destination of blit, verifying that depth and stencil
        // values are correct.
    
        // If depth is not 1, rendering would fail.
        glDepthFunc(GL_LESS);
    
        // If stencil is not 0x55, rendering would fail.
        glStencilFunc(GL_EQUAL, 0x55, 0xFF);
        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
        glStencilMask(0xFF);
    
        // Draw red
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.0f);
        ASSERT_GL_NO_ERROR();
    
        // Verify that the texture is now red
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::red);
    }
    
    // Draw, then blit depth/stencil with renderbuffers, without a color attachment. Note that this test
    // doesn't apply to depth/stencil textures as they are explicitly autoinvalidated between render
    // passes.
    //
    // This test first uses a draw call to fill in the depth/stencil buffer, then blits it to force a
    // resolve.  Then it uses a no-op draw call to start a "fullscreen" render pass followed by a
    // scissored draw to modify parts of the depth buffer:
    //
    // +--------------------+
    // |     D=1, S=0x55    |
    // |                    |
    // |     +--------+     |
    // |     |  D=0   |     |
    // |     | S=0xAA |     |
    // |     +--------+     |
    // |                    |
    // |                    |
    // +--------------------+
    //
    // Blit is used again to copy the depth/stencil attachment data, and the result is verified.
    TEST_P(MultisampledRenderToTextureES3Test, RenderbufferDrawThenBlitDepthStencilOnly)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
    
        // Skip on configurations that don't support VK_KHR_depth_stencil_resolve.  See comment
        // in RenderbufferClearThenBlitDepthStencil.
        ANGLE_SKIP_TEST_IF(IsVulkan() &&
                           (isSwiftshader() || IsAndroid() || IsAMD() || (IsWindows7() && IsNVIDIA())));
    
        // http://anglebug.com/5096
        ANGLE_SKIP_TEST_IF(IsLinux() && IsIntel() && IsVulkan());
    
        // http://anglebug.com/5110
        ANGLE_SKIP_TEST_IF(IsD3D());
    
        // Test failure introduced by Apple's changes (anglebug.com/5505)
        ANGLE_SKIP_TEST_IF(IsOSX() && IsARM64() && IsMetal());
    
        constexpr GLsizei kSize = 64;
    
        GLFramebuffer fboMS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
    
        // Create framebuffer to draw into, with depth/stencil attachment only.
        GLRenderbuffer depthStencilMS;
        glBindRenderbuffer(GL_RENDERBUFFER, depthStencilMS);
        glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, kSize, kSize);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
                                  depthStencilMS);
        ASSERT_GL_NO_ERROR();
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Set up program
        ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
        glUseProgram(drawColor);
        GLint colorUniformLocation =
            glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
        ASSERT_NE(colorUniformLocation, -1);
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
    
        // Output depth/stencil through draw
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_ALWAYS);
    
        glEnable(GL_STENCIL_TEST);
        glStencilFunc(GL_ALWAYS, 0x55, 0xFF);
        glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
        glStencilMask(0xFF);
    
        // Draw.  Depth/stencil is now:
        //
        // +--------------------+
        // |     D=1, S=0x55    |
        // |                    |
        // |                    |
        // |                    |
        // |                    |
        // |                    |
        // |                    |
        // |                    |
        // +--------------------+
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 1.0f);
        ASSERT_GL_NO_ERROR();
    
        // Create framebuffer as blit target.
        GLFramebuffer fbo;
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
    
        GLRenderbuffer depthStencil;
        glBindRenderbuffer(GL_RENDERBUFFER, depthStencil);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, kSize, kSize);
        glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
                                  depthStencil);
        ASSERT_GL_NO_ERROR();
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_DRAW_FRAMEBUFFER);
    
        // Blit depth/stencil
        glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize,
                          GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
    
        // Disable depth/stencil and draw again.  Depth/stencil is not modified.
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboMS);
        glDisable(GL_DEPTH_TEST);
        glDisable(GL_STENCIL_TEST);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.0f);
        ASSERT_GL_NO_ERROR();
    
        // Enable depth/stencil and do a scissored draw.  Depth/stencil is now:
        //
        // +--------------------+
        // |     D=1, S=0x55    |
        // |                    |
        // |     +--------+     |
        // |     |  D=0   |     |
        // |     | S=0xAA |     |
        // |     +--------+     |
        // |                    |
        // |                    |
        // +--------------------+
        glEnable(GL_DEPTH_TEST);
        glEnable(GL_STENCIL_TEST);
        glStencilFunc(GL_ALWAYS, 0xAA, 0xFF);
        glEnable(GL_SCISSOR_TEST);
        glScissor(kSize / 4, kSize / 4, kSize / 2, kSize / 2);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), -1.0f);
        glDisable(GL_SCISSOR_TEST);
        ASSERT_GL_NO_ERROR();
    
        // Blit depth/stencil again.
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
        glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize,
                          GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
        ASSERT_GL_NO_ERROR();
    
        // Draw into the framebuffer that was the destination of blit, verifying that depth and stencil
        // values are correct.
        GLTexture color;
        glBindTexture(GL_TEXTURE_2D, color);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0);
        ASSERT_GL_NO_ERROR();
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_DRAW_FRAMEBUFFER);
    
        // First, verify the outside border, where D=1 and S=0x55
        glDepthFunc(GL_LESS);
        glStencilFunc(GL_EQUAL, 0x55, 0xFF);
        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
        glStencilMask(0xFF);
        ASSERT_GL_NO_ERROR();
    
        // Draw green
        glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.95f);
        ASSERT_GL_NO_ERROR();
    
        // Then, verify the center, where D=0 and S=0xAA
        glDepthFunc(GL_GREATER);
        glStencilFunc(GL_EQUAL, 0xAA, 0xFF);
        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
        glStencilMask(0xFF);
    
        // Draw blue
        glUniform4f(colorUniformLocation, 0.0f, 0.0f, 1.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), -0.95f);
        ASSERT_GL_NO_ERROR();
    
        // Verify that the border is now green
        glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::green);
    
        // Verify that the center is now blue
        EXPECT_PIXEL_COLOR_EQ(kSize / 4, kSize / 4, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(3 * kSize / 4 - 1, kSize / 4, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(kSize / 4, 3 * kSize / 4 - 1, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(3 * kSize / 4 - 1, 3 * kSize / 4 - 1, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::blue);
    }
    
    // Test the depth read/write mode change within the renderpass while there is color unresolve
    // attachment
    TEST_P(MultisampledRenderToTextureTest, DepthReadWriteToggleWithStartedRenderPass)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
    
        // http://anglebug.com/5380
        ANGLE_SKIP_TEST_IF(IsLinux() && IsAMD() && IsVulkan());
    
        // Test failure introduced by Apple's changes (anglebug.com/5505)
        ANGLE_SKIP_TEST_IF(IsOSX() && IsARM64() && IsMetal());
    
        constexpr GLsizei kSize = 64;
    
        setupCopyTexProgram();
    
        GLFramebuffer fboMS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
    
        // Create framebuffer to draw into, with both color and depth attachments.
        GLTexture textureMS;
        GLRenderbuffer renderbufferMS;
        createAndAttachColorAttachment(true, kSize, GL_COLOR_ATTACHMENT0, nullptr, mTestSampleCount,
                                       &textureMS, &renderbufferMS);
    
        GLTexture dsTextureMS;
        GLRenderbuffer dsRenderbufferMS;
        glBindRenderbuffer(GL_RENDERBUFFER, dsRenderbufferMS);
        glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT16, kSize, kSize);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
                                  dsRenderbufferMS);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // First renderpass: draw with depth value 0.5f
        glViewport(0, 0, kSize, kSize);
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_ALWAYS);
        glDepthMask(GL_TRUE);
        glClearColor(0.0, 0.0, 0.0, 1.0);
        glClear(GL_COLOR_BUFFER_BIT);
        glEnable(GL_BLEND);
        glBlendFunc(GL_ONE, GL_ZERO);
        ANGLE_GL_PROGRAM(drawBlue, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue());
        drawQuad(drawBlue, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
        // The color check should end the renderpass
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
    
        // Create another FBO and render, jus so try to clear rendering cache. At least on pixel4,
        // the test now properly fail if I force the loadOP to DontCare in the next renderpass.
        constexpr bool clearRenderingCacheWithFBO = true;
        if (clearRenderingCacheWithFBO)
        {
            GLFramebuffer fboMS2;
            glBindFramebuffer(GL_FRAMEBUFFER, fboMS2);
            GLTexture textureMS2;
            GLRenderbuffer renderbufferMS2;
            createAndAttachColorAttachment(true, 2048, GL_COLOR_ATTACHMENT0, nullptr, mTestSampleCount,
                                           &textureMS2, &renderbufferMS2);
            GLTexture dsTextureMS2;
            GLRenderbuffer dsRenderbufferMS2;
            glBindRenderbuffer(GL_RENDERBUFFER, dsRenderbufferMS2);
            glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT16, 2048, 2048);
            glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
                                      dsRenderbufferMS2);
            EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
            ASSERT_GL_NO_ERROR();
            glViewport(0, 0, 2048, 2048);
            ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
            glUseProgram(drawColor);
            GLint colorUniformLocation =
                glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
            ASSERT_NE(colorUniformLocation, -1);
            glUniform4f(colorUniformLocation, 0.0f, 0.0f, 0.0f, 0.0f);
            drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
            ASSERT_GL_NO_ERROR();
        }
    
        // Second renderpass: Start with depth read only and then switch to depth write
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
        glViewport(0, 0, kSize, kSize);
        glDepthFunc(GL_LESS);
        // Draw red with depth read only. pass depth test, Result: color=Red, depth=0.5
        glDepthMask(GL_FALSE);
        glBlendFunc(GL_ONE, GL_ONE);
        ANGLE_GL_PROGRAM(drawRed, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
        glUseProgram(drawRed);
        drawQuad(drawRed, essl1_shaders::PositionAttrib(), 0.1f);
        ASSERT_GL_NO_ERROR();
    
        // Draw green with depth write. Pass depth test. Result: color=Green, depth=0.3
        glDepthMask(GL_TRUE);
        glBlendFunc(GL_ONE, GL_ONE);
        ANGLE_GL_PROGRAM(drawGreen, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
        glUseProgram(drawGreen);
        drawQuad(drawGreen, essl1_shaders::PositionAttrib(), 0.3f);
        ASSERT_GL_NO_ERROR();
    
        // Create a texture and copy into it.
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, kSize, kSize, 0);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        ASSERT_GL_NO_ERROR();
    
        // Verify the color has all three color in it.
        EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::white);
    }
    
    void MultisampledRenderToTextureES3Test::colorAttachment1Common(bool useRenderbuffer)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture2"));
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_draw_buffers"));
    
        // Qualcomm driver crashes in the presence of VK_ATTACHMENT_UNUSED.
        // http://anglebug.com/3423
        ANGLE_SKIP_TEST_IF(IsVulkan() && IsAndroid());
    
        // Fails on Intel Ubuntu 19.04 Mesa 19.0.2 Vulkan. http://anglebug.com/3616
        ANGLE_SKIP_TEST_IF(IsLinux() && IsIntel() && IsVulkan());
    
        constexpr GLsizei kSize = 64;
    
        setupCopyTexProgram();
    
        GLFramebuffer fboMS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
    
        // Create multisampled framebuffer to draw into, use color attachment 1
        GLTexture textureMS;
        GLRenderbuffer renderbufferMS;
        createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT1, nullptr,
                                       mTestSampleCount, &textureMS, &renderbufferMS);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Setup program to render into attachment 1.
        constexpr bool kBuffersEnabled[8] = {false, true};
    
        GLuint drawColor;
        setupUniformColorProgramMultiRenderTarget(kBuffersEnabled, &drawColor);
        glUseProgram(drawColor);
        GLint colorUniformLocation =
            glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
        ASSERT_NE(colorUniformLocation, -1);
    
        constexpr GLenum kDrawBuffers[] = {GL_NONE, GL_COLOR_ATTACHMENT1};
        glDrawBuffers(2, kDrawBuffers);
        glReadBuffer(GL_COLOR_ATTACHMENT1);
        ASSERT_GL_NO_ERROR();
    
        // Draw red into the multisampled color buffer.
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Create a texture and copy into it.
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, kSize, kSize, 0);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        ASSERT_GL_NO_ERROR();
    
        // Blend half-transparent green into the multisampled color buffer.
        glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 0.5f);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Verify that the texture is now yellow
        const GLColor kExpected(127, 127, 0, 191);
        EXPECT_PIXEL_COLOR_NEAR(0, 0, kExpected, 1);
        EXPECT_PIXEL_COLOR_NEAR(kSize - 1, 0, kExpected, 1);
        EXPECT_PIXEL_COLOR_NEAR(0, kSize - 1, kExpected, 1);
        EXPECT_PIXEL_COLOR_NEAR(kSize - 1, kSize - 1, kExpected, 1);
    
        // For completeness, verify that the texture used as copy target is red.
        verifyResults(texture, GLColor::red, kSize, 0, 0, kSize, kSize);
    
        ASSERT_GL_NO_ERROR();
    
        glDeleteProgram(drawColor);
    }
    
    // Draw, copy, then blend.  The copy will make sure an implicit resolve happens.  Regardless, the
    // following draw should retain the data written by the first draw command.
    // Uses color attachment 1.
    TEST_P(MultisampledRenderToTextureES3Test, ColorAttachment1)
    {
        colorAttachment1Common(false);
    }
    
    // Draw, copy, then blend.  The copy will make sure an implicit resolve happens.  Regardless, the
    // following draw should retain the data written by the first draw command.
    // Uses color attachment 1.  Uses renderbuffer.
    TEST_P(MultisampledRenderToTextureES3Test, RenderbufferColorAttachment1)
    {
        colorAttachment1Common(true);
    }
    
    void MultisampledRenderToTextureES3Test::colorAttachments0And3Common(bool useRenderbuffer)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
        ANGLE_SKIP_TEST_IF(!useRenderbuffer &&
                           !EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture2"));
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_draw_buffers"));
    
        // Qualcomm driver crashes in the presence of VK_ATTACHMENT_UNUSED.
        // http://anglebug.com/3423
        ANGLE_SKIP_TEST_IF(IsVulkan() && IsAndroid());
    
        constexpr GLsizei kSize = 64;
    
        setupCopyTexProgram();
    
        GLFramebuffer fboMS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
    
        // Create multisampled framebuffer to draw into, use color attachment 1
        GLTexture textureMS0;
        GLRenderbuffer renderbufferMS0;
        createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, nullptr,
                                       mTestSampleCount, &textureMS0, &renderbufferMS0);
    
        GLTexture textureMS3;
        GLRenderbuffer renderbufferMS3;
        createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT3, nullptr,
                                       mTestSampleCount, &textureMS3, &renderbufferMS3);
    
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Setup program to render into attachments 0 and 3.
        constexpr bool kBuffersEnabled[8] = {true, false, false, true};
    
        GLuint drawColor;
        setupUniformColorProgramMultiRenderTarget(kBuffersEnabled, &drawColor);
        glUseProgram(drawColor);
        GLint colorUniformLocation =
            glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
        ASSERT_NE(colorUniformLocation, -1);
    
        constexpr GLenum kDrawBuffers[] = {GL_COLOR_ATTACHMENT0, GL_NONE, GL_NONE,
                                           GL_COLOR_ATTACHMENT3};
        glDrawBuffers(4, kDrawBuffers);
        glReadBuffer(GL_COLOR_ATTACHMENT3);
        ASSERT_GL_NO_ERROR();
    
        // Draw red into the multisampled color buffers.
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Create a texture and copy from one of them.
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, kSize, kSize, 0);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        ASSERT_GL_NO_ERROR();
    
        // Blend half-transparent green into the multisampled color buffers.
        glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 0.5f);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Verify that the textures are now yellow
        const GLColor kExpected(127, 127, 0, 191);
        EXPECT_PIXEL_COLOR_NEAR(0, 0, kExpected, 1);
        EXPECT_PIXEL_COLOR_NEAR(kSize - 1, 0, kExpected, 1);
        EXPECT_PIXEL_COLOR_NEAR(0, kSize - 1, kExpected, 1);
        EXPECT_PIXEL_COLOR_NEAR(kSize - 1, kSize - 1, kExpected, 1);
    
        glReadBuffer(GL_COLOR_ATTACHMENT0);
        EXPECT_PIXEL_COLOR_NEAR(0, 0, kExpected, 1);
        EXPECT_PIXEL_COLOR_NEAR(kSize - 1, 0, kExpected, 1);
        EXPECT_PIXEL_COLOR_NEAR(0, kSize - 1, kExpected, 1);
        EXPECT_PIXEL_COLOR_NEAR(kSize - 1, kSize - 1, kExpected, 1);
    
        // For completeness, verify that the texture used as copy target is red.
        verifyResults(texture, GLColor::red, kSize, 0, 0, kSize, kSize);
    
        ASSERT_GL_NO_ERROR();
    
        glDeleteProgram(drawColor);
    }
    
    // Draw, copy, then blend.  The copy will make sure an implicit resolve happens.  Regardless, the
    // following draw should retain the data written by the first draw command.
    // Uses color attachments 0 and 3.
    TEST_P(MultisampledRenderToTextureES3Test, ColorAttachments0And3)
    {
        colorAttachments0And3Common(false);
    }
    
    // Draw, copy, then blend.  The copy will make sure an implicit resolve happens.  Regardless, the
    // following draw should retain the data written by the first draw command.
    // Uses color attachments 0 and 3.  Uses renderbuffer.
    TEST_P(MultisampledRenderToTextureES3Test, RenderbufferColorAttachments0And3)
    {
        // Test failure introduced by Apple's changes (anglebug.com/5505)
        ANGLE_SKIP_TEST_IF(IsOSX() && IsARM64() && IsMetal());
    
        colorAttachments0And3Common(true);
    }
    
    // Draw with depth buffer.  Uses EXT_multisampled_render_to_texture2.
    // The test works with a 64x1 texture.  The first draw call will render geometry whose depth is
    // different between top and bottom.  The second draw call will enable depth test and draw with the
    // average of the two depths.  Only half of the samples will take the new color.  Once resolved, the
    // expected color would be the average of the two draw colors.
    TEST_P(MultisampledRenderToTextureES3Test, DepthStencilAttachment)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture2"));
    
        constexpr GLsizei kWidth = 64;
    
        // Create multisampled framebuffer to draw into, with both color and depth attachments.
        GLTexture colorMS;
        glBindTexture(GL_TEXTURE_2D, colorMS);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kWidth, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        GLTexture depthMS;
        glBindTexture(GL_TEXTURE_2D, depthMS);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, kWidth, 1, 0, GL_DEPTH_STENCIL,
                     GL_UNSIGNED_INT_24_8_OES, nullptr);
    
        GLFramebuffer fboMS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
        glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                                             colorMS, 0, 4);
        glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
                                             depthMS, 0, 4);
        ASSERT_GL_NO_ERROR();
    
        // Setup draw program
        ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
        glUseProgram(drawColor);
        GLint colorUniformLocation =
            glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
        ASSERT_NE(colorUniformLocation, -1);
        GLint positionLocation = glGetAttribLocation(drawColor, essl1_shaders::PositionAttrib());
        ASSERT_NE(-1, positionLocation);
    
        // Setup vertices such that depth is varied from top to bottom.
        std::array<Vector3, 6> quadVertices = {
            Vector3(-1.0f, 1.0f, 0.8f), Vector3(-1.0f, -1.0f, 0.2f), Vector3(1.0f, -1.0f, 0.2f),
            Vector3(-1.0f, 1.0f, 0.8f), Vector3(1.0f, -1.0f, 0.2f),  Vector3(1.0f, 1.0f, 0.8f),
        };
        GLBuffer quadVertexBuffer;
        glBindBuffer(GL_ARRAY_BUFFER, quadVertexBuffer);
        glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 6, quadVertices.data(), GL_STATIC_DRAW);
        glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
        glEnableVertexAttribArray(positionLocation);
    
        // Draw red into the framebuffer.
        glViewport(0, 0, kWidth, 1);
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_ALWAYS);
        glDrawArrays(GL_TRIANGLES, 0, 6);
        ASSERT_GL_NO_ERROR();
    
        // Draw green such that half the samples of each pixel pass the depth test.
        glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 1.0f);
        glDepthFunc(GL_GREATER);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        const GLColor kExpected(127, 127, 0, 255);
        EXPECT_PIXEL_COLOR_NEAR(0, 0, kExpected, 1);
        EXPECT_PIXEL_COLOR_NEAR(kWidth - 1, 0, kExpected, 1);
        EXPECT_PIXEL_COLOR_NEAR(kWidth / 2, 0, kExpected, 1);
    
        glDisableVertexAttribArray(0);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
    }
    
    // Draw, copy, then blend.  The copy will make sure an implicit resolve happens.  Regardless, the
    // following draw should retain the data written by the first draw command.
    // Uses color attachments 0 and 1.  Attachment 0 is a normal multisampled texture, while attachment
    // 1 is a multisampled-render-to-texture texture.
    TEST_P(MultisampledRenderToTextureES31Test, MixedMultisampledAndMultisampledRenderToTexture)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture2"));
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_draw_buffers"));
    
        constexpr GLsizei kSize = 64;
    
        setupCopyTexProgram();
    
        // Create multisampled framebuffer to draw into, use color attachment 1
        GLTexture colorMS0;
        glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, colorMS0);
        glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, kSize, kSize, true);
    
        GLTexture colorMS1;
        glBindTexture(GL_TEXTURE_2D, colorMS1);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        GLFramebuffer fboMS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE,
                               colorMS0, 0);
        glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D,
                                             colorMS1, 0, 4);
        ASSERT_GL_NO_ERROR();
    
        // Setup program to render into attachments 0 and 1.
        constexpr bool kBuffersEnabled[8] = {true, true};
    
        GLuint drawColor;
        setupUniformColorProgramMultiRenderTarget(kBuffersEnabled, &drawColor);
        glUseProgram(drawColor);
        GLint colorUniformLocation =
            glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
        ASSERT_NE(colorUniformLocation, -1);
    
        constexpr GLenum kDrawBuffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
        glDrawBuffers(2, kDrawBuffers);
        glReadBuffer(GL_COLOR_ATTACHMENT1);
        ASSERT_GL_NO_ERROR();
    
        // Draw red into the multisampled color buffers.
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Create a texture and copy from one of them.
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, kSize, kSize, 0);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        ASSERT_GL_NO_ERROR();
    
        // Blend half-transparent green into the multisampled color buffers.
        glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 0.5f);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Verify that the textures are now yellow
        const GLColor kExpected(127, 127, 0, 191);
        EXPECT_PIXEL_COLOR_NEAR(0, 0, kExpected, 1);
        EXPECT_PIXEL_COLOR_NEAR(kSize - 1, 0, kExpected, 1);
        EXPECT_PIXEL_COLOR_NEAR(0, kSize - 1, kExpected, 1);
        EXPECT_PIXEL_COLOR_NEAR(kSize - 1, kSize - 1, kExpected, 1);
    
        // For completeness, verify that the texture used as copy target is red.
        verifyResults(texture, GLColor::red, kSize, 0, 0, kSize, kSize);
    
        ASSERT_GL_NO_ERROR();
    
        glDeleteProgram(drawColor);
    }
    
    void MultisampledRenderToTextureES31Test::blitFramebufferAttachment1Common(bool useRenderbuffer)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
        ANGLE_SKIP_TEST_IF(!useRenderbuffer &&
                           !EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture2"));
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_draw_buffers"));
    
        constexpr GLsizei kSize = 16;
    
        GLFramebuffer fboMS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
    
        // Create multisampled framebuffer to draw into, use color attachment 1
        GLTexture colorMS0;
        glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, colorMS0);
        glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, kSize, kSize, true);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE,
                               colorMS0, 0);
    
        GLTexture textureMS1;
        GLRenderbuffer renderbufferMS1;
        createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT1, nullptr,
                                       mTestSampleCount, &textureMS1, &renderbufferMS1);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Setup program to render into attachments 0 and 1.
        constexpr bool kBuffersEnabled[8] = {true, true};
    
        GLuint drawColor;
        setupUniformColorProgramMultiRenderTarget(kBuffersEnabled, &drawColor);
        glUseProgram(drawColor);
        GLint colorUniformLocation =
            glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
        ASSERT_NE(colorUniformLocation, -1);
    
        constexpr GLenum kDrawBuffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
        glDrawBuffers(2, kDrawBuffers);
        glReadBuffer(GL_COLOR_ATTACHMENT1);
        ASSERT_GL_NO_ERROR();
    
        // Draw red into the multisampled color buffers.
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Create single sampled framebuffer to use as dest.
        GLFramebuffer fboSS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboSS);
        GLTexture colorSS;
        glBindTexture(GL_TEXTURE_2D, colorSS);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorSS, 0);
        ASSERT_GL_NO_ERROR();
    
        // Bind MS to READ as SS is already bound to DRAW.
        glBindFramebuffer(GL_READ_FRAMEBUFFER, fboMS);
        glReadBuffer(GL_COLOR_ATTACHMENT1);
        glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
        ASSERT_GL_NO_ERROR();
    
        // Bind SS to READ so we can readPixels from it
        glBindFramebuffer(GL_FRAMEBUFFER, fboSS);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::red);
        ASSERT_GL_NO_ERROR();
    }
    
    // BlitFramebuffer functionality test with mixed color attachments where multisampled render to
    // texture as attachment 1 and is the read buffer.  This test makes sure the fact that attachment 0
    // is a true multisampled texture doesn't cause issues.
    // Uses EXT_multisampled_render_to_texture2.
    TEST_P(MultisampledRenderToTextureES31Test, BlitFramebufferAttachment1)
    {
        blitFramebufferAttachment1Common(false);
    }
    
    // BlitFramebuffer functionality test with mixed color attachments where multisampled render to
    // texture as attachment 1 and is the read buffer.  This test makes sure the fact that attachment 0
    // is a true multisampled texture doesn't cause issues.
    // Uses renderbuffer.
    TEST_P(MultisampledRenderToTextureES31Test, RenderbufferBlitFramebufferAttachment1)
    {
        blitFramebufferAttachment1Common(true);
    }
    
    void MultisampledRenderToTextureES3Test::blitFramebufferMixedColorAndDepthCommon(
        bool useRenderbuffer)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
    
        constexpr GLsizei kSize = 16;
    
        GLFramebuffer fboMS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
    
        // Create multisampled framebuffer to use as source.
        GLRenderbuffer depthMS;
        glBindRenderbuffer(GL_RENDERBUFFER, depthMS);
        glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT24, kSize, kSize);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthMS);
    
        GLTexture textureMS;
        GLRenderbuffer renderbufferMS;
        createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0, nullptr,
                                       mTestSampleCount, &textureMS, &renderbufferMS);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Clear depth to 0.5 and color to red.
        glClearDepthf(0.5f);
        glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
        ASSERT_GL_NO_ERROR();
    
        // Create single sampled framebuffer to use as dest.
        GLRenderbuffer depthSS;
        glBindRenderbuffer(GL_RENDERBUFFER, depthSS);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, kSize, kSize);
    
        GLTexture colorSS;
        glBindTexture(GL_TEXTURE_2D, colorSS);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        GLFramebuffer fboSS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboSS);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthSS);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorSS, 0);
        ASSERT_GL_NO_ERROR();
    
        // Bind MS to READ as SS is already bound to DRAW.
        glBindFramebuffer(GL_READ_FRAMEBUFFER, fboMS);
        glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize,
                          GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST);
        ASSERT_GL_NO_ERROR();
    
        // Bind SS to READ so we can readPixels from it
        glBindFramebuffer(GL_FRAMEBUFFER, fboSS);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::red);
        ASSERT_GL_NO_ERROR();
    
        // Use a small shader to verify depth.
        ANGLE_GL_PROGRAM(depthTestProgram, essl1_shaders::vs::Passthrough(), essl1_shaders::fs::Blue());
        ANGLE_GL_PROGRAM(depthTestProgramFail, essl1_shaders::vs::Passthrough(),
                         essl1_shaders::fs::Green());
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LESS);
        drawQuad(depthTestProgram, essl1_shaders::PositionAttrib(), -0.01f);
        drawQuad(depthTestProgramFail, essl1_shaders::PositionAttrib(), 0.01f);
        glDisable(GL_DEPTH_TEST);
        ASSERT_GL_NO_ERROR();
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::blue);
        ASSERT_GL_NO_ERROR();
    }
    
    // BlitFramebuffer functionality test with mixed multisampled-render-to-texture color attachment and
    // multisampled depth buffer.  This test makes sure that the color attachment is blitted, while
    // the depth/stencil attachment is resolved.
    TEST_P(MultisampledRenderToTextureES3Test, BlitFramebufferMixedColorAndDepth)
    {
        blitFramebufferMixedColorAndDepthCommon(false);
    }
    
    // BlitFramebuffer functionality test with mixed multisampled-render-to-texture color attachment and
    // multisampled depth buffer.  This test makes sure that the color attachment is blitted, while
    // the depth/stencil attachment is resolved.  Uses renderbuffer.
    TEST_P(MultisampledRenderToTextureES3Test, RenderbufferBlitFramebufferMixedColorAndDepth)
    {
        blitFramebufferMixedColorAndDepthCommon(true);
    }
    
    // Draw non-multisampled, draw multisampled, repeat.  This tests the same texture being bound
    // differently to two FBOs.
    TEST_P(MultisampledRenderToTextureTest, DrawNonMultisampledThenMultisampled)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
        constexpr GLsizei kSize = 64;
    
        // http://anglebug.com/4935
        ANGLE_SKIP_TEST_IF(IsD3D11());
    
        // Texture attachment to the two framebuffers.
        GLTexture color;
        glBindTexture(GL_TEXTURE_2D, color);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        // Create singlesampled framebuffer.
        GLFramebuffer fboSS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboSS);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0);
        ASSERT_GL_NO_ERROR();
    
        // Create multisampled framebuffer.
        GLFramebuffer fboMS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
        glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color,
                                             0, 4);
        ASSERT_GL_NO_ERROR();
    
        // Draw red into the multisampled color buffer.
        ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
        glUseProgram(drawColor);
        GLint colorUniformLocation =
            glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
        ASSERT_NE(colorUniformLocation, -1);
    
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Draw green into the singlesampled color buffer
        glBindFramebuffer(GL_FRAMEBUFFER, fboSS);
        glEnable(GL_SCISSOR_TEST);
        glScissor(kSize / 8, kSize / 8, 3 * kSize / 4, 3 * kSize / 4);
        glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Draw blue into the multisampled color buffer
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
        glScissor(kSize / 4, kSize / 4, kSize / 2, kSize / 2);
        glUniform4f(colorUniformLocation, 0.0f, 0.0f, 1.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Verify that the texture is red on the border, blue in the middle and green in between.
        glBindFramebuffer(GL_READ_FRAMEBUFFER, fboSS);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::red);
    
        EXPECT_PIXEL_COLOR_EQ(3 * kSize / 16, 3 * kSize / 16, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(13 * kSize / 16, 3 * kSize / 16, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(3 * kSize / 16, 13 * kSize / 16, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(13 * kSize / 16, 13 * kSize / 16, GLColor::green);
    
        EXPECT_PIXEL_COLOR_EQ(3 * kSize / 8, 3 * kSize / 8, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(5 * kSize / 8, 3 * kSize / 8, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(3 * kSize / 8, 5 * kSize / 8, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(5 * kSize / 8, 5 * kSize / 8, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::blue);
    
        ASSERT_GL_NO_ERROR();
    }
    
    // Draw multisampled, draw multisampled with another sample count, repeat.  This tests the same
    // texture being bound as multisampled-render-to-texture with different sample counts to two FBOs.
    TEST_P(MultisampledRenderToTextureTest, DrawMultisampledDifferentSamples)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
        constexpr GLsizei kSize = 64;
    
        GLsizei maxSamples = 0;
        glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
        ASSERT_GE(maxSamples, 4);
    
        // Texture attachment to the two framebuffers.
        GLTexture color;
        glBindTexture(GL_TEXTURE_2D, color);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        // Create two multisampled framebuffers.
        GLFramebuffer fboMS1;
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS1);
        glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color,
                                             0, 4);
    
        GLFramebuffer fboMS2;
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS2);
        glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color,
                                             0, maxSamples);
        ASSERT_GL_NO_ERROR();
    
        // Draw red into the first multisampled color buffer.
        ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
        glUseProgram(drawColor);
        GLint colorUniformLocation =
            glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
        ASSERT_NE(colorUniformLocation, -1);
    
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Draw green into the second multisampled color buffer
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS1);
        glEnable(GL_SCISSOR_TEST);
        glScissor(kSize / 8, kSize / 8, 3 * kSize / 4, 3 * kSize / 4);
        glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Draw blue into the first multisampled color buffer
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS2);
        glScissor(kSize / 4, kSize / 4, kSize / 2, kSize / 2);
        glUniform4f(colorUniformLocation, 0.0f, 0.0f, 1.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Verify that the texture is red on the border, blue in the middle and green in between.
        GLFramebuffer fboSS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboSS);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0);
        ASSERT_GL_NO_ERROR();
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::red);
    
        EXPECT_PIXEL_COLOR_EQ(3 * kSize / 16, 3 * kSize / 16, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(13 * kSize / 16, 3 * kSize / 16, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(3 * kSize / 16, 13 * kSize / 16, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(13 * kSize / 16, 13 * kSize / 16, GLColor::green);
    
        EXPECT_PIXEL_COLOR_EQ(3 * kSize / 8, 3 * kSize / 8, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(5 * kSize / 8, 3 * kSize / 8, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(3 * kSize / 8, 5 * kSize / 8, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(5 * kSize / 8, 5 * kSize / 8, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::blue);
    
        ASSERT_GL_NO_ERROR();
    }
    
    void MultisampledRenderToTextureES31Test::drawCopyThenBlendAllAttachmentsMixed(bool useRenderbuffer)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture2"));
    
        GLint maxDrawBuffers = 0;
        glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
    
        // At least 4 draw buffers per GLES3.0 spec.
        ASSERT_GE(maxDrawBuffers, 4);
    
        // Maximum 8 draw buffers exposed by ANGLE.
        constexpr GLint kImplMaxDrawBuffers = 8;
        maxDrawBuffers                      = std::min(maxDrawBuffers, kImplMaxDrawBuffers);
    
        // Integer formats are mixed in which have different sample count limits. A framebuffer must
        // have the same sample count for all attachments.
        const GLint sampleCount = std::min(mTestSampleCount, mMaxIntegerSamples);
    
        constexpr const char *kDecl[kImplMaxDrawBuffers] = {
            "layout(location = 0) out vec4 out0;",  "layout(location = 1) out ivec4 out1;",
            "layout(location = 2) out uvec4 out2;", "layout(location = 3) out vec4 out3;",
            "layout(location = 4) out uvec4 out4;", "layout(location = 5) out ivec4 out5;",
            "layout(location = 6) out ivec4 out6;", "layout(location = 7) out vec4 out7;",
        };
    
        constexpr GLType kGLType[kImplMaxDrawBuffers] = {
            {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE},           {GL_RGBA32I, GL_RGBA_INTEGER, GL_INT},
            {GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT}, {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE},
            {GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT}, {GL_RGBA32I, GL_RGBA_INTEGER, GL_INT},
            {GL_RGBA32I, GL_RGBA_INTEGER, GL_INT},           {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE},
        };
    
        constexpr const char *kAssign1[kImplMaxDrawBuffers] = {
            "out0 = vec4(1.0f, 0.0f, 0.0f, 1.0f);",
            "out1 = ivec4(-19, 13, 123456, -654321);",
            "out2 = uvec4(98765, 43210, 2, 0);",
            "out3 = vec4(0.0f, 1.0f, 0.0f, 1.0f);",
            "out4 = uvec4(10101010, 2345, 0, 991);",
            "out5 = ivec4(615243, -948576, -222, 111);",
            "out6 = ivec4(-8127931, -1392781, 246810, 1214161820);",
            "out7 = vec4(0.0f, 0.0f, 1.0f, 1.0f);",
        };
    
        constexpr const char *kAssign2[kImplMaxDrawBuffers] = {
            "out0 = vec4(0.0f, 1.0f, 0.0f, 0.5f);",
            "out1 = ivec4(0, 0, 0, 0);",
            "out2 = uvec4(0, 0, 0, 0);",
            "out3 = vec4(0.0f, 0.0f, 1.0f, 0.5f);",
            "out4 = uvec4(0, 0, 0, 0);",
            "out5 = ivec4(0, 0, 0, 0);",
            "out6 = ivec4(0, 0, 0, 0);",
            "out7 = vec4(1.0f, 0.0f, 0.0f, 0.5f);",
        };
    
        // Generate the shaders, [0] for first draw and [1] for second.
        std::stringstream fsStr[2];
        for (unsigned int index = 0; index < 2; ++index)
        {
            fsStr[index] << R"(#version 300 es
    precision highp float;
    )";
    
            for (GLint drawBuffer = 0; drawBuffer < maxDrawBuffers; ++drawBuffer)
            {
                fsStr[index] << kDecl[drawBuffer] << "\n";
            }
    
            fsStr[index] << R"(void main()
    {
    )";
    
            const char *const *assign = index == 0 ? kAssign1 : kAssign2;
            for (GLint drawBuffer = 0; drawBuffer < maxDrawBuffers; ++drawBuffer)
            {
                fsStr[index] << assign[drawBuffer] << "\n";
            }
    
            fsStr[index] << "}\n";
        }
    
        constexpr GLsizei kSize = 64;
    
        setupCopyTexProgram();
    
        // Create multisampled framebuffer to draw into
        GLFramebuffer fboMS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
    
        GLTexture textureMS[kImplMaxDrawBuffers];
        GLRenderbuffer renderbufferMS[kImplMaxDrawBuffers];
        for (GLint drawBuffer = 0; drawBuffer < maxDrawBuffers; ++drawBuffer)
        {
            createAndAttachColorAttachment(useRenderbuffer, kSize, GL_COLOR_ATTACHMENT0 + drawBuffer,
                                           &kGLType[drawBuffer], sampleCount, &textureMS[drawBuffer],
                                           &renderbufferMS[drawBuffer]);
        }
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Setup programs
        ANGLE_GL_PROGRAM(drawProg, essl3_shaders::vs::Simple(), fsStr[0].str().c_str());
        ANGLE_GL_PROGRAM(blendProg, essl3_shaders::vs::Simple(), fsStr[1].str().c_str());
    
        constexpr GLenum kDrawBuffers[] = {
            GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3,
            GL_COLOR_ATTACHMENT4, GL_COLOR_ATTACHMENT5, GL_COLOR_ATTACHMENT6, GL_COLOR_ATTACHMENT7};
        glDrawBuffers(maxDrawBuffers, kDrawBuffers);
        ASSERT_GL_NO_ERROR();
    
        // Draw into the multisampled color buffers.
        glUseProgram(drawProg);
        drawQuad(drawProg, essl3_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Create a texture and copy from one of them.
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, kSize, kSize, 0);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        ASSERT_GL_NO_ERROR();
    
        // Blend color buffers.
        glUseProgram(blendProg);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        drawQuad(blendProg, essl3_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Verify texture colors.
        glReadBuffer(GL_COLOR_ATTACHMENT0);
        const GLColor kExpected0(127, 127, 0, 191);
        EXPECT_PIXEL_COLOR_NEAR(0, 0, kExpected0, 1);
        EXPECT_PIXEL_COLOR_NEAR(kSize - 1, 0, kExpected0, 1);
        EXPECT_PIXEL_COLOR_NEAR(0, kSize - 1, kExpected0, 1);
        EXPECT_PIXEL_COLOR_NEAR(kSize - 1, kSize - 1, kExpected0, 1);
    
        glReadBuffer(GL_COLOR_ATTACHMENT3);
        const GLColor kExpected3(0, 127, 127, 191);
        EXPECT_PIXEL_COLOR_NEAR(0, 0, kExpected3, 1);
        EXPECT_PIXEL_COLOR_NEAR(kSize - 1, 0, kExpected3, 1);
        EXPECT_PIXEL_COLOR_NEAR(0, kSize - 1, kExpected3, 1);
        EXPECT_PIXEL_COLOR_NEAR(kSize - 1, kSize - 1, kExpected3, 1);
    
        if (maxDrawBuffers > 7)
        {
            glReadBuffer(GL_COLOR_ATTACHMENT7);
            const GLColor kExpected7(127, 0, 127, 191);
            EXPECT_PIXEL_COLOR_NEAR(0, 0, kExpected7, 1);
            EXPECT_PIXEL_COLOR_NEAR(kSize - 1, 0, kExpected7, 1);
            EXPECT_PIXEL_COLOR_NEAR(0, kSize - 1, kExpected7, 1);
            EXPECT_PIXEL_COLOR_NEAR(kSize - 1, kSize - 1, kExpected7, 1);
        }
    
        // For completeness, verify that the texture used as copy target is red.
        verifyResults(texture, GLColor::red, kSize, 0, 0, kSize, kSize);
    
        ASSERT_GL_NO_ERROR();
    }
    
    // Draw, copy, then blend with 8 mixed format attachments.  The copy will make sure an implicit
    // resolve happens.  Regardless, the following draw should retain the data written by the first draw
    // command.
    TEST_P(MultisampledRenderToTextureES31Test, DrawCopyThenBlendAllAttachmentsMixed)
    {
        drawCopyThenBlendAllAttachmentsMixed(false);
    }
    
    // Same as DrawCopyThenBlendAllAttachmentsMixed but with renderbuffers.
    TEST_P(MultisampledRenderToTextureES31Test, RenderbufferDrawCopyThenBlendAllAttachmentsMixed)
    {
        // Linux Intel Vulkan returns 0 for GL_MAX_INTEGER_SAMPLES http://anglebug.com/5988
        ANGLE_SKIP_TEST_IF(IsLinux() && IsIntel() && IsVulkan());
    
        drawCopyThenBlendAllAttachmentsMixed(true);
    }
    
    void MultisampledRenderToTextureES3Test::renderbufferUnresolveColorAndDepthStencilThenTwoColors(
        bool withDepth,
        bool withStencil)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture2"));
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_draw_buffers"));
    
        // http://anglebug.com/5083
        ANGLE_SKIP_TEST_IF(IsWindows() && IsAMD() && IsVulkan());
    
        // http://anglebug.com/5096
        ANGLE_SKIP_TEST_IF(IsLinux() && IsIntel() && IsVulkan());
    
        // http://anglebug.com/5380
        ANGLE_SKIP_TEST_IF(IsLinux() && IsAMD() && IsVulkan());
    
        constexpr GLsizei kSize = 64;
    
        setupCopyTexProgram();
    
        GLFramebuffer fboColorAndDepthStencil;
        glBindFramebuffer(GL_FRAMEBUFFER, fboColorAndDepthStencil);
    
        // Create framebuffer to draw into, with both color and depth/stencil attachments.
        GLTexture color1;
        glBindTexture(GL_TEXTURE_2D, color1);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                                             color1, 0, 4);
    
        GLenum depthStencilFormat = GL_DEPTH24_STENCIL8;
        GLenum depthStencilTarget = GL_DEPTH_STENCIL_ATTACHMENT;
    
        ASSERT_TRUE(withDepth || withStencil);
        if (withDepth && !withStencil)
        {
            depthStencilFormat = GL_DEPTH_COMPONENT24;
            depthStencilTarget = GL_DEPTH_ATTACHMENT;
        }
        if (!withDepth && withStencil)
        {
            depthStencilFormat = GL_STENCIL_INDEX8;
            depthStencilTarget = GL_STENCIL_ATTACHMENT;
        }
    
        GLRenderbuffer depthStencil;
        glBindRenderbuffer(GL_RENDERBUFFER, depthStencil);
        glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, depthStencilFormat, kSize, kSize);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, depthStencilTarget, GL_RENDERBUFFER, depthStencil);
        ASSERT_GL_NO_ERROR();
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Set viewport and clear depth/stencil
        glViewport(0, 0, kSize, kSize);
        glClearDepthf(1);
        glClearStencil(0x55);
        glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
        // If depth is not cleared to 1, rendering would fail.
        if (withDepth)
        {
            glEnable(GL_DEPTH_TEST);
            glDepthFunc(GL_LESS);
        }
    
        // If stencil is not cleared to 0x55, rendering would fail.
        if (withStencil)
        {
            glEnable(GL_STENCIL_TEST);
            glStencilFunc(GL_EQUAL, 0x55, 0xFF);
            glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
            glStencilMask(0xFF);
        }
    
        // Set up program
        ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
        glUseProgram(drawColor);
        GLint colorUniformLocation =
            glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
        ASSERT_NE(colorUniformLocation, -1);
    
        // Draw red
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.25f);
        ASSERT_GL_NO_ERROR();
    
        // Create a texture and copy into it.
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, kSize, kSize, 0);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        ASSERT_GL_NO_ERROR();
    
        // Draw again into the framebuffer, this time blending.  This tests that both the color and
        // depth/stencil data are preserved after the resolve incurred by the copy above.
        glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 0.5f);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.0f);
        ASSERT_GL_NO_ERROR();
    
        // Verify that the texture is now yellow
        const GLColor kExpected(127, 127, 0, 191);
        EXPECT_PIXEL_COLOR_NEAR(0, 0, kExpected, 1);
        EXPECT_PIXEL_COLOR_NEAR(kSize - 1, 0, kExpected, 1);
        EXPECT_PIXEL_COLOR_NEAR(0, kSize - 1, kExpected, 1);
        EXPECT_PIXEL_COLOR_NEAR(kSize - 1, kSize - 1, kExpected, 1);
    
        // For completeness, verify that the texture used as copy target is red.
        verifyResults(texture, GLColor::red, kSize, 0, 0, kSize, kSize);
    
        // Now create a framebuffer with two color attachments and do something similar.  This makes
        // sure that the fact that both these framebuffers have 2 attachments does not cause confusion,
        // for example by having the unresolve shader generated for the first framebuffer used for the
        // second framebuffer.
        GLFramebuffer fboTwoColors;
        glBindFramebuffer(GL_FRAMEBUFFER, fboTwoColors);
    
        GLTexture color2;
        glBindTexture(GL_TEXTURE_2D, color2);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                                             color2, 0, 4);
    
        GLTexture color3;
        glBindTexture(GL_TEXTURE_2D, color3);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D,
                                             color3, 0, 4);
    
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        glDisable(GL_DEPTH_TEST);
        glDisable(GL_STENCIL_TEST);
        glDisable(GL_BLEND);
    
        constexpr GLenum kDrawBuffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
        glDrawBuffers(2, kDrawBuffers);
        glReadBuffer(GL_COLOR_ATTACHMENT1);
    
        // Setup program
        constexpr bool kBuffersEnabled[8] = {true, true};
    
        GLuint drawColorMRT;
        setupUniformColorProgramMultiRenderTarget(kBuffersEnabled, &drawColorMRT);
        glUseProgram(drawColorMRT);
        GLint colorUniformLocationMRT =
            glGetUniformLocation(drawColorMRT, angle::essl1_shaders::ColorUniform());
        ASSERT_NE(colorUniformLocationMRT, -1);
    
        // Draw blue
        glUniform4f(colorUniformLocationMRT, 0.0f, 0.0f, 1.0f, 1.0f);
        drawQuad(drawColorMRT, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Copy into texture
        glBindTexture(GL_TEXTURE_2D, texture);
        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, kSize, kSize, 0);
    
        // Blend.
        glUniform4f(colorUniformLocationMRT, 0.0f, 1.0f, 0.0f, 0.5f);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        drawQuad(drawColorMRT, essl1_shaders::PositionAttrib(), 0.0f);
        ASSERT_GL_NO_ERROR();
    
        // Verify that the texture is now cyan
        const GLColor kExpected2(0, 127, 127, 191);
        EXPECT_PIXEL_COLOR_NEAR(0, 0, kExpected2, 1);
        EXPECT_PIXEL_COLOR_NEAR(kSize - 1, 0, kExpected2, 1);
        EXPECT_PIXEL_COLOR_NEAR(0, kSize - 1, kExpected2, 1);
        EXPECT_PIXEL_COLOR_NEAR(kSize - 1, kSize - 1, kExpected2, 1);
    
        // For completeness, verify that the texture used as copy target is blue.
        verifyResults(texture, GLColor::blue, kSize, 0, 0, kSize, kSize);
    }
    
    // Draw, copy, then blend once on a framebuffer with color and depth attachments, and once with two
    // color attachments.  Tests that unresolve is done correctly on two framebuffers with the same
    // number of attachments, but differing in depth being there.  Note that this test doesn't apply to
    // depth/stencil textures as they are explicitly autoinvalidated between render passes.
    TEST_P(MultisampledRenderToTextureES3Test, RenderbufferUnresolveColorAndDepthThenTwoColors)
    {
        renderbufferUnresolveColorAndDepthStencilThenTwoColors(true, false);
    }
    
    // Similar to RenderbufferUnresolveColorAndDepthThenTwoColors, but with stencil.
    TEST_P(MultisampledRenderToTextureES3Test, RenderbufferUnresolveColorAndStencilThenTwoColors)
    {
        renderbufferUnresolveColorAndDepthStencilThenTwoColors(false, true);
    }
    
    // Similar to RenderbufferUnresolveColorAndDepthThenTwoColors, but with depth and stencil.
    TEST_P(MultisampledRenderToTextureES3Test, RenderbufferUnresolveColorAndDepthStencilThenTwoColors)
    {
        renderbufferUnresolveColorAndDepthStencilThenTwoColors(true, true);
    }
    
    // Make sure deferred clears are flushed correctly when the framebuffer switches between
    // needing unresolve and not needing it.
    TEST_P(MultisampledRenderToTextureES3Test, ClearThenMaskedClearFramebufferTest)
    {
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
    
        // TODO(anglebug:5655): This test is failing on Linux AMD Vulkan since it was added.
        ANGLE_SKIP_TEST_IF(IsLinux() && IsAMD() && IsVulkan());
    
        // TODO(geofflang) http://anglebug.com/2894
        ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGL() && IsPixel2());
    
        constexpr GLsizei kSize = 16;
    
        GLFramebuffer fboMS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
    
        // Create multisampled framebuffer to use as source.
        GLRenderbuffer depthMS;
        glBindRenderbuffer(GL_RENDERBUFFER, depthMS);
        glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT24, kSize, kSize);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthMS);
        ASSERT_GL_NO_ERROR();
    
        GLTexture textureMS;
        GLRenderbuffer renderbufferMS;
        createAndAttachColorAttachment(false, kSize, GL_COLOR_ATTACHMENT0, nullptr, mTestSampleCount,
                                       &textureMS, &renderbufferMS);
        EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Clear depth to 0.5 and color to green.
        glClearDepthf(0.5f);
        glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
        glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
        ASSERT_GL_NO_ERROR();
    
        // Break the render pass.
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    
        // Draw red into the multisampled color buffer.  An unresolve operation is needed.
        ANGLE_GL_PROGRAM(drawRed, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_EQUAL);
        drawQuad(drawRed, essl1_shaders::PositionAttrib(), 0.0f);
        ASSERT_GL_NO_ERROR();
    
        // Break the render pass.
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
    
        // Clear color to transparent blue.
        glClearColor(0.0f, 0.0f, 1.0f, 0.0f);
        glClear(GL_COLOR_BUFFER_BIT);
    
        // Clear both color and depth, with color masked.
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glColorMask(GL_FALSE, GL_TRUE, GL_FALSE, GL_TRUE);
        glClearDepthf(0.3f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
        // Make sure the result is blue.
        EXPECT_PIXEL_RECT_EQ(0, 0, kSize - 1, kSize - 1, GLColor::blue);
        ASSERT_GL_NO_ERROR();
    }
    
    ANGLE_INSTANTIATE_TEST_ES2_AND_ES3_AND_ES31(MultisampledRenderToTextureTest);
    
    GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MultisampledRenderToTextureES3Test);
    ANGLE_INSTANTIATE_TEST_ES3(MultisampledRenderToTextureES3Test);
    
    GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MultisampledRenderToTextureES31Test);
    ANGLE_INSTANTIATE_TEST_ES31(MultisampledRenderToTextureES31Test);
    }  // namespace