ES31: Implement gl_in in Geometry Shader

This patch intends to implement geometry shader built-in interface
block instance gl_in defined in GL_OES_geometry_shader.

1. Add the definition of gl_in and its interface block gl_PerVertex
   into the symbol table.
2. Support gl_Position as a member of gl_in.
3. Set the array size of gl_in when a valid input primitive type is
   known.
4. Add check that it should be a compile error to index gl_in or
   call length() on gl_in without a valid input primitive declaration.

This patch also adds unit tests to cover all these new features.

BUG=angleproject:1941
TEST=angle_unittests

Change-Id: I8da20c943b29c9ce904834625b396aab6302e1e1
Reviewed-on: https://chromium-review.googlesource.com/605059
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index f1cb9ed..25ee442 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -95,6 +95,27 @@
     return true;
 }
 
+// Map input primitive types to input array sizes in a geometry shader.
+GLuint GetGeometryShaderInputArraySize(TLayoutPrimitiveType primitiveType)
+{
+    switch (primitiveType)
+    {
+        case EptPoints:
+            return 1u;
+        case EptLines:
+            return 2u;
+        case EptTriangles:
+            return 3u;
+        case EptLinesAdjacency:
+            return 4u;
+        case EptTrianglesAdjacency:
+            return 6u;
+        default:
+            UNREACHABLE();
+            return 0u;
+    }
+}
+
 }  // namespace
 
 // This tracks each binding point's current default offset for inheritance of subsequent
@@ -184,7 +205,8 @@
       mGeometryShaderInvocations(0),
       mGeometryShaderMaxVertices(-1),
       mMaxGeometryShaderInvocations(resources.MaxGeometryShaderInvocations),
-      mMaxGeometryShaderMaxVertices(resources.MaxGeometryOutputVertices)
+      mMaxGeometryShaderMaxVertices(resources.MaxGeometryOutputVertices),
+      mGeometryShaderInputArraySize(0)
 {
     mComputeShaderLocalSize.fill(-1);
 }
@@ -496,6 +518,9 @@
         case EvqComputeIn:
             message = "can't modify work group size variable";
             break;
+        case EvqPerVertexIn:
+            message = "can't modify any member in gl_in";
+            break;
         default:
             //
             // Type that can't be written to?
@@ -1748,6 +1773,13 @@
         type.setQualifier(EvqConst);
         node = new TIntermConstantUnion(constArray, type);
     }
+    // TODO(jiawei.shao@intel.com): set array sizes for user-defined geometry shader inputs.
+    else if (variable->getType().getQualifier() == EvqPerVertexIn)
+    {
+        TType type(variable->getType());
+        type.setArraySize(mGeometryShaderInputArraySize);
+        node = new TIntermSymbol(variable->getUniqueId(), variable->getName(), type);
+    }
     else
     {
         node = new TIntermSymbol(variable->getUniqueId(), variable->getName(), variable->getType());
@@ -2683,6 +2715,15 @@
     }
 }
 
+void TParseContext::setGeometryShaderInputArraySizes()
+{
+    // TODO(jiawei.shao@intel.com): check former input array sizes match the input primitive
+    // declaration.
+    ASSERT(mGeometryShaderInputArraySize == 0);
+    mGeometryShaderInputArraySize =
+        GetGeometryShaderInputArraySize(mGeometryShaderInputPrimitiveType);
+}
+
 bool TParseContext::parseGeometryShaderInputLayoutQualifier(const TTypeQualifier &typeQualifier)
 {
     ASSERT(typeQualifier.qualifier == EvqGeometryIn);
@@ -2708,6 +2749,7 @@
         if (mGeometryShaderInputPrimitiveType == EptUndefined)
         {
             mGeometryShaderInputPrimitiveType = layoutQualifier.primitiveType;
+            setGeometryShaderInputArraySizes();
         }
         else if (mGeometryShaderInputPrimitiveType != layoutQualifier.primitiveType)
         {
@@ -3337,6 +3379,7 @@
 
 //
 // Interface/uniform blocks
+// TODO(jiawei.shao@intel.com): implement GL_OES_shader_io_blocks.
 //
 TIntermDeclaration *TParseContext::addInterfaceBlock(
     const TTypeQualifierBuilder &typeQualifierBuilder,
@@ -3641,6 +3684,16 @@
         return CreateZeroNode(TType(EbtFloat, EbpHigh, EvqConst));
     }
 
+    if (baseExpression->getQualifier() == EvqPerVertexIn)
+    {
+        ASSERT(mShaderType == GL_GEOMETRY_SHADER_OES);
+        if (mGeometryShaderInputPrimitiveType == EptUndefined)
+        {
+            error(location, "missing input primitive declaration before indexing gl_in.", "[");
+            return CreateZeroNode(TType(EbtFloat, EbpHigh, EvqConst));
+        }
+    }
+
     TIntermConstantUnion *indexConstantUnion = indexExpression->getAsConstantUnion();
 
     // TODO(oetuaho@nvidia.com): Get rid of indexConstantUnion == nullptr below once ANGLE is able
@@ -3651,9 +3704,21 @@
     {
         if (baseExpression->isInterfaceBlock())
         {
-            error(location,
-                  "array indexes for interface blocks arrays must be constant integral expressions",
-                  "[");
+            // TODO(jiawei.shao@intel.com): implement GL_OES_shader_io_blocks.
+            switch (baseExpression->getQualifier())
+            {
+                case EvqPerVertexIn:
+                    break;
+                case EvqUniform:
+                case EvqBuffer:
+                    error(location,
+                          "array indexes for uniform block arrays and shader storage block arrays "
+                          "must be constant integral expressions",
+                          "[");
+                    break;
+                default:
+                    UNREACHABLE();
+            }
         }
         else if (baseExpression->getQualifier() == EvqFragmentOut)
         {
@@ -5326,6 +5391,11 @@
     {
         error(loc, "length can only be called on arrays", "length");
     }
+    else if (typedThis->getQualifier() == EvqPerVertexIn &&
+             mGeometryShaderInputPrimitiveType == EptUndefined)
+    {
+        error(loc, "missing input primitive declaration before calling length on gl_in", "length");
+    }
     else
     {
         arraySize = typedThis->getArraySize();