Edit

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

Branch :

  • Show log

    Commit

  • Author : Tim Van Patten
    Date : 2019-09-04 15:39:58
    Hash : 90a58622
    Message : Refactor ShaderVariable to Remove Specializations The following structs are being refactored and moved into the parent struct ShaderVariable: VariableWithLocation Uniform Attribute OutputVariable InterfaceBlockField Varying Bug: angleproject:3899 Test: CQ Change-Id: I389eb3ab4ed44a360e09fca75ecc78d64a277f83 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1785877 Reviewed-by: Jamie Madill <jmadill@chromium.org> Reviewed-by: Courtney Goeltzenleuchter <courtneygo@google.com> Commit-Queue: Tim Van Patten <timvp@google.com>

  • 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 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),
          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 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