Disallow glCopyTex* with multi-view read framebuffers

According to the ANGLE_multiview spec, glCopyTex* functions must
generate an INVALID_FRAMEBUFFER_OPERATION error if the active read
framebuffer has a multi-view layout.

BUG=angleproject:2062
TEST=angle_end2end_tests

Change-Id: Icadc4ac79843986076594da25a90ba807e511d1e
Reviewed-on: https://chromium-review.googlesource.com/589447
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Martin Radev <mradev@nvidia.com>
diff --git a/src/libANGLE/validationES.cpp b/src/libANGLE/validationES.cpp
index 5e7806b..206762a 100644
--- a/src/libANGLE/validationES.cpp
+++ b/src/libANGLE/validationES.cpp
@@ -2624,12 +2624,24 @@
     // In OpenGL ES it is undefined what happens when an operation tries to read from a missing
     // attachment and WebGL defines it to be an error. We do the check unconditionally as the
     // situation is an application error that would lead to a crash in ANGLE.
-    if (readFramebuffer->getReadColorbuffer() == nullptr)
+    const FramebufferAttachment *source = readFramebuffer->getReadColorbuffer();
+    if (source == nullptr)
     {
         ANGLE_VALIDATION_ERR(context, InvalidOperation(), MissingReadAttachment);
         return false;
     }
 
+    // ANGLE_multiview spec, Revision 1:
+    // Calling CopyTexSubImage3D, CopyTexImage2D, or CopyTexSubImage2D will result in an
+    // INVALID_FRAMEBUFFER_OPERATION error if the multi-view layout of the current read framebuffer
+    // is not NONE.
+    if (source->getMultiviewLayout() != GL_NONE)
+    {
+        context->handleError(InvalidFramebufferOperation()
+                             << "The active read framebuffer object has multiview attachments.");
+        return false;
+    }
+
     const gl::Caps &caps = context->getCaps();
 
     GLuint maxDimension = 0;
diff --git a/src/tests/gl_tests/FramebufferMultiviewTest.cpp b/src/tests/gl_tests/FramebufferMultiviewTest.cpp
index 7304b05..c605099 100644
--- a/src/tests/gl_tests/FramebufferMultiviewTest.cpp
+++ b/src/tests/gl_tests/FramebufferMultiviewTest.cpp
@@ -379,4 +379,48 @@
     glDeleteTextures(1, &otherTexture);
 }
 
+// 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;
+    }
+
+    mTexture2D = CreateTexture2D(GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE);
+    ASSERT_GL_NO_ERROR();
+
+    const GLint viewportOffsets[2] = {0};
+    glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTexture2D,
+                                                 0, 1, &viewportOffsets[0]);
+    ASSERT_GL_NO_ERROR();
+
+    // Test glCopyTexImage2D and glCopyTexSubImage2D.
+    {
+        GLuint tex = CreateTexture2D(GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE);
+
+        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);
+
+        glDeleteTextures(1, &tex);
+    }
+
+    // Test glCopyTexSubImage3D.
+    {
+        GLuint tex = 0u;
+        glGenTextures(1, &tex);
+        glBindTexture(GL_TEXTURE_3D, tex);
+        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);
+
+        glDeleteTextures(1, &tex);
+    }
+}
+
 ANGLE_INSTANTIATE_TEST(FramebufferMultiviewTest, ES3_OPENGL());
\ No newline at end of file