Edit

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

Branch :

  • Show log

    Commit

  • Author : Martin Radev
    Date : 2017-08-07 10:06:59
    Hash : 5c00d0db
    Message : D3D11: Handle Clear* commands for side-by-side framebuffers The patch modifies Clear11 to add support for clearing the color, depth and stencil attachments to a side-by-side framebuffer: - Color attachments are cleared through the command ClearView. - Depth and stencil attachments are cleared by drawing a quad on top of each view. BUG=angleproject:2062 TEST=angle_end2end_tests Change-Id: I5753a72222216a48cd954eb1219bc58f968735fc Reviewed-on: https://chromium-review.googlesource.com/603853 Commit-Queue: Martin Radev <mradev@nvidia.com> Reviewed-by: Geoff Lang <geofflang@chromium.org>

  • src/tests/gl_tests/FramebufferMultiviewTest.cpp
  • //
    // Copyright 2017 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.
    //
    // Framebuffer multiview tests:
    // The tests modify and examine the multiview state.
    //
    
    #include "test_utils/ANGLETest.h"
    #include "test_utils/gl_raii.h"
    
    using namespace angle;
    
    namespace
    {
    std::vector<GLenum> GetDrawBufferRange(size_t numColorAttachments)
    {
        std::vector<GLenum> drawBuffers(numColorAttachments);
        const size_t kBase = static_cast<size_t>(GL_COLOR_ATTACHMENT0);
        for (size_t i = 0u; i < drawBuffers.size(); ++i)
        {
            drawBuffers[i] = static_cast<GLenum>(kBase + i);
        }
        return drawBuffers;
    }
    }  // namespace
    
    class FramebufferMultiviewTest : public ANGLETest
    {
      protected:
        FramebufferMultiviewTest()
        {
            setWindowWidth(128);
            setWindowHeight(128);
            setWebGLCompatibilityEnabled(true);
        }
    
        void SetUp() override
        {
            ANGLETest::SetUp();
            glRequestExtensionANGLE = reinterpret_cast<PFNGLREQUESTEXTENSIONANGLEPROC>(
                eglGetProcAddress("glRequestExtensionANGLE"));
        }
    
        // Requests the ANGLE_multiview extension and returns true if the operation succeeds.
        bool requestMultiviewExtension()
        {
            if (extensionRequestable("GL_ANGLE_multiview"))
            {
                glRequestExtensionANGLE("GL_ANGLE_multiview");
            }
    
            if (!extensionEnabled("GL_ANGLE_multiview"))
            {
                std::cout << "Test skipped due to missing GL_ANGLE_multiview." << std::endl;
                return false;
            }
            return true;
        }
    
        PFNGLREQUESTEXTENSIONANGLEPROC glRequestExtensionANGLE = nullptr;
    };
    
    class FramebufferMultiviewClearTest : public FramebufferMultiviewTest
    {
      protected:
        FramebufferMultiviewClearTest() {}
    
        void initializeFBOs(size_t numColorBuffers, bool stencil, bool depth)
        {
            const std::vector<GLenum> &drawBuffers = GetDrawBufferRange(2);
    
            // Generate textures.
            mColorTex.resize(numColorBuffers);
            for (size_t i = 0u; i < numColorBuffers; ++i)
            {
                glBindTexture(GL_TEXTURE_2D, mColorTex[i]);
                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 4, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
            }
    
            if (stencil)
            {
                glBindTexture(GL_TEXTURE_2D, mStencilTex);
                glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, 4, 2, 0, GL_DEPTH_STENCIL,
                             GL_UNSIGNED_INT_24_8, nullptr);
            }
            else if (depth)
            {
                glBindTexture(GL_TEXTURE_2D, mDepthTex);
                glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, 4, 2, 0, GL_DEPTH_COMPONENT,
                             GL_FLOAT, nullptr);
            }
    
            // Generate multiview fbo and attach textures.
            const GLint kViewportOffsets[4] = {1, 0, 3, 0};
            glBindFramebuffer(GL_FRAMEBUFFER, mMultiviewFBO);
            const size_t kBase = static_cast<size_t>(GL_COLOR_ATTACHMENT0);
            for (size_t i = 0u; i < numColorBuffers; ++i)
            {
                glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER,
                                                             static_cast<GLenum>(kBase + i),
                                                             mColorTex[i], 0, 2, &kViewportOffsets[0]);
            }
    
            if (stencil)
            {
                glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER,
                                                             GL_DEPTH_STENCIL_ATTACHMENT, mStencilTex,
                                                             0, 2, &kViewportOffsets[0]);
            }
            else if (depth)
            {
                glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
                                                             mDepthTex, 0, 2, &kViewportOffsets[0]);
            }
            glDrawBuffers(static_cast<GLsizei>(drawBuffers.size()), drawBuffers.data());
    
            // Generate normal fbo and attach textures.
            glBindFramebuffer(GL_FRAMEBUFFER, mNonMultiviewFBO);
            for (size_t i = 0u; i < numColorBuffers; ++i)
            {
                glFramebufferTexture2D(GL_FRAMEBUFFER, static_cast<GLenum>(kBase + i), GL_TEXTURE_2D,
                                       mColorTex[i], 0);
            }
            if (stencil)
            {
                glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
                                       mStencilTex, 0);
            }
            else if (depth)
            {
                glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, mDepthTex,
                                       0);
            }
            glDrawBuffers(static_cast<GLsizei>(drawBuffers.size()), drawBuffers.data());
    
            ASSERT_GL_NO_ERROR();
        }
    
        void checkAlternatingColumnsOfRedAndGreenInFBO()
        {
            // column 0
            EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
            EXPECT_PIXEL_COLOR_EQ(0, 1, GLColor::red);
    
            // column 1
            EXPECT_PIXEL_COLOR_EQ(1, 0, GLColor::green);
            EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::green);
    
            // column 2
            EXPECT_PIXEL_COLOR_EQ(2, 0, GLColor::red);
            EXPECT_PIXEL_COLOR_EQ(2, 1, GLColor::red);
    
            // column 3
            EXPECT_PIXEL_COLOR_EQ(3, 0, GLColor::green);
            EXPECT_PIXEL_COLOR_EQ(3, 1, GLColor::green);
        }
    
        GLFramebuffer mMultiviewFBO;
        GLFramebuffer mNonMultiviewFBO;
        std::vector<GLTexture> mColorTex;
        GLTexture mDepthTex;
        GLTexture mStencilTex;
    };
    
    // Test that the framebuffer tokens introduced by ANGLE_multiview can be used query the framebuffer
    // state and that their corresponding default values are correctly set.
    TEST_P(FramebufferMultiviewTest, DefaultState)
    {
        if (!requestMultiviewExtension())
        {
            return;
        }
    
        GLFramebuffer fbo;
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    
        GLTexture tex;
        glBindTexture(GL_TEXTURE_2D, tex);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
    
        GLint numViews = -1;
        glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                              GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_ANGLE,
                                              &numViews);
        ASSERT_GL_NO_ERROR();
        EXPECT_EQ(1, numViews);
    
        GLint baseViewIndex = -1;
        glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                              GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_ANGLE,
                                              &baseViewIndex);
        ASSERT_GL_NO_ERROR();
        EXPECT_EQ(0, baseViewIndex);
    
        GLint multiviewLayout = GL_FRAMEBUFFER_MULTIVIEW_SIDE_BY_SIDE_ANGLE;
        glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                              GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_MULTIVIEW_LAYOUT_ANGLE,
                                              &multiviewLayout);
        ASSERT_GL_NO_ERROR();
        EXPECT_EQ(GL_NONE, multiviewLayout);
    
        GLint viewportOffsets[2] = {-1, -1};
        glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                              GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_VIEWPORT_OFFSETS_ANGLE,
                                              &viewportOffsets[0]);
        ASSERT_GL_NO_ERROR();
        EXPECT_EQ(0, viewportOffsets[0]);
        EXPECT_EQ(0, viewportOffsets[1]);
    }
    
    // Test that without having the ANGLE_multiview extension, querying for the framebuffer state using
    // the ANGLE_multiview tokens results in an INVALID_ENUM error.
    TEST_P(FramebufferMultiviewTest, NegativeFramebufferStateQueries)
    {
        GLFramebuffer fbo;
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    
        GLTexture tex;
        glBindTexture(GL_TEXTURE_2D, tex);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
    
        GLint numViews = -1;
        glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                              GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_ANGLE,
                                              &numViews);
        EXPECT_GL_ERROR(GL_INVALID_ENUM);
    
        GLint baseViewIndex = -1;
        glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                              GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_ANGLE,
                                              &baseViewIndex);
        EXPECT_GL_ERROR(GL_INVALID_ENUM);
    
        GLint multiviewLayout = GL_FRAMEBUFFER_MULTIVIEW_SIDE_BY_SIDE_ANGLE;
        glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                              GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_MULTIVIEW_LAYOUT_ANGLE,
                                              &multiviewLayout);
        EXPECT_GL_ERROR(GL_INVALID_ENUM);
    
        GLint viewportOffsets[2] = {-1, -1};
        glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                              GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_VIEWPORT_OFFSETS_ANGLE,
                                              &viewportOffsets[0]);
        EXPECT_GL_ERROR(GL_INVALID_ENUM);
    }
    
    // Test that the correct errors are generated whenever glFramebufferTextureMultiviewSideBySideANGLE
    // is called with invalid arguments.
    TEST_P(FramebufferMultiviewTest, InvalidMultiviewSideBySideArguments)
    {
        if (!requestMultiviewExtension())
        {
            return;
        }
    
        GLFramebuffer fbo;
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    
        GLTexture tex;
        glBindTexture(GL_TEXTURE_2D, tex);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        // Negative offsets.
        GLint viewportOffsets[2] = {-1};
        glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0, 1,
                                                     &viewportOffsets[0]);
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    
        // Negative number of views.
        viewportOffsets[0] = 0;
        viewportOffsets[1] = 0;
        glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0, -1,
                                                     &viewportOffsets[0]);
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    }
    
    // Test that the correct errors are generated whenever glFramebufferTextureMultiviewLayeredANGLE is
    // called with invalid arguments.
    TEST_P(FramebufferMultiviewTest, InvalidMultiviewLayeredArguments)
    {
        if (!requestMultiviewExtension())
        {
            return;
        }
    
        GLFramebuffer fbo;
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    
        GLTexture tex;
        glBindTexture(GL_TEXTURE_2D_ARRAY, tex);
        glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, 1, 1, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        ASSERT_GL_NO_ERROR();
    
        // Negative base view index.
        glFramebufferTextureMultiviewLayeredANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0, -1, 1);
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    
        // baseViewIndex + numViews is greater than MAX_TEXTURE_LAYERS.
        GLint maxTextureLayers = 0;
        glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &maxTextureLayers);
        glFramebufferTextureMultiviewLayeredANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0,
                                                  maxTextureLayers, 1);
        EXPECT_GL_ERROR(GL_INVALID_VALUE);
    }
    
    // Test that an INVALID_OPERATION error is generated whenever the ANGLE_multiview extension is not
    // available.
    TEST_P(FramebufferMultiviewTest, ExtensionNotAvailableCheck)
    {
        GLFramebuffer fbo;
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    
        GLTexture tex;
        glBindTexture(GL_TEXTURE_2D, tex);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        ASSERT_GL_NO_ERROR();
        const GLint kViewportOffsets[2] = {0};
        glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0, 1,
                                                     &kViewportOffsets[0]);
        EXPECT_GL_ERROR(GL_INVALID_OPERATION);
    }
    
    // Test that glFramebufferTextureMultiviewSideBySideANGLE modifies the internal multiview state.
    TEST_P(FramebufferMultiviewTest, ModifySideBySideState)
    {
        if (!requestMultiviewExtension())
        {
            return;
        }
    
        GLFramebuffer fbo;
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    
        GLTexture tex;
        glBindTexture(GL_TEXTURE_2D, tex);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        const GLint kViewportOffsets[4] = {0, 0, 1, 2};
        glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0, 2,
                                                     &kViewportOffsets[0]);
        ASSERT_GL_NO_ERROR();
    
        GLint numViews = -1;
        glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                              GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_ANGLE,
                                              &numViews);
        ASSERT_GL_NO_ERROR();
        EXPECT_EQ(2, numViews);
    
        GLint baseViewIndex = -1;
        glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                              GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_ANGLE,
                                              &baseViewIndex);
        ASSERT_GL_NO_ERROR();
        EXPECT_EQ(0, baseViewIndex);
    
        GLint multiviewLayout = GL_NONE;
        glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                              GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_MULTIVIEW_LAYOUT_ANGLE,
                                              &multiviewLayout);
        ASSERT_GL_NO_ERROR();
        EXPECT_EQ(GL_FRAMEBUFFER_MULTIVIEW_SIDE_BY_SIDE_ANGLE, multiviewLayout);
    
        GLint internalViewportOffsets[4] = {-1};
        glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                              GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_VIEWPORT_OFFSETS_ANGLE,
                                              &internalViewportOffsets[0]);
        ASSERT_GL_NO_ERROR();
        for (size_t i = 0u; i < 4u; ++i)
        {
            EXPECT_EQ(kViewportOffsets[i], internalViewportOffsets[i]);
        }
    }
    
    // Test framebuffer completeness status of a side-by-side framebuffer with color and depth
    // attachments.
    TEST_P(FramebufferMultiviewTest, IncompleteViewTargetsSideBySide)
    {
        if (!requestMultiviewExtension())
        {
            return;
        }
    
        GLFramebuffer fbo;
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    
        GLTexture tex;
        glBindTexture(GL_TEXTURE_2D, tex);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        const GLint kViewportOffsets[4]      = {0, 0, 2, 0};
        const GLint kOtherViewportOffsets[4] = {2, 0, 4, 0};
    
        // Set the 0th attachment and keep it as it is till the end of the test. The 1st or depth
        // attachment will be modified to change the framebuffer's status.
        glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0, 2,
                                                     &kViewportOffsets[0]);
        ASSERT_GL_NO_ERROR();
    
        // Color attachment 1.
        {
            GLTexture otherTex;
            glBindTexture(GL_TEXTURE_2D, otherTex);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
            // Test framebuffer completeness when the number of views differ.
            glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, otherTex,
                                                         0, 1, &kViewportOffsets[0]);
            ASSERT_GL_NO_ERROR();
            EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_ANGLE,
                             glCheckFramebufferStatus(GL_FRAMEBUFFER));
    
            // Test framebuffer completeness when the viewport offsets differ.
            glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, otherTex,
                                                         0, 2, &kOtherViewportOffsets[0]);
            ASSERT_GL_NO_ERROR();
            EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_ANGLE,
                             glCheckFramebufferStatus(GL_FRAMEBUFFER));
    
            // Test framebuffer completeness when attachment layouts differ.
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, otherTex, 0);
            ASSERT_GL_NO_ERROR();
            EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_ANGLE,
                             glCheckFramebufferStatus(GL_FRAMEBUFFER));
    
            // Test that framebuffer is complete when the number of views, viewport offsets and layouts
            // are the same.
            glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, otherTex,
                                                         0, 2, &kViewportOffsets[0]);
            ASSERT_GL_NO_ERROR();
            EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
    
            // Reset attachment 1
            glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, 0, 0, 1,
                                                         &kViewportOffsets[0]);
        }
    
        // Depth attachment.
        {
            GLTexture depthTex;
            glBindTexture(GL_TEXTURE_2D, depthTex);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, 1, 1, 0, GL_DEPTH_COMPONENT, GL_FLOAT,
                         nullptr);
    
            // Test framebuffer completeness when the number of views differ.
            glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTex,
                                                         0, 1, &kViewportOffsets[0]);
            ASSERT_GL_NO_ERROR();
            EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_ANGLE,
                             glCheckFramebufferStatus(GL_FRAMEBUFFER));
    
            // Test framebuffer completeness when the viewport offsets differ.
            glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTex,
                                                         0, 2, &kOtherViewportOffsets[0]);
            ASSERT_GL_NO_ERROR();
            EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_ANGLE,
                             glCheckFramebufferStatus(GL_FRAMEBUFFER));
    
            // Test framebuffer completeness when attachment layouts differ.
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTex, 0);
            ASSERT_GL_NO_ERROR();
            EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_ANGLE,
                             glCheckFramebufferStatus(GL_FRAMEBUFFER));
    
            // Test that framebuffer is complete when the number of views, viewport offsets and layouts
            // are the same.
            glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTex,
                                                         0, 2, &kViewportOffsets[0]);
            ASSERT_GL_NO_ERROR();
            EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
        }
    }
    
    // Test that the active read framebuffer cannot be read from through glCopyTex* if it has multi-view
    // attachments.
    TEST_P(FramebufferMultiviewTest, InvalidCopyTex)
    {
        if (!requestMultiviewExtension())
        {
            return;
        }
    
        GLFramebuffer fbo;
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    
        GLTexture tex;
        glBindTexture(GL_TEXTURE_2D, tex);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        const GLint kViewportOffsets[2] = {0};
        glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0, 1,
                                                     &kViewportOffsets[0]);
        ASSERT_GL_NO_ERROR();
    
        // Test glCopyTexImage2D and glCopyTexSubImage2D.
        {
            GLTexture tex2;
            glBindTexture(GL_TEXTURE_2D, tex2);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
            glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, 1, 1, 0);
            EXPECT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION);
    
            glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1);
            EXPECT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION);
        }
    
        // Test glCopyTexSubImage3D.
        {
            GLTexture tex2;
            glBindTexture(GL_TEXTURE_3D, tex2);
            glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
            glCopyTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, 0, 0, 1, 1);
            EXPECT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION);
        }
    }
    
    // Test that glBlitFramebuffer generates an invalid framebuffer operation when either the current
    // draw framebuffer, or current read framebuffer have multiview attachments.
    TEST_P(FramebufferMultiviewTest, InvalidBlit)
    {
        if (!requestMultiviewExtension())
        {
            return;
        }
    
        GLFramebuffer fbo;
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    
        GLTexture tex;
        glBindTexture(GL_TEXTURE_2D, tex);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        const GLint kViewportOffsets[2] = {0};
        glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0, 1,
                                                     &kViewportOffsets[0]);
        ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
        ASSERT_GL_NO_ERROR();
    
        // Blit with the active read framebuffer having multiview attachments.
        {
            glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
            glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
            glBlitFramebuffer(0, 0, 1, 1, 0, 0, 1, 1, GL_COLOR_BUFFER_BIT, GL_NEAREST);
            EXPECT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION);
        }
    
        // Blit with the active draw framebuffer having multiview attachments.
        {
            glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
            glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
            glBlitFramebuffer(0, 0, 1, 1, 0, 0, 1, 1, GL_COLOR_BUFFER_BIT, GL_NEAREST);
            EXPECT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION);
        }
    }
    
    // Test that glReadPixels generates an invalid framebuffer operation error if the current read
    // framebuffer has a multi-view layout.
    TEST_P(FramebufferMultiviewTest, InvalidReadPixels)
    {
        if (!requestMultiviewExtension())
        {
            return;
        }
    
        GLFramebuffer fbo;
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    
        GLTexture tex;
        glBindTexture(GL_TEXTURE_2D, tex);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        const GLint kViewportOffsets[2] = {0};
        glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0, 1,
                                                     &kViewportOffsets[0]);
        ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
        ASSERT_GL_NO_ERROR();
    
        GLColor pixelColor;
        glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixelColor.R);
        EXPECT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION);
    }
    
    // Test that glClear clears only the contents of each view if the scissor test is enabled.
    TEST_P(FramebufferMultiviewClearTest, SideBySideColorBufferClear)
    {
        if (!requestMultiviewExtension())
        {
            return;
        }
    
        initializeFBOs(1, false, false);
    
        // Clear the contents of the texture.
        glBindFramebuffer(GL_FRAMEBUFFER, mNonMultiviewFBO);
        glClearColor(1, 0, 0, 1);
        glClear(GL_COLOR_BUFFER_BIT);
    
        // Bind and specify viewport/scissor dimensions for each view.
        glBindFramebuffer(GL_FRAMEBUFFER, mMultiviewFBO);
        glViewport(0, 0, 1, 2);
        glScissor(0, 0, 1, 2);
        glEnable(GL_SCISSOR_TEST);
    
        glClearColor(0, 1, 0, 1);
        glClear(GL_COLOR_BUFFER_BIT);
    
        glBindFramebuffer(GL_FRAMEBUFFER, mNonMultiviewFBO);
    
        checkAlternatingColumnsOfRedAndGreenInFBO();
    }
    
    // Test that glFramebufferTextureMultiviewLayeredANGLE modifies the internal multiview state.
    TEST_P(FramebufferMultiviewTest, ModifyLayeredState)
    {
        if (!requestMultiviewExtension())
        {
            return;
        }
    
        GLFramebuffer multiviewFBO;
        glBindFramebuffer(GL_FRAMEBUFFER, multiviewFBO);
    
        GLTexture tex;
        glBindTexture(GL_TEXTURE_2D_ARRAY, tex);
        glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, 1, 1, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        ASSERT_GL_NO_ERROR();
    
        glFramebufferTextureMultiviewLayeredANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0, 1, 2);
        ASSERT_GL_NO_ERROR();
    
        GLint numViews = -1;
        glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                              GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_ANGLE,
                                              &numViews);
        ASSERT_GL_NO_ERROR();
        EXPECT_EQ(2, numViews);
    
        GLint baseViewIndex = -1;
        glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                              GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_ANGLE,
                                              &baseViewIndex);
        ASSERT_GL_NO_ERROR();
        EXPECT_EQ(1, baseViewIndex);
    
        GLint multiviewLayout = GL_NONE;
        glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                              GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_MULTIVIEW_LAYOUT_ANGLE,
                                              &multiviewLayout);
        ASSERT_GL_NO_ERROR();
        EXPECT_EQ(GL_FRAMEBUFFER_MULTIVIEW_LAYERED_ANGLE, multiviewLayout);
    
        GLint internalViewportOffsets[2] = {-1};
        glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                              GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_VIEWPORT_OFFSETS_ANGLE,
                                              &internalViewportOffsets[0]);
        ASSERT_GL_NO_ERROR();
        EXPECT_EQ(0, internalViewportOffsets[0]);
        EXPECT_EQ(0, internalViewportOffsets[1]);
    }
    
    // Test framebuffer completeness status of a layered framebuffer with color attachments.
    TEST_P(FramebufferMultiviewTest, IncompleteViewTargetsLayered)
    {
        if (!requestMultiviewExtension())
        {
            return;
        }
    
        GLFramebuffer fbo;
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    
        GLTexture tex;
        glBindTexture(GL_TEXTURE_2D_ARRAY, tex);
        glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, 1, 1, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        // Set the 0th attachment and keep it as it is till the end of the test. The 1st color
        // attachment will be modified to change the framebuffer's status.
        glFramebufferTextureMultiviewLayeredANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0, 0, 2);
        ASSERT_GL_NO_ERROR();
    
        GLTexture otherTexLayered;
        glBindTexture(GL_TEXTURE_2D_ARRAY, otherTexLayered);
        glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, 1, 1, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        // Test framebuffer completeness when the base view index differs.
        glFramebufferTextureMultiviewLayeredANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, otherTexLayered,
                                                  0, 1, 2);
        ASSERT_GL_NO_ERROR();
        EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_ANGLE,
                         glCheckFramebufferStatus(GL_FRAMEBUFFER));
    
        // Test framebuffer completeness when the 1st attachment has a side-by-side layout.
        const int kViewportOffsets[4] = {0, 0, 0, 0};
        GLTexture otherTex2D;
        glBindTexture(GL_TEXTURE_2D, otherTex2D);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 4, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, otherTex2D,
                                                     0, 2, kViewportOffsets);
        ASSERT_GL_NO_ERROR();
        EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_ANGLE,
                         glCheckFramebufferStatus(GL_FRAMEBUFFER));
    
        // Test framebuffer completeness when the 1st attachment has a normal layout.
        glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, otherTexLayered, 0, 0);
        ASSERT_GL_NO_ERROR();
        EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_ANGLE,
                         glCheckFramebufferStatus(GL_FRAMEBUFFER));
    
        // Test that framebuffer is complete when the number of views, base view index and layouts are
        // the same.
        glFramebufferTextureMultiviewLayeredANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, otherTexLayered,
                                                  0, 0, 2);
        ASSERT_GL_NO_ERROR();
        EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
    }
    
    // Test that glClear clears all of the contents if the scissor test is disabled.
    TEST_P(FramebufferMultiviewClearTest, SideBySideClearWithDisabledScissorTest)
    {
        if (!requestMultiviewExtension())
        {
            return;
        }
    
        initializeFBOs(1, false, false);
    
        // Clear the contents of the texture.
        glBindFramebuffer(GL_FRAMEBUFFER, mNonMultiviewFBO);
        glClearColor(0, 0, 0, 1);
        glClear(GL_COLOR_BUFFER_BIT);
    
        // Bind and specify viewport/scissor dimensions for each view.
        glBindFramebuffer(GL_FRAMEBUFFER, mMultiviewFBO);
        glViewport(0, 0, 1, 2);
        glScissor(0, 0, 1, 2);
    
        glClearColor(1, 0, 0, 1);
        glClear(GL_COLOR_BUFFER_BIT);
    
        glBindFramebuffer(GL_FRAMEBUFFER, mNonMultiviewFBO);
    
        for (int i = 0; i < 4; ++i)
        {
            for (int j = 0; j < 2; ++j)
            {
                EXPECT_PIXEL_COLOR_EQ(i, j, GLColor::red);
            }
        }
    }
    
    // Test that glClear clears the depth buffer of each view.
    TEST_P(FramebufferMultiviewClearTest, SideBySideDepthBufferClear)
    {
        if (!requestMultiviewExtension())
        {
            return;
        }
    
        // Create program to draw a quad.
        const std::string &vs =
            "#version 300 es\n"
            "in vec3 vPos;\n"
            "void main(){\n"
            "   gl_Position = vec4(vPos, 1.);\n"
            "}\n";
        const std::string &fs =
            "#version 300 es\n"
            "precision mediump float;\n"
            "uniform vec3 uCol;\n"
            "out vec4 col;\n"
            "void main(){\n"
            "   col = vec4(uCol,1.);\n"
            "}\n";
        ANGLE_GL_PROGRAM(program, vs, fs);
        glUseProgram(program);
        GLuint mColorUniformLoc = glGetUniformLocation(program, "uCol");
    
        initializeFBOs(1, false, true);
        glEnable(GL_DEPTH_TEST);
    
        // Clear the contents of the texture.
        glBindFramebuffer(GL_FRAMEBUFFER, mNonMultiviewFBO);
        glClearColor(0, 0, 0, 0);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
        // Dirty the depth and color buffers.
        glUniform3f(mColorUniformLoc, 1.0f, 0.0f, 0.0f);
        drawQuad(program, "vPos", 0.0f, 1.0f, true);
    
        // Switch to the multi-view framebuffer and clear only the rectangles covered by the views.
        glBindFramebuffer(GL_FRAMEBUFFER, mMultiviewFBO);
        glViewport(0, 0, 1, 2);
        glScissor(0, 0, 1, 2);
        glEnable(GL_SCISSOR_TEST);
        glClear(GL_DEPTH_BUFFER_BIT);
    
        // Draw a fullscreen quad to fill the cleared regions.
        glBindFramebuffer(GL_FRAMEBUFFER, mNonMultiviewFBO);
        glViewport(0, 0, 4, 2);
        glScissor(0, 0, 4, 2);
        glUniform3f(mColorUniformLoc, 0.0f, 1.0f, 0.0f);
        drawQuad(program, "vPos", 0.5f, 1.0f, true);
    
        checkAlternatingColumnsOfRedAndGreenInFBO();
    }
    
    // Test that glClear clears the stencil buffer of each view.
    TEST_P(FramebufferMultiviewClearTest, SideBySideStencilBufferClear)
    {
        if (!requestMultiviewExtension())
        {
            return;
        }
    
        // Create program to draw a quad.
        const std::string &vs =
            "#version 300 es\n"
            "in vec3 vPos;\n"
            "void main(){\n"
            "   gl_Position = vec4(vPos, 1.);\n"
            "}\n";
        const std::string &fs =
            "#version 300 es\n"
            "precision mediump float;\n"
            "uniform vec3 uCol;\n"
            "out vec4 col;\n"
            "void main(){\n"
            "   col = vec4(uCol,1.);\n"
            "}\n";
        ANGLE_GL_PROGRAM(program, vs, fs);
        glUseProgram(program);
        GLuint mColorUniformLoc = glGetUniformLocation(program, "uCol");
    
        initializeFBOs(1, true, false);
        glEnable(GL_STENCIL_TEST);
        glDisable(GL_DEPTH_TEST);
    
        // Set clear values and clear the whole framebuffer.
        glBindFramebuffer(GL_FRAMEBUFFER, mNonMultiviewFBO);
        glClearColor(0, 0, 0, 0);
        glClearStencil(0);
        glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
        // Update stencil test to always replace the stencil value with 0xFF.
        glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
        glStencilFunc(GL_ALWAYS, 0xFF, 0);
    
        // Draw a quad which covers the whole texture.
        glUniform3f(mColorUniformLoc, 1.0f, 0.0f, 0.0f);
        drawQuad(program, "vPos", 0.0f, 1.0f, true);
    
        // Switch to multiview framebuffer and clear portions of the texture.
        glBindFramebuffer(GL_FRAMEBUFFER, mMultiviewFBO);
        glViewport(0, 0, 1, 2);
        glScissor(0, 0, 1, 2);
        glEnable(GL_SCISSOR_TEST);
        glClear(GL_STENCIL_BUFFER_BIT);
    
        // Draw a fullscreen quad, but adjust the stencil function so that only the cleared regions pass
        // the test.
        glBindFramebuffer(GL_FRAMEBUFFER, mNonMultiviewFBO);
        glViewport(0, 0, 4, 2);
        glScissor(0, 0, 4, 2);
        glStencilFunc(GL_EQUAL, 0x00, 0xFF);
        glUniform3f(mColorUniformLoc, 0.0f, 1.0f, 0.0f);
        drawQuad(program, "vPos", 0.0f, 1.0f, true);
    
        checkAlternatingColumnsOfRedAndGreenInFBO();
    }
    
    // Test that glClearBufferf clears the color buffer of each view.
    TEST_P(FramebufferMultiviewClearTest, SideBySideClearBufferF)
    {
        if (!requestMultiviewExtension())
        {
            return;
        }
    
        initializeFBOs(2, false, false);
    
        // Clear the contents of the texture.
        glBindFramebuffer(GL_FRAMEBUFFER, mNonMultiviewFBO);
        glClearColor(1, 0, 0, 1);
        glClear(GL_COLOR_BUFFER_BIT);
    
        // Bind and specify viewport/scissor dimensions for each view.
        glBindFramebuffer(GL_FRAMEBUFFER, mMultiviewFBO);
        glViewport(0, 0, 1, 2);
        glScissor(0, 0, 1, 2);
        glEnable(GL_SCISSOR_TEST);
    
        float kClearValues[4] = {0.f, 1.f, 0.f, 1.f};
        glClearBufferfv(GL_COLOR, 1, kClearValues);
    
        glBindFramebuffer(GL_FRAMEBUFFER, mNonMultiviewFBO);
    
        // Check that glClearBufferfv has not modified the 0th color attachment.
        glReadBuffer(GL_COLOR_ATTACHMENT0);
        for (int i = 0; i < 4; ++i)
        {
            for (int j = 0; j < 2; ++j)
            {
                EXPECT_PIXEL_COLOR_EQ(i, j, GLColor::red);
            }
        }
    
        // Check that glClearBufferfv has correctly modified the 1th color attachment.
        glReadBuffer(GL_COLOR_ATTACHMENT1);
        checkAlternatingColumnsOfRedAndGreenInFBO();
    }
    
    ANGLE_INSTANTIATE_TEST(FramebufferMultiviewTest, ES3_OPENGL());
    ANGLE_INSTANTIATE_TEST(FramebufferMultiviewClearTest, ES3_OPENGL(), ES3_D3D11());