Allow reads from a multiview framebuffer with one view

It's safe to read from a multiview framebuffer if it is layered and
has just one view. The native OVR_multiview spec supports this as
well.

BUG=angleproject:2062
TEST=angle_end2end_tests

Change-Id: I04e1364390574075f7e06e39a64e3bf05a539a05
Reviewed-on: https://chromium-review.googlesource.com/1156509
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/tests/gl_tests/FramebufferMultiviewTest.cpp b/src/tests/gl_tests/FramebufferMultiviewTest.cpp
index 182a5ae..5552ba0 100644
--- a/src/tests/gl_tests/FramebufferMultiviewTest.cpp
+++ b/src/tests/gl_tests/FramebufferMultiviewTest.cpp
@@ -53,7 +53,6 @@
 
         if (!extensionEnabled("GL_ANGLE_multiview"))
         {
-            std::cout << "Test skipped due to missing GL_ANGLE_multiview." << std::endl;
             return false;
         }
         return true;
@@ -278,10 +277,7 @@
 // framebuffer state and that their corresponding default values are correctly set.
 TEST_P(FramebufferMultiviewTest, DefaultState)
 {
-    if (!requestMultiviewExtension())
-    {
-        return;
-    }
+    ANGLE_SKIP_TEST_IF(!requestMultiviewExtension());
 
     GLFramebuffer fbo;
     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
@@ -362,10 +358,7 @@
 // is called with invalid arguments.
 TEST_P(FramebufferMultiviewTest, InvalidMultiviewSideBySideArguments)
 {
-    if (!requestMultiviewExtension())
-    {
-        return;
-    }
+    ANGLE_SKIP_TEST_IF(!requestMultiviewExtension());
 
     GLFramebuffer fbo;
     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
@@ -392,10 +385,7 @@
 // called with invalid arguments.
 TEST_P(FramebufferMultiviewTest, InvalidMultiviewLayeredArguments)
 {
-    if (!requestMultiviewExtension())
-    {
-        return;
-    }
+    ANGLE_SKIP_TEST_IF(!requestMultiviewExtension());
 
     GLFramebuffer fbo;
     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
@@ -438,10 +428,7 @@
 // Test that glFramebufferTextureMultiviewSideBySideANGLE modifies the internal multiview state.
 TEST_P(FramebufferMultiviewTest, ModifySideBySideState)
 {
-    if (!requestMultiviewExtension())
-    {
-        return;
-    }
+    ANGLE_SKIP_TEST_IF(!requestMultiviewExtension());
 
     GLFramebuffer fbo;
     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
@@ -491,10 +478,7 @@
 // attachments.
 TEST_P(FramebufferMultiviewTest, IncompleteViewTargetsSideBySide)
 {
-    if (!requestMultiviewExtension())
-    {
-        return;
-    }
+    ANGLE_SKIP_TEST_IF(!requestMultiviewExtension());
 
     GLFramebuffer fbo;
     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
@@ -587,13 +571,10 @@
 }
 
 // Test that the active read framebuffer cannot be read from through glCopyTex* if it has multi-view
-// attachments.
+// attachments with a side-by-side layout.
 TEST_P(FramebufferMultiviewTest, InvalidCopyTex)
 {
-    if (!requestMultiviewExtension())
-    {
-        return;
-    }
+    ANGLE_SKIP_TEST_IF(!requestMultiviewExtension());
 
     GLFramebuffer fbo;
     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
@@ -631,14 +612,77 @@
     }
 }
 
+// Test that the active read framebuffer can be read with glCopyTex* if it only has one layered
+// view.
+TEST_P(FramebufferMultiviewTest, CopyTex)
+{
+    ANGLE_SKIP_TEST_IF(!requestMultiviewExtension());
+
+    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);
+
+    glFramebufferTextureMultiviewLayeredANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0, 0, 1);
+    ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
+    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);
+
+        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+        glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+        glClear(GL_COLOR_BUFFER_BIT);
+
+        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, 1, 1, 0);
+        ASSERT_GL_NO_ERROR();
+
+        // Test texture contents.
+        glBindFramebuffer(GL_FRAMEBUFFER, 0);
+        draw2DTexturedQuad(0.0f, 1.0f, true);
+        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
+
+        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+        glClearColor(0.0f, 1.0f, 1.0f, 1.0f);
+        glClear(GL_COLOR_BUFFER_BIT);
+
+        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1);
+        ASSERT_GL_NO_ERROR();
+
+        glBindFramebuffer(GL_FRAMEBUFFER, 0);
+        draw2DTexturedQuad(0.0f, 1.0f, true);
+        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::cyan);
+    }
+
+    // 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);
+
+        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+        glClearColor(1.0f, 1.0f, 0.0f, 1.0f);
+        glClear(GL_COLOR_BUFFER_BIT);
+        glCopyTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, 0, 0, 1, 1);
+        ASSERT_GL_NO_ERROR();
+
+        glBindFramebuffer(GL_FRAMEBUFFER, 0);
+        draw3DTexturedQuad(0.0f, 1.0f, true, 0.0f);
+        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::yellow);
+    }
+}
+
 // Test that glBlitFramebuffer generates an invalid framebuffer operation when either the current
-// draw framebuffer, or current read framebuffer have multiview attachments.
+// draw framebuffer, or current read framebuffer have multiview attachments with a side-by-side
+// layout.
 TEST_P(FramebufferMultiviewTest, InvalidBlit)
 {
-    if (!requestMultiviewExtension())
-    {
-        return;
-    }
+    ANGLE_SKIP_TEST_IF(!requestMultiviewExtension());
 
     GLFramebuffer fbo;
     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
@@ -670,14 +714,42 @@
     }
 }
 
+// Test that glBlitFramebuffer succeeds if the current read framebuffer has just one layered view.
+TEST_P(FramebufferMultiviewTest, Blit)
+{
+    ANGLE_SKIP_TEST_IF(!requestMultiviewExtension());
+
+    glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    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);
+
+    glFramebufferTextureMultiviewLayeredANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0, 0, 1);
+    ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
+    ASSERT_GL_NO_ERROR();
+
+    glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    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);
+    ASSERT_GL_NO_ERROR();
+
+    glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
+}
+
 // Test that glReadPixels generates an invalid framebuffer operation error if the current read
-// framebuffer has a multi-view layout.
+// framebuffer has a side-by-side multi-view layout.
 TEST_P(FramebufferMultiviewTest, InvalidReadPixels)
 {
-    if (!requestMultiviewExtension())
-    {
-        return;
-    }
+    ANGLE_SKIP_TEST_IF(!requestMultiviewExtension());
 
     GLFramebuffer fbo;
     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
@@ -697,13 +769,35 @@
     EXPECT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION);
 }
 
+// Test that glReadPixels succeeds from a layered multiview framebuffer with just one view.
+TEST_P(FramebufferMultiviewTest, ReadPixels)
+{
+    ANGLE_SKIP_TEST_IF(!requestMultiviewExtension());
+
+    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);
+
+    glFramebufferTextureMultiviewLayeredANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0, 0, 1);
+    ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
+    ASSERT_GL_NO_ERROR();
+
+    glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    GLColor pixelColor;
+    glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixelColor.R);
+    ASSERT_GL_NO_ERROR();
+    EXPECT_COLOR_NEAR(GLColor::green, pixelColor, 2);
+}
+
 // Test that glClear clears only the contents of each view if the scissor test is enabled.
 TEST_P(FramebufferMultiviewSideBySideClearTest, ColorBufferClear)
 {
-    if (!requestMultiviewExtension())
-    {
-        return;
-    }
+    ANGLE_SKIP_TEST_IF(!requestMultiviewExtension());
 
     initializeFBOs(1, false, false);
 
@@ -729,10 +823,7 @@
 // Test that glFramebufferTextureMultiviewLayeredANGLE modifies the internal multiview state.
 TEST_P(FramebufferMultiviewTest, ModifyLayeredState)
 {
-    if (!requestMultiviewExtension())
-    {
-        return;
-    }
+    ANGLE_SKIP_TEST_IF(!requestMultiviewExtension());
 
     GLFramebuffer multiviewFBO;
     glBindFramebuffer(GL_FRAMEBUFFER, multiviewFBO);
@@ -778,10 +869,7 @@
 // Test framebuffer completeness status of a layered framebuffer with color attachments.
 TEST_P(FramebufferMultiviewTest, IncompleteViewTargetsLayered)
 {
-    if (!requestMultiviewExtension())
-    {
-        return;
-    }
+    ANGLE_SKIP_TEST_IF(!requestMultiviewExtension());
 
     GLFramebuffer fbo;
     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
@@ -834,10 +922,7 @@
 // Test that glClear clears all of the contents if the scissor test is disabled.
 TEST_P(FramebufferMultiviewSideBySideClearTest, ClearWithDisabledScissorTest)
 {
-    if (!requestMultiviewExtension())
-    {
-        return;
-    }
+    ANGLE_SKIP_TEST_IF(!requestMultiviewExtension());
 
     initializeFBOs(1, false, false);
 
@@ -868,10 +953,7 @@
 // Test that glClear clears the depth buffer of each view.
 TEST_P(FramebufferMultiviewSideBySideClearTest, DepthBufferClear)
 {
-    if (!requestMultiviewExtension())
-    {
-        return;
-    }
+    ANGLE_SKIP_TEST_IF(!requestMultiviewExtension());
 
     // Create program to draw a quad.
     const std::string &vs =
@@ -924,10 +1006,7 @@
 // Test that glClear clears the stencil buffer of each view.
 TEST_P(FramebufferMultiviewSideBySideClearTest, StencilBufferClear)
 {
-    if (!requestMultiviewExtension())
-    {
-        return;
-    }
+    ANGLE_SKIP_TEST_IF(!requestMultiviewExtension());
 
     // Create program to draw a quad.
     const std::string &vs =
@@ -988,10 +1067,7 @@
 // Test that glClearBufferf clears the color buffer of each view.
 TEST_P(FramebufferMultiviewSideBySideClearTest, ClearBufferF)
 {
-    if (!requestMultiviewExtension())
-    {
-        return;
-    }
+    ANGLE_SKIP_TEST_IF(!requestMultiviewExtension());
 
     initializeFBOs(2, false, false);
 
@@ -1030,10 +1106,7 @@
 // layered FBO.
 TEST_P(FramebufferMultiviewLayeredClearTest, ColorBufferClear)
 {
-    if (!requestMultiviewExtension())
-    {
-        return;
-    }
+    ANGLE_SKIP_TEST_IF(!requestMultiviewExtension());
 
     initializeFBOs(1, 1, 4, 1, 2, 1, false, false);
 
@@ -1052,10 +1125,7 @@
 // Test that glClearBufferfv can be used to clear individual color buffers of a layered FBO.
 TEST_P(FramebufferMultiviewLayeredClearTest, ClearIndividualColorBuffer)
 {
-    if (!requestMultiviewExtension())
-    {
-        return;
-    }
+    ANGLE_SKIP_TEST_IF(!requestMultiviewExtension());
 
     initializeFBOs(1, 1, 4, 1, 2, 2, false, false);
 
@@ -1091,10 +1161,7 @@
 // to a layered FBO.
 TEST_P(FramebufferMultiviewLayeredClearTest, ClearBufferfi)
 {
-    if (!requestMultiviewExtension())
-    {
-        return;
-    }
+    ANGLE_SKIP_TEST_IF(!requestMultiviewExtension());
 
     // Create program to draw a quad.
     const std::string &vs =
@@ -1153,10 +1220,7 @@
 // Test that glClear does not clear the content of a detached texture.
 TEST_P(FramebufferMultiviewLayeredClearTest, UnmodifiedDetachedTexture)
 {
-    if (!requestMultiviewExtension())
-    {
-        return;
-    }
+    ANGLE_SKIP_TEST_IF(!requestMultiviewExtension());
 
     initializeFBOs(1, 1, 4, 1, 2, 2, false, false);
 
@@ -1196,10 +1260,7 @@
 // Test that glClear clears only the contents within the scissor rectangle of the attached layers.
 TEST_P(FramebufferMultiviewLayeredClearTest, ScissoredClear)
 {
-    if (!requestMultiviewExtension())
-    {
-        return;
-    }
+    ANGLE_SKIP_TEST_IF(!requestMultiviewExtension());
 
     initializeFBOs(2, 1, 4, 1, 2, 1, false, false);
 
@@ -1228,10 +1289,7 @@
 // to a layered FBO.
 TEST_P(FramebufferMultiviewLayeredClearTest, ScissoredClearBufferfi)
 {
-    if (!requestMultiviewExtension())
-    {
-        return;
-    }
+    ANGLE_SKIP_TEST_IF(!requestMultiviewExtension());
 
     // Create program to draw a quad.
     const std::string &vs =
@@ -1298,10 +1356,7 @@
 // arguments are invalid.
 TEST_P(FramebufferMultiviewTest, InvalidMultiviewArgumentsOnDetach)
 {
-    if (!requestMultiviewExtension())
-    {
-        return;
-    }
+    ANGLE_SKIP_TEST_IF(!requestMultiviewExtension());
 
     GLFramebuffer fbo;
     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
@@ -1331,10 +1386,7 @@
 // array are attached. The test is added because a special fast code path is used for this case.
 TEST_P(FramebufferMultiviewLayeredClearTest, ColorBufferClearAllLayersAttached)
 {
-    if (!requestMultiviewExtension())
-    {
-        return;
-    }
+    ANGLE_SKIP_TEST_IF(!requestMultiviewExtension());
 
     initializeFBOs(1, 1, 2, 0, 2, 1, false, false);
 
@@ -1346,6 +1398,6 @@
     EXPECT_EQ(GLColor::green, getLayerColor(1, GL_COLOR_ATTACHMENT0));
 }
 
-ANGLE_INSTANTIATE_TEST(FramebufferMultiviewTest, ES3_OPENGL());
+ANGLE_INSTANTIATE_TEST(FramebufferMultiviewTest, ES3_OPENGL(), ES3_D3D11());
 ANGLE_INSTANTIATE_TEST(FramebufferMultiviewSideBySideClearTest, ES3_OPENGL(), ES3_D3D11());
 ANGLE_INSTANTIATE_TEST(FramebufferMultiviewLayeredClearTest, ES3_OPENGL(), ES3_D3D11());
\ No newline at end of file