Edit

kc3-lang/angle/src/libANGLE/ProgramLinkedResources.cpp

Branch :

  • Show log

    Commit

  • Author : Mohan Maiya
    Date : 2021-02-24 09:49:42
    Hash : 550f2a3e
    Message : Vulkan: Shader support for EXT_shader_framebuffer_fetch_non_coherent Translator can accept gl_LastFragData and 'inout' variable to gain access to framebuffer attachment data. The Vulkan translator replaces it with the SubpassInput type variable. Note that this works only for the noncoherent version of the extension. Bug: angleproject:5454 Test: *EXTShaderFramebufferFetchNoncoherent*.* Change-Id: I392f84ee3ad3eb9fbd09d0b7ff83731a9a3f33f6 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2598060 Reviewed-by: Jamie Madill <jmadill@chromium.org> Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org> Commit-Queue: Mohan Maiya <m.maiya@samsung.com>

  • src/libANGLE/ProgramLinkedResources.cpp
  • //
    // Copyright 2017 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.
    //
    
    // ProgramLinkedResources.cpp: implements link-time checks for default block uniforms, and generates
    // uniform locations. Populates data structures related to uniforms so that they can be stored in
    // program state.
    
    #include "libANGLE/ProgramLinkedResources.h"
    
    #include "common/string_utils.h"
    #include "common/utilities.h"
    #include "libANGLE/Caps.h"
    #include "libANGLE/Context.h"
    #include "libANGLE/Shader.h"
    #include "libANGLE/features.h"
    
    namespace gl
    {
    namespace
    {
    LinkedUniform *FindUniform(std::vector<LinkedUniform> &list, const std::string &name)
    {
        for (LinkedUniform &uniform : list)
        {
            if (uniform.name == name)
                return &uniform;
        }
    
        return nullptr;
    }
    
    template <typename VarT>
    void SetActive(std::vector<VarT> *list, const std::string &name, ShaderType shaderType, bool active)
    {
        for (auto &variable : *list)
        {
            if (variable.name == name)
            {
                variable.setActive(shaderType, active);
                return;
            }
        }
    }
    
    // GLSL ES Spec 3.00.3, section 4.3.5.
    LinkMismatchError LinkValidateUniforms(const sh::ShaderVariable &uniform1,
                                           const sh::ShaderVariable &uniform2,
                                           std::string *mismatchedStructFieldName)
    {
    #if ANGLE_PROGRAM_LINK_VALIDATE_UNIFORM_PRECISION == ANGLE_ENABLED
        const bool validatePrecisionFeature = true;
    #else
        const bool validatePrecisionFeature = false;
    #endif
    
        // Validate precision match of uniforms iff they are statically used
        bool validatePrecision = uniform1.staticUse && uniform2.staticUse && validatePrecisionFeature;
        LinkMismatchError linkError = LinkValidateProgramVariables(
            uniform1, uniform2, validatePrecision, false, false, mismatchedStructFieldName);
        if (linkError != LinkMismatchError::NO_MISMATCH)
        {
            return linkError;
        }
    
        // GLSL ES Spec 3.10.4, section 4.4.5.
        if (uniform1.binding != -1 && uniform2.binding != -1 && uniform1.binding != uniform2.binding)
        {
            return LinkMismatchError::BINDING_MISMATCH;
        }
    
        // GLSL ES Spec 3.10.4, section 9.2.1.
        if (uniform1.location != -1 && uniform2.location != -1 &&
            uniform1.location != uniform2.location)
        {
            return LinkMismatchError::LOCATION_MISMATCH;
        }
        if (uniform1.offset != uniform2.offset)
        {
            return LinkMismatchError::OFFSET_MISMATCH;
        }
    
        return LinkMismatchError::NO_MISMATCH;
    }
    
    using ShaderUniform = std::pair<ShaderType, const sh::ShaderVariable *>;
    
    bool ValidateGraphicsUniformsPerShader(Shader *shaderToLink,
                                           bool extendLinkedUniforms,
                                           std::map<std::string, ShaderUniform> *linkedUniforms,
                                           InfoLog &infoLog)
    {
        ASSERT(shaderToLink && linkedUniforms);
    
        for (const sh::ShaderVariable &uniform : shaderToLink->getUniforms())
        {
            const auto &entry = linkedUniforms->find(uniform.name);
            if (entry != linkedUniforms->end())
            {
                const sh::ShaderVariable &linkedUniform = *(entry->second.second);
                std::string mismatchedStructFieldName;
                LinkMismatchError linkError =
                    LinkValidateUniforms(uniform, linkedUniform, &mismatchedStructFieldName);
                if (linkError != LinkMismatchError::NO_MISMATCH)
                {
                    LogLinkMismatch(infoLog, uniform.name, "uniform", linkError,
                                    mismatchedStructFieldName, entry->second.first,
                                    shaderToLink->getType());
                    return false;
                }
            }
            else if (extendLinkedUniforms)
            {
                (*linkedUniforms)[uniform.name] = std::make_pair(shaderToLink->getType(), &uniform);
            }
        }
    
        return true;
    }
    
    GLuint GetMaximumShaderUniformVectors(ShaderType shaderType, const Caps &caps)
    {
        switch (shaderType)
        {
            case ShaderType::Vertex:
                return static_cast<GLuint>(caps.maxVertexUniformVectors);
            case ShaderType::Fragment:
                return static_cast<GLuint>(caps.maxFragmentUniformVectors);
    
            case ShaderType::Compute:
            case ShaderType::Geometry:
            case ShaderType::TessControl:
            case ShaderType::TessEvaluation:
                return static_cast<GLuint>(caps.maxShaderUniformComponents[shaderType]) / 4;
    
            default:
                UNREACHABLE();
                return 0u;
        }
    }
    
    enum class UniformType : uint8_t
    {
        Variable      = 0,
        Sampler       = 1,
        Image         = 2,
        AtomicCounter = 3,
    
        InvalidEnum = 4,
        EnumCount   = 4,
    };
    
    const char *GetUniformResourceNameString(UniformType uniformType)
    {
        switch (uniformType)
        {
            case UniformType::Variable:
                return "uniform";
            case UniformType::Sampler:
                return "texture image unit";
            case UniformType::Image:
                return "image uniform";
            case UniformType::AtomicCounter:
                return "atomic counter";
            default:
                UNREACHABLE();
                return "";
        }
    }
    
    std::string GetUniformResourceLimitName(ShaderType shaderType, UniformType uniformType)
    {
        // Special case: MAX_TEXTURE_IMAGE_UNITS (no "MAX_FRAGMENT_TEXTURE_IMAGE_UNITS")
        if (shaderType == ShaderType::Fragment && uniformType == UniformType::Sampler)
        {
            return "MAX_TEXTURE_IMAGE_UNITS";
        }
    
        std::ostringstream ostream;
        ostream << "MAX_" << GetShaderTypeString(shaderType) << "_";
    
        switch (uniformType)
        {
            case UniformType::Variable:
                // For vertex and fragment shaders, ES 2.0 only defines MAX_VERTEX_UNIFORM_VECTORS and
                // MAX_FRAGMENT_UNIFORM_VECTORS ([OpenGL ES 2.0] Table 6.20).
                if (shaderType == ShaderType::Vertex || shaderType == ShaderType::Fragment)
                {
                    ostream << "UNIFORM_VECTORS";
                    break;
                }
                // For compute and geometry shaders, there are no definitions on
                // "MAX_COMPUTE_UNIFORM_VECTORS" or "MAX_GEOMETRY_UNIFORM_VECTORS_EXT"
                // ([OpenGL ES 3.1] Table 20.45, [EXT_geometry_shader] Table 20.43gs).
                else
                {
                    ostream << "UNIFORM_COMPONENTS";
                }
                break;
            case UniformType::Sampler:
                ostream << "TEXTURE_IMAGE_UNITS";
                break;
            case UniformType::Image:
                ostream << "IMAGE_UNIFORMS";
                break;
            case UniformType::AtomicCounter:
                ostream << "ATOMIC_COUNTERS";
                break;
            default:
                UNREACHABLE();
                return "";
        }
    
        if (shaderType == ShaderType::Geometry)
        {
            ostream << "_EXT";
        }
    
        return ostream.str();
    }
    
    void LogUniformsExceedLimit(ShaderType shaderType,
                                UniformType uniformType,
                                GLuint limit,
                                InfoLog &infoLog)
    {
        infoLog << GetShaderTypeString(shaderType) << " shader "
                << GetUniformResourceNameString(uniformType) << "s count exceeds "
                << GetUniformResourceLimitName(shaderType, uniformType) << "(" << limit << ")";
    }
    
    // The purpose of this visitor is to capture the uniforms in a uniform block. Each new uniform is
    // added to "uniformsOut".
    class UniformBlockEncodingVisitor : public sh::VariableNameVisitor
    {
      public:
        UniformBlockEncodingVisitor(const GetBlockMemberInfoFunc &getMemberInfo,
                                    const std::string &namePrefix,
                                    const std::string &mappedNamePrefix,
                                    std::vector<LinkedUniform> *uniformsOut,
                                    ShaderType shaderType,
                                    int blockIndex)
            : sh::VariableNameVisitor(namePrefix, mappedNamePrefix),
              mGetMemberInfo(getMemberInfo),
              mUniformsOut(uniformsOut),
              mShaderType(shaderType),
              mBlockIndex(blockIndex)
        {}
    
        void visitNamedVariable(const sh::ShaderVariable &variable,
                                bool isRowMajor,
                                const std::string &name,
                                const std::string &mappedName,
                                const std::vector<unsigned int> &arraySizes) override
        {
            // If getBlockMemberInfo returns false, the variable is optimized out.
            sh::BlockMemberInfo variableInfo;
            if (!mGetMemberInfo(name, mappedName, &variableInfo))
                return;
    
            std::string nameWithArrayIndex       = name;
            std::string mappedNameWithArrayIndex = mappedName;
    
            if (variable.isArray())
            {
                nameWithArrayIndex += "[0]";
                mappedNameWithArrayIndex += "[0]";
            }
    
            if (mBlockIndex == -1)
            {
                SetActive(mUniformsOut, nameWithArrayIndex, mShaderType, variable.active);
                return;
            }
    
            LinkedUniform newUniform(variable.type, variable.precision, nameWithArrayIndex,
                                     variable.arraySizes, -1, -1, -1, mBlockIndex, variableInfo);
            newUniform.mappedName = mappedNameWithArrayIndex;
            newUniform.setActive(mShaderType, variable.active);
    
            // Since block uniforms have no location, we don't need to store them in the uniform
            // locations list.
            mUniformsOut->push_back(newUniform);
        }
    
      private:
        const GetBlockMemberInfoFunc &mGetMemberInfo;
        std::vector<LinkedUniform> *mUniformsOut;
        const ShaderType mShaderType;
        const int mBlockIndex;
    };
    
    // The purpose of this visitor is to capture the buffer variables in a shader storage block. Each
    // new buffer variable is stored in "bufferVariablesOut".
    class ShaderStorageBlockVisitor : public sh::BlockEncoderVisitor
    {
      public:
        ShaderStorageBlockVisitor(const GetBlockMemberInfoFunc &getMemberInfo,
                                  const std::string &namePrefix,
                                  const std::string &mappedNamePrefix,
                                  std::vector<BufferVariable> *bufferVariablesOut,
                                  ShaderType shaderType,
                                  int blockIndex)
            : sh::BlockEncoderVisitor(namePrefix, mappedNamePrefix, &mStubEncoder),
              mGetMemberInfo(getMemberInfo),
              mBufferVariablesOut(bufferVariablesOut),
              mShaderType(shaderType),
              mBlockIndex(blockIndex)
        {}
    
        void visitNamedVariable(const sh::ShaderVariable &variable,
                                bool isRowMajor,
                                const std::string &name,
                                const std::string &mappedName,
                                const std::vector<unsigned int> &arraySizes) override
        {
            if (mSkipEnabled)
                return;
    
            // If getBlockMemberInfo returns false, the variable is optimized out.
            sh::BlockMemberInfo variableInfo;
            if (!mGetMemberInfo(name, mappedName, &variableInfo))
                return;
    
            std::string nameWithArrayIndex       = name;
            std::string mappedNameWithArrayIndex = mappedName;
    
            if (variable.isArray())
            {
                nameWithArrayIndex += "[0]";
                mappedNameWithArrayIndex += "[0]";
            }
    
            if (mBlockIndex == -1)
            {
                SetActive(mBufferVariablesOut, nameWithArrayIndex, mShaderType, variable.active);
                return;
            }
    
            BufferVariable newBufferVariable(variable.type, variable.precision, nameWithArrayIndex,
                                             variable.arraySizes, mBlockIndex, variableInfo);
            newBufferVariable.mappedName = mappedNameWithArrayIndex;
            newBufferVariable.setActive(mShaderType, variable.active);
    
            newBufferVariable.topLevelArraySize = mTopLevelArraySize;
    
            mBufferVariablesOut->push_back(newBufferVariable);
        }
    
      private:
        const GetBlockMemberInfoFunc &mGetMemberInfo;
        std::vector<BufferVariable> *mBufferVariablesOut;
        const ShaderType mShaderType;
        const int mBlockIndex;
        sh::StubBlockEncoder mStubEncoder;
    };
    
    struct ShaderUniformCount
    {
        unsigned int vectorCount        = 0;
        unsigned int samplerCount       = 0;
        unsigned int imageCount         = 0;
        unsigned int atomicCounterCount = 0;
        unsigned int fragmentInOutCount = 0;
    };
    
    ShaderUniformCount &operator+=(ShaderUniformCount &lhs, const ShaderUniformCount &rhs)
    {
        lhs.vectorCount += rhs.vectorCount;
        lhs.samplerCount += rhs.samplerCount;
        lhs.imageCount += rhs.imageCount;
        lhs.atomicCounterCount += rhs.atomicCounterCount;
        lhs.fragmentInOutCount += rhs.fragmentInOutCount;
        return lhs;
    }
    
    // The purpose of this visitor is to flatten struct and array uniforms into a list of singleton
    // uniforms. They are stored in separate lists by uniform type so they can be sorted in order.
    // Counts for each uniform category are stored and can be queried with "getCounts".
    class FlattenUniformVisitor : public sh::VariableNameVisitor
    {
      public:
        FlattenUniformVisitor(ShaderType shaderType,
                              const sh::ShaderVariable &uniform,
                              std::vector<LinkedUniform> *uniforms,
                              std::vector<LinkedUniform> *samplerUniforms,
                              std::vector<LinkedUniform> *imageUniforms,
                              std::vector<LinkedUniform> *atomicCounterUniforms,
                              std::vector<LinkedUniform> *inputAttachmentUniforms,
                              std::vector<UnusedUniform> *unusedUniforms)
            : sh::VariableNameVisitor("", ""),
              mShaderType(shaderType),
              mMarkActive(uniform.active),
              mMarkStaticUse(uniform.staticUse),
              mBinding(uniform.binding),
              mOffset(uniform.offset),
              mLocation(uniform.location),
              mUniforms(uniforms),
              mSamplerUniforms(samplerUniforms),
              mImageUniforms(imageUniforms),
              mAtomicCounterUniforms(atomicCounterUniforms),
              mInputAttachmentUniforms(inputAttachmentUniforms),
              mUnusedUniforms(unusedUniforms)
        {}
    
        void visitNamedOpaqueObject(const sh::ShaderVariable &variable,
                                    const std::string &name,
                                    const std::string &mappedName,
                                    const std::vector<unsigned int> &arraySizes) override
        {
            visitNamedVariable(variable, false, name, mappedName, arraySizes);
        }
    
        void visitNamedVariable(const sh::ShaderVariable &variable,
                                bool isRowMajor,
                                const std::string &name,
                                const std::string &mappedName,
                                const std::vector<unsigned int> &arraySizes) override
        {
            bool isSampler                          = IsSamplerType(variable.type);
            bool isImage                            = IsImageType(variable.type);
            bool isAtomicCounter                    = IsAtomicCounterType(variable.type);
            bool isFragmentInOut                    = variable.isFragmentInOut;
            std::vector<LinkedUniform> *uniformList = mUniforms;
            if (isSampler)
            {
                uniformList = mSamplerUniforms;
            }
            else if (isImage)
            {
                uniformList = mImageUniforms;
            }
            else if (isAtomicCounter)
            {
                uniformList = mAtomicCounterUniforms;
            }
            else if (isFragmentInOut)
            {
                uniformList = mInputAttachmentUniforms;
            }
    
            std::string fullNameWithArrayIndex(name);
            std::string fullMappedNameWithArrayIndex(mappedName);
    
            if (variable.isArray())
            {
                // We're following the GLES 3.1 November 2016 spec section 7.3.1.1 Naming Active
                // Resources and including [0] at the end of array variable names.
                fullNameWithArrayIndex += "[0]";
                fullMappedNameWithArrayIndex += "[0]";
            }
    
            LinkedUniform *existingUniform = FindUniform(*uniformList, fullNameWithArrayIndex);
            if (existingUniform)
            {
                if (getBinding() != -1)
                {
                    existingUniform->binding = getBinding();
                }
                if (getOffset() != -1)
                {
                    existingUniform->offset = getOffset();
                }
                if (mLocation != -1)
                {
                    existingUniform->location = mLocation;
                }
                if (mMarkActive)
                {
                    existingUniform->active = true;
                    existingUniform->setActive(mShaderType, true);
                }
                if (mMarkStaticUse)
                {
                    existingUniform->staticUse = true;
                }
            }
            else
            {
                LinkedUniform linkedUniform(variable.type, variable.precision, fullNameWithArrayIndex,
                                            variable.arraySizes, getBinding(), getOffset(), mLocation,
                                            -1, sh::kDefaultBlockMemberInfo);
                linkedUniform.mappedName          = fullMappedNameWithArrayIndex;
                linkedUniform.active              = mMarkActive;
                linkedUniform.staticUse           = mMarkStaticUse;
                linkedUniform.outerArraySizes     = arraySizes;
                linkedUniform.texelFetchStaticUse = variable.texelFetchStaticUse;
                linkedUniform.imageUnitFormat     = variable.imageUnitFormat;
                linkedUniform.isFragmentInOut     = variable.isFragmentInOut;
                if (variable.hasParentArrayIndex())
                {
                    linkedUniform.setParentArrayIndex(variable.parentArrayIndex());
                }
                if (mMarkActive)
                {
                    linkedUniform.setActive(mShaderType, true);
                }
                else
                {
                    mUnusedUniforms->emplace_back(
                        linkedUniform.name, linkedUniform.isSampler(), linkedUniform.isImage(),
                        linkedUniform.isAtomicCounter(), linkedUniform.isFragmentInOut);
                }
    
                uniformList->push_back(linkedUniform);
            }
    
            unsigned int elementCount = variable.getBasicTypeElementCount();
    
            // Samplers and images aren't "real" uniforms, so they don't count towards register usage.
            // Likewise, don't count "real" uniforms towards opaque count.
    
            if (!IsOpaqueType(variable.type) && !isFragmentInOut)
            {
                mUniformCount.vectorCount += VariableRegisterCount(variable.type) * elementCount;
            }
    
            mUniformCount.samplerCount += (isSampler ? elementCount : 0);
            mUniformCount.imageCount += (isImage ? elementCount : 0);
            mUniformCount.atomicCounterCount += (isAtomicCounter ? elementCount : 0);
            mUniformCount.fragmentInOutCount += (isFragmentInOut ? elementCount : 0);
    
            if (mLocation != -1)
            {
                mLocation += elementCount;
            }
        }
    
        void enterStructAccess(const sh::ShaderVariable &structVar, bool isRowMajor) override
        {
            mStructStackSize++;
            sh::VariableNameVisitor::enterStructAccess(structVar, isRowMajor);
        }
    
        void exitStructAccess(const sh::ShaderVariable &structVar, bool isRowMajor) override
        {
            mStructStackSize--;
            sh::VariableNameVisitor::exitStructAccess(structVar, isRowMajor);
        }
    
        ShaderUniformCount getCounts() const { return mUniformCount; }
    
      private:
        int getBinding() const { return mStructStackSize == 0 ? mBinding : -1; }
        int getOffset() const { return mStructStackSize == 0 ? mOffset : -1; }
    
        ShaderType mShaderType;
    
        // Active and StaticUse are given separately because they are tracked at struct granularity.
        bool mMarkActive;
        bool mMarkStaticUse;
        int mBinding;
        int mOffset;
        int mLocation;
        std::vector<LinkedUniform> *mUniforms;
        std::vector<LinkedUniform> *mSamplerUniforms;
        std::vector<LinkedUniform> *mImageUniforms;
        std::vector<LinkedUniform> *mAtomicCounterUniforms;
        std::vector<LinkedUniform> *mInputAttachmentUniforms;
        std::vector<UnusedUniform> *mUnusedUniforms;
        ShaderUniformCount mUniformCount;
        unsigned int mStructStackSize = 0;
    };
    
    class InterfaceBlockInfo final : angle::NonCopyable
    {
      public:
        InterfaceBlockInfo(CustomBlockLayoutEncoderFactory *customEncoderFactory)
            : mCustomEncoderFactory(customEncoderFactory)
        {}
    
        void getShaderBlockInfo(const std::vector<sh::InterfaceBlock> &interfaceBlocks);
    
        bool getBlockSize(const std::string &name, const std::string &mappedName, size_t *sizeOut);
        bool getBlockMemberInfo(const std::string &name,
                                const std::string &mappedName,
                                sh::BlockMemberInfo *infoOut);
    
      private:
        size_t getBlockInfo(const sh::InterfaceBlock &interfaceBlock);
    
        std::map<std::string, size_t> mBlockSizes;
        sh::BlockLayoutMap mBlockLayout;
        // Based on the interface block layout, the std140 or std430 encoders are used.  On some
        // platforms (currently only D3D), there could be another non-standard encoder used.
        CustomBlockLayoutEncoderFactory *mCustomEncoderFactory;
    };
    
    void InterfaceBlockInfo::getShaderBlockInfo(const std::vector<sh::InterfaceBlock> &interfaceBlocks)
    {
        for (const sh::InterfaceBlock &interfaceBlock : interfaceBlocks)
        {
            if (!IsActiveInterfaceBlock(interfaceBlock))
                continue;
    
            if (mBlockSizes.count(interfaceBlock.name) > 0)
                continue;
    
            size_t dataSize                  = getBlockInfo(interfaceBlock);
            mBlockSizes[interfaceBlock.name] = dataSize;
        }
    }
    
    size_t InterfaceBlockInfo::getBlockInfo(const sh::InterfaceBlock &interfaceBlock)
    {
        ASSERT(IsActiveInterfaceBlock(interfaceBlock));
    
        // define member uniforms
        sh::Std140BlockEncoder std140Encoder;
        sh::Std430BlockEncoder std430Encoder;
        sh::BlockLayoutEncoder *customEncoder = nullptr;
        sh::BlockLayoutEncoder *encoder       = nullptr;
    
        if (interfaceBlock.layout == sh::BLOCKLAYOUT_STD140)
        {
            encoder = &std140Encoder;
        }
        else if (interfaceBlock.layout == sh::BLOCKLAYOUT_STD430)
        {
            encoder = &std430Encoder;
        }
        else if (mCustomEncoderFactory)
        {
            encoder = customEncoder = mCustomEncoderFactory->makeEncoder();
        }
        else
        {
            UNREACHABLE();
            return 0;
        }
    
        sh::GetInterfaceBlockInfo(interfaceBlock.fields, interfaceBlock.fieldPrefix(), encoder,
                                  &mBlockLayout);
    
        size_t offset = encoder->getCurrentOffset();
    
        SafeDelete(customEncoder);
    
        return offset;
    }
    
    bool InterfaceBlockInfo::getBlockSize(const std::string &name,
                                          const std::string &mappedName,
                                          size_t *sizeOut)
    {
        size_t nameLengthWithoutArrayIndex;
        ParseArrayIndex(name, &nameLengthWithoutArrayIndex);
        std::string baseName = name.substr(0u, nameLengthWithoutArrayIndex);
        auto sizeIter        = mBlockSizes.find(baseName);
        if (sizeIter == mBlockSizes.end())
        {
            *sizeOut = 0;
            return false;
        }
    
        *sizeOut = sizeIter->second;
        return true;
    }
    
    bool InterfaceBlockInfo::getBlockMemberInfo(const std::string &name,
                                                const std::string &mappedName,
                                                sh::BlockMemberInfo *infoOut)
    {
        auto infoIter = mBlockLayout.find(name);
        if (infoIter == mBlockLayout.end())
        {
            *infoOut = sh::kDefaultBlockMemberInfo;
            return false;
        }
    
        *infoOut = infoIter->second;
        return true;
    }
    
    void GetFilteredVaryings(const std::vector<sh::ShaderVariable> &varyings,
                             std::vector<const sh::ShaderVariable *> *filteredVaryingsOut)
    {
        for (const sh::ShaderVariable &varying : varyings)
        {
            // Built-in varyings obey special rules
            if (varying.isBuiltIn())
            {
                continue;
            }
    
            filteredVaryingsOut->push_back(&varying);
        }
    }
    
    LinkMismatchError LinkValidateVaryings(const sh::ShaderVariable &outputVarying,
                                           const sh::ShaderVariable &inputVarying,
                                           int shaderVersion,
                                           ShaderType frontShaderType,
                                           ShaderType backShaderType,
                                           bool isSeparable,
                                           std::string *mismatchedStructFieldName)
    {
        // [ES 3.2 spec] 7.4.1 Shader Interface Matching:
        // Tessellation control shader per-vertex output variables and blocks and tessellation control,
        // tessellation evaluation, and geometry shader per-vertex input variables and blocks are
        // required to be declared as arrays, with each element representing input or output values for
        // a single vertex of a multi-vertex primitive. For the purposes of interface matching, such
        // variables and blocks are treated as though they were not declared as arrays.
        bool treatOutputAsNonArray =
            (frontShaderType == ShaderType::TessControl && !outputVarying.isPatch);
        bool treatInputAsNonArray =
            ((backShaderType == ShaderType::TessControl ||
              backShaderType == ShaderType::TessEvaluation || backShaderType == ShaderType::Geometry) &&
             !inputVarying.isPatch);
    
        // Skip the validation on the array sizes between a vertex output varying and a geometry input
        // varying as it has been done before.
        bool validatePrecision      = isSeparable && (shaderVersion > 100);
        LinkMismatchError linkError = LinkValidateProgramVariables(
            outputVarying, inputVarying, validatePrecision, treatOutputAsNonArray, treatInputAsNonArray,
            mismatchedStructFieldName);
        if (linkError != LinkMismatchError::NO_MISMATCH)
        {
            return linkError;
        }
    
        // Explicit locations must match if the names match.
        if (outputVarying.isSameNameAtLinkTime(inputVarying) &&
            outputVarying.location != inputVarying.location)
        {
            return LinkMismatchError::LOCATION_MISMATCH;
        }
    
        if (!sh::InterpolationTypesMatch(outputVarying.interpolation, inputVarying.interpolation))
        {
            return LinkMismatchError::INTERPOLATION_TYPE_MISMATCH;
        }
    
        if (shaderVersion == 100 && outputVarying.isInvariant != inputVarying.isInvariant)
        {
            return LinkMismatchError::INVARIANCE_MISMATCH;
        }
    
        return LinkMismatchError::NO_MISMATCH;
    }
    
    bool DoShaderVariablesMatch(int frontShaderVersion,
                                ShaderType frontShaderType,
                                ShaderType backShaderType,
                                const sh::ShaderVariable &input,
                                const sh::ShaderVariable &output,
                                bool isSeparable,
                                gl::InfoLog &infoLog)
    {
        bool namesMatch     = input.isSameNameAtLinkTime(output);
        bool locationsMatch = input.location != -1 && input.location == output.location;
    
        // An output block is considered to match an input block in the subsequent
        // shader if the two blocks have the same block name, and the members of the
        // block match exactly in name, type, qualification, and declaration order.
        //
        // - For the purposes of shader interface matching, the gl_PointSize
        //   member of the intrinsically declared gl_PerVertex shader interface
        //   block is ignored.
        // - Output blocks that do not match in name, but have a location and match
        //   in every other way listed above may be considered to match by some
        //   implementations, but not all - so this behaviour should not be relied
        //   upon.
    
        // An output variable is considered to match an input variable in the subsequent
        // shader if:
        //
        // - the two variables match in name, type, and qualification; or
        // - the two variables are declared with the same location qualifier and
        //   match in type and qualification.
    
        if (namesMatch || locationsMatch)
        {
            std::string mismatchedStructFieldName;
            LinkMismatchError linkError =
                LinkValidateVaryings(output, input, frontShaderVersion, frontShaderType, backShaderType,
                                     isSeparable, &mismatchedStructFieldName);
            if (linkError != LinkMismatchError::NO_MISMATCH)
            {
                LogLinkMismatch(infoLog, input.name, "varying", linkError, mismatchedStructFieldName,
                                frontShaderType, backShaderType);
                return false;
            }
    
            return true;
        }
    
        return false;
    }
    }  // anonymous namespace
    
    UniformLinker::UniformLinker(const ProgramState &state) : mState(state) {}
    
    UniformLinker::~UniformLinker() = default;
    
    void UniformLinker::getResults(std::vector<LinkedUniform> *uniforms,
                                   std::vector<UnusedUniform> *unusedUniforms,
                                   std::vector<VariableLocation> *uniformLocations)
    {
        uniforms->swap(mUniforms);
        unusedUniforms->swap(mUnusedUniforms);
        uniformLocations->swap(mUniformLocations);
    }
    
    bool UniformLinker::link(const Caps &caps,
                             InfoLog &infoLog,
                             const ProgramAliasedBindings &uniformLocationBindings)
    {
        if (mState.getAttachedShader(ShaderType::Vertex) &&
            mState.getAttachedShader(ShaderType::Fragment))
        {
            ASSERT(mState.getAttachedShader(ShaderType::Compute) == nullptr);
            if (!validateGraphicsUniforms(infoLog))
            {
                return false;
            }
        }
    
        // Flatten the uniforms list (nested fields) into a simple list (no nesting).
        // Also check the maximum uniform vector and sampler counts.
        if (!flattenUniformsAndCheckCaps(caps, infoLog))
        {
            return false;
        }
    
        if (!checkMaxCombinedAtomicCounters(caps, infoLog))
        {
            return false;
        }
    
        if (!indexUniforms(infoLog, uniformLocationBindings))
        {
            return false;
        }
    
        return true;
    }
    
    bool UniformLinker::validateGraphicsUniforms(InfoLog &infoLog) const
    {
        // Check that uniforms defined in the graphics shaders are identical
        std::map<std::string, ShaderUniform> linkedUniforms;
    
        for (const ShaderType shaderType : kAllGraphicsShaderTypes)
        {
            Shader *currentShader = mState.getAttachedShader(shaderType);
            if (currentShader)
            {
                if (shaderType == ShaderType::Vertex)
                {
                    for (const sh::ShaderVariable &vertexUniform : currentShader->getUniforms())
                    {
                        linkedUniforms[vertexUniform.name] =
                            std::make_pair(ShaderType::Vertex, &vertexUniform);
                    }
                }
                else
                {
                    bool isLastShader = (shaderType == ShaderType::Fragment);
                    if (!ValidateGraphicsUniformsPerShader(currentShader, !isLastShader,
                                                           &linkedUniforms, infoLog))
                    {
                        return false;
                    }
                }
            }
        }
    
        return true;
    }
    
    bool UniformLinker::indexUniforms(InfoLog &infoLog,
                                      const ProgramAliasedBindings &uniformLocationBindings)
    {
        // Locations which have been allocated for an unused uniform.
        std::set<GLuint> ignoredLocations;
    
        int maxUniformLocation = -1;
    
        // Gather uniform locations that have been set either using the bindUniformLocation API or by
        // using a location layout qualifier and check conflicts between them.
        if (!gatherUniformLocationsAndCheckConflicts(infoLog, uniformLocationBindings,
                                                     &ignoredLocations, &maxUniformLocation))
        {
            return false;
        }
    
        // Conflicts have been checked, now we can prune non-statically used uniforms. Code further down
        // the line relies on only having statically used uniforms in mUniforms.
        pruneUnusedUniforms();
    
        // Gather uniforms that have their location pre-set and uniforms that don't yet have a location.
        std::vector<VariableLocation> unlocatedUniforms;
        std::map<GLuint, VariableLocation> preLocatedUniforms;
    
        for (size_t uniformIndex = 0; uniformIndex < mUniforms.size(); uniformIndex++)
        {
            const LinkedUniform &uniform = mUniforms[uniformIndex];
    
            if ((uniform.isBuiltIn() && !uniform.isEmulatedBuiltIn()) ||
                IsAtomicCounterType(uniform.type) || uniform.isFragmentInOut)
            {
                continue;
            }
    
            int preSetLocation = uniformLocationBindings.getBinding(uniform);
            int shaderLocation = uniform.location;
    
            if (shaderLocation != -1)
            {
                preSetLocation = shaderLocation;
            }
    
            unsigned int elementCount = uniform.getBasicTypeElementCount();
            for (unsigned int arrayIndex = 0; arrayIndex < elementCount; arrayIndex++)
            {
                VariableLocation location(arrayIndex, static_cast<unsigned int>(uniformIndex));
    
                if ((arrayIndex == 0 && preSetLocation != -1) || shaderLocation != -1)
                {
                    int elementLocation                 = preSetLocation + arrayIndex;
                    preLocatedUniforms[elementLocation] = location;
                }
                else
                {
                    unlocatedUniforms.push_back(location);
                }
            }
        }
    
        // Make enough space for all uniforms, with pre-set locations or not.
        mUniformLocations.resize(
            std::max(unlocatedUniforms.size() + preLocatedUniforms.size() + ignoredLocations.size(),
                     static_cast<size_t>(maxUniformLocation + 1)));
    
        // Assign uniforms with pre-set locations
        for (const auto &uniform : preLocatedUniforms)
        {
            mUniformLocations[uniform.first] = uniform.second;
        }
    
        // Assign ignored uniforms
        for (const auto &ignoredLocation : ignoredLocations)
        {
            mUniformLocations[ignoredLocation].markIgnored();
        }
    
        // Automatically assign locations for the rest of the uniforms
        size_t nextUniformLocation = 0;
        for (const auto &unlocatedUniform : unlocatedUniforms)
        {
            while (mUniformLocations[nextUniformLocation].used() ||
                   mUniformLocations[nextUniformLocation].ignored)
            {
                nextUniformLocation++;
            }
    
            ASSERT(nextUniformLocation < mUniformLocations.size());
            mUniformLocations[nextUniformLocation] = unlocatedUniform;
            nextUniformLocation++;
        }
    
        return true;
    }
    
    bool UniformLinker::gatherUniformLocationsAndCheckConflicts(
        InfoLog &infoLog,
        const ProgramAliasedBindings &uniformLocationBindings,
        std::set<GLuint> *ignoredLocations,
        int *maxUniformLocation)
    {
        // All the locations where another uniform can't be located.
        std::set<GLuint> reservedLocations;
    
        for (const LinkedUniform &uniform : mUniforms)
        {
            if ((uniform.isBuiltIn() && !uniform.isEmulatedBuiltIn()) || uniform.isFragmentInOut)
            {
                // The uniform of the fragment inout is not a normal uniform type. So, in the case of
                // the fragment inout, this routine should be skipped.
                continue;
            }
    
            int apiBoundLocation = uniformLocationBindings.getBinding(uniform);
            int shaderLocation   = uniform.location;
    
            if (shaderLocation != -1)
            {
                unsigned int elementCount = uniform.getBasicTypeElementCount();
    
                for (unsigned int arrayIndex = 0; arrayIndex < elementCount; arrayIndex++)
                {
                    // GLSL ES 3.10 section 4.4.3
                    int elementLocation = shaderLocation + arrayIndex;
                    *maxUniformLocation = std::max(*maxUniformLocation, elementLocation);
                    if (reservedLocations.find(elementLocation) != reservedLocations.end())
                    {
                        infoLog << "Multiple uniforms bound to location " << elementLocation << ".";
                        return false;
                    }
                    reservedLocations.insert(elementLocation);
                    if (!uniform.active)
                    {
                        ignoredLocations->insert(elementLocation);
                    }
                }
            }
            else if (apiBoundLocation != -1 && uniform.staticUse)
            {
                // Only the first location is reserved even if the uniform is an array.
                *maxUniformLocation = std::max(*maxUniformLocation, apiBoundLocation);
                if (reservedLocations.find(apiBoundLocation) != reservedLocations.end())
                {
                    infoLog << "Multiple uniforms bound to location " << apiBoundLocation << ".";
                    return false;
                }
                reservedLocations.insert(apiBoundLocation);
                if (!uniform.active)
                {
                    ignoredLocations->insert(apiBoundLocation);
                }
            }
        }
    
        // Record the uniform locations that were bound using the API for uniforms that were not found
        // from the shader. Other uniforms should not be assigned to those locations.
        for (const auto &locationBinding : uniformLocationBindings)
        {
            GLuint location = locationBinding.second.location;
            if (reservedLocations.find(location) == reservedLocations.end())
            {
                ignoredLocations->insert(location);
                *maxUniformLocation = std::max(*maxUniformLocation, static_cast<int>(location));
            }
        }
    
        return true;
    }
    
    void UniformLinker::pruneUnusedUniforms()
    {
        auto uniformIter = mUniforms.begin();
        while (uniformIter != mUniforms.end())
        {
            if (uniformIter->active)
            {
                ++uniformIter;
            }
            else
            {
                mUnusedUniforms.emplace_back(uniformIter->name, uniformIter->isSampler(),
                                             uniformIter->isImage(), uniformIter->isAtomicCounter(),
                                             uniformIter->isFragmentInOut);
                uniformIter = mUniforms.erase(uniformIter);
            }
        }
    }
    
    bool UniformLinker::flattenUniformsAndCheckCapsForShader(
        Shader *shader,
        const Caps &caps,
        std::vector<LinkedUniform> &samplerUniforms,
        std::vector<LinkedUniform> &imageUniforms,
        std::vector<LinkedUniform> &atomicCounterUniforms,
        std::vector<LinkedUniform> &inputAttachmentUniforms,
        std::vector<UnusedUniform> &unusedUniforms,
        InfoLog &infoLog)
    {
        ShaderUniformCount shaderUniformCount;
        for (const sh::ShaderVariable &uniform : shader->getUniforms())
        {
            FlattenUniformVisitor flattener(shader->getType(), uniform, &mUniforms, &samplerUniforms,
                                            &imageUniforms, &atomicCounterUniforms,
                                            &inputAttachmentUniforms, &unusedUniforms);
            sh::TraverseShaderVariable(uniform, false, &flattener);
    
            if (uniform.active)
            {
                shaderUniformCount += flattener.getCounts();
            }
            else
            {
                unusedUniforms.emplace_back(uniform.name, IsSamplerType(uniform.type),
                                            IsImageType(uniform.type),
                                            IsAtomicCounterType(uniform.type), uniform.isFragmentInOut);
            }
        }
    
        ShaderType shaderType = shader->getType();
    
        // TODO (jiawei.shao@intel.com): check whether we need finer-grained component counting
        GLuint maxUniformVectorsCount = GetMaximumShaderUniformVectors(shaderType, caps);
        if (shaderUniformCount.vectorCount > maxUniformVectorsCount)
        {
            GLuint maxUniforms = 0u;
    
            // See comments in GetUniformResourceLimitName()
            if (shaderType == ShaderType::Vertex || shaderType == ShaderType::Fragment)
            {
                maxUniforms = maxUniformVectorsCount;
            }
            else
            {
                maxUniforms = maxUniformVectorsCount * 4;
            }
    
            LogUniformsExceedLimit(shaderType, UniformType::Variable, maxUniforms, infoLog);
            return false;
        }
    
        if (shaderUniformCount.samplerCount >
            static_cast<GLuint>(caps.maxShaderTextureImageUnits[shaderType]))
        {
            LogUniformsExceedLimit(shaderType, UniformType::Sampler,
                                   caps.maxShaderTextureImageUnits[shaderType], infoLog);
            return false;
        }
    
        if (shaderUniformCount.imageCount >
            static_cast<GLuint>(caps.maxShaderImageUniforms[shaderType]))
        {
            LogUniformsExceedLimit(shaderType, UniformType::Image,
                                   caps.maxShaderImageUniforms[shaderType], infoLog);
            return false;
        }
    
        if (shaderUniformCount.atomicCounterCount >
            static_cast<GLuint>(caps.maxShaderAtomicCounters[shaderType]))
        {
            LogUniformsExceedLimit(shaderType, UniformType::AtomicCounter,
                                   caps.maxShaderAtomicCounters[shaderType], infoLog);
            return false;
        }
    
        return true;
    }
    
    bool UniformLinker::flattenUniformsAndCheckCaps(const Caps &caps, InfoLog &infoLog)
    {
        std::vector<LinkedUniform> samplerUniforms;
        std::vector<LinkedUniform> imageUniforms;
        std::vector<LinkedUniform> atomicCounterUniforms;
        std::vector<LinkedUniform> inputAttachmentUniforms;
        std::vector<UnusedUniform> unusedUniforms;
    
        for (const ShaderType shaderType : AllShaderTypes())
        {
            Shader *shader = mState.getAttachedShader(shaderType);
            if (!shader)
            {
                continue;
            }
    
            if (!flattenUniformsAndCheckCapsForShader(shader, caps, samplerUniforms, imageUniforms,
                                                      atomicCounterUniforms, inputAttachmentUniforms,
                                                      unusedUniforms, infoLog))
            {
                return false;
            }
        }
    
        mUniforms.insert(mUniforms.end(), samplerUniforms.begin(), samplerUniforms.end());
        mUniforms.insert(mUniforms.end(), imageUniforms.begin(), imageUniforms.end());
        mUniforms.insert(mUniforms.end(), atomicCounterUniforms.begin(), atomicCounterUniforms.end());
        mUniforms.insert(mUniforms.end(), inputAttachmentUniforms.begin(),
                         inputAttachmentUniforms.end());
        mUnusedUniforms.insert(mUnusedUniforms.end(), unusedUniforms.begin(), unusedUniforms.end());
        return true;
    }
    
    bool UniformLinker::checkMaxCombinedAtomicCounters(const Caps &caps, InfoLog &infoLog)
    {
        unsigned int atomicCounterCount = 0;
        for (const auto &uniform : mUniforms)
        {
            if (IsAtomicCounterType(uniform.type) && uniform.active)
            {
                atomicCounterCount += uniform.getBasicTypeElementCount();
                if (atomicCounterCount > static_cast<GLuint>(caps.maxCombinedAtomicCounters))
                {
                    infoLog << "atomic counter count exceeds MAX_COMBINED_ATOMIC_COUNTERS"
                            << caps.maxCombinedAtomicCounters << ").";
                    return false;
                }
            }
        }
        return true;
    }
    
    // InterfaceBlockLinker implementation.
    InterfaceBlockLinker::InterfaceBlockLinker() = default;
    
    InterfaceBlockLinker::~InterfaceBlockLinker() = default;
    
    void InterfaceBlockLinker::init(std::vector<InterfaceBlock> *blocksOut,
                                    std::vector<std::string> *unusedInterfaceBlocksOut)
    {
        mBlocksOut                = blocksOut;
        mUnusedInterfaceBlocksOut = unusedInterfaceBlocksOut;
    }
    
    void InterfaceBlockLinker::addShaderBlocks(ShaderType shaderType,
                                               const std::vector<sh::InterfaceBlock> *blocks)
    {
        mShaderBlocks[shaderType] = blocks;
    }
    
    void InterfaceBlockLinker::linkBlocks(const GetBlockSizeFunc &getBlockSize,
                                          const GetBlockMemberInfoFunc &getMemberInfo) const
    {
        ASSERT(mBlocksOut->empty());
    
        std::set<std::string> visitedList;
    
        for (const ShaderType shaderType : AllShaderTypes())
        {
            if (!mShaderBlocks[shaderType])
            {
                continue;
            }
    
            for (const sh::InterfaceBlock &block : *mShaderBlocks[shaderType])
            {
                if (!IsActiveInterfaceBlock(block))
                {
                    mUnusedInterfaceBlocksOut->push_back(block.name);
                    continue;
                }
    
                if (visitedList.count(block.name) == 0)
                {
                    defineInterfaceBlock(getBlockSize, getMemberInfo, block, shaderType);
                    visitedList.insert(block.name);
                    continue;
                }
    
                if (!block.active)
                {
                    mUnusedInterfaceBlocksOut->push_back(block.name);
                    continue;
                }
    
                for (InterfaceBlock &priorBlock : *mBlocksOut)
                {
                    if (block.name == priorBlock.name)
                    {
                        priorBlock.setActive(shaderType, true);
    
                        std::unique_ptr<sh::ShaderVariableVisitor> visitor(
                            getVisitor(getMemberInfo, block.fieldPrefix(), block.fieldMappedPrefix(),
                                       shaderType, -1));
    
                        sh::TraverseShaderVariables(block.fields, false, visitor.get());
                    }
                }
            }
        }
    }
    
    void InterfaceBlockLinker::defineInterfaceBlock(const GetBlockSizeFunc &getBlockSize,
                                                    const GetBlockMemberInfoFunc &getMemberInfo,
                                                    const sh::InterfaceBlock &interfaceBlock,
                                                    ShaderType shaderType) const
    {
        size_t blockSize = 0;
        std::vector<unsigned int> blockIndexes;
    
        int blockIndex = static_cast<int>(mBlocksOut->size());
        // Track the first and last block member index to determine the range of active block members in
        // the block.
        size_t firstBlockMemberIndex = getCurrentBlockMemberIndex();
    
        std::unique_ptr<sh::ShaderVariableVisitor> visitor(
            getVisitor(getMemberInfo, interfaceBlock.fieldPrefix(), interfaceBlock.fieldMappedPrefix(),
                       shaderType, blockIndex));
        sh::TraverseShaderVariables(interfaceBlock.fields, false, visitor.get());
    
        size_t lastBlockMemberIndex = getCurrentBlockMemberIndex();
    
        for (size_t blockMemberIndex = firstBlockMemberIndex; blockMemberIndex < lastBlockMemberIndex;
             ++blockMemberIndex)
        {
            blockIndexes.push_back(static_cast<unsigned int>(blockMemberIndex));
        }
    
        unsigned int firstFieldArraySize = interfaceBlock.fields[0].getArraySizeProduct();
    
        for (unsigned int arrayElement = 0; arrayElement < interfaceBlock.elementCount();
             ++arrayElement)
        {
            std::string blockArrayName       = interfaceBlock.name;
            std::string blockMappedArrayName = interfaceBlock.mappedName;
            if (interfaceBlock.isArray())
            {
                blockArrayName += ArrayString(arrayElement);
                blockMappedArrayName += ArrayString(arrayElement);
            }
    
            // Don't define this block at all if it's not active in the implementation.
            if (!getBlockSize(blockArrayName, blockMappedArrayName, &blockSize))
            {
                continue;
            }
    
            // ESSL 3.10 section 4.4.4 page 58:
            // Any uniform or shader storage block declared without a binding qualifier is initially
            // assigned to block binding point zero.
            int blockBinding =
                (interfaceBlock.binding == -1 ? 0 : interfaceBlock.binding + arrayElement);
            InterfaceBlock block(interfaceBlock.name, interfaceBlock.mappedName,
                                 interfaceBlock.isArray(), arrayElement, firstFieldArraySize,
                                 blockBinding);
            block.memberIndexes = blockIndexes;
            block.setActive(shaderType, interfaceBlock.active);
    
            // Since all block elements in an array share the same active interface blocks, they
            // will all be active once any block member is used. So, since interfaceBlock.name[0]
            // was active, here we will add every block element in the array.
            block.dataSize = static_cast<unsigned int>(blockSize);
            mBlocksOut->push_back(block);
        }
    }
    
    // UniformBlockLinker implementation.
    UniformBlockLinker::UniformBlockLinker() = default;
    
    UniformBlockLinker::~UniformBlockLinker() {}
    
    void UniformBlockLinker::init(std::vector<InterfaceBlock> *blocksOut,
                                  std::vector<LinkedUniform> *uniformsOut,
                                  std::vector<std::string> *unusedInterfaceBlocksOut)
    {
        InterfaceBlockLinker::init(blocksOut, unusedInterfaceBlocksOut);
        mUniformsOut = uniformsOut;
    }
    
    size_t UniformBlockLinker::getCurrentBlockMemberIndex() const
    {
        return mUniformsOut->size();
    }
    
    sh::ShaderVariableVisitor *UniformBlockLinker::getVisitor(
        const GetBlockMemberInfoFunc &getMemberInfo,
        const std::string &namePrefix,
        const std::string &mappedNamePrefix,
        ShaderType shaderType,
        int blockIndex) const
    {
        return new UniformBlockEncodingVisitor(getMemberInfo, namePrefix, mappedNamePrefix,
                                               mUniformsOut, shaderType, blockIndex);
    }
    
    // ShaderStorageBlockLinker implementation.
    ShaderStorageBlockLinker::ShaderStorageBlockLinker() = default;
    
    ShaderStorageBlockLinker::~ShaderStorageBlockLinker() = default;
    
    void ShaderStorageBlockLinker::init(std::vector<InterfaceBlock> *blocksOut,
                                        std::vector<BufferVariable> *bufferVariablesOut,
                                        std::vector<std::string> *unusedInterfaceBlocksOut)
    {
        InterfaceBlockLinker::init(blocksOut, unusedInterfaceBlocksOut);
        mBufferVariablesOut = bufferVariablesOut;
    }
    
    size_t ShaderStorageBlockLinker::getCurrentBlockMemberIndex() const
    {
        return mBufferVariablesOut->size();
    }
    
    sh::ShaderVariableVisitor *ShaderStorageBlockLinker::getVisitor(
        const GetBlockMemberInfoFunc &getMemberInfo,
        const std::string &namePrefix,
        const std::string &mappedNamePrefix,
        ShaderType shaderType,
        int blockIndex) const
    {
        return new ShaderStorageBlockVisitor(getMemberInfo, namePrefix, mappedNamePrefix,
                                             mBufferVariablesOut, shaderType, blockIndex);
    }
    
    // AtomicCounterBufferLinker implementation.
    AtomicCounterBufferLinker::AtomicCounterBufferLinker() = default;
    
    AtomicCounterBufferLinker::~AtomicCounterBufferLinker() = default;
    
    void AtomicCounterBufferLinker::init(std::vector<AtomicCounterBuffer> *atomicCounterBuffersOut)
    {
        mAtomicCounterBuffersOut = atomicCounterBuffersOut;
    }
    
    void AtomicCounterBufferLinker::link(const std::map<int, unsigned int> &sizeMap) const
    {
        for (auto &atomicCounterBuffer : *mAtomicCounterBuffersOut)
        {
            auto bufferSize = sizeMap.find(atomicCounterBuffer.binding);
            ASSERT(bufferSize != sizeMap.end());
            atomicCounterBuffer.dataSize = bufferSize->second;
        }
    }
    
    ProgramLinkedResources::ProgramLinkedResources() = default;
    
    ProgramLinkedResources::~ProgramLinkedResources() = default;
    
    void ProgramLinkedResources::init(std::vector<InterfaceBlock> *uniformBlocksOut,
                                      std::vector<LinkedUniform> *uniformsOut,
                                      std::vector<InterfaceBlock> *shaderStorageBlocksOut,
                                      std::vector<BufferVariable> *bufferVariablesOut,
                                      std::vector<AtomicCounterBuffer> *atomicCounterBuffersOut)
    {
        uniformBlockLinker.init(uniformBlocksOut, uniformsOut, &unusedInterfaceBlocks);
        shaderStorageBlockLinker.init(shaderStorageBlocksOut, bufferVariablesOut,
                                      &unusedInterfaceBlocks);
        atomicCounterBufferLinker.init(atomicCounterBuffersOut);
    }
    
    void ProgramLinkedResourcesLinker::linkResources(const ProgramState &programState,
                                                     const ProgramLinkedResources &resources) const
    {
        // Gather uniform interface block info.
        InterfaceBlockInfo uniformBlockInfo(mCustomEncoderFactory);
        for (const ShaderType shaderType : AllShaderTypes())
        {
            Shader *shader = programState.getAttachedShader(shaderType);
            if (shader)
            {
                uniformBlockInfo.getShaderBlockInfo(shader->getUniformBlocks());
            }
        }
    
        auto getUniformBlockSize = [&uniformBlockInfo](const std::string &name,
                                                       const std::string &mappedName, size_t *sizeOut) {
            return uniformBlockInfo.getBlockSize(name, mappedName, sizeOut);
        };
    
        auto getUniformBlockMemberInfo = [&uniformBlockInfo](const std::string &name,
                                                             const std::string &mappedName,
                                                             sh::BlockMemberInfo *infoOut) {
            return uniformBlockInfo.getBlockMemberInfo(name, mappedName, infoOut);
        };
    
        // Link uniform interface blocks.
        resources.uniformBlockLinker.linkBlocks(getUniformBlockSize, getUniformBlockMemberInfo);
    
        // Gather storage buffer interface block info.
        InterfaceBlockInfo shaderStorageBlockInfo(mCustomEncoderFactory);
        for (const ShaderType shaderType : AllShaderTypes())
        {
            Shader *shader = programState.getAttachedShader(shaderType);
            if (shader)
            {
                shaderStorageBlockInfo.getShaderBlockInfo(shader->getShaderStorageBlocks());
            }
        }
        auto getShaderStorageBlockSize = [&shaderStorageBlockInfo](const std::string &name,
                                                                   const std::string &mappedName,
                                                                   size_t *sizeOut) {
            return shaderStorageBlockInfo.getBlockSize(name, mappedName, sizeOut);
        };
    
        auto getShaderStorageBlockMemberInfo = [&shaderStorageBlockInfo](const std::string &name,
                                                                         const std::string &mappedName,
                                                                         sh::BlockMemberInfo *infoOut) {
            return shaderStorageBlockInfo.getBlockMemberInfo(name, mappedName, infoOut);
        };
    
        // Link storage buffer interface blocks.
        resources.shaderStorageBlockLinker.linkBlocks(getShaderStorageBlockSize,
                                                      getShaderStorageBlockMemberInfo);
    
        // Gather and link atomic counter buffer interface blocks.
        std::map<int, unsigned int> sizeMap;
        getAtomicCounterBufferSizeMap(programState, sizeMap);
        resources.atomicCounterBufferLinker.link(sizeMap);
    }
    
    void ProgramLinkedResourcesLinker::getAtomicCounterBufferSizeMap(
        const ProgramState &programState,
        std::map<int, unsigned int> &sizeMapOut) const
    {
        for (unsigned int index : programState.getAtomicCounterUniformRange())
        {
            const LinkedUniform &glUniform = programState.getUniforms()[index];
    
            auto &bufferDataSize = sizeMapOut[glUniform.binding];
    
            // Calculate the size of the buffer by finding the end of the last uniform with the same
            // binding. The end of the uniform is calculated by finding the initial offset of the
            // uniform and adding size of the uniform. For arrays, the size is the number of elements
            // times the element size (should always by 4 for atomic_units).
            unsigned dataOffset =
                glUniform.offset + static_cast<unsigned int>(glUniform.getBasicTypeElementCount() *
                                                             glUniform.getElementSize());
            if (dataOffset > bufferDataSize)
            {
                bufferDataSize = dataOffset;
            }
        }
    }
    
    // Note: this is broken for pipelines with modified/discarded shaders. http://anglebug.com/5506
    bool LinkValidateProgramGlobalNames(InfoLog &infoLog, const HasAttachedShaders &programOrPipeline)
    {
        angle::HashMap<std::string, const sh::ShaderVariable *> uniformMap;
        using BlockAndFieldPair = std::pair<const sh::InterfaceBlock *, const sh::ShaderVariable *>;
        angle::HashMap<std::string, std::vector<BlockAndFieldPair>> uniformBlockFieldMap;
    
        for (ShaderType shaderType : kAllGraphicsShaderTypes)
        {
            Shader *shader = programOrPipeline.getAttachedShader(shaderType);
            if (!shader)
            {
                continue;
            }
    
            // Build a map of Uniforms
            const std::vector<sh::ShaderVariable> uniforms = shader->getUniforms();
            for (const auto &uniform : uniforms)
            {
                uniformMap[uniform.name] = &uniform;
            }
    
            // Build a map of Uniform Blocks
            // This will also detect any field name conflicts between Uniform Blocks without instance
            // names
            const std::vector<sh::InterfaceBlock> &uniformBlocks = shader->getUniformBlocks();
            for (const auto &uniformBlock : uniformBlocks)
            {
                // Only uniform blocks without an instance name can create a conflict with their field
                // names
                if (!uniformBlock.instanceName.empty())
                {
                    continue;
                }
    
                for (const auto &field : uniformBlock.fields)
                {
                    if (!uniformBlockFieldMap.count(field.name))
                    {
                        // First time we've seen this uniform block field name, so add the
                        // (Uniform Block, Field) pair immediately since there can't be a conflict yet
                        BlockAndFieldPair blockAndFieldPair(&uniformBlock, &field);
                        std::vector<BlockAndFieldPair> newUniformBlockList;
                        newUniformBlockList.push_back(blockAndFieldPair);
                        uniformBlockFieldMap[field.name] = newUniformBlockList;
                        continue;
                    }
    
                    // We've seen this name before.
                    // We need to check each of the uniform blocks that contain a field with this name
                    // to see if there's a conflict or not.
                    std::vector<BlockAndFieldPair> prevBlockFieldPairs =
                        uniformBlockFieldMap[field.name];
                    for (const auto &prevBlockFieldPair : prevBlockFieldPairs)
                    {
                        const sh::InterfaceBlock *prevUniformBlock      = prevBlockFieldPair.first;
                        const sh::ShaderVariable *prevUniformBlockField = prevBlockFieldPair.second;
    
                        if (uniformBlock.isSameInterfaceBlockAtLinkTime(*prevUniformBlock))
                        {
                            // The same uniform block should, by definition, contain the same field name
                            continue;
                        }
    
                        // The uniform blocks don't match, so check if the necessary field properties
                        // also match
                        if ((field.name == prevUniformBlockField->name) &&
                            (field.type == prevUniformBlockField->type) &&
                            (field.precision == prevUniformBlockField->precision))
                        {
                            infoLog << "Name conflicts between uniform block field names: "
                                    << field.name;
                            return false;
                        }
                    }
    
                    // No conflict, so record this pair
                    BlockAndFieldPair blockAndFieldPair(&uniformBlock, &field);
                    uniformBlockFieldMap[field.name].push_back(blockAndFieldPair);
                }
            }
        }
    
        // Validate no uniform names conflict with attribute names
        Shader *vertexShader = programOrPipeline.getAttachedShader(ShaderType::Vertex);
        if (vertexShader)
        {
            // ESSL 3.00.6 section 4.3.5:
            // If a uniform variable name is declared in one stage (e.g., a vertex shader)
            // but not in another (e.g., a fragment shader), then that name is still
            // available in the other stage for a different use.
            std::unordered_set<std::string> uniforms;
            for (const sh::ShaderVariable &uniform : vertexShader->getUniforms())
            {
                uniforms.insert(uniform.name);
            }
            for (const auto &attrib : vertexShader->getActiveAttributes())
            {
                if (uniforms.count(attrib.name))
                {
                    infoLog << "Name conflicts between a uniform and an attribute: " << attrib.name;
                    return false;
                }
            }
        }
    
        // Validate no Uniform Block fields conflict with other Uniforms
        for (const auto &uniformBlockField : uniformBlockFieldMap)
        {
            const std::string &fieldName = uniformBlockField.first;
            if (uniformMap.count(fieldName))
            {
                infoLog << "Name conflicts between a uniform and a uniform block field: " << fieldName;
                return false;
            }
        }
    
        return true;
    }
    
    // [OpenGL ES 3.2] Chapter 7.4.1 "Shader Interface Matching"
    bool LinkValidateShaderInterfaceMatching(const std::vector<sh::ShaderVariable> &outputVaryings,
                                             const std::vector<sh::ShaderVariable> &inputVaryings,
                                             ShaderType frontShaderType,
                                             ShaderType backShaderType,
                                             int frontShaderVersion,
                                             int backShaderVersion,
                                             bool isSeparable,
                                             gl::InfoLog &infoLog)
    {
        ASSERT(frontShaderVersion == backShaderVersion);
    
        std::vector<const sh::ShaderVariable *> filteredInputVaryings;
        std::vector<const sh::ShaderVariable *> filteredOutputVaryings;
    
        GetFilteredVaryings(inputVaryings, &filteredInputVaryings);
        GetFilteredVaryings(outputVaryings, &filteredOutputVaryings);
    
        // Separable programs require the number of inputs and outputs match
        if (isSeparable && filteredInputVaryings.size() < filteredOutputVaryings.size())
        {
            infoLog << GetShaderTypeString(backShaderType)
                    << " does not consume all varyings generated by "
                    << GetShaderTypeString(frontShaderType);
            return false;
        }
        if (isSeparable && filteredInputVaryings.size() > filteredOutputVaryings.size())
        {
            infoLog << GetShaderTypeString(frontShaderType)
                    << " does not generate all varyings consumed by "
                    << GetShaderTypeString(backShaderType);
            return false;
        }
    
        // All inputs must match all outputs
        for (const sh::ShaderVariable *input : filteredInputVaryings)
        {
            bool match = false;
            for (const sh::ShaderVariable *output : filteredOutputVaryings)
            {
                if (DoShaderVariablesMatch(frontShaderVersion, frontShaderType, backShaderType, *input,
                                           *output, isSeparable, infoLog))
                {
                    match = true;
                    break;
                }
            }
    
            // We permit unmatched, unreferenced varyings. Note that this specifically depends on
            // whether the input is statically used - a statically used input should fail this test even
            // if it is not active. GLSL ES 3.00.6 section 4.3.10.
            if (!match && input->staticUse)
            {
                const std::string &name =
                    input->isShaderIOBlock ? input->structOrBlockName : input->name;
                infoLog << GetShaderTypeString(backShaderType) << " varying " << name
                        << " does not match any " << GetShaderTypeString(frontShaderType) << " varying";
                return false;
            }
        }
    
        return true;
    }
    
    LinkMismatchError LinkValidateProgramVariables(const sh::ShaderVariable &variable1,
                                                   const sh::ShaderVariable &variable2,
                                                   bool validatePrecision,
                                                   bool treatVariable1AsNonArray,
                                                   bool treatVariable2AsNonArray,
                                                   std::string *mismatchedStructOrBlockMemberName)
    {
        if (variable1.type != variable2.type)
        {
            return LinkMismatchError::TYPE_MISMATCH;
        }
    
        bool variable1IsArray = variable1.isArray();
        bool variable2IsArray = variable2.isArray();
        if (treatVariable1AsNonArray)
        {
            ASSERT(variable1IsArray);
            variable1IsArray = false;
        }
        if (treatVariable2AsNonArray)
        {
            ASSERT(variable2IsArray);
            variable2IsArray = false;
        }
        // TODO(anglebug.com/5557): Investigate interactions with arrays-of-arrays.
        if (variable1IsArray != variable2IsArray)
        {
            return LinkMismatchError::ARRAYNESS_MISMATCH;
        }
        if (!treatVariable1AsNonArray && !treatVariable2AsNonArray &&
            variable1.arraySizes != variable2.arraySizes)
        {
            return LinkMismatchError::ARRAY_SIZE_MISMATCH;
        }
        if (validatePrecision && variable1.precision != variable2.precision)
        {
            return LinkMismatchError::PRECISION_MISMATCH;
        }
        if (!variable1.isShaderIOBlock && !variable2.isShaderIOBlock &&
            variable1.structOrBlockName != variable2.structOrBlockName)
        {
            return LinkMismatchError::STRUCT_NAME_MISMATCH;
        }
        if (variable1.imageUnitFormat != variable2.imageUnitFormat)
        {
            return LinkMismatchError::FORMAT_MISMATCH;
        }
    
        if (variable1.fields.size() != variable2.fields.size())
        {
            return LinkMismatchError::FIELD_NUMBER_MISMATCH;
        }
        const unsigned int numMembers = static_cast<unsigned int>(variable1.fields.size());
        for (unsigned int memberIndex = 0; memberIndex < numMembers; memberIndex++)
        {
            const sh::ShaderVariable &member1 = variable1.fields[memberIndex];
            const sh::ShaderVariable &member2 = variable2.fields[memberIndex];
    
            if (member1.name != member2.name)
            {
                return LinkMismatchError::FIELD_NAME_MISMATCH;
            }
    
            if (member1.interpolation != member2.interpolation)
            {
                return LinkMismatchError::INTERPOLATION_TYPE_MISMATCH;
            }
    
            if (variable1.isShaderIOBlock && variable2.isShaderIOBlock)
            {
                if (member1.location != member2.location)
                {
                    return LinkMismatchError::FIELD_LOCATION_MISMATCH;
                }
    
                if (member1.structOrBlockName != member2.structOrBlockName)
                {
                    return LinkMismatchError::FIELD_STRUCT_NAME_MISMATCH;
                }
            }
    
            LinkMismatchError linkErrorOnField = LinkValidateProgramVariables(
                member1, member2, validatePrecision, false, false, mismatchedStructOrBlockMemberName);
            if (linkErrorOnField != LinkMismatchError::NO_MISMATCH)
            {
                AddProgramVariableParentPrefix(member1.name, mismatchedStructOrBlockMemberName);
                return linkErrorOnField;
            }
        }
    
        return LinkMismatchError::NO_MISMATCH;
    }
    
    void AddProgramVariableParentPrefix(const std::string &parentName, std::string *mismatchedFieldName)
    {
        ASSERT(mismatchedFieldName);
        if (mismatchedFieldName->empty())
        {
            *mismatchedFieldName = parentName;
        }
        else
        {
            std::ostringstream stream;
            stream << parentName << "." << *mismatchedFieldName;
            *mismatchedFieldName = stream.str();
        }
    }
    
    bool LinkValidateBuiltInVaryingsInvariant(const std::vector<sh::ShaderVariable> &vertexVaryings,
                                              const std::vector<sh::ShaderVariable> &fragmentVaryings,
                                              int vertexShaderVersion,
                                              InfoLog &infoLog)
    {
        bool glPositionIsInvariant   = false;
        bool glPointSizeIsInvariant  = false;
        bool glFragCoordIsInvariant  = false;
        bool glPointCoordIsInvariant = false;
    
        for (const sh::ShaderVariable &varying : vertexVaryings)
        {
            if (!varying.isBuiltIn())
            {
                continue;
            }
            if (varying.name.compare("gl_Position") == 0)
            {
                glPositionIsInvariant = varying.isInvariant;
            }
            else if (varying.name.compare("gl_PointSize") == 0)
            {
                glPointSizeIsInvariant = varying.isInvariant;
            }
        }
    
        for (const sh::ShaderVariable &varying : fragmentVaryings)
        {
            if (!varying.isBuiltIn())
            {
                continue;
            }
            if (varying.name.compare("gl_FragCoord") == 0)
            {
                glFragCoordIsInvariant = varying.isInvariant;
            }
            else if (varying.name.compare("gl_PointCoord") == 0)
            {
                glPointCoordIsInvariant = varying.isInvariant;
            }
        }
    
        // There is some ambiguity in ESSL 1.00.17 paragraph 4.6.4 interpretation,
        // for example, https://cvs.khronos.org/bugzilla/show_bug.cgi?id=13842.
        // Not requiring invariance to match is supported by:
        // dEQP, WebGL CTS, Nexus 5X GLES
        if (glFragCoordIsInvariant && !glPositionIsInvariant)
        {
            infoLog << "gl_FragCoord can only be declared invariant if and only if gl_Position is "
                       "declared invariant.";
            return false;
        }
        if (glPointCoordIsInvariant && !glPointSizeIsInvariant)
        {
            infoLog << "gl_PointCoord can only be declared invariant if and only if gl_PointSize is "
                       "declared invariant.";
            return false;
        }
    
        return true;
    }
    
    bool LinkValidateBuiltInVaryings(const std::vector<sh::ShaderVariable> &outputVaryings,
                                     const std::vector<sh::ShaderVariable> &inputVaryings,
                                     ShaderType outputShaderType,
                                     ShaderType inputShaderType,
                                     int outputShaderVersion,
                                     int inputShaderVersion,
                                     InfoLog &infoLog)
    {
        ASSERT(outputShaderVersion == inputShaderVersion);
    
        // Only ESSL 1.0 has restrictions on matching input and output invariance
        if (inputShaderVersion == 100 && outputShaderType == ShaderType::Vertex &&
            inputShaderType == ShaderType::Fragment)
        {
            return LinkValidateBuiltInVaryingsInvariant(outputVaryings, inputVaryings,
                                                        outputShaderVersion, infoLog);
        }
    
        uint32_t sizeClipDistance = 0;
        uint32_t sizeCullDistance = 0;
    
        for (const sh::ShaderVariable &varying : outputVaryings)
        {
            if (!varying.isBuiltIn())
            {
                continue;
            }
            if (varying.name.compare("gl_ClipDistance") == 0)
            {
                sizeClipDistance = varying.getOutermostArraySize();
            }
            else if (varying.name.compare("gl_CullDistance") == 0)
            {
                sizeCullDistance = varying.getOutermostArraySize();
            }
        }
    
        for (const sh::ShaderVariable &varying : inputVaryings)
        {
            if (!varying.isBuiltIn())
            {
                continue;
            }
            if (varying.name.compare("gl_ClipDistance") == 0)
            {
                if (sizeClipDistance != varying.getOutermostArraySize())
                {
                    infoLog << "If either shader redeclares the built-in arrays gl_ClipDistance[] the "
                               "array must have the same size in both shaders.";
                    return false;
                }
            }
            else if (varying.name.compare("gl_CullDistance") == 0)
            {
                if (sizeCullDistance != varying.getOutermostArraySize())
                {
                    infoLog << "If either shader redeclares the built-in arrays gl_CullDistance[] the "
                               "array must have the same size in both shaders.";
                    return false;
                }
            }
        }
        return true;
    }
    }  // namespace gl