Edit

kc3-lang/angle/src/compiler/translator/CollectVariables.cpp

Branch :

  • Show log

    Commit

  • Author : Tobin Ehlis
    Date : 2019-11-15 14:40:31
    Hash : 240befe5
    Message : Add support for gl_HelperInvocation Added HelperInvocation to builtin_variables.json, regenerate the codegen portions of compiler, and plumb support for HelperInvocation through the rest of the compiler. Skipping some fails on Android and Swiftshader for this initial change and will debug/fix those issues in a follow-on. Bug: angleproject:4110 Change-Id: I781a2782ace84200bc615a2cc26b908a62e2aa26 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1922061 Commit-Queue: Tobin Ehlis <tobine@google.com> Reviewed-by: Jamie Madill <jmadill@chromium.org>

  • src/compiler/translator/CollectVariables.cpp
  • //
    // Copyright 2002 The ANGLE Project Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    //
    // CollectVariables.cpp: Collect lists of shader interface variables based on the AST.
    
    #include "compiler/translator/CollectVariables.h"
    
    #include "angle_gl.h"
    #include "common/utilities.h"
    #include "compiler/translator/HashNames.h"
    #include "compiler/translator/SymbolTable.h"
    #include "compiler/translator/tree_util/IntermTraverse.h"
    #include "compiler/translator/util.h"
    
    namespace sh
    {
    
    namespace
    {
    
    BlockLayoutType GetBlockLayoutType(TLayoutBlockStorage blockStorage)
    {
        switch (blockStorage)
        {
            case EbsPacked:
                return BLOCKLAYOUT_PACKED;
            case EbsShared:
                return BLOCKLAYOUT_SHARED;
            case EbsStd140:
                return BLOCKLAYOUT_STD140;
            case EbsStd430:
                return BLOCKLAYOUT_STD430;
            default:
                UNREACHABLE();
                return BLOCKLAYOUT_SHARED;
        }
    }
    
    // TODO(jiawei.shao@intel.com): implement GL_EXT_shader_io_blocks.
    BlockType GetBlockType(TQualifier qualifier)
    {
        switch (qualifier)
        {
            case EvqUniform:
                return BlockType::BLOCK_UNIFORM;
            case EvqBuffer:
                return BlockType::BLOCK_BUFFER;
            case EvqPerVertexIn:
                return BlockType::BLOCK_IN;
            default:
                UNREACHABLE();
                return BlockType::BLOCK_UNIFORM;
        }
    }
    
    template <class VarT>
    VarT *FindVariable(const ImmutableString &name, std::vector<VarT> *infoList)
    {
        // TODO(zmo): optimize this function.
        for (size_t ii = 0; ii < infoList->size(); ++ii)
        {
            if (name == (*infoList)[ii].name)
                return &((*infoList)[ii]);
        }
    
        return nullptr;
    }
    
    void MarkActive(ShaderVariable *variable)
    {
        if (!variable->active)
        {
            if (variable->isStruct())
            {
                // Conservatively assume all fields are statically used as well.
                for (auto &field : variable->fields)
                {
                    MarkActive(&field);
                }
            }
            variable->staticUse = true;
            variable->active    = true;
        }
    }
    
    ShaderVariable *FindVariableInInterfaceBlock(const ImmutableString &name,
                                                 const TInterfaceBlock *interfaceBlock,
                                                 std::vector<InterfaceBlock> *infoList)
    {
        ASSERT(interfaceBlock);
        InterfaceBlock *namedBlock = FindVariable(interfaceBlock->name(), infoList);
        ASSERT(namedBlock);
    
        // Set static use on the parent interface block here
        namedBlock->staticUse = true;
        namedBlock->active    = true;
        return FindVariable(name, &namedBlock->fields);
    }
    
    // Traverses the intermediate tree to collect all attributes, uniforms, varyings, fragment outputs,
    // and interface blocks.
    class CollectVariablesTraverser : public TIntermTraverser
    {
      public:
        CollectVariablesTraverser(std::vector<ShaderVariable> *attribs,
                                  std::vector<ShaderVariable> *outputVariables,
                                  std::vector<ShaderVariable> *uniforms,
                                  std::vector<ShaderVariable> *inputVaryings,
                                  std::vector<ShaderVariable> *outputVaryings,
                                  std::vector<InterfaceBlock> *uniformBlocks,
                                  std::vector<InterfaceBlock> *shaderStorageBlocks,
                                  std::vector<InterfaceBlock> *inBlocks,
                                  ShHashFunction64 hashFunction,
                                  TSymbolTable *symbolTable,
                                  GLenum shaderType,
                                  const TExtensionBehavior &extensionBehavior);
    
        bool visitInvariantDeclaration(Visit visit, TIntermInvariantDeclaration *node) override;
        void visitSymbol(TIntermSymbol *symbol) override;
        bool visitDeclaration(Visit, TIntermDeclaration *node) override;
        bool visitBinary(Visit visit, TIntermBinary *binaryNode) override;
    
      private:
        std::string getMappedName(const TSymbol *symbol) const;
    
        void setFieldOrVariableProperties(const TType &type,
                                          bool staticUse,
                                          ShaderVariable *variableOut) const;
        void setFieldProperties(const TType &type,
                                const ImmutableString &name,
                                bool staticUse,
                                ShaderVariable *variableOut) const;
        void setCommonVariableProperties(const TType &type,
                                         const TVariable &variable,
                                         ShaderVariable *variableOut) const;
    
        ShaderVariable recordAttribute(const TIntermSymbol &variable) const;
        ShaderVariable recordOutputVariable(const TIntermSymbol &variable) const;
        ShaderVariable recordVarying(const TIntermSymbol &variable) const;
        void recordInterfaceBlock(const char *instanceName,
                                  const TType &interfaceBlockType,
                                  InterfaceBlock *interfaceBlock) const;
        ShaderVariable recordUniform(const TIntermSymbol &variable) const;
    
        void setBuiltInInfoFromSymbol(const TVariable &variable, ShaderVariable *info);
    
        void recordBuiltInVaryingUsed(const TVariable &variable,
                                      bool *addedFlag,
                                      std::vector<ShaderVariable> *varyings);
        void recordBuiltInFragmentOutputUsed(const TVariable &variable, bool *addedFlag);
        void recordBuiltInAttributeUsed(const TVariable &variable, bool *addedFlag);
        InterfaceBlock *recordGLInUsed(const TType &glInType);
        InterfaceBlock *findNamedInterfaceBlock(const ImmutableString &name) const;
    
        std::vector<ShaderVariable> *mAttribs;
        std::vector<ShaderVariable> *mOutputVariables;
        std::vector<ShaderVariable> *mUniforms;
        std::vector<ShaderVariable> *mInputVaryings;
        std::vector<ShaderVariable> *mOutputVaryings;
        std::vector<InterfaceBlock> *mUniformBlocks;
        std::vector<InterfaceBlock> *mShaderStorageBlocks;
        std::vector<InterfaceBlock> *mInBlocks;
    
        std::map<std::string, ShaderVariable *> mInterfaceBlockFields;
    
        // Shader uniforms
        bool mDepthRangeAdded;
    
        // Vertex Shader builtins
        bool mInstanceIDAdded;
        bool mVertexIDAdded;
        bool mPointSizeAdded;
        bool mDrawIDAdded;
        bool mBaseVertexAdded;
        bool mBaseInstanceAdded;
    
        // Vertex Shader and Geometry Shader builtins
        bool mPositionAdded;
    
        // Fragment Shader builtins
        bool mPointCoordAdded;
        bool mFrontFacingAdded;
        bool mHelperInvocationAdded;
        bool mFragCoordAdded;
        bool mLastFragDataAdded;
        bool mFragColorAdded;
        bool mFragDataAdded;
        bool mFragDepthEXTAdded;
        bool mFragDepthAdded;
        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;
    
        GLenum mShaderType;
        const TExtensionBehavior &mExtensionBehavior;
    };
    
    CollectVariablesTraverser::CollectVariablesTraverser(
        std::vector<sh::ShaderVariable> *attribs,
        std::vector<sh::ShaderVariable> *outputVariables,
        std::vector<sh::ShaderVariable> *uniforms,
        std::vector<sh::ShaderVariable> *inputVaryings,
        std::vector<sh::ShaderVariable> *outputVaryings,
        std::vector<sh::InterfaceBlock> *uniformBlocks,
        std::vector<sh::InterfaceBlock> *shaderStorageBlocks,
        std::vector<sh::InterfaceBlock> *inBlocks,
        ShHashFunction64 hashFunction,
        TSymbolTable *symbolTable,
        GLenum shaderType,
        const TExtensionBehavior &extensionBehavior)
        : TIntermTraverser(true, false, false, symbolTable),
          mAttribs(attribs),
          mOutputVariables(outputVariables),
          mUniforms(uniforms),
          mInputVaryings(inputVaryings),
          mOutputVaryings(outputVaryings),
          mUniformBlocks(uniformBlocks),
          mShaderStorageBlocks(shaderStorageBlocks),
          mInBlocks(inBlocks),
          mDepthRangeAdded(false),
          mInstanceIDAdded(false),
          mVertexIDAdded(false),
          mPointSizeAdded(false),
          mDrawIDAdded(false),
          mBaseVertexAdded(false),
          mBaseInstanceAdded(false),
          mPositionAdded(false),
          mPointCoordAdded(false),
          mFrontFacingAdded(false),
          mHelperInvocationAdded(false),
          mFragCoordAdded(false),
          mLastFragDataAdded(false),
          mFragColorAdded(false),
          mFragDataAdded(false),
          mFragDepthEXTAdded(false),
          mFragDepthAdded(false),
          mSecondaryFragColorEXTAdded(false),
          mSecondaryFragDataEXTAdded(false),
          mPerVertexInAdded(false),
          mPrimitiveIDInAdded(false),
          mInvocationIDAdded(false),
          mPrimitiveIDAdded(false),
          mLayerAdded(false),
          mHashFunction(hashFunction),
          mShaderType(shaderType),
          mExtensionBehavior(extensionBehavior)
    {}
    
    std::string CollectVariablesTraverser::getMappedName(const TSymbol *symbol) const
    {
        return HashName(symbol, mHashFunction, nullptr).data();
    }
    
    void CollectVariablesTraverser::setBuiltInInfoFromSymbol(const TVariable &variable,
                                                             ShaderVariable *info)
    {
        const TType &type = variable.getType();
    
        info->name       = variable.name().data();
        info->mappedName = variable.name().data();
        info->type       = GLVariableType(type);
        info->precision  = GLVariablePrecision(type);
        if (auto *arraySizes = type.getArraySizes())
        {
            info->arraySizes.assign(arraySizes->begin(), arraySizes->end());
        }
    }
    
    void CollectVariablesTraverser::recordBuiltInVaryingUsed(const TVariable &variable,
                                                             bool *addedFlag,
                                                             std::vector<ShaderVariable> *varyings)
    {
        ASSERT(varyings);
        if (!(*addedFlag))
        {
            ShaderVariable info;
            setBuiltInInfoFromSymbol(variable, &info);
            info.staticUse   = true;
            info.active      = true;
            info.isInvariant = mSymbolTable->isVaryingInvariant(variable);
            varyings->push_back(info);
            (*addedFlag) = true;
        }
    }
    
    void CollectVariablesTraverser::recordBuiltInFragmentOutputUsed(const TVariable &variable,
                                                                    bool *addedFlag)
    {
        if (!(*addedFlag))
        {
            ShaderVariable info;
            setBuiltInInfoFromSymbol(variable, &info);
            info.staticUse = true;
            info.active    = true;
            mOutputVariables->push_back(info);
            (*addedFlag) = true;
        }
    }
    
    void CollectVariablesTraverser::recordBuiltInAttributeUsed(const TVariable &variable,
                                                               bool *addedFlag)
    {
        if (!(*addedFlag))
        {
            ShaderVariable info;
            setBuiltInInfoFromSymbol(variable, &info);
            info.staticUse = true;
            info.active    = true;
            info.location  = -1;
            mAttribs->push_back(info);
            (*addedFlag) = true;
        }
    }
    
    InterfaceBlock *CollectVariablesTraverser::recordGLInUsed(const TType &glInType)
    {
        if (!mPerVertexInAdded)
        {
            ASSERT(glInType.getQualifier() == EvqPerVertexIn);
            InterfaceBlock info;
            recordInterfaceBlock("gl_in", glInType, &info);
    
            mPerVertexInAdded = true;
            mInBlocks->push_back(info);
            return &mInBlocks->back();
        }
        else
        {
            return FindVariable(ImmutableString("gl_PerVertex"), mInBlocks);
        }
    }
    
    bool CollectVariablesTraverser::visitInvariantDeclaration(Visit visit,
                                                              TIntermInvariantDeclaration *node)
    {
        // We should not mark variables as active just based on an invariant declaration, so we don't
        // traverse the symbols declared invariant.
        return false;
    }
    
    // We want to check whether a uniform/varying is active because we need to skip updating inactive
    // ones. We also only count the active ones in packing computing. Also, gl_FragCoord, gl_PointCoord,
    // and gl_FrontFacing count toward varying counting if they are active in a fragment shader.
    void CollectVariablesTraverser::visitSymbol(TIntermSymbol *symbol)
    {
        ASSERT(symbol != nullptr);
    
        if (symbol->variable().symbolType() == SymbolType::AngleInternal ||
            symbol->variable().symbolType() == SymbolType::Empty)
        {
            // Internal variables or nameless variables are not collected.
            return;
        }
    
        ShaderVariable *var = nullptr;
    
        const ImmutableString &symbolName = symbol->getName();
    
        // Check the qualifier from the variable, not from the symbol node. The node may have a
        // different qualifier if it's the result of a folded ternary node.
        TQualifier qualifier = symbol->variable().getType().getQualifier();
    
        if (IsVaryingIn(qualifier))
        {
            var = FindVariable(symbolName, mInputVaryings);
        }
        else if (IsVaryingOut(qualifier))
        {
            var = FindVariable(symbolName, mOutputVaryings);
        }
        else if (symbol->getType().getBasicType() == EbtInterfaceBlock)
        {
            UNREACHABLE();
        }
        else if (symbolName == "gl_DepthRange")
        {
            ASSERT(qualifier == EvqUniform);
    
            if (!mDepthRangeAdded)
            {
                ShaderVariable info;
                const char kName[] = "gl_DepthRange";
                info.name          = kName;
                info.mappedName    = kName;
                info.type          = GL_NONE;
                info.precision     = GL_NONE;
                info.staticUse     = true;
                info.active        = true;
    
                ShaderVariable nearInfo(GL_FLOAT);
                const char kNearName[] = "near";
                nearInfo.name          = kNearName;
                nearInfo.mappedName    = kNearName;
                nearInfo.precision     = GL_HIGH_FLOAT;
                nearInfo.staticUse     = true;
                nearInfo.active        = true;
    
                ShaderVariable farInfo(GL_FLOAT);
                const char kFarName[] = "far";
                farInfo.name          = kFarName;
                farInfo.mappedName    = kFarName;
                farInfo.precision     = GL_HIGH_FLOAT;
                farInfo.staticUse     = true;
                farInfo.active        = true;
    
                ShaderVariable diffInfo(GL_FLOAT);
                const char kDiffName[] = "diff";
                diffInfo.name          = kDiffName;
                diffInfo.mappedName    = kDiffName;
                diffInfo.precision     = GL_HIGH_FLOAT;
                diffInfo.staticUse     = true;
                diffInfo.active        = true;
    
                info.fields.push_back(nearInfo);
                info.fields.push_back(farInfo);
                info.fields.push_back(diffInfo);
    
                mUniforms->push_back(info);
                mDepthRangeAdded = true;
            }
        }
        else
        {
            switch (qualifier)
            {
                case EvqAttribute:
                case EvqVertexIn:
                    var = FindVariable(symbolName, mAttribs);
                    break;
                case EvqFragmentOut:
                    var = FindVariable(symbolName, mOutputVariables);
                    break;
                case EvqUniform:
                {
                    const TInterfaceBlock *interfaceBlock = symbol->getType().getInterfaceBlock();
                    if (interfaceBlock)
                    {
                        var = FindVariableInInterfaceBlock(symbolName, interfaceBlock, mUniformBlocks);
                    }
                    else
                    {
                        var = FindVariable(symbolName, mUniforms);
                    }
    
                    // It's an internal error to reference an undefined user uniform
                    ASSERT(!symbolName.beginsWith("gl_") || var);
                }
                break;
                case EvqBuffer:
                {
                    const TInterfaceBlock *interfaceBlock = symbol->getType().getInterfaceBlock();
                    var =
                        FindVariableInInterfaceBlock(symbolName, interfaceBlock, mShaderStorageBlocks);
                }
                break;
                case EvqFragCoord:
                    recordBuiltInVaryingUsed(symbol->variable(), &mFragCoordAdded, mInputVaryings);
                    return;
                case EvqFrontFacing:
                    recordBuiltInVaryingUsed(symbol->variable(), &mFrontFacingAdded, mInputVaryings);
                    return;
                case EvqHelperInvocation:
                    recordBuiltInVaryingUsed(symbol->variable(), &mHelperInvocationAdded,
                                             mInputVaryings);
                    return;
                case EvqPointCoord:
                    recordBuiltInVaryingUsed(symbol->variable(), &mPointCoordAdded, mInputVaryings);
                    return;
                case EvqInstanceID:
                    // Whenever the SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW option is set,
                    // gl_InstanceID is added inside expressions to initialize ViewID_OVR and
                    // InstanceID. Note that gl_InstanceID is not added to the symbol table for ESSL1
                    // shaders.
                    recordBuiltInAttributeUsed(symbol->variable(), &mInstanceIDAdded);
                    return;
                case EvqVertexID:
                    recordBuiltInAttributeUsed(symbol->variable(), &mVertexIDAdded);
                    return;
                case EvqPosition:
                    recordBuiltInVaryingUsed(symbol->variable(), &mPositionAdded, mOutputVaryings);
                    return;
                case EvqPointSize:
                    recordBuiltInVaryingUsed(symbol->variable(), &mPointSizeAdded, mOutputVaryings);
                    return;
                case EvqDrawID:
                    recordBuiltInAttributeUsed(symbol->variable(), &mDrawIDAdded);
                    return;
                case EvqBaseVertex:
                    recordBuiltInAttributeUsed(symbol->variable(), &mBaseVertexAdded);
                    return;
                case EvqBaseInstance:
                    recordBuiltInAttributeUsed(symbol->variable(), &mBaseInstanceAdded);
                    return;
                case EvqLastFragData:
                    recordBuiltInVaryingUsed(symbol->variable(), &mLastFragDataAdded, mInputVaryings);
                    return;
                case EvqFragColor:
                    recordBuiltInFragmentOutputUsed(symbol->variable(), &mFragColorAdded);
                    return;
                case EvqFragData:
                    if (!mFragDataAdded)
                    {
                        ShaderVariable info;
                        setBuiltInInfoFromSymbol(symbol->variable(), &info);
                        if (!IsExtensionEnabled(mExtensionBehavior, TExtension::EXT_draw_buffers))
                        {
                            ASSERT(info.arraySizes.size() == 1u);
                            info.arraySizes.back() = 1u;
                        }
                        info.staticUse = true;
                        info.active    = true;
                        mOutputVariables->push_back(info);
                        mFragDataAdded = true;
                    }
                    return;
                case EvqFragDepthEXT:
                    recordBuiltInFragmentOutputUsed(symbol->variable(), &mFragDepthEXTAdded);
                    return;
                case EvqFragDepth:
                    recordBuiltInFragmentOutputUsed(symbol->variable(), &mFragDepthAdded);
                    return;
                case EvqSecondaryFragColorEXT:
                    recordBuiltInFragmentOutputUsed(symbol->variable(), &mSecondaryFragColorEXTAdded);
                    return;
                case EvqSecondaryFragDataEXT:
                    recordBuiltInFragmentOutputUsed(symbol->variable(), &mSecondaryFragDataEXTAdded);
                    return;
                case EvqInvocationID:
                    recordBuiltInVaryingUsed(symbol->variable(), &mInvocationIDAdded, mInputVaryings);
                    break;
                case EvqPrimitiveIDIn:
                    recordBuiltInVaryingUsed(symbol->variable(), &mPrimitiveIDInAdded, mInputVaryings);
                    break;
                case EvqPrimitiveID:
                    if (mShaderType == GL_GEOMETRY_SHADER_EXT)
                    {
                        recordBuiltInVaryingUsed(symbol->variable(), &mPrimitiveIDAdded,
                                                 mOutputVaryings);
                    }
                    else
                    {
                        ASSERT(mShaderType == GL_FRAGMENT_SHADER);
                        recordBuiltInVaryingUsed(symbol->variable(), &mPrimitiveIDAdded,
                                                 mInputVaryings);
                    }
                    break;
                case EvqLayer:
                    if (mShaderType == GL_GEOMETRY_SHADER_EXT)
                    {
                        recordBuiltInVaryingUsed(symbol->variable(), &mLayerAdded, mOutputVaryings);
                    }
                    else if (mShaderType == GL_FRAGMENT_SHADER)
                    {
                        recordBuiltInVaryingUsed(symbol->variable(), &mLayerAdded, mInputVaryings);
                    }
                    else
                    {
                        ASSERT(mShaderType == GL_VERTEX_SHADER &&
                               (IsExtensionEnabled(mExtensionBehavior, TExtension::OVR_multiview2) ||
                                IsExtensionEnabled(mExtensionBehavior, TExtension::OVR_multiview)));
                    }
                    break;
                default:
                    break;
            }
        }
        if (var)
        {
            MarkActive(var);
        }
    }
    
    void CollectVariablesTraverser::setFieldOrVariableProperties(const TType &type,
                                                                 bool staticUse,
                                                                 ShaderVariable *variableOut) const
    {
        ASSERT(variableOut);
    
        variableOut->staticUse = staticUse;
    
        const TStructure *structure = type.getStruct();
        if (!structure)
        {
            variableOut->type      = GLVariableType(type);
            variableOut->precision = GLVariablePrecision(type);
        }
        else
        {
            // Structures use a NONE type that isn't exposed outside ANGLE.
            variableOut->type = GL_NONE;
            if (structure->symbolType() != SymbolType::Empty)
            {
                variableOut->structName = structure->name().data();
            }
    
            const TFieldList &fields = structure->fields();
    
            for (const TField *field : fields)
            {
                // Regardless of the variable type (uniform, in/out etc.) its fields are always plain
                // ShaderVariable objects.
                ShaderVariable fieldVariable;
                setFieldProperties(*field->type(), field->name(), staticUse, &fieldVariable);
                variableOut->fields.push_back(fieldVariable);
            }
        }
        if (auto *arraySizes = type.getArraySizes())
        {
            variableOut->arraySizes.assign(arraySizes->begin(), arraySizes->end());
        }
    }
    
    void CollectVariablesTraverser::setFieldProperties(const TType &type,
                                                       const ImmutableString &name,
                                                       bool staticUse,
                                                       ShaderVariable *variableOut) const
    {
        ASSERT(variableOut);
        setFieldOrVariableProperties(type, staticUse, variableOut);
        variableOut->name.assign(name.data(), name.length());
        variableOut->mappedName = HashName(name, mHashFunction, nullptr).data();
    }
    
    void CollectVariablesTraverser::setCommonVariableProperties(const TType &type,
                                                                const TVariable &variable,
                                                                ShaderVariable *variableOut) const
    {
        ASSERT(variableOut);
    
        variableOut->staticUse = mSymbolTable->isStaticallyUsed(variable);
        setFieldOrVariableProperties(type, variableOut->staticUse, variableOut);
        ASSERT(variable.symbolType() != SymbolType::Empty);
        variableOut->name.assign(variable.name().data(), variable.name().length());
        variableOut->mappedName = getMappedName(&variable);
    }
    
    ShaderVariable CollectVariablesTraverser::recordAttribute(const TIntermSymbol &variable) const
    {
        const TType &type = variable.getType();
        ASSERT(!type.getStruct());
    
        ShaderVariable attribute;
        setCommonVariableProperties(type, variable.variable(), &attribute);
    
        attribute.location = type.getLayoutQualifier().location;
        return attribute;
    }
    
    ShaderVariable CollectVariablesTraverser::recordOutputVariable(const TIntermSymbol &variable) const
    {
        const TType &type = variable.getType();
        ASSERT(!type.getStruct());
    
        ShaderVariable outputVariable;
        setCommonVariableProperties(type, variable.variable(), &outputVariable);
    
        outputVariable.location = type.getLayoutQualifier().location;
        outputVariable.index    = type.getLayoutQualifier().index;
        return outputVariable;
    }
    
    ShaderVariable CollectVariablesTraverser::recordVarying(const TIntermSymbol &variable) const
    {
        const TType &type = variable.getType();
    
        ShaderVariable varying;
        setCommonVariableProperties(type, variable.variable(), &varying);
        varying.location = type.getLayoutQualifier().location;
    
        switch (type.getQualifier())
        {
            case EvqVaryingIn:
            case EvqVaryingOut:
            case EvqVertexOut:
            case EvqSmoothOut:
            case EvqFlatOut:
            case EvqCentroidOut:
            case EvqGeometryOut:
                if (mSymbolTable->isVaryingInvariant(variable.variable()) || type.isInvariant())
                {
                    varying.isInvariant = true;
                }
                break;
            default:
                break;
        }
    
        varying.interpolation = GetInterpolationType(type.getQualifier());
        return varying;
    }
    
    // TODO(jiawei.shao@intel.com): implement GL_EXT_shader_io_blocks.
    void CollectVariablesTraverser::recordInterfaceBlock(const char *instanceName,
                                                         const TType &interfaceBlockType,
                                                         InterfaceBlock *interfaceBlock) const
    {
        ASSERT(interfaceBlockType.getBasicType() == EbtInterfaceBlock);
        ASSERT(interfaceBlock);
    
        const TInterfaceBlock *blockType = interfaceBlockType.getInterfaceBlock();
        ASSERT(blockType);
    
        interfaceBlock->name       = blockType->name().data();
        interfaceBlock->mappedName = getMappedName(blockType);
        if (instanceName != nullptr)
        {
            interfaceBlock->instanceName = instanceName;
            const TSymbol *blockSymbol   = nullptr;
            if (strncmp(instanceName, "gl_in", 5u) == 0)
            {
                blockSymbol = mSymbolTable->getGlInVariableWithArraySize();
            }
            else
            {
                blockSymbol = mSymbolTable->findGlobal(ImmutableString(instanceName));
            }
            ASSERT(blockSymbol && blockSymbol->isVariable());
            interfaceBlock->staticUse =
                mSymbolTable->isStaticallyUsed(*static_cast<const TVariable *>(blockSymbol));
        }
        ASSERT(!interfaceBlockType.isArrayOfArrays());  // Disallowed by GLSL ES 3.10 section 4.3.9
        interfaceBlock->arraySize =
            interfaceBlockType.isArray() ? interfaceBlockType.getOutermostArraySize() : 0;
    
        interfaceBlock->blockType = GetBlockType(interfaceBlockType.getQualifier());
        if (interfaceBlock->blockType == BlockType::BLOCK_UNIFORM ||
            interfaceBlock->blockType == BlockType::BLOCK_BUFFER)
        {
            // TODO(oetuaho): Remove setting isRowMajorLayout.
            interfaceBlock->isRowMajorLayout = false;
            interfaceBlock->binding          = blockType->blockBinding();
            interfaceBlock->layout           = GetBlockLayoutType(blockType->blockStorage());
        }
    
        // Gather field information
        bool anyFieldStaticallyUsed = false;
        for (const TField *field : blockType->fields())
        {
            const TType &fieldType = *field->type();
    
            bool staticUse = false;
            if (instanceName == nullptr)
            {
                // Static use of individual fields has been recorded, since they are present in the
                // symbol table as variables.
                const TSymbol *fieldSymbol = mSymbolTable->findGlobal(field->name());
                ASSERT(fieldSymbol && fieldSymbol->isVariable());
                staticUse =
                    mSymbolTable->isStaticallyUsed(*static_cast<const TVariable *>(fieldSymbol));
                if (staticUse)
                {
                    anyFieldStaticallyUsed = true;
                }
            }
    
            ShaderVariable fieldVariable;
            setFieldProperties(fieldType, field->name(), staticUse, &fieldVariable);
            fieldVariable.isRowMajorLayout =
                (fieldType.getLayoutQualifier().matrixPacking == EmpRowMajor);
            interfaceBlock->fields.push_back(fieldVariable);
        }
        if (anyFieldStaticallyUsed)
        {
            interfaceBlock->staticUse = true;
        }
    }
    
    ShaderVariable CollectVariablesTraverser::recordUniform(const TIntermSymbol &variable) const
    {
        ShaderVariable uniform;
        setCommonVariableProperties(variable.getType(), variable.variable(), &uniform);
        uniform.binding = variable.getType().getLayoutQualifier().binding;
        uniform.imageUnitFormat =
            GetImageInternalFormatType(variable.getType().getLayoutQualifier().imageInternalFormat);
        uniform.location  = variable.getType().getLayoutQualifier().location;
        uniform.offset    = variable.getType().getLayoutQualifier().offset;
        uniform.readonly  = variable.getType().getMemoryQualifier().readonly;
        uniform.writeonly = variable.getType().getMemoryQualifier().writeonly;
        return uniform;
    }
    
    bool CollectVariablesTraverser::visitDeclaration(Visit, TIntermDeclaration *node)
    {
        const TIntermSequence &sequence = *(node->getSequence());
        ASSERT(!sequence.empty());
    
        const TIntermTyped &typedNode = *(sequence.front()->getAsTyped());
        TQualifier qualifier          = typedNode.getQualifier();
    
        bool isShaderVariable = qualifier == EvqAttribute || qualifier == EvqVertexIn ||
                                qualifier == EvqFragmentOut || qualifier == EvqUniform ||
                                IsVarying(qualifier);
    
        if (typedNode.getBasicType() != EbtInterfaceBlock && !isShaderVariable)
        {
            return true;
        }
    
        for (TIntermNode *variableNode : sequence)
        {
            // The only case in which the sequence will not contain a TIntermSymbol node is
            // initialization. It will contain a TInterBinary node in that case. Since attributes,
            // uniforms, varyings, outputs and interface blocks cannot be initialized in a shader, we
            // must have only TIntermSymbol nodes in the sequence in the cases we are interested in.
            const TIntermSymbol &variable = *variableNode->getAsSymbolNode();
            if (variable.variable().symbolType() == SymbolType::AngleInternal)
            {
                // Internal variables are not collected.
                continue;
            }
    
            // TODO(jiawei.shao@intel.com): implement GL_EXT_shader_io_blocks.
            if (typedNode.getBasicType() == EbtInterfaceBlock)
            {
                InterfaceBlock interfaceBlock;
                recordInterfaceBlock(variable.variable().symbolType() != SymbolType::Empty
                                         ? variable.getName().data()
                                         : nullptr,
                                     variable.getType(), &interfaceBlock);
    
                switch (qualifier)
                {
                    case EvqUniform:
                        mUniformBlocks->push_back(interfaceBlock);
                        break;
                    case EvqBuffer:
                        mShaderStorageBlocks->push_back(interfaceBlock);
                        break;
                    default:
                        UNREACHABLE();
                }
            }
            else
            {
                ASSERT(variable.variable().symbolType() != SymbolType::Empty);
                switch (qualifier)
                {
                    case EvqAttribute:
                    case EvqVertexIn:
                        mAttribs->push_back(recordAttribute(variable));
                        break;
                    case EvqFragmentOut:
                        mOutputVariables->push_back(recordOutputVariable(variable));
                        break;
                    case EvqUniform:
                        mUniforms->push_back(recordUniform(variable));
                        break;
                    default:
                        if (IsVaryingIn(qualifier))
                        {
                            mInputVaryings->push_back(recordVarying(variable));
                        }
                        else
                        {
                            ASSERT(IsVaryingOut(qualifier));
                            mOutputVaryings->push_back(recordVarying(variable));
                        }
                        break;
                }
            }
        }
    
        // None of the recorded variables can have initializers, so we don't need to traverse the
        // declarators.
        return false;
    }
    
    // TODO(jiawei.shao@intel.com): add search on mInBlocks and mOutBlocks when implementing
    // GL_EXT_shader_io_blocks.
    InterfaceBlock *CollectVariablesTraverser::findNamedInterfaceBlock(
        const ImmutableString &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)
        {
            // NOTE: we do not determine static use / activeness for individual blocks of an array.
            TIntermTyped *blockNode = binaryNode->getLeft()->getAsTyped();
            ASSERT(blockNode);
    
            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();
            if (!namedBlock)
            {
                namedBlock = findNamedInterfaceBlock(interfaceBlock->name());
            }
            ASSERT(namedBlock);
            ASSERT(namedBlock->staticUse);
            namedBlock->active      = true;
            unsigned int fieldIndex = static_cast<unsigned int>(constantUnion->getIConst(0));
            ASSERT(fieldIndex < namedBlock->fields.size());
            // TODO(oetuaho): Would be nicer to record static use of fields of named interface blocks
            // more accurately at parse time - now we only mark the fields statically used if they are
            // active. http://anglebug.com/2440
            // We need to mark this field and all of its sub-fields, as static/active
            MarkActive(&namedBlock->fields[fieldIndex]);
    
            if (traverseIndexExpression)
            {
                ASSERT(interfaceIndexingNode);
                interfaceIndexingNode->getRight()->traverse(this);
            }
            return false;
        }
    
        return true;
    }
    
    }  // anonymous namespace
    
    void CollectVariables(TIntermBlock *root,
                          std::vector<ShaderVariable> *attributes,
                          std::vector<ShaderVariable> *outputVariables,
                          std::vector<ShaderVariable> *uniforms,
                          std::vector<ShaderVariable> *inputVaryings,
                          std::vector<ShaderVariable> *outputVaryings,
                          std::vector<InterfaceBlock> *uniformBlocks,
                          std::vector<InterfaceBlock> *shaderStorageBlocks,
                          std::vector<InterfaceBlock> *inBlocks,
                          ShHashFunction64 hashFunction,
                          TSymbolTable *symbolTable,
                          GLenum shaderType,
                          const TExtensionBehavior &extensionBehavior)
    {
        CollectVariablesTraverser collect(attributes, outputVariables, uniforms, inputVaryings,
                                          outputVaryings, uniformBlocks, shaderStorageBlocks, inBlocks,
                                          hashFunction, symbolTable, shaderType, extensionBehavior);
        root->traverse(&collect);
    }
    
    }  // namespace sh