Edit

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

Branch :

  • Show log

    Commit

  • Author : Jiawei Shao
    Date : 2017-08-31 14:25:54
    Hash : 4cc89e2b
    Message : ES31: Enable 'location' layout qualifier on shader interfaces in compiler This patch enables 'location' layout qualifier for vertex outputs and fragment shader inputs when the shader version is 3.1 in ANGLE GLSL compiler and adds the check on location conflicts for these varyings. According to GLSL ES 3.1 SPEC (Chapter 4.4.1 and Chapter 4.4.2), 'location' layout qualifier is allowed on both inputs and outputs of vertex and fragment shaders. 'location' layout qualifier on shader interfaces is only valid on shaders whose version is 3.1 and above. According to GLSL ES 3.0 SPEC, vertex shader cannot have output layout qualifiers (Chapter 4.3.8.2) and fragment shader cannot have input layout qualifiers (Chapter 4.3.8.1). The 'location' qualifier on varyings is used in the shader interface matching defined in OpenGL ES 3.1. (OpenGL ES 3.1 SPEC Chapter 7.4.1). This new link rule will be added to Program.cpp in another patch. For the OpenGL ES 3.1 extension GL_OES_geometry_shader, according to GL_OES_shader_io_blocks SPEC (Chapter 4.4.1 and Chapter 4.4.2), 'location' layout qualifier is both valid on geometry shader inputs and outputs. This feature will be implemented together with other rules on geometry shader inputs and outputs. BUG=angleproject:2144 TEST=angle_unittests Change-Id: I62d85f7144c177448321c2db36ed7aaeaa1fb205 Reviewed-on: https://chromium-review.googlesource.com/645366 Commit-Queue: Geoff Lang <geofflang@chromium.org> Reviewed-by: Geoff Lang <geofflang@chromium.org>

  • src/compiler/translator/CollectVariables.cpp
  • //
    // Copyright (c) 2002-2013 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/IntermTraverse.h"
    #include "compiler/translator/SymbolTable.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_STANDARD;
            default:
                UNREACHABLE();
                return BLOCKLAYOUT_SHARED;
        }
    }
    
    // TODO(jiawei.shao@intel.com): implement GL_OES_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 TString &name, std::vector<VarT> *infoList)
    {
        // TODO(zmo): optimize this function.
        for (size_t ii = 0; ii < infoList->size(); ++ii)
        {
            if ((*infoList)[ii].name.c_str() == name)
                return &((*infoList)[ii]);
        }
    
        return nullptr;
    }
    
    // Note that this shouldn't be called for interface blocks - static use information is collected for
    // individual fields in case of interface blocks.
    void MarkStaticallyUsed(ShaderVariable *variable)
    {
        if (!variable->staticUse)
        {
            if (variable->isStruct())
            {
                // Conservatively assume all fields are statically used as well.
                for (auto &field : variable->fields)
                {
                    MarkStaticallyUsed(&field);
                }
            }
            variable->staticUse = true;
        }
    }
    
    // Traverses the intermediate tree to collect all attributes, uniforms, varyings, fragment outputs,
    // and interface blocks.
    class CollectVariablesTraverser : public TIntermTraverser
    {
      public:
        CollectVariablesTraverser(std::vector<Attribute> *attribs,
                                  std::vector<OutputVariable> *outputVariables,
                                  std::vector<Uniform> *uniforms,
                                  std::vector<Varying> *inputVaryings,
                                  std::vector<Varying> *outputVaryings,
                                  std::vector<InterfaceBlock> *uniformBlocks,
                                  std::vector<InterfaceBlock> *shaderStorageBlocks,
                                  std::vector<InterfaceBlock> *inBlocks,
                                  ShHashFunction64 hashFunction,
                                  TSymbolTable *symbolTable,
                                  int shaderVersion,
                                  GLenum shaderType,
                                  const TExtensionBehavior &extensionBehavior);
    
        void visitSymbol(TIntermSymbol *symbol) override;
        bool visitDeclaration(Visit, TIntermDeclaration *node) override;
        bool visitBinary(Visit visit, TIntermBinary *binaryNode) override;
    
      private:
        std::string getMappedName(const TName &name) const;
    
        void setCommonVariableProperties(const TType &type,
                                         const TName &name,
                                         ShaderVariable *variableOut) const;
    
        Attribute recordAttribute(const TIntermSymbol &variable) const;
        OutputVariable recordOutputVariable(const TIntermSymbol &variable) const;
        Varying recordVarying(const TIntermSymbol &variable) const;
        void recordInterfaceBlock(const TType &interfaceBlockType,
                                  InterfaceBlock *interfaceBlock) const;
        Uniform recordUniform(const TIntermSymbol &variable) const;
    
        void setBuiltInInfoFromSymbolTable(const char *name, ShaderVariable *info);
    
        void recordBuiltInVaryingUsed(const char *name,
                                      bool *addedFlag,
                                      std::vector<Varying> *varyings);
        void recordBuiltInFragmentOutputUsed(const char *name, bool *addedFlag);
        void recordBuiltInAttributeUsed(const char *name, bool *addedFlag);
        InterfaceBlock *recordGLInUsed(const TType &glInType);
        InterfaceBlock *findNamedInterfaceBlock(const TString &name) const;
    
        std::vector<Attribute> *mAttribs;
        std::vector<OutputVariable> *mOutputVariables;
        std::vector<Uniform> *mUniforms;
        std::vector<Varying> *mInputVaryings;
        std::vector<Varying> *mOutputVaryings;
        std::vector<InterfaceBlock> *mUniformBlocks;
        std::vector<InterfaceBlock> *mShaderStorageBlocks;
        std::vector<InterfaceBlock> *mInBlocks;
    
        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 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;
    
        int mShaderVersion;
        GLenum mShaderType;
        const TExtensionBehavior &mExtensionBehavior;
    };
    
    CollectVariablesTraverser::CollectVariablesTraverser(
        std::vector<sh::Attribute> *attribs,
        std::vector<sh::OutputVariable> *outputVariables,
        std::vector<sh::Uniform> *uniforms,
        std::vector<sh::Varying> *inputVaryings,
        std::vector<sh::Varying> *outputVaryings,
        std::vector<sh::InterfaceBlock> *uniformBlocks,
        std::vector<sh::InterfaceBlock> *shaderStorageBlocks,
        std::vector<sh::InterfaceBlock> *inBlocks,
        ShHashFunction64 hashFunction,
        TSymbolTable *symbolTable,
        int shaderVersion,
        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),
          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),
          mShaderVersion(shaderVersion),
          mShaderType(shaderType),
          mExtensionBehavior(extensionBehavior)
    {
    }
    
    std::string CollectVariablesTraverser::getMappedName(const TName &name) const
    {
        return HashName(name, mHashFunction, nullptr).c_str();
    }
    
    void CollectVariablesTraverser::setBuiltInInfoFromSymbolTable(const char *name,
                                                                  ShaderVariable *info)
    {
        TVariable *symbolTableVar =
            reinterpret_cast<TVariable *>(mSymbolTable->findBuiltIn(name, mShaderVersion));
        ASSERT(symbolTableVar);
        const TType &type = symbolTableVar->getType();
    
        info->name       = name;
        info->mappedName = name;
        info->type       = GLVariableType(type);
        ASSERT(!type.isArrayOfArrays());
        info->arraySize = type.isArray() ? type.getOutermostArraySize() : 0;
        info->precision = GLVariablePrecision(type);
    }
    
    void CollectVariablesTraverser::recordBuiltInVaryingUsed(const char *name,
                                                             bool *addedFlag,
                                                             std::vector<Varying> *varyings)
    {
        ASSERT(varyings);
        if (!(*addedFlag))
        {
            Varying info;
            setBuiltInInfoFromSymbolTable(name, &info);
            info.staticUse   = true;
            info.isInvariant = mSymbolTable->isVaryingInvariant(name);
            varyings->push_back(info);
            (*addedFlag) = true;
        }
    }
    
    void CollectVariablesTraverser::recordBuiltInFragmentOutputUsed(const char *name, bool *addedFlag)
    {
        if (!(*addedFlag))
        {
            OutputVariable info;
            setBuiltInInfoFromSymbolTable(name, &info);
            info.staticUse = true;
            mOutputVariables->push_back(info);
            (*addedFlag) = true;
        }
    }
    
    void CollectVariablesTraverser::recordBuiltInAttributeUsed(const char *name, bool *addedFlag)
    {
        if (!(*addedFlag))
        {
            Attribute info;
            setBuiltInInfoFromSymbolTable(name, &info);
            info.staticUse = 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(glInType, &info);
            info.staticUse = true;
    
            mPerVertexInAdded = true;
            mInBlocks->push_back(info);
            return &mInBlocks->back();
        }
        else
        {
            return FindVariable("gl_PerVertex", mInBlocks);
        }
    }
    
    // We want to check whether a uniform/varying is statically used
    // because we only count the used ones in packing computing.
    // Also, gl_FragCoord, gl_PointCoord, and gl_FrontFacing count
    // toward varying counting if they are statically used in a fragment
    // shader.
    void CollectVariablesTraverser::visitSymbol(TIntermSymbol *symbol)
    {
        ASSERT(symbol != nullptr);
    
        if (symbol->getName().isInternal())
        {
            // Internal variables are not collected.
            return;
        }
    
        ShaderVariable *var       = nullptr;
        const TString &symbolName = symbol->getName().getString();
    
        if (IsVaryingIn(symbol->getQualifier()))
        {
            var = FindVariable(symbolName, mInputVaryings);
        }
        else if (IsVaryingOut(symbol->getQualifier()))
        {
            var = FindVariable(symbolName, mOutputVaryings);
        }
        else if (symbol->getType().getBasicType() == EbtInterfaceBlock)
        {
            UNREACHABLE();
        }
        else if (symbolName == "gl_DepthRange")
        {
            ASSERT(symbol->getQualifier() == EvqUniform);
    
            if (!mDepthRangeAdded)
            {
                Uniform info;
                const char kName[] = "gl_DepthRange";
                info.name          = kName;
                info.mappedName    = kName;
                info.type          = GL_NONE;
                info.arraySize     = 0;
                info.precision     = GL_NONE;
                info.staticUse     = true;
    
                ShaderVariable nearInfo;
                const char kNearName[] = "near";
                nearInfo.name          = kNearName;
                nearInfo.mappedName    = kNearName;
                nearInfo.type          = GL_FLOAT;
                nearInfo.arraySize     = 0;
                nearInfo.precision     = GL_HIGH_FLOAT;
                nearInfo.staticUse     = true;
    
                ShaderVariable farInfo;
                const char kFarName[] = "far";
                farInfo.name          = kFarName;
                farInfo.mappedName    = kFarName;
                farInfo.type          = GL_FLOAT;
                farInfo.arraySize     = 0;
                farInfo.precision     = GL_HIGH_FLOAT;
                farInfo.staticUse     = true;
    
                ShaderVariable diffInfo;
                const char kDiffName[] = "diff";
                diffInfo.name          = kDiffName;
                diffInfo.mappedName    = kDiffName;
                diffInfo.type          = GL_FLOAT;
                diffInfo.arraySize     = 0;
                diffInfo.precision     = GL_HIGH_FLOAT;
                diffInfo.staticUse     = true;
    
                info.fields.push_back(nearInfo);
                info.fields.push_back(farInfo);
                info.fields.push_back(diffInfo);
    
                mUniforms->push_back(info);
                mDepthRangeAdded = true;
            }
        }
        else
        {
            switch (symbol->getQualifier())
            {
                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)
                    {
                        InterfaceBlock *namedBlock =
                            FindVariable(interfaceBlock->name(), mUniformBlocks);
                        ASSERT(namedBlock);
                        var = FindVariable(symbolName, &namedBlock->fields);
    
                        // Set static use on the parent interface block here
                        namedBlock->staticUse = true;
                    }
                    else
                    {
                        var = FindVariable(symbolName, mUniforms);
                    }
    
                    // It's an internal error to reference an undefined user uniform
                    ASSERT(symbolName.compare(0, 3, "gl_") != 0 || var);
                }
                break;
                case EvqFragCoord:
                    recordBuiltInVaryingUsed("gl_FragCoord", &mFragCoordAdded, mInputVaryings);
                    return;
                case EvqFrontFacing:
                    recordBuiltInVaryingUsed("gl_FrontFacing", &mFrontFacingAdded, mInputVaryings);
                    return;
                case EvqPointCoord:
                    recordBuiltInVaryingUsed("gl_PointCoord", &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. gl_InstanceID is not added to the symbol table for ESSL1 shaders
                    // which makes it necessary to populate the type information explicitly instead of
                    // extracting it from the symbol table.
                    if (!mInstanceIDAdded)
                    {
                        Attribute info;
                        const char kName[] = "gl_InstanceID";
                        info.name          = kName;
                        info.mappedName    = kName;
                        info.type          = GL_INT;
                        info.arraySize     = 0;
                        info.precision     = GL_HIGH_INT;  // Defined by spec.
                        info.staticUse     = true;
                        info.location      = -1;
                        mAttribs->push_back(info);
                        mInstanceIDAdded = true;
                    }
                    return;
                case EvqVertexID:
                    recordBuiltInAttributeUsed("gl_VertexID", &mVertexIDAdded);
                    return;
                case EvqPosition:
                    recordBuiltInVaryingUsed("gl_Position", &mPositionAdded, mOutputVaryings);
                    return;
                case EvqPointSize:
                    recordBuiltInVaryingUsed("gl_PointSize", &mPointSizeAdded, mOutputVaryings);
                    return;
                case EvqLastFragData:
                    recordBuiltInVaryingUsed("gl_LastFragData", &mLastFragDataAdded, mInputVaryings);
                    return;
                case EvqFragColor:
                    recordBuiltInFragmentOutputUsed("gl_FragColor", &mFragColorAdded);
                    return;
                case EvqFragData:
                    if (!mFragDataAdded)
                    {
                        OutputVariable info;
                        setBuiltInInfoFromSymbolTable("gl_FragData", &info);
                        if (!IsExtensionEnabled(mExtensionBehavior, TExtension::EXT_draw_buffers))
                        {
                            info.arraySize = 1;
                        }
                        info.staticUse = true;
                        mOutputVariables->push_back(info);
                        mFragDataAdded = true;
                    }
                    return;
                case EvqFragDepthEXT:
                    recordBuiltInFragmentOutputUsed("gl_FragDepthEXT", &mFragDepthEXTAdded);
                    return;
                case EvqFragDepth:
                    recordBuiltInFragmentOutputUsed("gl_FragDepth", &mFragDepthAdded);
                    return;
                case EvqSecondaryFragColorEXT:
                    recordBuiltInFragmentOutputUsed("gl_SecondaryFragColorEXT",
                                                    &mSecondaryFragColorEXTAdded);
                    return;
                case EvqSecondaryFragDataEXT:
                    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, TExtension::OVR_multiview));
                    }
                    break;
                default:
                    break;
            }
        }
        if (var)
        {
            MarkStaticallyUsed(var);
        }
    }
    
    void CollectVariablesTraverser::setCommonVariableProperties(const TType &type,
                                                                const TName &name,
                                                                ShaderVariable *variableOut) const
    {
        ASSERT(variableOut);
    
        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;
            variableOut->structName = structure->name().c_str();
    
            const TFieldList &fields = structure->fields();
    
            for (TField *field : fields)
            {
                // Regardless of the variable type (uniform, in/out etc.) its fields are always plain
                // ShaderVariable objects.
                ShaderVariable fieldVariable;
                setCommonVariableProperties(*field->type(), TName(field->name()), &fieldVariable);
                variableOut->fields.push_back(fieldVariable);
            }
        }
        variableOut->name       = name.getString().c_str();
        variableOut->mappedName = getMappedName(name);
    
        // TODO(oetuaho@nvidia.com): Uniforms can be arrays of arrays, so this assert will need to be
        // removed.
        ASSERT(!type.isArrayOfArrays());
        variableOut->arraySize = type.isArray() ? type.getOutermostArraySize() : 0;
    }
    
    Attribute CollectVariablesTraverser::recordAttribute(const TIntermSymbol &variable) const
    {
        const TType &type = variable.getType();
        ASSERT(!type.getStruct());
    
        Attribute attribute;
        setCommonVariableProperties(type, variable.getName(), &attribute);
    
        attribute.location = type.getLayoutQualifier().location;
        return attribute;
    }
    
    OutputVariable CollectVariablesTraverser::recordOutputVariable(const TIntermSymbol &variable) const
    {
        const TType &type = variable.getType();
        ASSERT(!type.getStruct());
    
        OutputVariable outputVariable;
        setCommonVariableProperties(type, variable.getName(), &outputVariable);
    
        outputVariable.location = type.getLayoutQualifier().location;
        return outputVariable;
    }
    
    Varying CollectVariablesTraverser::recordVarying(const TIntermSymbol &variable) const
    {
        const TType &type = variable.getType();
    
        Varying varying;
        setCommonVariableProperties(type, variable.getName(), &varying);
        varying.location = type.getLayoutQualifier().location;
    
        switch (type.getQualifier())
        {
            case EvqVaryingIn:
            case EvqVaryingOut:
            case EvqVertexOut:
            case EvqSmoothOut:
            case EvqFlatOut:
            case EvqCentroidOut:
                if (mSymbolTable->isVaryingInvariant(std::string(variable.getSymbol().c_str())) ||
                    type.isInvariant())
                {
                    varying.isInvariant = true;
                }
                break;
            default:
                break;
        }
    
        varying.interpolation = GetInterpolationType(type.getQualifier());
        return varying;
    }
    
    // TODO(jiawei.shao@intel.com): implement GL_OES_shader_io_blocks.
    void CollectVariablesTraverser::recordInterfaceBlock(const TType &interfaceBlockType,
                                                         InterfaceBlock *interfaceBlock) const
    {
        ASSERT(interfaceBlockType.getBasicType() == EbtInterfaceBlock);
        ASSERT(interfaceBlock);
    
        const TInterfaceBlock *blockType = interfaceBlockType.getInterfaceBlock();
        ASSERT(blockType);
    
        interfaceBlock->name       = blockType->name().c_str();
        interfaceBlock->mappedName = getMappedName(TName(blockType->name()));
        interfaceBlock->instanceName =
            (blockType->hasInstanceName() ? blockType->instanceName().c_str() : "");
        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)
        {
            interfaceBlock->isRowMajorLayout = (blockType->matrixPacking() == EmpRowMajor);
            interfaceBlock->binding          = blockType->blockBinding();
            interfaceBlock->layout           = GetBlockLayoutType(blockType->blockStorage());
        }
    
        // Gather field information
        for (const TField *field : blockType->fields())
        {
            const TType &fieldType = *field->type();
    
            InterfaceBlockField fieldVariable;
            setCommonVariableProperties(fieldType, TName(field->name()), &fieldVariable);
            fieldVariable.isRowMajorLayout =
                (fieldType.getLayoutQualifier().matrixPacking == EmpRowMajor);
            interfaceBlock->fields.push_back(fieldVariable);
        }
    }
    
    Uniform CollectVariablesTraverser::recordUniform(const TIntermSymbol &variable) const
    {
        Uniform uniform;
        setCommonVariableProperties(variable.getType(), variable.getName(), &uniform);
        uniform.binding  = variable.getType().getLayoutQualifier().binding;
        uniform.location = variable.getType().getLayoutQualifier().location;
        uniform.offset   = variable.getType().getLayoutQualifier().offset;
        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.getName().isInternal())
            {
                // Internal variables are not collected.
                continue;
            }
    
            // TODO(jiawei.shao@intel.com): implement GL_OES_shader_io_blocks.
            if (typedNode.getBasicType() == EbtInterfaceBlock)
            {
                InterfaceBlock interfaceBlock;
                recordInterfaceBlock(variable.getType(), &interfaceBlock);
    
                switch (qualifier)
                {
                    case EvqUniform:
                        mUniformBlocks->push_back(interfaceBlock);
                        break;
                    case EvqBuffer:
                        mShaderStorageBlocks->push_back(interfaceBlock);
                        break;
                    default:
                        UNREACHABLE();
                }
            }
            else
            {
                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_OES_shader_io_blocks.
    InterfaceBlock *CollectVariablesTraverser::findNamedInterfaceBlock(const TString &blockName) const
    {
        InterfaceBlock *namedBlock = FindVariable(blockName, mUniformBlocks);
        if (!namedBlock)
        {
            namedBlock = FindVariable(blockName, mShaderStorageBlocks);
        }
        return namedBlock;
    }
    
    bool CollectVariablesTraverser::visitBinary(Visit, TIntermBinary *binaryNode)
    {
        if (binaryNode->getOp() == EOpIndexDirectInterfaceBlock)
        {
            // NOTE: we do not determine static use 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);
            namedBlock->staticUse   = true;
            unsigned int fieldIndex = static_cast<unsigned int>(constantUnion->getIConst(0));
            ASSERT(fieldIndex < namedBlock->fields.size());
            namedBlock->fields[fieldIndex].staticUse = true;
    
            if (traverseIndexExpression)
            {
                ASSERT(interfaceIndexingNode);
                interfaceIndexingNode->getRight()->traverse(this);
            }
            return false;
        }
    
        return true;
    }
    
    }  // anonymous namespace
    
    void CollectVariables(TIntermBlock *root,
                          std::vector<Attribute> *attributes,
                          std::vector<OutputVariable> *outputVariables,
                          std::vector<Uniform> *uniforms,
                          std::vector<Varying> *inputVaryings,
                          std::vector<Varying> *outputVaryings,
                          std::vector<InterfaceBlock> *uniformBlocks,
                          std::vector<InterfaceBlock> *shaderStorageBlocks,
                          std::vector<InterfaceBlock> *inBlocks,
                          ShHashFunction64 hashFunction,
                          TSymbolTable *symbolTable,
                          int shaderVersion,
                          GLenum shaderType,
                          const TExtensionBehavior &extensionBehavior)
    {
        CollectVariablesTraverser collect(attributes, outputVariables, uniforms, inputVaryings,
                                          outputVaryings, uniformBlocks, shaderStorageBlocks, inBlocks,
                                          hashFunction, symbolTable, shaderVersion, shaderType,
                                          extensionBehavior);
        root->traverse(&collect);
    }
    
    }  // namespace sh