ES31: Implement GL_OES_geometry_shader built-ins in GLSL compiler

This patch intends to implement all built-in constants, variables and
functions defined in OpenGL ES 3.1 extension GL_OES_geometry_shader
in ANGLE GLSL compiler.

1. Add all built-in constants defined in GL_OES_geometry_shader.
2. Add built-in functions EmitVertex() and EndPrimitive() required
   in Geometry Shader.
3. Add built-in variables gl_PrimitiveIDIn and gl_InvocationID to
   Geometry Shader.
4. Add built-in variables gl_PrimitiveID and gl_Layer to both
   Geometry Shader and Fragment Shader when GL_OES_geometry_shader
   is enabled.

BUG=angleproject:1941
TEST=angle_unittests

Change-Id: I92821553ed0efee2ccb77fead6e065e7799819d0
Reviewed-on: https://chromium-review.googlesource.com/627670
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/compiler/translator/BaseTypes.h b/src/compiler/translator/BaseTypes.h
index 00f1108..eda024e 100644
--- a/src/compiler/translator/BaseTypes.h
+++ b/src/compiler/translator/BaseTypes.h
@@ -576,8 +576,11 @@
     // GLSL ES 3.1 extension OES_geometry_shader qualifiers
     EvqGeometryIn,
     EvqGeometryOut,
-    EvqPerVertexIn,
-    EvqLayer,  // gl_Layer
+    EvqPerVertexIn,    // gl_in
+    EvqPrimitiveIDIn,  // gl_PrimitiveIDIn
+    EvqInvocationID,   // gl_InvocationID
+    EvqPrimitiveID,    // gl_PrimitiveID
+    EvqLayer,          // gl_Layer
 
     // end of list
     EvqLast
diff --git a/src/compiler/translator/CollectVariables.cpp b/src/compiler/translator/CollectVariables.cpp
index abe4592..8753d77 100644
--- a/src/compiler/translator/CollectVariables.cpp
+++ b/src/compiler/translator/CollectVariables.cpp
@@ -100,6 +100,7 @@
                               ShHashFunction64 hashFunction,
                               TSymbolTable *symbolTable,
                               int shaderVersion,
+                              GLenum shaderType,
                               const TExtensionBehavior &extensionBehavior);
 
     void visitSymbol(TIntermSymbol *symbol) override;
@@ -139,15 +140,21 @@
 
     std::map<std::string, InterfaceBlockField *> mInterfaceBlockFields;
 
+    // Shader uniforms
     bool mDepthRangeAdded;
+
+    // Vertex Shader builtins
+    bool mInstanceIDAdded;
+    bool mVertexIDAdded;
+    bool mPointSizeAdded;
+
+    // Vertex Shader and Geometry Shader builtins
+    bool mPositionAdded;
+
+    // Fragment Shader builtins
     bool mPointCoordAdded;
     bool mFrontFacingAdded;
     bool mFragCoordAdded;
-
-    bool mInstanceIDAdded;
-    bool mVertexIDAdded;
-    bool mPositionAdded;
-    bool mPointSizeAdded;
     bool mLastFragDataAdded;
     bool mFragColorAdded;
     bool mFragDataAdded;
@@ -156,11 +163,19 @@
     bool mSecondaryFragColorEXTAdded;
     bool mSecondaryFragDataEXTAdded;
 
+    // Geometry Shader builtins
     bool mPerVertexInAdded;
+    bool mPrimitiveIDInAdded;
+    bool mInvocationIDAdded;
+
+    // Geometry Shader and Fragment Shader builtins
+    bool mPrimitiveIDAdded;
+    bool mLayerAdded;
 
     ShHashFunction64 mHashFunction;
 
     int mShaderVersion;
+    GLenum mShaderType;
     const TExtensionBehavior &mExtensionBehavior;
 };
 
@@ -176,6 +191,7 @@
     ShHashFunction64 hashFunction,
     TSymbolTable *symbolTable,
     int shaderVersion,
+    GLenum shaderType,
     const TExtensionBehavior &extensionBehavior)
     : TIntermTraverser(true, false, false, symbolTable),
       mAttribs(attribs),
@@ -187,13 +203,13 @@
       mShaderStorageBlocks(shaderStorageBlocks),
       mInBlocks(inBlocks),
       mDepthRangeAdded(false),
+      mInstanceIDAdded(false),
+      mVertexIDAdded(false),
+      mPointSizeAdded(false),
+      mPositionAdded(false),
       mPointCoordAdded(false),
       mFrontFacingAdded(false),
       mFragCoordAdded(false),
-      mInstanceIDAdded(false),
-      mVertexIDAdded(false),
-      mPositionAdded(false),
-      mPointSizeAdded(false),
       mLastFragDataAdded(false),
       mFragColorAdded(false),
       mFragDataAdded(false),
@@ -202,8 +218,13 @@
       mSecondaryFragColorEXTAdded(false),
       mSecondaryFragDataEXTAdded(false),
       mPerVertexInAdded(false),
+      mPrimitiveIDInAdded(false),
+      mInvocationIDAdded(false),
+      mPrimitiveIDAdded(false),
+      mLayerAdded(false),
       mHashFunction(hashFunction),
       mShaderVersion(shaderVersion),
+      mShaderType(shaderType),
       mExtensionBehavior(extensionBehavior)
 {
 }
@@ -470,6 +491,38 @@
                 recordBuiltInFragmentOutputUsed("gl_SecondaryFragDataEXT",
                                                 &mSecondaryFragDataEXTAdded);
                 return;
+            case EvqInvocationID:
+                recordBuiltInVaryingUsed("gl_InvocationID", &mInvocationIDAdded, mInputVaryings);
+                break;
+            case EvqPrimitiveIDIn:
+                recordBuiltInVaryingUsed("gl_PrimitiveIDIn", &mPrimitiveIDInAdded, mInputVaryings);
+                break;
+            case EvqPrimitiveID:
+                if (mShaderType == GL_GEOMETRY_SHADER_OES)
+                {
+                    recordBuiltInVaryingUsed("gl_PrimitiveID", &mPrimitiveIDAdded, mOutputVaryings);
+                }
+                else
+                {
+                    ASSERT(mShaderType == GL_FRAGMENT_SHADER);
+                    recordBuiltInVaryingUsed("gl_PrimitiveID", &mPrimitiveIDAdded, mInputVaryings);
+                }
+                break;
+            case EvqLayer:
+                if (mShaderType == GL_GEOMETRY_SHADER_OES)
+                {
+                    recordBuiltInVaryingUsed("gl_Layer", &mLayerAdded, mOutputVaryings);
+                }
+                else if (mShaderType == GL_FRAGMENT_SHADER)
+                {
+                    recordBuiltInVaryingUsed("gl_Layer", &mLayerAdded, mInputVaryings);
+                }
+                else
+                {
+                    ASSERT(mShaderType == GL_VERTEX_SHADER &&
+                           IsExtensionEnabled(mExtensionBehavior, "GL_OVR_multiview"));
+                }
+                break;
             default:
                 break;
         }
@@ -783,11 +836,13 @@
                       ShHashFunction64 hashFunction,
                       TSymbolTable *symbolTable,
                       int shaderVersion,
+                      GLenum shaderType,
                       const TExtensionBehavior &extensionBehavior)
 {
     CollectVariablesTraverser collect(attributes, outputVariables, uniforms, inputVaryings,
                                       outputVaryings, uniformBlocks, shaderStorageBlocks, inBlocks,
-                                      hashFunction, symbolTable, shaderVersion, extensionBehavior);
+                                      hashFunction, symbolTable, shaderVersion, shaderType,
+                                      extensionBehavior);
     root->traverse(&collect);
 }
 
diff --git a/src/compiler/translator/CollectVariables.h b/src/compiler/translator/CollectVariables.h
index fb257f9..4d0d119 100644
--- a/src/compiler/translator/CollectVariables.h
+++ b/src/compiler/translator/CollectVariables.h
@@ -30,6 +30,7 @@
                       ShHashFunction64 hashFunction,
                       TSymbolTable *symbolTable,
                       int shaderVersion,
+                      GLenum shaderType,
                       const TExtensionBehavior &extensionBehavior);
 }
 
diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp
index 8ef6285..4b39fe1 100644
--- a/src/compiler/translator/Compiler.cpp
+++ b/src/compiler/translator/Compiler.cpp
@@ -476,7 +476,8 @@
             ASSERT(!variablesCollected);
             CollectVariables(root, &attributes, &outputVariables, &uniforms, &inputVaryings,
                              &outputVaryings, &uniformBlocks, &shaderStorageBlocks, &inBlocks,
-                             hashFunction, &symbolTable, shaderVersion, extensionBehavior);
+                             hashFunction, &symbolTable, shaderVersion, shaderType,
+                             extensionBehavior);
             collectInterfaceBlocks();
             variablesCollected = true;
             if (compileOptions & SH_USE_UNUSED_STANDARD_SHARED_BLOCKS)
@@ -740,9 +741,18 @@
         << ":MaxFragmentAtomicCounterBuffers:" << compileResources.MaxFragmentAtomicCounterBuffers
         << ":MaxCombinedAtomicCounterBuffers:" << compileResources.MaxCombinedAtomicCounterBuffers
         << ":MaxAtomicCounterBufferSize:" << compileResources.MaxAtomicCounterBufferSize
-        << ":MaxGeometryOutputVertices:" << compileResources.MaxGeometryOutputVertices
         << ":MaxGeometryUniformComponents:" << compileResources.MaxGeometryUniformComponents
-        << ":MaxGeometryShaderInvocations:" << compileResources.MaxGeometryShaderInvocations;
+        << ":MaxGeometryUniformBlocks:" << compileResources.MaxGeometryUniformBlocks
+        << ":MaxGeometryInputComponents:" << compileResources.MaxGeometryInputComponents
+        << ":MaxGeometryOutputComponents:" << compileResources.MaxGeometryOutputComponents
+        << ":MaxGeometryOutputVertices:" << compileResources.MaxGeometryOutputVertices
+        << ":MaxGeometryTotalOutputComponents:" << compileResources.MaxGeometryTotalOutputComponents
+        << ":MaxGeometryTextureImageUnits:" << compileResources.MaxGeometryTextureImageUnits
+        << ":MaxGeometryAtomicCounterBuffers:" << compileResources.MaxGeometryAtomicCounterBuffers
+        << ":MaxGeometryAtomicCounters:" << compileResources.MaxGeometryAtomicCounters
+        << ":MaxGeometryShaderStorageBlocks:" << compileResources.MaxGeometryShaderStorageBlocks
+        << ":MaxGeometryShaderInvocations:" << compileResources.MaxGeometryShaderInvocations
+        << ":MaxGeometryImageUniforms:" << compileResources.MaxGeometryImageUniforms;
     // clang-format on
 
     builtInResourcesString = strstream.str();
@@ -1010,7 +1020,7 @@
 void TCompiler::initializeOutputVariables(TIntermBlock *root)
 {
     InitVariableList list;
-    if (shaderType == GL_VERTEX_SHADER)
+    if (shaderType == GL_VERTEX_SHADER || shaderType == GL_GEOMETRY_SHADER_OES)
     {
         for (auto var : outputVaryings)
         {
diff --git a/src/compiler/translator/Initialize.cpp b/src/compiler/translator/Initialize.cpp
index 52ae411..dc2cd12 100644
--- a/src/compiler/translator/Initialize.cpp
+++ b/src/compiler/translator/Initialize.cpp
@@ -676,6 +676,15 @@
                                                       voidType, "groupMemoryBarrier");
     }
 
+    if (type == GL_GEOMETRY_SHADER_OES)
+    {
+        const char *extension = "GL_OES_geometry_shader";
+        symbolTable.insertBuiltInFunctionNoParametersExt(ESSL3_1_BUILTINS, extension, EOpEmitVertex,
+                                                         voidType, "EmitVertex");
+        symbolTable.insertBuiltInFunctionNoParametersExt(ESSL3_1_BUILTINS, extension,
+                                                         EOpEndPrimitive, voidType, "EndPrimitive");
+    }
+
     //
     // Depth range in window coordinates
     //
@@ -720,7 +729,7 @@
     {
         symbolTable.insertConstIntExt(COMMON_BUILTINS, "GL_EXT_blend_func_extended",
                                       "gl_MaxDualSourceDrawBuffersEXT",
-                                      resources.MaxDualSourceDrawBuffers);
+                                      resources.MaxDualSourceDrawBuffers, EbpMedium);
     }
 
     symbolTable.insertConstInt(ESSL3_BUILTINS, "gl_MaxVertexOutputVectors",
@@ -777,6 +786,29 @@
                                resources.MaxCombinedAtomicCounterBuffers, EbpMedium);
     symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxAtomicCounterBufferSize",
                                resources.MaxAtomicCounterBufferSize, EbpMedium);
+
+    if (resources.OES_geometry_shader)
+    {
+        const char *ext = "GL_OES_geometry_shader";
+        symbolTable.insertConstIntExt(ESSL3_1_BUILTINS, ext, "gl_MaxGeometryInputComponents",
+                                      resources.MaxGeometryInputComponents, EbpMedium);
+        symbolTable.insertConstIntExt(ESSL3_1_BUILTINS, ext, "gl_MaxGeometryOutputComponents",
+                                      resources.MaxGeometryOutputComponents, EbpMedium);
+        symbolTable.insertConstIntExt(ESSL3_1_BUILTINS, ext, "gl_MaxGeometryImageUniforms",
+                                      resources.MaxGeometryImageUniforms, EbpMedium);
+        symbolTable.insertConstIntExt(ESSL3_1_BUILTINS, ext, "gl_MaxGeometryTextureImageUnits",
+                                      resources.MaxGeometryTextureImageUnits, EbpMedium);
+        symbolTable.insertConstIntExt(ESSL3_1_BUILTINS, ext, "gl_MaxGeometryOutputVertices",
+                                      resources.MaxGeometryOutputVertices, EbpMedium);
+        symbolTable.insertConstIntExt(ESSL3_1_BUILTINS, ext, "gl_MaxGeometryTotalOutputComponents",
+                                      resources.MaxGeometryTotalOutputComponents, EbpMedium);
+        symbolTable.insertConstIntExt(ESSL3_1_BUILTINS, ext, "gl_MaxGeometryUniformComponents",
+                                      resources.MaxGeometryUniformComponents, EbpMedium);
+        symbolTable.insertConstIntExt(ESSL3_1_BUILTINS, ext, "gl_MaxGeometryAtomicCounters",
+                                      resources.MaxGeometryAtomicCounters, EbpMedium);
+        symbolTable.insertConstIntExt(ESSL3_1_BUILTINS, ext, "gl_MaxGeometryAtomicCounterBuffers",
+                                      resources.MaxGeometryAtomicCounterBuffers, EbpMedium);
+    }
 }
 
 void IdentifyBuiltIns(sh::GLenum type,
@@ -872,6 +904,15 @@
                                               TType(EbtFloat, EbpMedium, EvqLastFragColor, 4));
             }
 
+            if (resources.OES_geometry_shader)
+            {
+                const char *extension = "GL_OES_geometry_shader";
+                symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension, "gl_PrimitiveID",
+                                              TType(EbtInt, EbpHigh, EvqPrimitiveID, 1));
+                symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension, "gl_Layer",
+                                              TType(EbtInt, EbpHigh, EvqLayer, 1));
+            }
+
             break;
         }
         case GL_VERTEX_SHADER:
@@ -909,7 +950,6 @@
 
         case GL_GEOMETRY_SHADER_OES:
         {
-            // TODO(jiawei.shao@intel.com): add all Geometry Shader built-in variables.
             const char *extension = "GL_OES_geometry_shader";
 
             // Add built-in interface block gl_PerVertex and the built-in array gl_in.
@@ -932,6 +972,20 @@
             glInType.makeArray(0u);
             symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension, "gl_in", glInType);
 
+            TType glPositionType(EbtFloat, EbpHigh, EvqPosition, 4);
+            glPositionType.setInterfaceBlock(new TInterfaceBlock(
+                glPerVertexString, fieldList, nullptr, TLayoutQualifier::create()));
+            symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension, "gl_Position",
+                                          glPositionType);
+            symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension, "gl_PrimitiveIDIn",
+                                          TType(EbtInt, EbpHigh, EvqPrimitiveIDIn, 1));
+            symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension, "gl_InvocationID",
+                                          TType(EbtInt, EbpHigh, EvqInvocationID, 1));
+            symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension, "gl_PrimitiveID",
+                                          TType(EbtInt, EbpHigh, EvqPrimitiveID, 1));
+            symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension, "gl_Layer",
+                                          TType(EbtInt, EbpHigh, EvqLayer, 1));
+
             break;
         }
         default:
diff --git a/src/compiler/translator/Operator.cpp b/src/compiler/translator/Operator.cpp
index da92115..61e39e6 100644
--- a/src/compiler/translator/Operator.cpp
+++ b/src/compiler/translator/Operator.cpp
@@ -341,6 +341,11 @@
             return "memoryBarrierShared";
         case EOpGroupMemoryBarrier:
             return "groupMemoryBarrier";
+
+        case EOpEmitVertex:
+            return "EmitVertex";
+        case EOpEndPrimitive:
+            return "EndPrimitive";
         default:
             break;
     }
diff --git a/src/compiler/translator/Operator.h b/src/compiler/translator/Operator.h
index 23f78a6..c42a564 100644
--- a/src/compiler/translator/Operator.h
+++ b/src/compiler/translator/Operator.h
@@ -237,7 +237,11 @@
     EOpMemoryBarrierBuffer,
     EOpMemoryBarrierImage,
     EOpMemoryBarrierShared,
-    EOpGroupMemoryBarrier
+    EOpGroupMemoryBarrier,
+
+    //  Geometry only
+    EOpEmitVertex,
+    EOpEndPrimitive
 };
 
 // Returns the string corresponding to the operator in GLSL
diff --git a/src/compiler/translator/OutputGLSLBase.cpp b/src/compiler/translator/OutputGLSLBase.cpp
index e2c5b16..c0c0437 100644
--- a/src/compiler/translator/OutputGLSLBase.cpp
+++ b/src/compiler/translator/OutputGLSLBase.cpp
@@ -1006,6 +1006,8 @@
         case EOpMemoryBarrierImage:
         case EOpMemoryBarrierShared:
         case EOpGroupMemoryBarrier:
+        case EOpEmitVertex:
+        case EOpEndPrimitive:
             writeBuiltInFunctionTriplet(visit, node->getOp(), node->getUseEmulatedFunction());
             break;
         default:
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index a1c12f3..6a312b6 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -521,6 +521,24 @@
         case EvqPerVertexIn:
             message = "can't modify any member in gl_in";
             break;
+        case EvqPrimitiveIDIn:
+            message = "can't modify gl_PrimitiveIDIn";
+            break;
+        case EvqInvocationID:
+            message = "can't modify gl_InvocationID";
+            break;
+        case EvqPrimitiveID:
+            if (mShaderType == GL_FRAGMENT_SHADER)
+            {
+                message = "can't modify gl_PrimitiveID in a fragment shader";
+            }
+            break;
+        case EvqLayer:
+            if (mShaderType == GL_FRAGMENT_SHADER)
+            {
+                message = "can't modify gl_Layer in a fragment shader";
+            }
+            break;
         default:
             //
             // Type that can't be written to?
diff --git a/src/compiler/translator/ShaderLang.cpp b/src/compiler/translator/ShaderLang.cpp
index f9a5bb5..75e5490 100644
--- a/src/compiler/translator/ShaderLang.cpp
+++ b/src/compiler/translator/ShaderLang.cpp
@@ -236,10 +236,18 @@
 
     resources->MaxUniformBufferBindings = 32;
 
-    // TODO(jiawei.shao@intel.com): add complete geometry shader constants.
-    resources->MaxGeometryUniformComponents = 1024;
-    resources->MaxGeometryOutputVertices    = 256;
-    resources->MaxGeometryShaderInvocations = 32;
+    resources->MaxGeometryUniformComponents     = 1024;
+    resources->MaxGeometryUniformBlocks         = 12;
+    resources->MaxGeometryInputComponents       = 64;
+    resources->MaxGeometryOutputComponents      = 64;
+    resources->MaxGeometryOutputVertices        = 256;
+    resources->MaxGeometryTotalOutputComponents = 1024;
+    resources->MaxGeometryTextureImageUnits     = 16;
+    resources->MaxGeometryAtomicCounterBuffers  = 0;
+    resources->MaxGeometryAtomicCounters        = 0;
+    resources->MaxGeometryShaderStorageBlocks   = 0;
+    resources->MaxGeometryShaderInvocations     = 32;
+    resources->MaxGeometryImageUniforms         = 0;
 }
 
 //
@@ -365,7 +373,7 @@
     return GetShaderVariables<Uniform>(handle);
 }
 
-const std::vector<sh::Varying> *GetInputVaryings(const ShHandle handle)
+const std::vector<Varying> *GetInputVaryings(const ShHandle handle)
 {
     TCompiler *compiler = GetCompilerFromHandle(handle);
     if (compiler == nullptr)
@@ -375,7 +383,7 @@
     return &compiler->getInputVaryings();
 }
 
-const std::vector<sh::Varying> *GetOutputVaryings(const ShHandle handle)
+const std::vector<Varying> *GetOutputVaryings(const ShHandle handle)
 {
     TCompiler *compiler = GetCompilerFromHandle(handle);
     if (compiler == nullptr)
diff --git a/src/compiler/translator/SymbolTable.cpp b/src/compiler/translator/SymbolTable.cpp
index 5d15c9b..1c2de5c 100644
--- a/src/compiler/translator/SymbolTable.cpp
+++ b/src/compiler/translator/SymbolTable.cpp
@@ -557,6 +557,16 @@
     insert(level, new TFunction(this, NewPoolTString(name), rvalue, op));
 }
 
+void TSymbolTable::insertBuiltInFunctionNoParametersExt(ESymbolLevel level,
+                                                        const char *ext,
+                                                        TOperator op,
+                                                        const TType *rvalue,
+                                                        const char *name)
+{
+    insertUnmangledBuiltInName(name, level);
+    insert(level, new TFunction(this, NewPoolTString(name), rvalue, op, ext));
+}
+
 TPrecision TSymbolTable::getDefaultPrecision(TBasicType type) const
 {
     if (!SupportsPrecision(type))
diff --git a/src/compiler/translator/SymbolTable.h b/src/compiler/translator/SymbolTable.h
index 6d9b714..58788a1 100644
--- a/src/compiler/translator/SymbolTable.h
+++ b/src/compiler/translator/SymbolTable.h
@@ -361,10 +361,14 @@
         return insert(level, constant);
     }
 
-    bool insertConstIntExt(ESymbolLevel level, const char *ext, const char *name, int value)
+    bool insertConstIntExt(ESymbolLevel level,
+                           const char *ext,
+                           const char *name,
+                           int value,
+                           TPrecision precision)
     {
         TVariable *constant =
-            new TVariable(this, NewPoolTString(name), TType(EbtInt, EbpUndefined, EvqConst, 1));
+            new TVariable(this, NewPoolTString(name), TType(EbtInt, precision, EvqConst, 1));
         TConstantUnion *unionArray = new TConstantUnion[1];
         unionArray[0].setIConst(value);
         constant->shareConstPointer(unionArray);
@@ -451,6 +455,12 @@
                                            const TType *rvalue,
                                            const char *name);
 
+    void insertBuiltInFunctionNoParametersExt(ESymbolLevel level,
+                                              const char *ext,
+                                              TOperator op,
+                                              const TType *rvalue,
+                                              const char *name);
+
     TSymbol *find(const TString &name,
                   int shaderVersion,
                   bool *builtIn   = nullptr,