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>
diff --git a/src/tests/gl_tests/FramebufferMultiviewTest.cpp b/src/tests/gl_tests/FramebufferMultiviewTest.cpp
index 6d834f2..7ace41e 100644
--- a/src/tests/gl_tests/FramebufferMultiviewTest.cpp
+++ b/src/tests/gl_tests/FramebufferMultiviewTest.cpp
@@ -12,6 +12,20 @@
 
 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:
@@ -48,6 +62,108 @@
     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)
@@ -472,59 +588,32 @@
 }
 
 // Test that glClear clears only the contents of each view if the scissor test is enabled.
-TEST_P(FramebufferMultiviewTest, SideBySideClear)
+TEST_P(FramebufferMultiviewClearTest, SideBySideColorBufferClear)
 {
     if (!requestMultiviewExtension())
     {
         return;
     }
 
-    GLFramebuffer multiviewFBO;
-    glBindFramebuffer(GL_FRAMEBUFFER, multiviewFBO);
-
-    GLTexture tex;
-    glBindTexture(GL_TEXTURE_2D, tex);
-    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 4, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
-
-    const GLint kViewportOffsets[4] = {1, 0, 3, 0};
-    glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0, 2,
-                                                 &kViewportOffsets[0]);
-
-    // Create and bind a normal framebuffer to access the 2D texture.
-    GLFramebuffer normalFBO;
-    glBindFramebuffer(GL_FRAMEBUFFER, normalFBO);
-    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
+    initializeFBOs(1, false, false);
 
     // Clear the contents of the texture.
-    glClearColor(0, 0, 0, 1);
+    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, multiviewFBO);
+    glBindFramebuffer(GL_FRAMEBUFFER, mMultiviewFBO);
     glViewport(0, 0, 1, 2);
     glScissor(0, 0, 1, 2);
     glEnable(GL_SCISSOR_TEST);
 
-    glClearColor(1, 0, 0, 1);
+    glClearColor(0, 1, 0, 1);
     glClear(GL_COLOR_BUFFER_BIT);
 
-    glBindFramebuffer(GL_FRAMEBUFFER, normalFBO);
+    glBindFramebuffer(GL_FRAMEBUFFER, mNonMultiviewFBO);
 
-    // column 0
-    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black);
-    EXPECT_PIXEL_COLOR_EQ(0, 1, GLColor::black);
-
-    // column 1
-    EXPECT_PIXEL_COLOR_EQ(1, 0, GLColor::red);
-    EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::red);
-
-    // column 2
-    EXPECT_PIXEL_COLOR_EQ(2, 0, GLColor::black);
-    EXPECT_PIXEL_COLOR_EQ(2, 1, GLColor::black);
-
-    // column 3
-    EXPECT_PIXEL_COLOR_EQ(3, 0, GLColor::red);
-    EXPECT_PIXEL_COLOR_EQ(3, 1, GLColor::red);
+    checkAlternatingColumnsOfRedAndGreenInFBO();
 }
 
 // Test that glFramebufferTextureMultiviewLayeredANGLE modifies the internal multiview state.
@@ -633,42 +722,29 @@
 }
 
 // Test that glClear clears all of the contents if the scissor test is disabled.
-TEST_P(FramebufferMultiviewTest, SideBySideClearWithDisabledScissorTest)
+TEST_P(FramebufferMultiviewClearTest, SideBySideClearWithDisabledScissorTest)
 {
     if (!requestMultiviewExtension())
     {
         return;
     }
 
-    GLFramebuffer multiviewFBO;
-    glBindFramebuffer(GL_FRAMEBUFFER, multiviewFBO);
-
-    GLTexture tex;
-    glBindTexture(GL_TEXTURE_2D, tex);
-    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 4, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
-
-    const GLint kViewportOffsets[2] = {1, 0};
-    glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0, 1,
-                                                 &kViewportOffsets[0]);
-
-    // Create and bind a normal framebuffer to access the 2D texture.
-    GLFramebuffer normalFBO;
-    glBindFramebuffer(GL_FRAMEBUFFER, normalFBO);
-    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
+    initializeFBOs(1, false, false);
 
     // Clear the contents of the texture.
-    glClearColor(0, 1, 0, 0);
+    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, multiviewFBO);
+    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, normalFBO);
+    glBindFramebuffer(GL_FRAMEBUFFER, mNonMultiviewFBO);
 
     for (int i = 0; i < 4; ++i)
     {
@@ -679,4 +755,166 @@
     }
 }
 
-ANGLE_INSTANTIATE_TEST(FramebufferMultiviewTest, ES3_OPENGL());
\ No newline at end of file
+// 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());