Edit

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

Branch :

  • Show log

    Commit

  • Author : Brandon Schade
    Date : 2021-07-21 12:42:56
    Hash : 217acac2
    Message : Vulkan: Add support for EXT_primitive_bounding_box 1. Added support for EXT_primitive_bounding_box extension 2. Renamed shader variable gl_BoundingBoxEXT[] to ANGLEBoundingBox[] Bug: angleproject:3576 Test: dEQP-GLES31.functional.primitive_bounding_box.* Change-Id: I15fa9af50c6fd8e86d225670ddd8eb39f6e65d35 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3053618 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: Jamie Madill <jmadill@chromium.org> Reviewed-by: Shahbaz Youssefi <syoussefi@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;
        }
    }
    
    BlockType GetBlockType(TQualifier qualifier)
    {
        switch (qualifier)
        {
            case EvqUniform:
                return BlockType::BLOCK_UNIFORM;
            case EvqBuffer:
                return BlockType::BLOCK_BUFFER;
            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);
    }
    
    ShaderVariable *FindShaderIOBlockVariable(const ImmutableString &blockName,
                                              std::vector<ShaderVariable> *infoList)
    {
        for (size_t index = 0; index < infoList->size(); ++index)
        {
            if (blockName == (*infoList)[index].structOrBlockName)
                return &(*infoList)[index];
        }
    
        return nullptr;
    }
    
    // Traverses the intermediate tree to collect all attributes, uniforms, varyings, fragment outputs,
    // shared data 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<ShaderVariable> *sharedVariables,
                                  std::vector<InterfaceBlock> *uniformBlocks,
                                  std::vector<InterfaceBlock> *shaderStorageBlocks,
                                  ShHashFunction64 hashFunction,
                                  TSymbolTable *symbolTable,
                                  GLenum shaderType,
                                  const TExtensionBehavior &extensionBehavior,
                                  const ShBuiltInResources &resources,
                                  int tessControlShaderOutputVertices);
    
        bool visitGlobalQualifierDeclaration(Visit visit,
                                             TIntermGlobalQualifierDeclaration *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,
                                          bool isShaderIOBlock,
                                          bool isPatch,
                                          ShaderVariable *variableOut) const;
        void setFieldProperties(const TType &type,
                                const ImmutableString &name,
                                bool staticUse,
                                bool isShaderIOBlock,
                                bool isPatch,
                                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 *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<ShaderVariable> *mSharedVariables;
        std::vector<InterfaceBlock> *mUniformBlocks;
        std::vector<InterfaceBlock> *mShaderStorageBlocks;
    
        std::map<std::string, ShaderVariable *> mInterfaceBlockFields;
    
        // Shader uniforms
        bool mDepthRangeAdded;
        bool mNumSamplesAdded;
    
        // Compute Shader builtins
        bool mNumWorkGroupsAdded;
        bool mWorkGroupIDAdded;
        bool mLocalInvocationIDAdded;
        bool mGlobalInvocationIDAdded;
        bool mLocalInvocationIndexAdded;
    
        // Vertex Shader builtins
        bool mInstanceIDAdded;
        bool mVertexIDAdded;
        bool mPointSizeAdded;
        bool mDrawIDAdded;
    
        // Vertex Shader and Geometry Shader builtins
        bool mPositionAdded;
        bool mClipDistanceAdded;
        bool mCullDistanceAdded;
    
        // Fragment Shader builtins
        bool mPointCoordAdded;
        bool mFrontFacingAdded;
        bool mHelperInvocationAdded;
        bool mFragCoordAdded;
        bool mLastFragDataAdded;
        bool mFragColorAdded;
        bool mFragDataAdded;
        bool mFragDepthAdded;
        bool mSecondaryFragColorEXTAdded;
        bool mSecondaryFragDataEXTAdded;
        bool mSampleIDAdded;
        bool mSamplePositionAdded;
        bool mSampleMaskAdded;
        bool mSampleMaskInAdded;
    
        // Geometry and Tessellation Shader builtins
        bool mPerVertexInAdded;
        bool mPerVertexOutAdded;
    
        // Geometry Shader builtins
        bool mPrimitiveIDInAdded;
        bool mInvocationIDAdded;
    
        // Geometry Shader and Fragment Shader builtins
        bool mPrimitiveIDAdded;
        bool mLayerAdded;
    
        // Shared memory variables
        bool mSharedVariableAdded;
    
        // Tessellation Shader builtins
        bool mPatchVerticesInAdded;
        bool mTessLevelOuterAdded;
        bool mTessLevelInnerAdded;
        bool mBoundingBoxEXTAdded;
        bool mTessCoordAdded;
        const int mTessControlShaderOutputVertices;
    
        ShHashFunction64 mHashFunction;
    
        GLenum mShaderType;
        const TExtensionBehavior &mExtensionBehavior;
        const ShBuiltInResources &mResources;
    };
    
    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::ShaderVariable> *sharedVariables,
        std::vector<sh::InterfaceBlock> *uniformBlocks,
        std::vector<sh::InterfaceBlock> *shaderStorageBlocks,
        ShHashFunction64 hashFunction,
        TSymbolTable *symbolTable,
        GLenum shaderType,
        const TExtensionBehavior &extensionBehavior,
        const ShBuiltInResources &resources,
        int tessControlShaderOutputVertices)
        : TIntermTraverser(true, false, false, symbolTable),
          mAttribs(attribs),
          mOutputVariables(outputVariables),
          mUniforms(uniforms),
          mInputVaryings(inputVaryings),
          mOutputVaryings(outputVaryings),
          mSharedVariables(sharedVariables),
          mUniformBlocks(uniformBlocks),
          mShaderStorageBlocks(shaderStorageBlocks),
          mDepthRangeAdded(false),
          mNumSamplesAdded(false),
          mNumWorkGroupsAdded(false),
          mWorkGroupIDAdded(false),
          mLocalInvocationIDAdded(false),
          mGlobalInvocationIDAdded(false),
          mLocalInvocationIndexAdded(false),
          mInstanceIDAdded(false),
          mVertexIDAdded(false),
          mPointSizeAdded(false),
          mDrawIDAdded(false),
          mPositionAdded(false),
          mClipDistanceAdded(false),
          mCullDistanceAdded(false),
          mPointCoordAdded(false),
          mFrontFacingAdded(false),
          mHelperInvocationAdded(false),
          mFragCoordAdded(false),
          mLastFragDataAdded(false),
          mFragColorAdded(false),
          mFragDataAdded(false),
          mFragDepthAdded(false),
          mSecondaryFragColorEXTAdded(false),
          mSecondaryFragDataEXTAdded(false),
          mSampleIDAdded(false),
          mSamplePositionAdded(false),
          mSampleMaskAdded(false),
          mSampleMaskInAdded(false),
          mPerVertexInAdded(false),
          mPerVertexOutAdded(false),
          mPrimitiveIDInAdded(false),
          mInvocationIDAdded(false),
          mPrimitiveIDAdded(false),
          mLayerAdded(false),
          mSharedVariableAdded(false),
          mPatchVerticesInAdded(false),
          mTessLevelOuterAdded(false),
          mTessLevelInnerAdded(false),
          mBoundingBoxEXTAdded(false),
          mTessCoordAdded(false),
          mTessControlShaderOutputVertices(tessControlShaderOutputVertices),
          mHashFunction(hashFunction),
          mShaderType(shaderType),
          mExtensionBehavior(extensionBehavior),
          mResources(resources)
    {}
    
    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();
    
        bool isShaderIOBlock =
            IsShaderIoBlock(type.getQualifier()) && type.getInterfaceBlock() != nullptr;
        bool isPatch = type.getQualifier() == EvqTessLevelInner ||
                       type.getQualifier() == EvqTessLevelOuter ||
                       type.getQualifier() == EvqBoundingBoxEXT;
    
        setFieldOrVariableProperties(type, true, isShaderIOBlock, isPatch, info);
    }
    
    void CollectVariablesTraverser::recordBuiltInVaryingUsed(const TVariable &variable,
                                                             bool *addedFlag,
                                                             std::vector<ShaderVariable> *varyings)
    {
        ASSERT(varyings);
        if (!(*addedFlag))
        {
            ShaderVariable info;
            setBuiltInInfoFromSymbol(variable, &info);
            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.active = true;
            mOutputVariables->push_back(info);
            (*addedFlag) = true;
        }
    }
    
    void CollectVariablesTraverser::recordBuiltInAttributeUsed(const TVariable &variable,
                                                               bool *addedFlag)
    {
        if (!(*addedFlag))
        {
            ShaderVariable info;
            setBuiltInInfoFromSymbol(variable, &info);
            info.active   = true;
            info.location = -1;
            mAttribs->push_back(info);
            (*addedFlag) = true;
        }
    }
    
    bool CollectVariablesTraverser::visitGlobalQualifierDeclaration(
        Visit visit,
        TIntermGlobalQualifierDeclaration *node)
    {
        // We should not mark variables as active just based on an invariant/precise 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();
        const TInterfaceBlock *interfaceBlock = symbol->getType().getInterfaceBlock();
    
        if (IsVaryingIn(qualifier))
        {
            if (interfaceBlock)
            {
                var = FindShaderIOBlockVariable(interfaceBlock->name(), mInputVaryings);
            }
            else
            {
                var = FindVariable(symbolName, mInputVaryings);
            }
        }
        else if (IsVaryingOut(qualifier))
        {
            if (interfaceBlock)
            {
                var = FindShaderIOBlockVariable(interfaceBlock->name(), mOutputVaryings);
            }
            else
            {
                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 if (symbolName == "gl_NumSamples")
        {
            ASSERT(qualifier == EvqUniform);
    
            if (!mNumSamplesAdded)
            {
                ShaderVariable info;
                const char kName[] = "gl_NumSamples";
                info.name          = kName;
                info.mappedName    = kName;
                info.type          = GL_INT;
                info.precision     = GL_LOW_INT;
                info.staticUse     = true;
                info.active        = true;
    
                mUniforms->push_back(info);
                mNumSamplesAdded = true;
            }
        }
        else
        {
            switch (qualifier)
            {
                case EvqAttribute:
                case EvqVertexIn:
                    var = FindVariable(symbolName, mAttribs);
                    break;
                case EvqFragmentOut:
                case EvqFragmentInOut:
                    var = FindVariable(symbolName, mOutputVariables);
                    break;
                case EvqUniform:
                {
                    if (interfaceBlock)
                    {
                        var = FindVariableInInterfaceBlock(symbolName, interfaceBlock, mUniformBlocks);
                    }
                    else
                    {
                        var = FindVariable(symbolName, mUniforms);
                    }
    
                    // It's an internal error to reference an undefined user uniform
                    ASSERT(!gl::IsBuiltInName(symbolName.data()) || var);
                }
                break;
                case EvqBuffer:
                {
                    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 EvqNumWorkGroups:
                    recordBuiltInAttributeUsed(symbol->variable(), &mNumWorkGroupsAdded);
                    return;
                case EvqWorkGroupID:
                    recordBuiltInAttributeUsed(symbol->variable(), &mWorkGroupIDAdded);
                    return;
                case EvqLocalInvocationID:
                    recordBuiltInAttributeUsed(symbol->variable(), &mLocalInvocationIDAdded);
                    return;
                case EvqGlobalInvocationID:
                    recordBuiltInAttributeUsed(symbol->variable(), &mGlobalInvocationIDAdded);
                    return;
                case EvqLocalInvocationIndex:
                    recordBuiltInAttributeUsed(symbol->variable(), &mLocalInvocationIndexAdded);
                    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 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.active = true;
                        mOutputVariables->push_back(info);
                        mFragDataAdded = true;
                    }
                    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 ||
                               mShaderType == GL_TESS_CONTROL_SHADER ||
                               mShaderType == GL_TESS_EVALUATION_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;
                case EvqShared:
                    if (mShaderType == GL_COMPUTE_SHADER)
                    {
                        recordBuiltInVaryingUsed(symbol->variable(), &mSharedVariableAdded,
                                                 mSharedVariables);
                    }
                    break;
                case EvqClipDistance:
                    recordBuiltInVaryingUsed(
                        symbol->variable(), &mClipDistanceAdded,
                        mShaderType == GL_FRAGMENT_SHADER ? mInputVaryings : mOutputVaryings);
                    return;
                case EvqCullDistance:
                    recordBuiltInVaryingUsed(
                        symbol->variable(), &mCullDistanceAdded,
                        mShaderType == GL_FRAGMENT_SHADER ? mInputVaryings : mOutputVaryings);
                    return;
                case EvqSampleID:
                    recordBuiltInVaryingUsed(symbol->variable(), &mSampleIDAdded, mInputVaryings);
                    return;
                case EvqSamplePosition:
                    recordBuiltInVaryingUsed(symbol->variable(), &mSamplePositionAdded, mInputVaryings);
                    return;
                case EvqSampleMaskIn:
                    recordBuiltInVaryingUsed(symbol->variable(), &mSampleMaskInAdded, mInputVaryings);
                    return;
                case EvqSampleMask:
                    recordBuiltInFragmentOutputUsed(symbol->variable(), &mSampleMaskAdded);
                    return;
                case EvqPatchVerticesIn:
                    recordBuiltInVaryingUsed(symbol->variable(), &mPatchVerticesInAdded,
                                             mInputVaryings);
                    break;
                case EvqTessCoord:
                    recordBuiltInVaryingUsed(symbol->variable(), &mTessCoordAdded, mInputVaryings);
                    break;
                case EvqTessLevelOuter:
                    if (mShaderType == GL_TESS_CONTROL_SHADER)
                    {
                        recordBuiltInVaryingUsed(symbol->variable(), &mTessLevelOuterAdded,
                                                 mOutputVaryings);
                    }
                    else
                    {
                        ASSERT(mShaderType == GL_TESS_EVALUATION_SHADER);
                        recordBuiltInVaryingUsed(symbol->variable(), &mTessLevelOuterAdded,
                                                 mInputVaryings);
                    }
                    break;
                case EvqTessLevelInner:
                    if (mShaderType == GL_TESS_CONTROL_SHADER)
                    {
                        recordBuiltInVaryingUsed(symbol->variable(), &mTessLevelInnerAdded,
                                                 mOutputVaryings);
                    }
                    else
                    {
                        ASSERT(mShaderType == GL_TESS_EVALUATION_SHADER);
                        recordBuiltInVaryingUsed(symbol->variable(), &mTessLevelInnerAdded,
                                                 mInputVaryings);
                    }
                    break;
                case EvqBoundingBoxEXT:
                    recordBuiltInVaryingUsed(symbol->variable(), &mBoundingBoxEXTAdded,
                                             mOutputVaryings);
                    break;
                default:
                    break;
            }
        }
        if (var)
        {
            MarkActive(var);
        }
    }
    
    void CollectVariablesTraverser::setFieldOrVariableProperties(const TType &type,
                                                                 bool staticUse,
                                                                 bool isShaderIOBlock,
                                                                 bool isPatch,
                                                                 ShaderVariable *variableOut) const
    {
        ASSERT(variableOut);
    
        variableOut->staticUse       = staticUse;
        variableOut->isShaderIOBlock = isShaderIOBlock;
        variableOut->isPatch         = isPatch;
    
        const TStructure *structure           = type.getStruct();
        const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock();
        if (structure)
        {
            // Structures use a NONE type that isn't exposed outside ANGLE.
            variableOut->type = GL_NONE;
            if (structure->symbolType() != SymbolType::Empty)
            {
                variableOut->structOrBlockName = 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, isShaderIOBlock, isPatch,
                                   &fieldVariable);
                variableOut->fields.push_back(fieldVariable);
            }
        }
        else if (interfaceBlock && isShaderIOBlock)
        {
            variableOut->type = GL_NONE;
            if (interfaceBlock->symbolType() != SymbolType::Empty)
            {
                variableOut->structOrBlockName = interfaceBlock->name().data();
                variableOut->mappedStructOrBlockName =
                    HashName(interfaceBlock->name(), mHashFunction, nullptr).data();
            }
            const TFieldList &fields = interfaceBlock->fields();
            for (const TField *field : fields)
            {
                ShaderVariable fieldVariable;
                setFieldProperties(*field->type(), field->name(), staticUse, true, isPatch,
                                   &fieldVariable);
                fieldVariable.isShaderIOBlock = true;
                variableOut->fields.push_back(fieldVariable);
            }
        }
        else
        {
            variableOut->type      = GLVariableType(type);
            variableOut->precision = GLVariablePrecision(type);
        }
    
        const TSpan<const unsigned int> &arraySizes = type.getArraySizes();
        if (!arraySizes.empty())
        {
            variableOut->arraySizes.assign(arraySizes.begin(), arraySizes.end());
    
            if (arraySizes[0] == 0)
            {
                // Tessellation Control & Evaluation shader inputs:
                // Declaring an array size is optional. If no size is specified, it will be taken from
                // the implementation-dependent maximum patch size (gl_MaxPatchVertices).
                if (type.getQualifier() == EvqTessControlIn ||
                    type.getQualifier() == EvqTessEvaluationIn)
                {
                    variableOut->arraySizes[0] = mResources.MaxPatchVertices;
                }
    
                // Tessellation Control shader outputs:
                // Declaring an array size is optional. If no size is specified, it will be taken from
                // output patch size declared in the shader.
                if (type.getQualifier() == EvqTessControlOut)
                {
                    ASSERT(mTessControlShaderOutputVertices > 0);
                    variableOut->arraySizes[0] = mTessControlShaderOutputVertices;
                }
            }
        }
    }
    
    void CollectVariablesTraverser::setFieldProperties(const TType &type,
                                                       const ImmutableString &name,
                                                       bool staticUse,
                                                       bool isShaderIOBlock,
                                                       bool isPatch,
                                                       ShaderVariable *variableOut) const
    {
        ASSERT(variableOut);
        setFieldOrVariableProperties(type, staticUse, isShaderIOBlock, isPatch, 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);
        ASSERT(type.getInterfaceBlock() == nullptr || IsShaderIoBlock(type.getQualifier()) ||
               type.getQualifier() == EvqPatchIn || type.getQualifier() == EvqPatchOut);
    
        const bool staticUse       = mSymbolTable->isStaticallyUsed(variable);
        const bool isShaderIOBlock = type.getInterfaceBlock() != nullptr;
        const bool isPatch = type.getQualifier() == EvqPatchIn || type.getQualifier() == EvqPatchOut;
    
        setFieldOrVariableProperties(type, staticUse, isShaderIOBlock, isPatch, variableOut);
    
        const bool isNamed = variable.symbolType() != SymbolType::Empty;
    
        ASSERT(isNamed || isShaderIOBlock);
        if (isNamed)
        {
            variableOut->name.assign(variable.name().data(), variable.name().length());
            variableOut->mappedName = getMappedName(&variable);
        }
    
        // For I/O blocks, additionally store the name of the block as blockName.  If the variable is
        // unnamed, this name will be used instead for the purpose of interface matching.
        if (isShaderIOBlock)
        {
            const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock();
            ASSERT(interfaceBlock);
    
            variableOut->structOrBlockName.assign(interfaceBlock->name().data(),
                                                  interfaceBlock->name().length());
            variableOut->mappedStructOrBlockName =
                HashName(interfaceBlock->name(), mHashFunction, nullptr).data();
            variableOut->isShaderIOBlock = true;
        }
    }
    
    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;
        outputVariable.yuv      = type.getLayoutQualifier().yuv;
        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 EvqNoPerspectiveOut:
            case EvqCentroidOut:
            case EvqGeometryOut:
            case EvqSampleOut:
                if (mSymbolTable->isVaryingInvariant(variable.variable()) || type.isInvariant())
                {
                    varying.isInvariant = true;
                }
                break;
            case EvqPatchIn:
            case EvqPatchOut:
                varying.isPatch = true;
                break;
            default:
                break;
        }
    
        varying.interpolation = GetInterpolationType(type.getQualifier());
    
        // Shader I/O block properties
        if (type.getBasicType() == EbtInterfaceBlock)
        {
            bool isBlockImplicitLocation = false;
            int location                 = type.getLayoutQualifier().location;
    
            // when a interface has not location in layout, assign to the zero.
            if (location < 0)
            {
                location                = 0;
                isBlockImplicitLocation = true;
            }
    
            const TInterfaceBlock *blockType = type.getInterfaceBlock();
            ASSERT(blockType->fields().size() == varying.fields.size());
    
            for (size_t fieldIndex = 0; fieldIndex < varying.fields.size(); ++fieldIndex)
            {
                const TField *blockField      = blockType->fields()[fieldIndex];
                ShaderVariable &fieldVariable = varying.fields[fieldIndex];
                const TType &fieldType        = *blockField->type();
    
                fieldVariable.hasImplicitLocation = isBlockImplicitLocation;
                fieldVariable.isPatch             = varying.isPatch;
    
                int fieldLocation = fieldType.getLayoutQualifier().location;
                if (fieldLocation >= 0)
                {
                    fieldVariable.hasImplicitLocation = false;
                    fieldVariable.location            = fieldLocation;
                    location                          = fieldLocation;
                }
                else
                {
                    fieldVariable.location = location;
                    location += fieldType.getLocationCount();
                }
    
                if (fieldType.getQualifier() != EvqGlobal)
                {
                    fieldVariable.interpolation = GetFieldInterpolationType(fieldType.getQualifier());
                }
            }
        }
    
        return varying;
    }
    
    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);
    
        const bool isGLInBuiltin = (instanceName != nullptr) && strncmp(instanceName, "gl_in", 5u) == 0;
        if (instanceName != nullptr)
        {
            interfaceBlock->instanceName = instanceName;
            const TSymbol *blockSymbol   = nullptr;
            if (isGLInBuiltin)
            {
                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, false, false, &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 == EvqFragmentInOut ||
                                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;
            }
    
            // SpirvTransformer::transform uses a map of ShaderVariables, it needs member variables and
            // (named or unnamed) structure as ShaderVariable. at link between two shaders, validation
            // between of named and unnamed, needs the same structure, its members, and members order
            // except instance name.
            if (typedNode.getBasicType() == EbtInterfaceBlock && !IsShaderIoBlock(qualifier) &&
                qualifier != EvqPatchIn && qualifier != EvqPatchOut)
            {
                InterfaceBlock interfaceBlock;
                bool isUnnamed    = variable.variable().symbolType() == SymbolType::Empty;
                const TType &type = variable.getType();
                recordInterfaceBlock(isUnnamed ? nullptr : variable.getName().data(), type,
                                     &interfaceBlock);
    
                // all fields in interface block will be added for updating interface variables because
                // the temporal structure variable will be ignored.
                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 ||
                       IsShaderIoBlock(qualifier) || qualifier == EvqPatchIn ||
                       qualifier == EvqPatchOut);
                switch (qualifier)
                {
                    case EvqAttribute:
                    case EvqVertexIn:
                        mAttribs->push_back(recordAttribute(variable));
                        break;
                    case EvqFragmentOut:
                    case EvqFragmentInOut:
                        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;
    }
    
    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)
            {
                ASSERT(interfaceIndexingNode->getOp() == EOpIndexDirect ||
                       interfaceIndexingNode->getOp() == EOpIndexIndirect);
                traverseIndexExpression = true;
                blockNode               = interfaceIndexingNode->getLeft();
            }
    
            const TType &interfaceNodeType        = blockNode->getType();
            const TInterfaceBlock *interfaceBlock = interfaceNodeType.getInterfaceBlock();
            const TQualifier qualifier            = interfaceNodeType.getQualifier();
    
            // If it's a shader I/O block, look in varyings
            ShaderVariable *ioBlockVar = nullptr;
            if (qualifier == EvqPerVertexIn)
            {
                TIntermSymbol *symbolNode = blockNode->getAsSymbolNode();
                ASSERT(symbolNode);
                recordBuiltInVaryingUsed(symbolNode->variable(), &mPerVertexInAdded, mInputVaryings);
                ioBlockVar = FindShaderIOBlockVariable(interfaceBlock->name(), mInputVaryings);
            }
            else if (IsVaryingIn(qualifier))
            {
                ioBlockVar = FindShaderIOBlockVariable(interfaceBlock->name(), mInputVaryings);
            }
            else if (qualifier == EvqPerVertexOut)
            {
                TIntermSymbol *symbolNode = blockNode->getAsSymbolNode();
                ASSERT(symbolNode);
                recordBuiltInVaryingUsed(symbolNode->variable(), &mPerVertexOutAdded, mOutputVaryings);
                ioBlockVar = FindShaderIOBlockVariable(interfaceBlock->name(), mOutputVaryings);
            }
            else if (IsVaryingOut(qualifier))
            {
                ioBlockVar = FindShaderIOBlockVariable(interfaceBlock->name(), mOutputVaryings);
            }
    
            if (ioBlockVar)
            {
                MarkActive(ioBlockVar);
            }
            else
            {
                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<ShaderVariable> *sharedVariables,
                          std::vector<InterfaceBlock> *uniformBlocks,
                          std::vector<InterfaceBlock> *shaderStorageBlocks,
                          ShHashFunction64 hashFunction,
                          TSymbolTable *symbolTable,
                          GLenum shaderType,
                          const TExtensionBehavior &extensionBehavior,
                          const ShBuiltInResources &resources,
                          int tessControlShaderOutputVertices)
    {
        CollectVariablesTraverser collect(
            attributes, outputVariables, uniforms, inputVaryings, outputVaryings, sharedVariables,
            uniformBlocks, shaderStorageBlocks, hashFunction, symbolTable, shaderType,
            extensionBehavior, resources, tessControlShaderOutputVertices);
        root->traverse(&collect);
    }
    
    }  // namespace sh