GL: Optimize multi-view layered Clear* commands

Until this patch multi-view layered framebuffers used to be cleared by
attaching a single layer of each attachment to a framebuffer and calling
the Clear* command for that internal framebuffer.
According to the GL 4.1+ specifications, Clear* commands clear all of
the layers of an attached 2D texture array. If all of the layers are
active for a multi-view layered framebuffer, then we can directly call
the corresponding Clear* command instead of iterating over each layer
and clearing it.

BUG=angleproject:2062
TEST=angle_end2end_tests

Change-Id: Ie4dfd9fff47715b502f358272bfc47c0373c4e91
Reviewed-on: https://chromium-review.googlesource.com/649209
Commit-Queue: Martin Radev <mradev@nvidia.com>
Reviewed-by: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/libANGLE/renderer/gl/FramebufferGL.cpp b/src/libANGLE/renderer/gl/FramebufferGL.cpp
index 811fd5d..009b75a 100644
--- a/src/libANGLE/renderer/gl/FramebufferGL.cpp
+++ b/src/libANGLE/renderer/gl/FramebufferGL.cpp
@@ -104,8 +104,61 @@
     }
 }
 
-bool RequiresMultiviewClear(const FramebufferAttachment *attachment, bool scissorTestEnabled)
+bool AreAllLayersActive(const FramebufferAttachment &attachment)
 {
+    int baseViewIndex = attachment.getBaseViewIndex();
+    if (baseViewIndex != 0)
+    {
+        return false;
+    }
+    const ImageIndex &imageIndex = attachment.getTextureImageIndex();
+    int numLayers =
+        static_cast<int>(attachment.getTexture()->getDepth(imageIndex.type, imageIndex.mipIndex));
+    return (attachment.getNumViews() == numLayers);
+}
+
+bool RequiresMultiviewClear(const FramebufferState &state, bool scissorTestEnabled)
+{
+    // Get one attachment and check whether all layers are attached.
+    const FramebufferAttachment *attachment = nullptr;
+    bool allTextureArraysAreFullyAttached   = true;
+    for (const FramebufferAttachment &colorAttachment : state.getColorAttachments())
+    {
+        if (colorAttachment.isAttached())
+        {
+            if (colorAttachment.getMultiviewLayout() == GL_NONE)
+            {
+                return false;
+            }
+            attachment = &colorAttachment;
+            allTextureArraysAreFullyAttached =
+                allTextureArraysAreFullyAttached && AreAllLayersActive(*attachment);
+        }
+    }
+
+    const FramebufferAttachment *depthAttachment = state.getDepthAttachment();
+    if (depthAttachment)
+    {
+        if (depthAttachment->getMultiviewLayout() == GL_NONE)
+        {
+            return false;
+        }
+        attachment = depthAttachment;
+        allTextureArraysAreFullyAttached =
+            allTextureArraysAreFullyAttached && AreAllLayersActive(*attachment);
+    }
+    const FramebufferAttachment *stencilAttachment = state.getStencilAttachment();
+    if (stencilAttachment)
+    {
+        if (stencilAttachment->getMultiviewLayout() == GL_NONE)
+        {
+            return false;
+        }
+        attachment = stencilAttachment;
+        allTextureArraysAreFullyAttached =
+            allTextureArraysAreFullyAttached && AreAllLayersActive(*attachment);
+    }
+
     if (attachment == nullptr)
     {
         return false;
@@ -113,13 +166,11 @@
     switch (attachment->getMultiviewLayout())
     {
         case GL_FRAMEBUFFER_MULTIVIEW_LAYERED_ANGLE:
-            // TODO(mradev): Optimize this for layered FBOs in which all of the layers in each
-            // attachment are active.
-            return true;
+            // If all layers of each texture array are active, then there is no need to issue a
+            // special multiview clear.
+            return !allTextureArraysAreFullyAttached;
         case GL_FRAMEBUFFER_MULTIVIEW_SIDE_BY_SIDE_ANGLE:
             return (scissorTestEnabled == true);
-        case GL_NONE:
-            return false;
         default:
             UNREACHABLE();
     }
@@ -241,8 +292,7 @@
     syncClearState(context, mask);
     mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
 
-    const auto &firstAttachment = mState.getFirstNonNullAttachment();
-    if (!RequiresMultiviewClear(firstAttachment, context->getGLState().isScissorTestEnabled()))
+    if (!RequiresMultiviewClear(mState, context->getGLState().isScissorTestEnabled()))
     {
         mFunctions->clear(mask);
     }
@@ -264,8 +314,7 @@
     syncClearBufferState(context, buffer, drawbuffer);
     mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
 
-    const auto &firstAttachment = mState.getFirstNonNullAttachment();
-    if (!RequiresMultiviewClear(firstAttachment, context->getGLState().isScissorTestEnabled()))
+    if (!RequiresMultiviewClear(mState, context->getGLState().isScissorTestEnabled()))
     {
         mFunctions->clearBufferfv(buffer, drawbuffer, values);
     }
@@ -288,8 +337,7 @@
     syncClearBufferState(context, buffer, drawbuffer);
     mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
 
-    const auto &firstAttachment = mState.getFirstNonNullAttachment();
-    if (!RequiresMultiviewClear(firstAttachment, context->getGLState().isScissorTestEnabled()))
+    if (!RequiresMultiviewClear(mState, context->getGLState().isScissorTestEnabled()))
     {
         mFunctions->clearBufferuiv(buffer, drawbuffer, values);
     }
@@ -312,8 +360,7 @@
     syncClearBufferState(context, buffer, drawbuffer);
     mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
 
-    const auto &firstAttachment = mState.getFirstNonNullAttachment();
-    if (!RequiresMultiviewClear(firstAttachment, context->getGLState().isScissorTestEnabled()))
+    if (!RequiresMultiviewClear(mState, context->getGLState().isScissorTestEnabled()))
     {
         mFunctions->clearBufferiv(buffer, drawbuffer, values);
     }
@@ -337,8 +384,7 @@
     syncClearBufferState(context, buffer, drawbuffer);
     mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
 
-    const auto &firstAttachment = mState.getFirstNonNullAttachment();
-    if (!RequiresMultiviewClear(firstAttachment, context->getGLState().isScissorTestEnabled()))
+    if (!RequiresMultiviewClear(mState, context->getGLState().isScissorTestEnabled()))
     {
         mFunctions->clearBufferfi(buffer, drawbuffer, depth, stencil);
     }