Edit

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

Branch :

  • Show log

    Commit

  • Author : Jamie Madill
    Date : 2021-03-23 15:35:30
    Hash : 2a43f43a
    Message : Revert "Save/Load missing members" This reverts commit b2e76cf58fc012b068aab4716a432bb8951e93e3. Reason for revert: Causing MSan failures on Linux. See bug. Bug: chromium:1191344 Original change's description: > Save/Load missing members > > There are several class/struct members that are missing when a program > is serialized/deserialized. This leads to errors when attempting to link > programs that have been deserialized. For example, when drawing with a > PPO that contains programs which were created with glProgramBinary(). > > This CL adds saving/loading the missing members. > > Bug: b/182409935 > Change-Id: I637c6cd8c174acd6da8d51433893323a32e5d7c0 > Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2770683 > Commit-Queue: Tim Van Patten <timvp@google.com> > Reviewed-by: Jamie Madill <jmadill@chromium.org> > Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org> > Reviewed-by: Cody Northrop <cnorthrop@google.com> Bug: b/182409935 Change-Id: I1209257ed6bb55ba2d01d92bd3305d5e3ad6ee28 No-Presubmit: true No-Tree-Checks: true No-Try: true Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2780015 Reviewed-by: Jamie Madill <jmadill@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>

  • src/libANGLE/ProgramExecutable.cpp
  • //
    // Copyright 2020 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.
    //
    // ProgramExecutable.cpp: Collects the interfaces common to both Programs and
    // ProgramPipelines in order to execute/draw with either.
    
    #include "libANGLE/ProgramExecutable.h"
    
    #include "libANGLE/Context.h"
    #include "libANGLE/Program.h"
    #include "libANGLE/ProgramPipeline.h"
    #include "libANGLE/Shader.h"
    
    namespace gl
    {
    namespace
    {
    bool IncludeSameArrayElement(const std::set<std::string> &nameSet, const std::string &name)
    {
        std::vector<unsigned int> subscripts;
        std::string baseName = ParseResourceName(name, &subscripts);
        for (const std::string &nameInSet : nameSet)
        {
            std::vector<unsigned int> arrayIndices;
            std::string arrayName = ParseResourceName(nameInSet, &arrayIndices);
            if (baseName == arrayName &&
                (subscripts.empty() || arrayIndices.empty() || subscripts == arrayIndices))
            {
                return true;
            }
        }
        return false;
    }
    
    // Find the matching varying or field by name.
    const sh::ShaderVariable *FindOutputVaryingOrField(const ProgramMergedVaryings &varyings,
                                                       ShaderType stage,
                                                       const std::string &name)
    {
        const sh::ShaderVariable *var = nullptr;
        for (const ProgramVaryingRef &ref : varyings)
        {
            if (ref.frontShaderStage != stage)
            {
                continue;
            }
    
            const sh::ShaderVariable *varying = ref.get(stage);
            if (varying->name == name)
            {
                var = varying;
                break;
            }
            GLuint fieldIndex = 0;
            var               = varying->findField(name, &fieldIndex);
            if (var != nullptr)
            {
                break;
            }
        }
        return var;
    }
    }  // anonymous namespace
    
    ProgramExecutable::ProgramExecutable()
        : mMaxActiveAttribLocation(0),
          mAttributesTypeMask(0),
          mAttributesMask(0),
          mActiveSamplerRefCounts{},
          mCanDrawWith(false),
          mYUVOutput(false),
          mTransformFeedbackBufferMode(GL_INTERLEAVED_ATTRIBS),
          mDefaultUniformRange(0, 0),
          mSamplerUniformRange(0, 0),
          mImageUniformRange(0, 0),
          mFragmentInoutRange(0, 0),
          mPipelineHasGraphicsUniformBuffers(false),
          mPipelineHasComputeUniformBuffers(false),
          mPipelineHasGraphicsStorageBuffers(false),
          mPipelineHasComputeStorageBuffers(false),
          mPipelineHasGraphicsAtomicCounterBuffers(false),
          mPipelineHasComputeAtomicCounterBuffers(false),
          mPipelineHasGraphicsDefaultUniforms(false),
          mPipelineHasComputeDefaultUniforms(false),
          mPipelineHasGraphicsTextures(false),
          mPipelineHasComputeTextures(false),
          mPipelineHasGraphicsImages(false),
          mPipelineHasComputeImages(false),
          mIsCompute(false),
          // [GL_EXT_geometry_shader] Table 20.22
          mGeometryShaderInputPrimitiveType(PrimitiveMode::Triangles),
          mGeometryShaderOutputPrimitiveType(PrimitiveMode::TriangleStrip),
          mGeometryShaderInvocations(1),
          mGeometryShaderMaxVertices(0),
          mTessControlShaderVertices(0),
          mTessGenMode(GL_NONE),
          mTessGenSpacing(GL_NONE),
          mTessGenVertexOrder(GL_NONE),
          mTessGenPointMode(GL_NONE)
    {
        reset();
    }
    
    ProgramExecutable::ProgramExecutable(const ProgramExecutable &other)
        : mLinkedGraphicsShaderStages(other.mLinkedGraphicsShaderStages),
          mLinkedComputeShaderStages(other.mLinkedComputeShaderStages),
          mActiveAttribLocationsMask(other.mActiveAttribLocationsMask),
          mMaxActiveAttribLocation(other.mMaxActiveAttribLocation),
          mAttributesTypeMask(other.mAttributesTypeMask),
          mAttributesMask(other.mAttributesMask),
          mActiveSamplersMask(other.mActiveSamplersMask),
          mActiveSamplerRefCounts(other.mActiveSamplerRefCounts),
          mActiveSamplerTypes(other.mActiveSamplerTypes),
          mActiveSamplerYUV(other.mActiveSamplerYUV),
          mActiveSamplerFormats(other.mActiveSamplerFormats),
          mActiveSamplerShaderBits(other.mActiveSamplerShaderBits),
          mActiveImagesMask(other.mActiveImagesMask),
          mActiveImageShaderBits(other.mActiveImageShaderBits),
          mCanDrawWith(other.mCanDrawWith),
          mOutputVariables(other.mOutputVariables),
          mOutputLocations(other.mOutputLocations),
          mSecondaryOutputLocations(other.mSecondaryOutputLocations),
          mYUVOutput(other.mYUVOutput),
          mProgramInputs(other.mProgramInputs),
          mLinkedTransformFeedbackVaryings(other.mLinkedTransformFeedbackVaryings),
          mTransformFeedbackStrides(other.mTransformFeedbackStrides),
          mTransformFeedbackBufferMode(other.mTransformFeedbackBufferMode),
          mUniforms(other.mUniforms),
          mDefaultUniformRange(other.mDefaultUniformRange),
          mSamplerUniformRange(other.mSamplerUniformRange),
          mUniformBlocks(other.mUniformBlocks),
          mActiveUniformBlockBindings(other.mActiveUniformBlockBindings),
          mAtomicCounterBuffers(other.mAtomicCounterBuffers),
          mImageUniformRange(other.mImageUniformRange),
          mComputeShaderStorageBlocks(other.mComputeShaderStorageBlocks),
          mGraphicsShaderStorageBlocks(other.mGraphicsShaderStorageBlocks),
          mFragmentInoutRange(other.mFragmentInoutRange),
          mPipelineHasGraphicsUniformBuffers(other.mPipelineHasGraphicsUniformBuffers),
          mPipelineHasComputeUniformBuffers(other.mPipelineHasComputeUniformBuffers),
          mPipelineHasGraphicsStorageBuffers(other.mPipelineHasGraphicsStorageBuffers),
          mPipelineHasComputeStorageBuffers(other.mPipelineHasComputeStorageBuffers),
          mPipelineHasGraphicsAtomicCounterBuffers(other.mPipelineHasGraphicsAtomicCounterBuffers),
          mPipelineHasComputeAtomicCounterBuffers(other.mPipelineHasComputeAtomicCounterBuffers),
          mPipelineHasGraphicsDefaultUniforms(other.mPipelineHasGraphicsDefaultUniforms),
          mPipelineHasComputeDefaultUniforms(other.mPipelineHasComputeDefaultUniforms),
          mPipelineHasGraphicsTextures(other.mPipelineHasGraphicsTextures),
          mPipelineHasComputeTextures(other.mPipelineHasComputeTextures),
          mPipelineHasGraphicsImages(other.mPipelineHasGraphicsImages),
          mPipelineHasComputeImages(other.mPipelineHasComputeImages),
          mIsCompute(other.mIsCompute)
    {
        reset();
    }
    
    ProgramExecutable::~ProgramExecutable() = default;
    
    void ProgramExecutable::reset()
    {
        resetInfoLog();
        mActiveAttribLocationsMask.reset();
        mAttributesTypeMask.reset();
        mAttributesMask.reset();
        mMaxActiveAttribLocation = 0;
    
        mActiveSamplersMask.reset();
        mActiveSamplerRefCounts = {};
        mActiveSamplerTypes.fill(TextureType::InvalidEnum);
        mActiveSamplerYUV.reset();
        mActiveSamplerFormats.fill(SamplerFormat::InvalidEnum);
    
        mActiveImagesMask.reset();
    
        mProgramInputs.clear();
        mLinkedTransformFeedbackVaryings.clear();
        mUniforms.clear();
        mUniformBlocks.clear();
        mActiveUniformBlockBindings.reset();
        mComputeShaderStorageBlocks.clear();
        mGraphicsShaderStorageBlocks.clear();
        mAtomicCounterBuffers.clear();
        mOutputVariables.clear();
        mOutputLocations.clear();
        mSecondaryOutputLocations.clear();
        mYUVOutput = false;
        mSamplerBindings.clear();
        mComputeImageBindings.clear();
        mGraphicsImageBindings.clear();
    
        mPipelineHasGraphicsUniformBuffers       = false;
        mPipelineHasComputeUniformBuffers        = false;
        mPipelineHasGraphicsStorageBuffers       = false;
        mPipelineHasComputeStorageBuffers        = false;
        mPipelineHasGraphicsAtomicCounterBuffers = false;
        mPipelineHasComputeAtomicCounterBuffers  = false;
        mPipelineHasGraphicsDefaultUniforms      = false;
        mPipelineHasComputeDefaultUniforms       = false;
        mPipelineHasGraphicsTextures             = false;
        mPipelineHasComputeTextures              = false;
    
        mGeometryShaderInputPrimitiveType  = PrimitiveMode::Triangles;
        mGeometryShaderOutputPrimitiveType = PrimitiveMode::TriangleStrip;
        mGeometryShaderInvocations         = 1;
        mGeometryShaderMaxVertices         = 0;
    
        mTessControlShaderVertices = 0;
        mTessGenMode               = GL_NONE;
        mTessGenSpacing            = GL_NONE;
        mTessGenVertexOrder        = GL_NONE;
        mTessGenPointMode          = GL_NONE;
    }
    
    void ProgramExecutable::load(gl::BinaryInputStream *stream)
    {
        static_assert(MAX_VERTEX_ATTRIBS * 2 <= sizeof(uint32_t) * 8,
                      "Too many vertex attribs for mask: All bits of mAttributesTypeMask types and "
                      "mask fit into 32 bits each");
        mAttributesTypeMask        = gl::ComponentTypeMask(stream->readInt<uint32_t>());
        mAttributesMask            = gl::AttributesMask(stream->readInt<uint32_t>());
        mActiveAttribLocationsMask = gl::AttributesMask(stream->readInt<uint32_t>());
        mMaxActiveAttribLocation   = stream->readInt<unsigned int>();
    
        unsigned int fragmentInoutRangeLow  = stream->readInt<uint32_t>();
        unsigned int fragmentInoutRangeHigh = stream->readInt<uint32_t>();
        mFragmentInoutRange                 = RangeUI(fragmentInoutRangeLow, fragmentInoutRangeHigh);
    
        mLinkedGraphicsShaderStages = ShaderBitSet(stream->readInt<uint8_t>());
        mLinkedComputeShaderStages  = ShaderBitSet(stream->readInt<uint8_t>());
        mIsCompute                  = stream->readBool();
    
        mPipelineHasGraphicsUniformBuffers       = stream->readBool();
        mPipelineHasComputeUniformBuffers        = stream->readBool();
        mPipelineHasGraphicsStorageBuffers       = stream->readBool();
        mPipelineHasComputeStorageBuffers        = stream->readBool();
        mPipelineHasGraphicsAtomicCounterBuffers = stream->readBool();
        mPipelineHasComputeAtomicCounterBuffers  = stream->readBool();
        mPipelineHasGraphicsDefaultUniforms      = stream->readBool();
        mPipelineHasComputeDefaultUniforms       = stream->readBool();
        mPipelineHasGraphicsTextures             = stream->readBool();
        mPipelineHasComputeTextures              = stream->readBool();
    
        mGeometryShaderInputPrimitiveType  = stream->readEnum<PrimitiveMode>();
        mGeometryShaderOutputPrimitiveType = stream->readEnum<PrimitiveMode>();
        mGeometryShaderInvocations         = stream->readInt<int>();
        mGeometryShaderMaxVertices         = stream->readInt<int>();
    
        mTessControlShaderVertices = stream->readInt<int>();
        mTessGenMode               = stream->readInt<GLenum>();
        mTessGenSpacing            = stream->readInt<GLenum>();
        mTessGenVertexOrder        = stream->readInt<GLenum>();
        mTessGenPointMode          = stream->readInt<GLenum>();
    
        size_t attribCount = stream->readInt<size_t>();
        ASSERT(getProgramInputs().empty());
        for (size_t attribIndex = 0; attribIndex < attribCount; ++attribIndex)
        {
            sh::ShaderVariable attrib;
            LoadShaderVar(stream, &attrib);
            attrib.location = stream->readInt<int>();
            mProgramInputs.push_back(attrib);
        }
    
        size_t uniformCount = stream->readInt<size_t>();
        ASSERT(getUniforms().empty());
        for (size_t uniformIndex = 0; uniformIndex < uniformCount; ++uniformIndex)
        {
            LinkedUniform uniform;
            LoadShaderVar(stream, &uniform);
    
            uniform.bufferIndex = stream->readInt<int>();
            LoadBlockMemberInfo(stream, &uniform.blockInfo);
    
            stream->readIntVector<unsigned int>(&uniform.outerArraySizes);
    
            uniform.typeInfo = &GetUniformTypeInfo(uniform.type);
    
            // Active shader info
            for (ShaderType shaderType : gl::AllShaderTypes())
            {
                uniform.setActive(shaderType, stream->readBool());
            }
    
            mUniforms.push_back(uniform);
        }
    
        size_t uniformBlockCount = stream->readInt<size_t>();
        ASSERT(getUniformBlocks().empty());
        for (size_t uniformBlockIndex = 0; uniformBlockIndex < uniformBlockCount; ++uniformBlockIndex)
        {
            InterfaceBlock uniformBlock;
            LoadInterfaceBlock(stream, &uniformBlock);
            mUniformBlocks.push_back(uniformBlock);
    
            mActiveUniformBlockBindings.set(uniformBlockIndex, uniformBlock.binding != 0);
        }
    
        size_t shaderStorageBlockCount = stream->readInt<size_t>();
        ASSERT(getShaderStorageBlocks().empty());
        for (size_t shaderStorageBlockIndex = 0; shaderStorageBlockIndex < shaderStorageBlockCount;
             ++shaderStorageBlockIndex)
        {
            InterfaceBlock shaderStorageBlock;
            LoadInterfaceBlock(stream, &shaderStorageBlock);
            if (isCompute())
            {
                mComputeShaderStorageBlocks.push_back(shaderStorageBlock);
            }
            else
            {
                mGraphicsShaderStorageBlocks.push_back(shaderStorageBlock);
            }
        }
    
        size_t atomicCounterBufferCount = stream->readInt<size_t>();
        ASSERT(getAtomicCounterBuffers().empty());
        for (size_t bufferIndex = 0; bufferIndex < atomicCounterBufferCount; ++bufferIndex)
        {
            AtomicCounterBuffer atomicCounterBuffer;
            LoadShaderVariableBuffer(stream, &atomicCounterBuffer);
    
            mAtomicCounterBuffers.push_back(atomicCounterBuffer);
        }
    
        size_t transformFeedbackVaryingCount = stream->readInt<size_t>();
        ASSERT(mLinkedTransformFeedbackVaryings.empty());
        for (size_t transformFeedbackVaryingIndex = 0;
             transformFeedbackVaryingIndex < transformFeedbackVaryingCount;
             ++transformFeedbackVaryingIndex)
        {
            sh::ShaderVariable varying;
            stream->readIntVector<unsigned int>(&varying.arraySizes);
            stream->readInt(&varying.type);
            stream->readString(&varying.name);
    
            GLuint arrayIndex = stream->readInt<GLuint>();
    
            mLinkedTransformFeedbackVaryings.emplace_back(varying, arrayIndex);
        }
    
        mTransformFeedbackBufferMode = stream->readInt<GLint>();
    
        size_t outputCount = stream->readInt<size_t>();
        ASSERT(getOutputVariables().empty());
        for (size_t outputIndex = 0; outputIndex < outputCount; ++outputIndex)
        {
            sh::ShaderVariable output;
            LoadShaderVar(stream, &output);
            output.location = stream->readInt<int>();
            output.index    = stream->readInt<int>();
            mOutputVariables.push_back(output);
        }
    
        size_t outputVarCount = stream->readInt<size_t>();
        ASSERT(getOutputLocations().empty());
        for (size_t outputIndex = 0; outputIndex < outputVarCount; ++outputIndex)
        {
            VariableLocation locationData;
            stream->readInt(&locationData.arrayIndex);
            stream->readInt(&locationData.index);
            stream->readBool(&locationData.ignored);
            mOutputLocations.push_back(locationData);
        }
    
        size_t secondaryOutputVarCount = stream->readInt<size_t>();
        ASSERT(getSecondaryOutputLocations().empty());
        for (size_t outputIndex = 0; outputIndex < secondaryOutputVarCount; ++outputIndex)
        {
            VariableLocation locationData;
            stream->readInt(&locationData.arrayIndex);
            stream->readInt(&locationData.index);
            stream->readBool(&locationData.ignored);
            mSecondaryOutputLocations.push_back(locationData);
        }
    
        unsigned int defaultUniformRangeLow  = stream->readInt<unsigned int>();
        unsigned int defaultUniformRangeHigh = stream->readInt<unsigned int>();
        mDefaultUniformRange                 = RangeUI(defaultUniformRangeLow, defaultUniformRangeHigh);
    
        unsigned int samplerRangeLow  = stream->readInt<unsigned int>();
        unsigned int samplerRangeHigh = stream->readInt<unsigned int>();
        mSamplerUniformRange          = RangeUI(samplerRangeLow, samplerRangeHigh);
    
        size_t samplerCount = stream->readInt<size_t>();
        for (size_t samplerIndex = 0; samplerIndex < samplerCount; ++samplerIndex)
        {
            TextureType textureType = stream->readEnum<TextureType>();
            GLenum samplerType      = stream->readInt<GLenum>();
            SamplerFormat format    = stream->readEnum<SamplerFormat>();
            size_t bindingCount     = stream->readInt<size_t>();
            mSamplerBindings.emplace_back(textureType, samplerType, format, bindingCount);
        }
    
        unsigned int imageRangeLow  = stream->readInt<unsigned int>();
        unsigned int imageRangeHigh = stream->readInt<unsigned int>();
        mImageUniformRange          = RangeUI(imageRangeLow, imageRangeHigh);
    
        size_t imageBindingCount = stream->readInt<size_t>();
        for (size_t imageIndex = 0; imageIndex < imageBindingCount; ++imageIndex)
        {
            size_t elementCount     = stream->readInt<size_t>();
            TextureType textureType = static_cast<TextureType>(stream->readInt<unsigned int>());
            ImageBinding imageBinding(elementCount, textureType);
            for (size_t elementIndex = 0; elementIndex < elementCount; ++elementIndex)
            {
                imageBinding.boundImageUnits[elementIndex] = stream->readInt<unsigned int>();
            }
            if (isCompute())
            {
                mComputeImageBindings.emplace_back(imageBinding);
            }
            else
            {
                mGraphicsImageBindings.emplace_back(imageBinding);
            }
        }
    }
    
    void ProgramExecutable::save(gl::BinaryOutputStream *stream) const
    {
        static_assert(MAX_VERTEX_ATTRIBS * 2 <= sizeof(uint32_t) * 8,
                      "All bits of mAttributesTypeMask types and mask fit into 32 bits each");
        stream->writeInt(static_cast<uint32_t>(mAttributesTypeMask.to_ulong()));
        stream->writeInt(static_cast<uint32_t>(mAttributesMask.to_ulong()));
        stream->writeInt(static_cast<uint32_t>(mActiveAttribLocationsMask.to_ulong()));
        stream->writeInt(mMaxActiveAttribLocation);
    
        stream->writeInt(mFragmentInoutRange.low());
        stream->writeInt(mFragmentInoutRange.high());
    
        stream->writeInt(mLinkedGraphicsShaderStages.bits());
        stream->writeInt(mLinkedComputeShaderStages.bits());
        stream->writeBool(mIsCompute);
    
        stream->writeBool(mPipelineHasGraphicsUniformBuffers);
        stream->writeBool(mPipelineHasComputeUniformBuffers);
        stream->writeBool(mPipelineHasGraphicsStorageBuffers);
        stream->writeBool(mPipelineHasComputeStorageBuffers);
        stream->writeBool(mPipelineHasGraphicsAtomicCounterBuffers);
        stream->writeBool(mPipelineHasComputeAtomicCounterBuffers);
        stream->writeBool(mPipelineHasGraphicsDefaultUniforms);
        stream->writeBool(mPipelineHasComputeDefaultUniforms);
        stream->writeBool(mPipelineHasGraphicsTextures);
        stream->writeBool(mPipelineHasComputeTextures);
    
        ASSERT(mGeometryShaderInvocations >= 1 && mGeometryShaderMaxVertices >= 0);
        stream->writeEnum(mGeometryShaderInputPrimitiveType);
        stream->writeEnum(mGeometryShaderOutputPrimitiveType);
        stream->writeInt(mGeometryShaderInvocations);
        stream->writeInt(mGeometryShaderMaxVertices);
    
        stream->writeInt(mTessControlShaderVertices);
        stream->writeInt(mTessGenMode);
        stream->writeInt(mTessGenSpacing);
        stream->writeInt(mTessGenVertexOrder);
        stream->writeInt(mTessGenPointMode);
    
        stream->writeInt(getProgramInputs().size());
        for (const sh::ShaderVariable &attrib : getProgramInputs())
        {
            WriteShaderVar(stream, attrib);
            stream->writeInt(attrib.location);
        }
    
        stream->writeInt(getUniforms().size());
        for (const LinkedUniform &uniform : getUniforms())
        {
            WriteShaderVar(stream, uniform);
    
            // FIXME: referenced
    
            stream->writeInt(uniform.bufferIndex);
            WriteBlockMemberInfo(stream, uniform.blockInfo);
    
            stream->writeIntVector(uniform.outerArraySizes);
    
            // Active shader info
            for (ShaderType shaderType : gl::AllShaderTypes())
            {
                stream->writeBool(uniform.isActive(shaderType));
            }
        }
    
        stream->writeInt(getUniformBlocks().size());
        for (const InterfaceBlock &uniformBlock : getUniformBlocks())
        {
            WriteInterfaceBlock(stream, uniformBlock);
        }
    
        stream->writeInt(getShaderStorageBlocks().size());
        for (const InterfaceBlock &shaderStorageBlock : getShaderStorageBlocks())
        {
            WriteInterfaceBlock(stream, shaderStorageBlock);
        }
    
        stream->writeInt(mAtomicCounterBuffers.size());
        for (const AtomicCounterBuffer &atomicCounterBuffer : getAtomicCounterBuffers())
        {
            WriteShaderVariableBuffer(stream, atomicCounterBuffer);
        }
    
        stream->writeInt(getLinkedTransformFeedbackVaryings().size());
        for (const auto &var : getLinkedTransformFeedbackVaryings())
        {
            stream->writeIntVector(var.arraySizes);
            stream->writeInt(var.type);
            stream->writeString(var.name);
    
            stream->writeIntOrNegOne(var.arrayIndex);
        }
    
        stream->writeInt(getTransformFeedbackBufferMode());
    
        stream->writeInt(getOutputVariables().size());
        for (const sh::ShaderVariable &output : getOutputVariables())
        {
            WriteShaderVar(stream, output);
            stream->writeInt(output.location);
            stream->writeInt(output.index);
        }
    
        stream->writeInt(getOutputLocations().size());
        for (const auto &outputVar : getOutputLocations())
        {
            stream->writeInt(outputVar.arrayIndex);
            stream->writeIntOrNegOne(outputVar.index);
            stream->writeBool(outputVar.ignored);
        }
    
        stream->writeInt(getSecondaryOutputLocations().size());
        for (const auto &outputVar : getSecondaryOutputLocations())
        {
            stream->writeInt(outputVar.arrayIndex);
            stream->writeIntOrNegOne(outputVar.index);
            stream->writeBool(outputVar.ignored);
        }
    
        stream->writeInt(getDefaultUniformRange().low());
        stream->writeInt(getDefaultUniformRange().high());
    
        stream->writeInt(getSamplerUniformRange().low());
        stream->writeInt(getSamplerUniformRange().high());
    
        stream->writeInt(getSamplerBindings().size());
        for (const auto &samplerBinding : getSamplerBindings())
        {
            stream->writeEnum(samplerBinding.textureType);
            stream->writeInt(samplerBinding.samplerType);
            stream->writeEnum(samplerBinding.format);
            stream->writeInt(samplerBinding.boundTextureUnits.size());
        }
    
        stream->writeInt(getImageUniformRange().low());
        stream->writeInt(getImageUniformRange().high());
    
        stream->writeInt(getImageBindings().size());
        for (const auto &imageBinding : getImageBindings())
        {
            stream->writeInt(imageBinding.boundImageUnits.size());
            stream->writeInt(static_cast<unsigned int>(imageBinding.textureType));
            for (size_t i = 0; i < imageBinding.boundImageUnits.size(); ++i)
            {
                stream->writeInt(imageBinding.boundImageUnits[i]);
            }
        }
    }
    
    int ProgramExecutable::getInfoLogLength() const
    {
        return static_cast<int>(mInfoLog.getLength());
    }
    
    void ProgramExecutable::getInfoLog(GLsizei bufSize, GLsizei *length, char *infoLog) const
    {
        return mInfoLog.getLog(bufSize, length, infoLog);
    }
    
    std::string ProgramExecutable::getInfoLogString() const
    {
        return mInfoLog.str();
    }
    
    bool ProgramExecutable::isAttribLocationActive(size_t attribLocation) const
    {
        // TODO(timvp): http://anglebug.com/3570: Enable this assert here somehow.
        //    ASSERT(!mLinkingState);
        ASSERT(attribLocation < mActiveAttribLocationsMask.size());
        return mActiveAttribLocationsMask[attribLocation];
    }
    
    AttributesMask ProgramExecutable::getAttributesMask() const
    {
        // TODO(timvp): http://anglebug.com/3570: Enable this assert here somehow.
        //    ASSERT(!mLinkingState);
        return mAttributesMask;
    }
    
    bool ProgramExecutable::hasDefaultUniforms() const
    {
        return !getDefaultUniformRange().empty() ||
               (isCompute() ? mPipelineHasComputeDefaultUniforms : mPipelineHasGraphicsDefaultUniforms);
    }
    
    bool ProgramExecutable::hasTextures() const
    {
        return !getSamplerBindings().empty() ||
               (isCompute() ? mPipelineHasComputeTextures : mPipelineHasGraphicsTextures);
    }
    
    // TODO: http://anglebug.com/3570: Remove mHas*UniformBuffers once PPO's have valid data in
    // mUniformBlocks
    bool ProgramExecutable::hasUniformBuffers() const
    {
        return !getUniformBlocks().empty() ||
               (isCompute() ? mPipelineHasComputeUniformBuffers : mPipelineHasGraphicsUniformBuffers);
    }
    
    bool ProgramExecutable::hasStorageBuffers() const
    {
        return (isCompute() ? hasComputeStorageBuffers() : hasGraphicsStorageBuffers());
    }
    
    bool ProgramExecutable::hasGraphicsStorageBuffers() const
    {
        return !mGraphicsShaderStorageBlocks.empty() || mPipelineHasGraphicsStorageBuffers;
    }
    
    bool ProgramExecutable::hasComputeStorageBuffers() const
    {
        return !mComputeShaderStorageBlocks.empty() || mPipelineHasComputeStorageBuffers;
    }
    
    bool ProgramExecutable::hasAtomicCounterBuffers() const
    {
        return !getAtomicCounterBuffers().empty() ||
               (isCompute() ? mPipelineHasComputeAtomicCounterBuffers
                            : mPipelineHasGraphicsAtomicCounterBuffers);
    }
    
    bool ProgramExecutable::hasImages() const
    {
        return (isCompute() ? hasComputeImages() : hasGraphicsImages());
    }
    
    bool ProgramExecutable::hasGraphicsImages() const
    {
        return !mGraphicsImageBindings.empty() || mPipelineHasGraphicsImages;
    }
    
    bool ProgramExecutable::hasComputeImages() const
    {
        return !mComputeImageBindings.empty() || mPipelineHasComputeImages;
    }
    
    bool ProgramExecutable::usesFramebufferFetch() const
    {
        return (mFragmentInoutRange.length() > 0);
    }
    
    GLuint ProgramExecutable::getUniformIndexFromImageIndex(GLuint imageIndex) const
    {
        ASSERT(imageIndex < mImageUniformRange.length());
        return imageIndex + mImageUniformRange.low();
    }
    
    void ProgramExecutable::updateActiveSamplers(const ProgramState &programState)
    {
        const std::vector<SamplerBinding> &samplerBindings = programState.getSamplerBindings();
    
        for (uint32_t samplerIndex = 0; samplerIndex < samplerBindings.size(); ++samplerIndex)
        {
            const SamplerBinding &samplerBinding = samplerBindings[samplerIndex];
            uint32_t uniformIndex = programState.getUniformIndexFromSamplerIndex(samplerIndex);
            const gl::LinkedUniform &samplerUniform = programState.getUniforms()[uniformIndex];
    
            for (GLint textureUnit : samplerBinding.boundTextureUnits)
            {
                if (++mActiveSamplerRefCounts[textureUnit] == 1)
                {
                    mActiveSamplerTypes[textureUnit]   = samplerBinding.textureType;
                    mActiveSamplerYUV[textureUnit]     = IsSamplerYUVType(samplerBinding.samplerType);
                    mActiveSamplerFormats[textureUnit] = samplerBinding.format;
                    mActiveSamplerShaderBits[textureUnit] = samplerUniform.activeShaders();
                }
                else
                {
                    if (mActiveSamplerTypes[textureUnit] != samplerBinding.textureType)
                    {
                        mActiveSamplerTypes[textureUnit] = TextureType::InvalidEnum;
                    }
                    if (mActiveSamplerYUV.test(textureUnit) !=
                        IsSamplerYUVType(samplerBinding.samplerType))
                    {
                        mActiveSamplerYUV[textureUnit] = false;
                    }
                    if (mActiveSamplerFormats[textureUnit] != samplerBinding.format)
                    {
                        mActiveSamplerFormats[textureUnit] = SamplerFormat::InvalidEnum;
                    }
                }
                mActiveSamplersMask.set(textureUnit);
            }
        }
    }
    
    void ProgramExecutable::updateActiveImages(const ProgramExecutable &executable)
    {
        const std::vector<ImageBinding> *imageBindings = getImageBindings();
        for (uint32_t imageIndex = 0; imageIndex < imageBindings->size(); ++imageIndex)
        {
            const gl::ImageBinding &imageBinding = imageBindings->at(imageIndex);
    
            uint32_t uniformIndex = executable.getUniformIndexFromImageIndex(imageIndex);
            const gl::LinkedUniform &imageUniform = executable.getUniforms()[uniformIndex];
            const ShaderBitSet shaderBits         = imageUniform.activeShaders();
            for (GLint imageUnit : imageBinding.boundImageUnits)
            {
                mActiveImagesMask.set(imageUnit);
                if (isCompute())
                {
                    mActiveImageShaderBits[imageUnit].set(gl::ShaderType::Compute);
                }
                else
                {
                    mActiveImageShaderBits[imageUnit] |= shaderBits;
                }
            }
        }
    }
    
    void ProgramExecutable::setSamplerUniformTextureTypeAndFormat(
        size_t textureUnitIndex,
        std::vector<SamplerBinding> &samplerBindings)
    {
        bool foundBinding         = false;
        TextureType foundType     = TextureType::InvalidEnum;
        bool foundYUV             = false;
        SamplerFormat foundFormat = SamplerFormat::InvalidEnum;
    
        for (const SamplerBinding &binding : samplerBindings)
        {
            // A conflict exists if samplers of different types are sourced by the same texture unit.
            // We need to check all bound textures to detect this error case.
            for (GLuint textureUnit : binding.boundTextureUnits)
            {
                if (textureUnit == textureUnitIndex)
                {
                    if (!foundBinding)
                    {
                        foundBinding = true;
                        foundType    = binding.textureType;
                        foundYUV     = IsSamplerYUVType(binding.samplerType);
                        foundFormat  = binding.format;
                    }
                    else
                    {
                        if (foundType != binding.textureType)
                        {
                            foundType = TextureType::InvalidEnum;
                        }
                        if (foundYUV != IsSamplerYUVType(binding.samplerType))
                        {
                            foundYUV = false;
                        }
                        if (foundFormat != binding.format)
                        {
                            foundFormat = SamplerFormat::InvalidEnum;
                        }
                    }
                }
            }
        }
    
        mActiveSamplerTypes[textureUnitIndex]   = foundType;
        mActiveSamplerYUV[textureUnitIndex]     = foundYUV;
        mActiveSamplerFormats[textureUnitIndex] = foundFormat;
    }
    
    void ProgramExecutable::updateCanDrawWith()
    {
        mCanDrawWith =
            (hasLinkedShaderStage(ShaderType::Vertex) && hasLinkedShaderStage(ShaderType::Fragment));
    }
    
    void ProgramExecutable::saveLinkedStateInfo(const ProgramState &state)
    {
        for (ShaderType shaderType : getLinkedShaderStages())
        {
            Shader *shader = state.getAttachedShader(shaderType);
            ASSERT(shader);
            mLinkedOutputVaryings[shaderType] = shader->getOutputVaryings();
            mLinkedInputVaryings[shaderType]  = shader->getInputVaryings();
            mLinkedShaderVersions[shaderType] = shader->getShaderVersion();
        }
    }
    
    bool ProgramExecutable::isYUVOutput() const
    {
        return !isCompute() && mYUVOutput;
    }
    
    ShaderType ProgramExecutable::getLinkedTransformFeedbackStage() const
    {
        return GetLastPreFragmentStage(mLinkedGraphicsShaderStages);
    }
    
    bool ProgramExecutable::linkMergedVaryings(
        const Context *context,
        const HasAttachedShaders &programOrPipeline,
        const ProgramMergedVaryings &mergedVaryings,
        const std::vector<std::string> &transformFeedbackVaryingNames,
        bool isSeparable,
        ProgramVaryingPacking *varyingPacking)
    {
        ShaderType tfStage = programOrPipeline.getTransformFeedbackStage();
    
        if (!linkValidateTransformFeedback(context, mergedVaryings, tfStage,
                                           transformFeedbackVaryingNames))
        {
            return false;
        }
    
        // Map the varyings to the register file
        // In WebGL, we use a slightly different handling for packing variables.
        gl::PackMode packMode = PackMode::ANGLE_RELAXED;
        if (context->getLimitations().noFlexibleVaryingPacking)
        {
            // D3D9 pack mode is strictly more strict than WebGL, so takes priority.
            packMode = PackMode::ANGLE_NON_CONFORMANT_D3D9;
        }
        else if (context->getExtensions().webglCompatibility)
        {
            packMode = PackMode::WEBGL_STRICT;
        }
    
        // Build active shader stage map.
        ShaderBitSet attachedShadersMask;
        for (ShaderType shaderType : kAllGraphicsShaderTypes)
        {
            if (programOrPipeline.getAttachedShader(shaderType))
            {
                attachedShadersMask[shaderType] = true;
            }
        }
    
        if (!varyingPacking->collectAndPackUserVaryings(mInfoLog, context->getCaps(), packMode,
                                                        attachedShadersMask, mergedVaryings,
                                                        transformFeedbackVaryingNames, isSeparable))
        {
            return false;
        }
    
        gatherTransformFeedbackVaryings(mergedVaryings, tfStage, transformFeedbackVaryingNames);
        updateTransformFeedbackStrides();
    
        return true;
    }
    
    bool ProgramExecutable::linkValidateTransformFeedback(
        const Context *context,
        const ProgramMergedVaryings &varyings,
        ShaderType stage,
        const std::vector<std::string> &transformFeedbackVaryingNames)
    {
        const Version &version = context->getClientVersion();
    
        // Validate the tf names regardless of the actual program varyings.
        std::set<std::string> uniqueNames;
        for (const std::string &tfVaryingName : transformFeedbackVaryingNames)
        {
            if (version < Version(3, 1) && tfVaryingName.find('[') != std::string::npos)
            {
                mInfoLog << "Capture of array elements is undefined and not supported.";
                return false;
            }
            if (version >= Version(3, 1))
            {
                if (IncludeSameArrayElement(uniqueNames, tfVaryingName))
                {
                    mInfoLog << "Two transform feedback varyings include the same array element ("
                             << tfVaryingName << ").";
                    return false;
                }
            }
            else
            {
                if (uniqueNames.count(tfVaryingName) > 0)
                {
                    mInfoLog << "Two transform feedback varyings specify the same output variable ("
                             << tfVaryingName << ").";
                    return false;
                }
            }
            uniqueNames.insert(tfVaryingName);
        }
    
        // Validate against program varyings.
        size_t totalComponents = 0;
        for (const std::string &tfVaryingName : transformFeedbackVaryingNames)
        {
            std::vector<unsigned int> subscripts;
            std::string baseName = ParseResourceName(tfVaryingName, &subscripts);
    
            const sh::ShaderVariable *var = FindOutputVaryingOrField(varyings, stage, baseName);
            if (var == nullptr)
            {
                mInfoLog << "Transform feedback varying " << tfVaryingName
                         << " does not exist in the vertex shader.";
                return false;
            }
    
            // Validate the matching variable.
            if (var->isStruct())
            {
                mInfoLog << "Struct cannot be captured directly (" << baseName << ").";
                return false;
            }
    
            size_t elementCount   = 0;
            size_t componentCount = 0;
    
            if (var->isArray())
            {
                if (version < Version(3, 1))
                {
                    mInfoLog << "Capture of arrays is undefined and not supported.";
                    return false;
                }
    
                // GLSL ES 3.10 section 4.3.6: A vertex output can't be an array of arrays.
                ASSERT(!var->isArrayOfArrays());
    
                if (!subscripts.empty() && subscripts[0] >= var->getOutermostArraySize())
                {
                    mInfoLog << "Cannot capture outbound array element '" << tfVaryingName << "'.";
                    return false;
                }
                elementCount = (subscripts.empty() ? var->getOutermostArraySize() : 1);
            }
            else
            {
                if (!subscripts.empty())
                {
                    mInfoLog << "Varying '" << baseName
                             << "' is not an array to be captured by element.";
                    return false;
                }
                elementCount = 1;
            }
    
            const Caps &caps = context->getCaps();
    
            // TODO(jmadill): Investigate implementation limits on D3D11
            componentCount = VariableComponentCount(var->type) * elementCount;
            if (mTransformFeedbackBufferMode == GL_SEPARATE_ATTRIBS &&
                componentCount > static_cast<GLuint>(caps.maxTransformFeedbackSeparateComponents))
            {
                mInfoLog << "Transform feedback varying " << tfVaryingName << " components ("
                         << componentCount << ") exceed the maximum separate components ("
                         << caps.maxTransformFeedbackSeparateComponents << ").";
                return false;
            }
    
            totalComponents += componentCount;
            if (mTransformFeedbackBufferMode == GL_INTERLEAVED_ATTRIBS &&
                totalComponents > static_cast<GLuint>(caps.maxTransformFeedbackInterleavedComponents))
            {
                mInfoLog << "Transform feedback varying total components (" << totalComponents
                         << ") exceed the maximum interleaved components ("
                         << caps.maxTransformFeedbackInterleavedComponents << ").";
                return false;
            }
        }
        return true;
    }
    
    void ProgramExecutable::gatherTransformFeedbackVaryings(
        const ProgramMergedVaryings &varyings,
        ShaderType stage,
        const std::vector<std::string> &transformFeedbackVaryingNames)
    {
        // Gather the linked varyings that are used for transform feedback, they should all exist.
        mLinkedTransformFeedbackVaryings.clear();
        for (const std::string &tfVaryingName : transformFeedbackVaryingNames)
        {
            std::vector<unsigned int> subscripts;
            std::string baseName = ParseResourceName(tfVaryingName, &subscripts);
            size_t subscript     = GL_INVALID_INDEX;
            if (!subscripts.empty())
            {
                subscript = subscripts.back();
            }
            for (const ProgramVaryingRef &ref : varyings)
            {
                if (ref.frontShaderStage != stage)
                {
                    continue;
                }
    
                const sh::ShaderVariable *varying = ref.get(stage);
                if (baseName == varying->name)
                {
                    mLinkedTransformFeedbackVaryings.emplace_back(*varying,
                                                                  static_cast<GLuint>(subscript));
                    break;
                }
                else if (varying->isStruct())
                {
                    GLuint fieldIndex = 0;
                    const auto *field = varying->findField(tfVaryingName, &fieldIndex);
                    if (field != nullptr)
                    {
                        mLinkedTransformFeedbackVaryings.emplace_back(*field, *varying);
                        break;
                    }
                }
            }
        }
    }
    
    void ProgramExecutable::updateTransformFeedbackStrides()
    {
        if (mTransformFeedbackBufferMode == GL_INTERLEAVED_ATTRIBS)
        {
            mTransformFeedbackStrides.resize(1);
            size_t totalSize = 0;
            for (const TransformFeedbackVarying &varying : mLinkedTransformFeedbackVaryings)
            {
                totalSize += varying.size() * VariableExternalSize(varying.type);
            }
            mTransformFeedbackStrides[0] = static_cast<GLsizei>(totalSize);
        }
        else
        {
            mTransformFeedbackStrides.resize(mLinkedTransformFeedbackVaryings.size());
            for (size_t i = 0; i < mLinkedTransformFeedbackVaryings.size(); i++)
            {
                TransformFeedbackVarying &varying = mLinkedTransformFeedbackVaryings[i];
                mTransformFeedbackStrides[i] =
                    static_cast<GLsizei>(varying.size() * VariableExternalSize(varying.type));
            }
        }
    }
    
    }  // namespace gl