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/CollectVariables.cpp b/src/compiler/translator/CollectVariables.cpp
index 4ffae59..5815940 100644
--- a/src/compiler/translator/CollectVariables.cpp
+++ b/src/compiler/translator/CollectVariables.cpp
@@ -36,6 +36,7 @@
     }
 }
 
+// TODO(jiawei.shao@intel.com): implement GL_OES_shader_io_blocks.
 BlockType GetBlockType(TQualifier qualifier)
 {
     switch (qualifier)
@@ -44,6 +45,8 @@
             return BlockType::BLOCK_UNIFORM;
         case EvqBuffer:
             return BlockType::BLOCK_BUFFER;
+        case EvqPerVertexIn:
+            return BlockType::BLOCK_IN;
         default:
             UNREACHABLE();
             return BlockType::BLOCK_UNIFORM;
@@ -93,6 +96,7 @@
                               std::vector<Varying> *outputVaryings,
                               std::vector<InterfaceBlock> *uniformBlocks,
                               std::vector<InterfaceBlock> *shaderStorageBlocks,
+                              std::vector<InterfaceBlock> *inBlocks,
                               ShHashFunction64 hashFunction,
                               TSymbolTable *symbolTable,
                               int shaderVersion,
@@ -110,7 +114,8 @@
     Attribute recordAttribute(const TIntermSymbol &variable) const;
     OutputVariable recordOutputVariable(const TIntermSymbol &variable) const;
     Varying recordVarying(const TIntermSymbol &variable) const;
-    InterfaceBlock recordInterfaceBlock(const TIntermSymbol &variable) const;
+    void recordInterfaceBlock(const TType &interfaceBlockType,
+                              InterfaceBlock *interfaceBlock) const;
     Uniform recordUniform(const TIntermSymbol &variable) const;
 
     void setBuiltInInfoFromSymbolTable(const char *name, ShaderVariable *info);
@@ -120,6 +125,8 @@
                                   std::vector<Varying> *varyings);
     void recordBuiltInFragmentOutputUsed(const char *name, bool *addedFlag);
     void recordBuiltInAttributeUsed(const char *name, bool *addedFlag);
+    InterfaceBlock *recordGLInUsed(const TType &glInType);
+    InterfaceBlock *findNamedInterfaceBlock(const TString &name) const;
 
     std::vector<Attribute> *mAttribs;
     std::vector<OutputVariable> *mOutputVariables;
@@ -128,6 +135,7 @@
     std::vector<Varying> *mOutputVaryings;
     std::vector<InterfaceBlock> *mUniformBlocks;
     std::vector<InterfaceBlock> *mShaderStorageBlocks;
+    std::vector<InterfaceBlock> *mInBlocks;
 
     std::map<std::string, InterfaceBlockField *> mInterfaceBlockFields;
 
@@ -148,6 +156,8 @@
     bool mSecondaryFragColorEXTAdded;
     bool mSecondaryFragDataEXTAdded;
 
+    bool mPerVertexInAdded;
+
     ShHashFunction64 mHashFunction;
 
     int mShaderVersion;
@@ -162,6 +172,7 @@
     std::vector<sh::Varying> *outputVaryings,
     std::vector<sh::InterfaceBlock> *uniformBlocks,
     std::vector<sh::InterfaceBlock> *shaderStorageBlocks,
+    std::vector<sh::InterfaceBlock> *inBlocks,
     ShHashFunction64 hashFunction,
     TSymbolTable *symbolTable,
     int shaderVersion,
@@ -174,6 +185,7 @@
       mOutputVaryings(outputVaryings),
       mUniformBlocks(uniformBlocks),
       mShaderStorageBlocks(shaderStorageBlocks),
+      mInBlocks(inBlocks),
       mDepthRangeAdded(false),
       mPointCoordAdded(false),
       mFrontFacingAdded(false),
@@ -189,6 +201,7 @@
       mFragDepthAdded(false),
       mSecondaryFragColorEXTAdded(false),
       mSecondaryFragDataEXTAdded(false),
+      mPerVertexInAdded(false),
       mHashFunction(hashFunction),
       mShaderVersion(shaderVersion),
       mExtensionBehavior(extensionBehavior)
@@ -251,6 +264,25 @@
     }
 }
 
+InterfaceBlock *CollectVariablesTraverser::recordGLInUsed(const TType &glInType)
+{
+    if (!mPerVertexInAdded)
+    {
+        ASSERT(glInType.getQualifier() == EvqPerVertexIn);
+        InterfaceBlock info;
+        recordInterfaceBlock(glInType, &info);
+        info.staticUse = true;
+
+        mPerVertexInAdded = true;
+        mInBlocks->push_back(info);
+        return &mInBlocks->back();
+    }
+    else
+    {
+        return FindVariable("gl_PerVertex", mInBlocks);
+    }
+}
+
 // We want to check whether a uniform/varying is statically used
 // because we only count the used ones in packing computing.
 // Also, gl_FragCoord, gl_PointCoord, and gl_FrontFacing count
@@ -535,21 +567,30 @@
     return varying;
 }
 
-InterfaceBlock CollectVariablesTraverser::recordInterfaceBlock(const TIntermSymbol &variable) const
+// TODO(jiawei.shao@intel.com): implement GL_OES_shader_io_blocks.
+void CollectVariablesTraverser::recordInterfaceBlock(const TType &interfaceBlockType,
+                                                     InterfaceBlock *interfaceBlock) const
 {
-    const TInterfaceBlock *blockType = variable.getType().getInterfaceBlock();
+    ASSERT(interfaceBlockType.getBasicType() == EbtInterfaceBlock);
+    ASSERT(interfaceBlock);
+
+    const TInterfaceBlock *blockType = interfaceBlockType.getInterfaceBlock();
     ASSERT(blockType);
 
-    InterfaceBlock interfaceBlock;
-    interfaceBlock.name       = blockType->name().c_str();
-    interfaceBlock.mappedName = HashName(blockType->name().c_str(), mHashFunction).c_str();
-    interfaceBlock.instanceName =
+    interfaceBlock->name       = blockType->name().c_str();
+    interfaceBlock->mappedName = HashName(blockType->name().c_str(), mHashFunction).c_str();
+    interfaceBlock->instanceName =
         (blockType->hasInstanceName() ? blockType->instanceName().c_str() : "");
-    interfaceBlock.arraySize        = variable.getArraySize();
-    interfaceBlock.isRowMajorLayout = (blockType->matrixPacking() == EmpRowMajor);
-    interfaceBlock.binding          = blockType->blockBinding();
-    interfaceBlock.layout           = GetBlockLayoutType(blockType->blockStorage());
-    interfaceBlock.blockType        = GetBlockType(variable.getType().getQualifier());
+    interfaceBlock->arraySize = interfaceBlockType.getArraySize();
+
+    interfaceBlock->blockType = GetBlockType(interfaceBlockType.getQualifier());
+    if (interfaceBlock->blockType == BlockType::BLOCK_UNIFORM ||
+        interfaceBlock->blockType == BlockType::BLOCK_BUFFER)
+    {
+        interfaceBlock->isRowMajorLayout = (blockType->matrixPacking() == EmpRowMajor);
+        interfaceBlock->binding          = blockType->blockBinding();
+        interfaceBlock->layout           = GetBlockLayoutType(blockType->blockStorage());
+    }
 
     // Gather field information
     for (const TField *field : blockType->fields())
@@ -560,9 +601,8 @@
         setCommonVariableProperties(fieldType, field->name(), &fieldVariable);
         fieldVariable.isRowMajorLayout =
             (fieldType.getLayoutQualifier().matrixPacking == EmpRowMajor);
-        interfaceBlock.fields.push_back(fieldVariable);
+        interfaceBlock->fields.push_back(fieldVariable);
     }
-    return interfaceBlock;
 }
 
 Uniform CollectVariablesTraverser::recordUniform(const TIntermSymbol &variable) const
@@ -605,15 +645,22 @@
             continue;
         }
 
+        // TODO(jiawei.shao@intel.com): implement GL_OES_shader_io_blocks.
         if (typedNode.getBasicType() == EbtInterfaceBlock)
         {
-            if (qualifier == EvqUniform)
+            InterfaceBlock interfaceBlock;
+            recordInterfaceBlock(variable.getType(), &interfaceBlock);
+
+            switch (qualifier)
             {
-                mUniformBlocks->push_back(recordInterfaceBlock(variable));
-            }
-            else if (qualifier == EvqBuffer)
-            {
-                mShaderStorageBlocks->push_back(recordInterfaceBlock(variable));
+                case EvqUniform:
+                    mUniformBlocks->push_back(interfaceBlock);
+                    break;
+                case EvqBuffer:
+                    mShaderStorageBlocks->push_back(interfaceBlock);
+                    break;
+                default:
+                    UNREACHABLE();
             }
         }
         else
@@ -650,6 +697,18 @@
     return false;
 }
 
+// TODO(jiawei.shao@intel.com): add search on mInBlocks and mOutBlocks when implementing
+// GL_OES_shader_io_blocks.
+InterfaceBlock *CollectVariablesTraverser::findNamedInterfaceBlock(const TString &blockName) const
+{
+    InterfaceBlock *namedBlock = FindVariable(blockName, mUniformBlocks);
+    if (!namedBlock)
+    {
+        namedBlock = FindVariable(blockName, mShaderStorageBlocks);
+    }
+    return namedBlock;
+}
+
 bool CollectVariablesTraverser::visitBinary(Visit, TIntermBinary *binaryNode)
 {
     if (binaryNode->getOp() == EOpIndexDirectInterfaceBlock)
@@ -661,17 +720,43 @@
         TIntermConstantUnion *constantUnion = binaryNode->getRight()->getAsConstantUnion();
         ASSERT(constantUnion);
 
+        InterfaceBlock *namedBlock = nullptr;
+
+        bool traverseIndexExpression         = false;
+        TIntermBinary *interfaceIndexingNode = blockNode->getAsBinaryNode();
+        if (interfaceIndexingNode)
+        {
+            TIntermTyped *interfaceNode = interfaceIndexingNode->getLeft()->getAsTyped();
+            ASSERT(interfaceNode);
+
+            const TType &interfaceType = interfaceNode->getType();
+            if (interfaceType.getQualifier() == EvqPerVertexIn)
+            {
+                namedBlock = recordGLInUsed(interfaceType);
+                ASSERT(namedBlock);
+
+                // We need to continue traversing to collect useful variables in the index
+                // expression of gl_in.
+                traverseIndexExpression = true;
+            }
+        }
+
         const TInterfaceBlock *interfaceBlock = blockNode->getType().getInterfaceBlock();
-        InterfaceBlock *namedBlock = FindVariable(interfaceBlock->name(), mUniformBlocks);
         if (!namedBlock)
         {
-            namedBlock = FindVariable(interfaceBlock->name(), mShaderStorageBlocks);
+            namedBlock = findNamedInterfaceBlock(interfaceBlock->name());
         }
         ASSERT(namedBlock);
         namedBlock->staticUse   = true;
         unsigned int fieldIndex = static_cast<unsigned int>(constantUnion->getIConst(0));
         ASSERT(fieldIndex < namedBlock->fields.size());
         namedBlock->fields[fieldIndex].staticUse = true;
+
+        if (traverseIndexExpression)
+        {
+            ASSERT(interfaceIndexingNode);
+            interfaceIndexingNode->getRight()->traverse(this);
+        }
         return false;
     }
 
@@ -688,13 +773,14 @@
                       std::vector<Varying> *outputVaryings,
                       std::vector<InterfaceBlock> *uniformBlocks,
                       std::vector<InterfaceBlock> *shaderStorageBlocks,
+                      std::vector<InterfaceBlock> *inBlocks,
                       ShHashFunction64 hashFunction,
                       TSymbolTable *symbolTable,
                       int shaderVersion,
                       const TExtensionBehavior &extensionBehavior)
 {
     CollectVariablesTraverser collect(attributes, outputVariables, uniforms, inputVaryings,
-                                      outputVaryings, uniformBlocks, shaderStorageBlocks,
+                                      outputVaryings, uniformBlocks, shaderStorageBlocks, inBlocks,
                                       hashFunction, symbolTable, shaderVersion, extensionBehavior);
     root->traverse(&collect);
 }