Validate ANGLE_multiview end-points

The patch adds validation for the ANGLE_multiview end-points
and propagates the calls to the unimplemented stubs in Context.

The patch also removes the
glFramebufferTextureMultiviewSideBySideRobustANGLE end-point
because the size of the viewport offsets buffer can trivially
be computed from the number of views.

BUG=angleproject:2062

Change-Id: I9a10bc00c19825e586d3df2750fabea4daf5ef8f
Reviewed-on: https://chromium-review.googlesource.com/573861
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/validationES3.cpp b/src/libANGLE/validationES3.cpp
index 6c755e1..6cc1827 100644
--- a/src/libANGLE/validationES3.cpp
+++ b/src/libANGLE/validationES3.cpp
@@ -12,6 +12,7 @@
 #include "common/mathutil.h"
 #include "common/utilities.h"
 #include "libANGLE/Context.h"
+#include "libANGLE/ErrorStrings.h"
 #include "libANGLE/Framebuffer.h"
 #include "libANGLE/FramebufferAttachment.h"
 #include "libANGLE/Renderbuffer.h"
@@ -24,6 +25,65 @@
 namespace gl
 {
 
+namespace
+{
+bool ValidateFramebufferTextureMultiviewBaseANGLE(Context *context,
+                                                  GLenum target,
+                                                  GLenum attachment,
+                                                  GLuint texture,
+                                                  GLint level,
+                                                  GLsizei numViews)
+{
+    if (!context->getExtensions().multiview)
+    {
+        context->handleError(InvalidOperation() << "ANGLE_multiview is not available.");
+        return false;
+    }
+
+    if (!ValidateFramebufferTextureBase(context, target, attachment, texture, level))
+    {
+        return false;
+    }
+
+    if (numViews < 1)
+    {
+        context->handleError(InvalidValue() << "numViews cannot be less than 1.");
+        return false;
+    }
+
+    const Extensions &extensions = context->getExtensions();
+    if (static_cast<GLuint>(numViews) > extensions.maxViews)
+    {
+        context->handleError(InvalidValue()
+                             << "numViews cannot be greater than GL_MAX_VIEWS_ANGLE.");
+        return false;
+    }
+
+    return true;
+}
+
+bool ValidateFramebufferTextureMultiviewLevelAndFormat(Context *context,
+                                                       Texture *texture,
+                                                       GLint level)
+{
+    GLenum texTarget = texture->getTarget();
+    if (!ValidMipLevel(context, texTarget, level))
+    {
+        ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidMipLevel);
+        return false;
+    }
+
+    const auto &format = texture->getFormat(texTarget, level);
+    if (format.info->compressed)
+    {
+        ANGLE_VALIDATION_ERR(context, InvalidOperation(), CompressedTexturesNotAttachable);
+        return false;
+    }
+    return true;
+}
+
+}  // namespace
+
 static bool ValidateTexImageFormatCombination(gl::Context *context,
                                               GLenum target,
                                               GLenum internalFormat,
@@ -2571,4 +2631,108 @@
     return ValidateDrawElementsInstancedCommon(context, mode, count, type, indices, instanceCount);
 }
 
+bool ValidateFramebufferTextureMultiviewLayeredANGLE(Context *context,
+                                                     GLenum target,
+                                                     GLenum attachment,
+                                                     GLuint texture,
+                                                     GLint level,
+                                                     GLint baseViewIndex,
+                                                     GLsizei numViews)
+{
+
+    if (!ValidateFramebufferTextureMultiviewBaseANGLE(context, target, attachment, texture, level,
+                                                      numViews))
+    {
+        return false;
+    }
+
+    if (baseViewIndex < 0)
+    {
+        context->handleError(InvalidValue() << "baseViewIndex cannot be less than 0.");
+        return false;
+    }
+
+    if (texture != 0)
+    {
+        Texture *tex = context->getTexture(texture);
+        ASSERT(tex);
+
+        switch (tex->getTarget())
+        {
+            case GL_TEXTURE_2D_ARRAY:
+            {
+                const Caps &caps = context->getCaps();
+                if (static_cast<GLuint>(baseViewIndex + numViews) > caps.maxArrayTextureLayers)
+                {
+                    context->handleError(InvalidValue() << "baseViewIndex+numViews cannot be "
+                                                           "greater than "
+                                                           "GL_MAX_ARRAY_TEXTURE_LAYERS.");
+                    return false;
+                }
+            }
+            break;
+            default:
+                context->handleError(InvalidOperation()
+                                     << "Texture's target must be GL_TEXTURE_2D_ARRAY.");
+                return false;
+        }
+
+        if (!ValidateFramebufferTextureMultiviewLevelAndFormat(context, tex, level))
+        {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool ValidateFramebufferTextureMultiviewSideBySideANGLE(Context *context,
+                                                        GLenum target,
+                                                        GLenum attachment,
+                                                        GLuint texture,
+                                                        GLint level,
+                                                        GLsizei numViews,
+                                                        const GLint *viewportOffsets)
+{
+    if (!ValidateFramebufferTextureMultiviewBaseANGLE(context, target, attachment, texture, level,
+                                                      numViews))
+    {
+        return false;
+    }
+
+    const GLsizei numViewportOffsetValues = numViews * 2;
+    for (GLsizei i = 0; i < numViewportOffsetValues; ++i)
+    {
+        if (viewportOffsets[i] < 0)
+        {
+            context->handleError(InvalidValue()
+                                 << "viewportOffsets cannot contain negative values.");
+            return false;
+        }
+    }
+
+    if (texture != 0)
+    {
+        Texture *tex = context->getTexture(texture);
+        ASSERT(tex);
+
+        switch (tex->getTarget())
+        {
+            case GL_TEXTURE_2D:
+                break;
+            default:
+                context->handleError(InvalidOperation()
+                                     << "Texture's target must be GL_TEXTURE_2D.");
+                return false;
+        }
+
+        if (!ValidateFramebufferTextureMultiviewLevelAndFormat(context, tex, level))
+        {
+            return false;
+        }
+    }
+
+    return true;
+}
+
 }  // namespace gl