Handle Clear* commands for side-by-side framebuffers

Clear* commands for side-by-side framebuffers require special handling
because only the scissor rectangle of the first viewport is used in the
scissor test as defined in the OpenGL 4.1+ specs.

To enable clearing of each view of a side-by-side framebuffer all views
are iterated over, the corresponding scissor rectangle is set as first,
and a Clear* call is made to the driver. Afterwards the scissor state is
restored.

BUG=angleproject:2062
TEST=angle_end2end_tests

Change-Id: I138663ea61b4f0c9302872108e7dfbadf451b3ec
Reviewed-on: https://chromium-review.googlesource.com/590233
Commit-Queue: Martin Radev <mradev@nvidia.com>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/tests/gl_tests/FramebufferMultiviewTest.cpp b/src/tests/gl_tests/FramebufferMultiviewTest.cpp
index f6c4a90..d65705a 100644
--- a/src/tests/gl_tests/FramebufferMultiviewTest.cpp
+++ b/src/tests/gl_tests/FramebufferMultiviewTest.cpp
@@ -14,15 +14,24 @@
 namespace
 {
 
-GLuint CreateTexture2D(GLenum internalFormat, GLenum format, GLenum type)
+GLuint CreateTexture2D(GLenum internalFormat,
+                       GLenum format,
+                       GLenum type,
+                       GLsizei width,
+                       GLsizei height)
 {
     GLuint tex;
     glGenTextures(1, &tex);
     glBindTexture(GL_TEXTURE_2D, tex);
-    glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, 1, 1, 0, format, type, nullptr);
+    glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, nullptr);
     return tex;
 }
 
+GLuint CreateTexture2D(GLenum internalFormat, GLenum format, GLenum type)
+{
+    return CreateTexture2D(internalFormat, format, type, 1, 1);
+}
+
 }  // namespace
 
 class FramebufferMultiviewTest : public ANGLETest
@@ -481,4 +490,56 @@
     EXPECT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION);
 }
 
+// Test that glClear clears only the contents of each view.
+TEST_P(FramebufferMultiviewTest, SideBySideClear)
+{
+    if (!requestMultiviewExtension())
+    {
+        return;
+    }
+
+    mTexture2D                     = CreateTexture2D(GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, 4, 2);
+    const GLint viewportOffsets[4] = {1, 0, 3, 0};
+    glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTexture2D,
+                                                 0, 2, &viewportOffsets[0]);
+
+    // Create and bind a normal framebuffer to access the 2D texture.
+    GLuint fbo;
+    glGenFramebuffers(1, &fbo);
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture2D, 0);
+
+    // Clear the contents of the texture.
+    glClearColor(0, 0, 0, 0);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    // Bind and specify viewport/scissor dimensions for each view.
+    glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
+    glViewport(0, 0, 1, 2);
+    glScissor(0, 0, 1, 2);
+
+    glClearColor(1, 0, 0, 0);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+    // column 0
+    EXPECT_PIXEL_EQ(0, 0, 0, 0, 0, 0);
+    EXPECT_PIXEL_EQ(0, 1, 0, 0, 0, 0);
+
+    // column 1
+    EXPECT_PIXEL_EQ(1, 0, 255, 0, 0, 0);
+    EXPECT_PIXEL_EQ(1, 1, 255, 0, 0, 0);
+
+    // column 2
+    EXPECT_PIXEL_EQ(2, 0, 0, 0, 0, 0);
+    EXPECT_PIXEL_EQ(2, 1, 0, 0, 0, 0);
+
+    // column 3
+    EXPECT_PIXEL_EQ(3, 0, 255, 0, 0, 0);
+    EXPECT_PIXEL_EQ(3, 1, 255, 0, 0, 0);
+
+    glDeleteFramebuffers(1, &fbo);
+}
+
 ANGLE_INSTANTIATE_TEST(FramebufferMultiviewTest, ES3_OPENGL());
\ No newline at end of file