Add shader translator support for OVR_multiview

The OVR_multiview and OVR_multiview2 extensions add gl_ViewID_OVR to
shaders. gl_ViewID_OVR can be translated either as is in GLSL output
or as a uniform by setting the SH_TRANSLATE_VIEWID_OVR_AS_UNIFORM
compiler flag.

If WebGL output is selected, the shaders will be validated according
to proposed rules in the WEBGL_multiview spec.

BUG=angleproject:1669
TEST=angle_unittests

Change-Id: I19ea3a6c8b4edb78be03f1a50a96bfef018870d0
Reviewed-on: https://chromium-review.googlesource.com/422848
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index df8ce9f..6e7f854 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -106,7 +106,10 @@
       mUsesSecondaryOutputs(false),
       mMinProgramTexelOffset(resources.MinProgramTexelOffset),
       mMaxProgramTexelOffset(resources.MaxProgramTexelOffset),
+      mMultiviewAvailable(resources.OVR_multiview == 1),
       mComputeShaderLocalSizeDeclared(false),
+      mNumViews(-1),
+      mMaxNumViews(resources.MaxViewsOVR),
       mDeclaringFunction(false)
 {
     mComputeShaderLocalSize.fill(-1);
@@ -1085,6 +1088,12 @@
     // In GLSL ES, an extension's default behavior is "disable".
     if (iter->second == EBhDisable || iter->second == EBhUndefined)
     {
+        // TODO(oetuaho@nvidia.com): This is slightly hacky. Might be better if symbols could be
+        // associated with more than one extension.
+        if (extension == "GL_OVR_multiview")
+        {
+            return checkCanUseExtension(line, "GL_OVR_multiview2");
+        }
         error(line, "extension is disabled", extension.c_str());
         return false;
     }
@@ -1481,6 +1490,14 @@
 {
     const TVariable *variable = getNamedVariable(location, name, symbol);
 
+    if (variable->getType().getQualifier() == EvqViewIDOVR && IsWebGLBasedSpec(mShaderSpec) &&
+        mShaderType == GL_FRAGMENT_SHADER && !isExtensionEnabled("GL_OVR_multiview2"))
+    {
+        // WEBGL_multiview spec
+        error(location, "Need to enable OVR_multiview2 to use gl_ViewID_OVR in fragment shader",
+              "gl_ViewID_OVR");
+    }
+
     if (variable->getConstPointer())
     {
         const TConstantUnion *constArray = variable->getConstPointer();
@@ -2315,10 +2332,37 @@
 
         mComputeShaderLocalSizeDeclared = true;
     }
+    else if (mMultiviewAvailable &&
+             (isExtensionEnabled("GL_OVR_multiview") || isExtensionEnabled("GL_OVR_multiview2")) &&
+             typeQualifier.qualifier == EvqVertexIn)
+    {
+        // This error is only specified in WebGL, but tightens unspecified behavior in the native
+        // specification.
+        if (mNumViews != -1 && layoutQualifier.numViews != mNumViews)
+        {
+            error(typeQualifier.line, "Number of views does not match the previous declaration",
+                  "layout");
+            return;
+        }
+
+        if (layoutQualifier.numViews == -1)
+        {
+            error(typeQualifier.line, "No num_views specified", "layout");
+            return;
+        }
+
+        if (layoutQualifier.numViews > mMaxNumViews)
+        {
+            error(typeQualifier.line, "num_views greater than the value of GL_MAX_VIEWS_OVR",
+                  "layout");
+            return;
+        }
+
+        mNumViews = layoutQualifier.numViews;
+    }
     else
     {
-
-        if (!checkWorkGroupSizeIsNotSpecified(typeQualifier.line, typeQualifier.layoutQualifier))
+        if (!checkWorkGroupSizeIsNotSpecified(typeQualifier.line, layoutQualifier))
         {
             return;
         }
@@ -2337,7 +2381,7 @@
             return;
         }
 
-        checkLocationIsNotSpecified(typeQualifier.line, typeQualifier.layoutQualifier);
+        checkLocationIsNotSpecified(typeQualifier.line, layoutQualifier);
 
         if (layoutQualifier.matrixPacking != EmpUnspecified)
         {
@@ -3348,6 +3392,20 @@
     (*localSize)[index] = intValue;
 }
 
+void TParseContext::parseNumViews(int intValue,
+                                  const TSourceLoc &intValueLine,
+                                  const std::string &intValueString,
+                                  int *numViews)
+{
+    // This error is only specified in WebGL, but tightens unspecified behavior in the native
+    // specification.
+    if (intValue < 1)
+    {
+        error(intValueLine, "out of range: num_views must be positive", intValueString.c_str());
+    }
+    *numViews = intValue;
+}
+
 TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierType,
                                                      const TSourceLoc &qualifierTypeLine,
                                                      int intValue,
@@ -3386,6 +3444,12 @@
         parseLocalSize(qualifierType, qualifierTypeLine, intValue, intValueLine, intValueString, 2u,
                        &qualifier.localSize);
     }
+    else if (qualifierType == "num_views" && mMultiviewAvailable &&
+             (isExtensionEnabled("GL_OVR_multiview") || isExtensionEnabled("GL_OVR_multiview2")) &&
+             mShaderType == GL_VERTEX_SHADER)
+    {
+        parseNumViews(intValue, intValueLine, intValueString, &qualifier.numViews);
+    }
     else
     {
         error(qualifierTypeLine, "invalid layout qualifier", qualifierType.c_str());