Edit

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

Branch :

  • Show log

    Commit

  • Author : Corentin Wallez
    Date : 2017-11-08 14:00:32
    Hash : f0e89be6
    Message : Use packed enums for the texture types and targets, part 1 In OpenGL there are two enum "sets" used by the API that are very similar: texture types (or bind point) and texture targets. They only differ in that texture types have GL_TEXTURE_CUBEMAP and target have GL_TEXTURE_CUBEMAP_[POSITIVE|NEGATIVE]_[X|Y|Z]. This is a problem because in ANGLE we use GLenum to pass around both types of data, making it difficult to know which of type and target a variable is. In addition these enums are placed somewhat randomly in the space of OpenGL enums, making it slow to have a mapping from texture types to some data. Such a mapping is in hot-code with gl::State::mTextures. This commit stack makes the texture types and target enums be translated to internal packed enums right at the OpenGL entry point and used throughout ANGLE to have type safety and performance gains. This is the first of two commit which does the refactor for all of the validation and stops inside gl::Context. This was the best place to split patches without having many conversions from packed enums to GL enums. BUG=angleproject:2169 Change-Id: Ib43da7e71c253bd9fe210fb0ec0de61bc286e6d3 Reviewed-on: https://chromium-review.googlesource.com/758835 Commit-Queue: Corentin Wallez <cwallez@chromium.org> Reviewed-by: Jamie Madill <jmadill@chromium.org>

  • src/libANGLE/MemoryProgramCache.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.
    //
    // MemoryProgramCache: Stores compiled and linked programs in memory so they don't
    //   always have to be re-compiled. Can be used in conjunction with the platform
    //   layer to warm up the cache from disk.
    
    #include "libANGLE/MemoryProgramCache.h"
    
    #include <GLSLANG/ShaderVars.h>
    #include <anglebase/sha1.h>
    
    #include "common/utilities.h"
    #include "common/version.h"
    #include "libANGLE/BinaryStream.h"
    #include "libANGLE/Context.h"
    #include "libANGLE/Uniform.h"
    #include "libANGLE/histogram_macros.h"
    #include "libANGLE/renderer/ProgramImpl.h"
    #include "platform/Platform.h"
    
    namespace gl
    {
    
    namespace
    {
    enum CacheResult
    {
        kCacheMiss,
        kCacheHitMemory,
        kCacheHitDisk,
        kCacheResultMax,
    };
    
    constexpr unsigned int kWarningLimit = 3;
    
    void WriteShaderVar(BinaryOutputStream *stream, const sh::ShaderVariable &var)
    {
        stream->writeInt(var.type);
        stream->writeInt(var.precision);
        stream->writeString(var.name);
        stream->writeString(var.mappedName);
        stream->writeIntVector(var.arraySizes);
        stream->writeInt(var.staticUse);
        stream->writeString(var.structName);
        ASSERT(var.fields.empty());
    }
    
    void LoadShaderVar(BinaryInputStream *stream, sh::ShaderVariable *var)
    {
        var->type       = stream->readInt<GLenum>();
        var->precision  = stream->readInt<GLenum>();
        var->name       = stream->readString();
        var->mappedName = stream->readString();
        stream->readIntVector<unsigned int>(&var->arraySizes);
        var->staticUse  = stream->readBool();
        var->structName = stream->readString();
    }
    
    void WriteShaderVariableBuffer(BinaryOutputStream *stream, const ShaderVariableBuffer &var)
    {
        stream->writeInt(var.binding);
        stream->writeInt(var.dataSize);
    
        stream->writeInt(var.vertexStaticUse);
        stream->writeInt(var.fragmentStaticUse);
        stream->writeInt(var.computeStaticUse);
        stream->writeInt(var.geometryStaticUse);
    
        stream->writeInt(var.memberIndexes.size());
        for (unsigned int memberCounterIndex : var.memberIndexes)
        {
            stream->writeInt(memberCounterIndex);
        }
    }
    
    void LoadShaderVariableBuffer(BinaryInputStream *stream, ShaderVariableBuffer *var)
    {
        var->binding           = stream->readInt<int>();
        var->dataSize          = stream->readInt<unsigned int>();
        var->vertexStaticUse   = stream->readBool();
        var->fragmentStaticUse = stream->readBool();
        var->computeStaticUse  = stream->readBool();
        var->geometryStaticUse = stream->readBool();
    
        unsigned int numMembers = stream->readInt<unsigned int>();
        for (unsigned int blockMemberIndex = 0; blockMemberIndex < numMembers; blockMemberIndex++)
        {
            var->memberIndexes.push_back(stream->readInt<unsigned int>());
        }
    }
    
    void WriteBufferVariable(BinaryOutputStream *stream, const BufferVariable &var)
    {
        WriteShaderVar(stream, var);
    
        stream->writeInt(var.bufferIndex);
        stream->writeInt(var.blockInfo.offset);
        stream->writeInt(var.blockInfo.arrayStride);
        stream->writeInt(var.blockInfo.matrixStride);
        stream->writeInt(var.blockInfo.isRowMajorMatrix);
        stream->writeInt(var.blockInfo.topLevelArrayStride);
        stream->writeInt(var.topLevelArraySize);
        stream->writeInt(var.vertexStaticUse);
        stream->writeInt(var.fragmentStaticUse);
        stream->writeInt(var.computeStaticUse);
    }
    
    void LoadBufferVariable(BinaryInputStream *stream, BufferVariable *var)
    {
        LoadShaderVar(stream, var);
    
        var->bufferIndex                   = stream->readInt<int>();
        var->blockInfo.offset              = stream->readInt<int>();
        var->blockInfo.arrayStride         = stream->readInt<int>();
        var->blockInfo.matrixStride        = stream->readInt<int>();
        var->blockInfo.isRowMajorMatrix    = stream->readBool();
        var->blockInfo.topLevelArrayStride = stream->readInt<int>();
        var->topLevelArraySize             = stream->readInt<int>();
        var->vertexStaticUse               = stream->readBool();
        var->fragmentStaticUse             = stream->readBool();
        var->computeStaticUse              = stream->readBool();
    }
    
    void WriteInterfaceBlock(BinaryOutputStream *stream, const InterfaceBlock &block)
    {
        stream->writeString(block.name);
        stream->writeString(block.mappedName);
        stream->writeInt(block.isArray);
        stream->writeInt(block.arrayElement);
    
        WriteShaderVariableBuffer(stream, block);
    }
    
    void LoadInterfaceBlock(BinaryInputStream *stream, InterfaceBlock *block)
    {
        block->name         = stream->readString();
        block->mappedName   = stream->readString();
        block->isArray      = stream->readBool();
        block->arrayElement = stream->readInt<unsigned int>();
    
        LoadShaderVariableBuffer(stream, block);
    }
    
    class HashStream final : angle::NonCopyable
    {
      public:
        std::string str() { return mStringStream.str(); }
    
        template <typename T>
        HashStream &operator<<(T value)
        {
            mStringStream << value << kSeparator;
            return *this;
        }
    
      private:
        static constexpr char kSeparator = ':';
        std::ostringstream mStringStream;
    };
    
    HashStream &operator<<(HashStream &stream, const Shader *shader)
    {
        if (shader)
        {
            stream << shader->getSourceString().c_str() << shader->getSourceString().length()
                   << shader->getCompilerResourcesString().c_str();
        }
        return stream;
    }
    
    HashStream &operator<<(HashStream &stream, const ProgramBindings &bindings)
    {
        for (const auto &binding : bindings)
        {
            stream << binding.first << binding.second;
        }
        return stream;
    }
    
    HashStream &operator<<(HashStream &stream, const std::vector<std::string> &strings)
    {
        for (const auto &str : strings)
        {
            stream << str;
        }
        return stream;
    }
    
    }  // anonymous namespace
    
    MemoryProgramCache::MemoryProgramCache(size_t maxCacheSizeBytes)
        : mProgramBinaryCache(maxCacheSizeBytes), mIssuedWarnings(0)
    {
    }
    
    MemoryProgramCache::~MemoryProgramCache()
    {
    }
    
    // static
    LinkResult MemoryProgramCache::Deserialize(const Context *context,
                                               const Program *program,
                                               ProgramState *state,
                                               const uint8_t *binary,
                                               size_t length,
                                               InfoLog &infoLog)
    {
        BinaryInputStream stream(binary, length);
    
        unsigned char commitString[ANGLE_COMMIT_HASH_SIZE];
        stream.readBytes(commitString, ANGLE_COMMIT_HASH_SIZE);
        if (memcmp(commitString, ANGLE_COMMIT_HASH, sizeof(unsigned char) * ANGLE_COMMIT_HASH_SIZE) !=
            0)
        {
            infoLog << "Invalid program binary version.";
            return false;
        }
    
        int majorVersion = stream.readInt<int>();
        int minorVersion = stream.readInt<int>();
        if (majorVersion != context->getClientMajorVersion() ||
            minorVersion != context->getClientMinorVersion())
        {
            infoLog << "Cannot load program binaries across different ES context versions.";
            return false;
        }
    
        state->mComputeShaderLocalSize[0] = stream.readInt<int>();
        state->mComputeShaderLocalSize[1] = stream.readInt<int>();
        state->mComputeShaderLocalSize[2] = stream.readInt<int>();
    
        state->mGeometryShaderInputPrimitiveType  = stream.readInt<GLenum>();
        state->mGeometryShaderOutputPrimitiveType = stream.readInt<GLenum>();
        state->mGeometryShaderInvocations         = stream.readInt<int>();
        state->mGeometryShaderMaxVertices         = stream.readInt<int>();
    
        state->mNumViews = stream.readInt<int>();
    
        static_assert(MAX_VERTEX_ATTRIBS * 2 <= sizeof(uint32_t) * 8,
                      "All bits of mAttributesTypeMask types and mask fit into 32 bits each");
        state->mAttributesTypeMask.from_ulong(stream.readInt<uint32_t>());
        state->mAttributesMask = stream.readInt<uint32_t>();
    
        static_assert(MAX_VERTEX_ATTRIBS <= sizeof(unsigned long) * 8,
                      "Too many vertex attribs for mask");
        state->mActiveAttribLocationsMask = stream.readInt<unsigned long>();
    
        unsigned int attribCount = stream.readInt<unsigned int>();
        ASSERT(state->mAttributes.empty());
        for (unsigned int attribIndex = 0; attribIndex < attribCount; ++attribIndex)
        {
            sh::Attribute attrib;
            LoadShaderVar(&stream, &attrib);
            attrib.location = stream.readInt<int>();
            state->mAttributes.push_back(attrib);
        }
    
        unsigned int uniformCount = stream.readInt<unsigned int>();
        ASSERT(state->mUniforms.empty());
        for (unsigned int uniformIndex = 0; uniformIndex < uniformCount; ++uniformIndex)
        {
            LinkedUniform uniform;
            LoadShaderVar(&stream, &uniform);
    
            uniform.bufferIndex                = stream.readInt<int>();
            uniform.blockInfo.offset           = stream.readInt<int>();
            uniform.blockInfo.arrayStride      = stream.readInt<int>();
            uniform.blockInfo.matrixStride     = stream.readInt<int>();
            uniform.blockInfo.isRowMajorMatrix = stream.readBool();
    
            uniform.typeInfo = &GetUniformTypeInfo(uniform.type);
    
            state->mUniforms.push_back(uniform);
        }
    
        const unsigned int uniformIndexCount = stream.readInt<unsigned int>();
        ASSERT(state->mUniformLocations.empty());
        for (unsigned int uniformIndexIndex = 0; uniformIndexIndex < uniformIndexCount;
             uniformIndexIndex++)
        {
            VariableLocation variable;
            stream.readInt(&variable.arrayIndex);
            stream.readInt(&variable.index);
            stream.readBool(&variable.ignored);
    
            state->mUniformLocations.push_back(variable);
        }
    
        unsigned int uniformBlockCount = stream.readInt<unsigned int>();
        ASSERT(state->mUniformBlocks.empty());
        for (unsigned int uniformBlockIndex = 0; uniformBlockIndex < uniformBlockCount;
             ++uniformBlockIndex)
        {
            InterfaceBlock uniformBlock;
            LoadInterfaceBlock(&stream, &uniformBlock);
            state->mUniformBlocks.push_back(uniformBlock);
    
            state->mActiveUniformBlockBindings.set(uniformBlockIndex, uniformBlock.binding != 0);
        }
    
        unsigned int bufferVariableCount = stream.readInt<unsigned int>();
        ASSERT(state->mBufferVariables.empty());
        for (unsigned int index = 0; index < bufferVariableCount; ++index)
        {
            BufferVariable bufferVariable;
            LoadBufferVariable(&stream, &bufferVariable);
            state->mBufferVariables.push_back(bufferVariable);
        }
    
        unsigned int shaderStorageBlockCount = stream.readInt<unsigned int>();
        ASSERT(state->mShaderStorageBlocks.empty());
        for (unsigned int shaderStorageBlockIndex = 0;
             shaderStorageBlockIndex < shaderStorageBlockCount; ++shaderStorageBlockIndex)
        {
            InterfaceBlock shaderStorageBlock;
            LoadInterfaceBlock(&stream, &shaderStorageBlock);
            state->mShaderStorageBlocks.push_back(shaderStorageBlock);
        }
    
        unsigned int atomicCounterBufferCount = stream.readInt<unsigned int>();
        ASSERT(state->mAtomicCounterBuffers.empty());
        for (unsigned int bufferIndex = 0; bufferIndex < atomicCounterBufferCount; ++bufferIndex)
        {
            AtomicCounterBuffer atomicCounterBuffer;
            LoadShaderVariableBuffer(&stream, &atomicCounterBuffer);
    
            state->mAtomicCounterBuffers.push_back(atomicCounterBuffer);
        }
    
        unsigned int transformFeedbackVaryingCount = stream.readInt<unsigned int>();
    
        // Reject programs that use transform feedback varyings if the hardware cannot support them.
        if (transformFeedbackVaryingCount > 0 &&
            context->getWorkarounds().disableProgramCachingForTransformFeedback)
        {
            infoLog << "Current driver does not support transform feedback in binary programs.";
            return false;
        }
    
        ASSERT(state->mLinkedTransformFeedbackVaryings.empty());
        for (unsigned int transformFeedbackVaryingIndex = 0;
             transformFeedbackVaryingIndex < transformFeedbackVaryingCount;
             ++transformFeedbackVaryingIndex)
        {
            sh::Varying varying;
            stream.readIntVector<unsigned int>(&varying.arraySizes);
            stream.readInt(&varying.type);
            stream.readString(&varying.name);
    
            GLuint arrayIndex = stream.readInt<GLuint>();
    
            state->mLinkedTransformFeedbackVaryings.emplace_back(varying, arrayIndex);
        }
    
        stream.readInt(&state->mTransformFeedbackBufferMode);
    
        unsigned int outputCount = stream.readInt<unsigned int>();
        ASSERT(state->mOutputVariables.empty());
        for (unsigned int outputIndex = 0; outputIndex < outputCount; ++outputIndex)
        {
            sh::OutputVariable output;
            LoadShaderVar(&stream, &output);
            output.location = stream.readInt<int>();
            state->mOutputVariables.push_back(output);
        }
    
        unsigned int outputVarCount = stream.readInt<unsigned int>();
        ASSERT(state->mOutputLocations.empty());
        for (unsigned int outputIndex = 0; outputIndex < outputVarCount; ++outputIndex)
        {
            VariableLocation locationData;
            stream.readInt(&locationData.arrayIndex);
            stream.readInt(&locationData.index);
            stream.readBool(&locationData.ignored);
            state->mOutputLocations.push_back(locationData);
        }
    
        unsigned int outputTypeCount = stream.readInt<unsigned int>();
        for (unsigned int outputIndex = 0; outputIndex < outputTypeCount; ++outputIndex)
        {
            state->mOutputVariableTypes.push_back(stream.readInt<GLenum>());
        }
    
        static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS * 2 <= 8 * sizeof(uint32_t),
                      "All bits of mDrawBufferTypeMask and mActiveOutputVariables types and mask fit "
                      "into 32 bits each");
        state->mDrawBufferTypeMask.from_ulong(stream.readInt<uint32_t>());
        state->mActiveOutputVariables = stream.readInt<uint32_t>();
    
        unsigned int samplerRangeLow  = stream.readInt<unsigned int>();
        unsigned int samplerRangeHigh = stream.readInt<unsigned int>();
        state->mSamplerUniformRange   = RangeUI(samplerRangeLow, samplerRangeHigh);
        unsigned int samplerCount = stream.readInt<unsigned int>();
        for (unsigned int samplerIndex = 0; samplerIndex < samplerCount; ++samplerIndex)
        {
            TextureType textureType = stream.readEnum<TextureType>();
            size_t bindingCount = stream.readInt<size_t>();
            bool unreferenced   = stream.readBool();
            state->mSamplerBindings.emplace_back(
                SamplerBinding(textureType, bindingCount, unreferenced));
        }
    
        unsigned int imageRangeLow  = stream.readInt<unsigned int>();
        unsigned int imageRangeHigh = stream.readInt<unsigned int>();
        state->mImageUniformRange   = RangeUI(imageRangeLow, imageRangeHigh);
        unsigned int imageBindingCount = stream.readInt<unsigned int>();
        for (unsigned int imageIndex = 0; imageIndex < imageBindingCount; ++imageIndex)
        {
            unsigned int elementCount = stream.readInt<unsigned int>();
            ImageBinding imageBinding(elementCount);
            for (unsigned int i = 0; i < elementCount; ++i)
            {
                imageBinding.boundImageUnits[i] = stream.readInt<unsigned int>();
            }
            state->mImageBindings.emplace_back(imageBinding);
        }
    
        unsigned int atomicCounterRangeLow  = stream.readInt<unsigned int>();
        unsigned int atomicCounterRangeHigh = stream.readInt<unsigned int>();
        state->mAtomicCounterUniformRange   = RangeUI(atomicCounterRangeLow, atomicCounterRangeHigh);
    
        static_assert(SHADER_TYPE_MAX <= sizeof(unsigned long) * 8, "Too many shader types");
        state->mLinkedShaderStages = stream.readInt<unsigned long>();
    
        return program->getImplementation()->load(context, infoLog, &stream);
    }
    
    // static
    void MemoryProgramCache::Serialize(const Context *context,
                                       const gl::Program *program,
                                       angle::MemoryBuffer *binaryOut)
    {
        BinaryOutputStream stream;
    
        stream.writeBytes(reinterpret_cast<const unsigned char *>(ANGLE_COMMIT_HASH),
                          ANGLE_COMMIT_HASH_SIZE);
    
        // nullptr context is supported when computing binary length.
        if (context)
        {
            stream.writeInt(context->getClientVersion().major);
            stream.writeInt(context->getClientVersion().minor);
        }
        else
        {
            stream.writeInt(2);
            stream.writeInt(0);
        }
    
        const auto &state = program->getState();
    
        const auto &computeLocalSize = state.getComputeShaderLocalSize();
    
        stream.writeInt(computeLocalSize[0]);
        stream.writeInt(computeLocalSize[1]);
        stream.writeInt(computeLocalSize[2]);
    
        ASSERT(state.mGeometryShaderInvocations >= 1 && state.mGeometryShaderMaxVertices >= 0);
        stream.writeInt(state.mGeometryShaderInputPrimitiveType);
        stream.writeInt(state.mGeometryShaderOutputPrimitiveType);
        stream.writeInt(state.mGeometryShaderInvocations);
        stream.writeInt(state.mGeometryShaderMaxVertices);
    
        stream.writeInt(state.mNumViews);
    
        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<int>(state.mAttributesTypeMask.to_ulong()));
        stream.writeInt(static_cast<int>(state.mAttributesMask.to_ulong()));
    
        stream.writeInt(state.getActiveAttribLocationsMask().to_ulong());
    
        stream.writeInt(state.getAttributes().size());
        for (const sh::Attribute &attrib : state.getAttributes())
        {
            WriteShaderVar(&stream, attrib);
            stream.writeInt(attrib.location);
        }
    
        stream.writeInt(state.getUniforms().size());
        for (const LinkedUniform &uniform : state.getUniforms())
        {
            WriteShaderVar(&stream, uniform);
    
            // FIXME: referenced
    
            stream.writeInt(uniform.bufferIndex);
            stream.writeInt(uniform.blockInfo.offset);
            stream.writeInt(uniform.blockInfo.arrayStride);
            stream.writeInt(uniform.blockInfo.matrixStride);
            stream.writeInt(uniform.blockInfo.isRowMajorMatrix);
        }
    
        stream.writeInt(state.getUniformLocations().size());
        for (const auto &variable : state.getUniformLocations())
        {
            stream.writeInt(variable.arrayIndex);
            stream.writeIntOrNegOne(variable.index);
            stream.writeInt(variable.ignored);
        }
    
        stream.writeInt(state.getUniformBlocks().size());
        for (const InterfaceBlock &uniformBlock : state.getUniformBlocks())
        {
            WriteInterfaceBlock(&stream, uniformBlock);
        }
    
        stream.writeInt(state.getBufferVariables().size());
        for (const BufferVariable &bufferVariable : state.getBufferVariables())
        {
            WriteBufferVariable(&stream, bufferVariable);
        }
    
        stream.writeInt(state.getShaderStorageBlocks().size());
        for (const InterfaceBlock &shaderStorageBlock : state.getShaderStorageBlocks())
        {
            WriteInterfaceBlock(&stream, shaderStorageBlock);
        }
    
        stream.writeInt(state.mAtomicCounterBuffers.size());
        for (const auto &atomicCounterBuffer : state.mAtomicCounterBuffers)
        {
            WriteShaderVariableBuffer(&stream, atomicCounterBuffer);
        }
    
        // Warn the app layer if saving a binary with unsupported transform feedback.
        if (!state.getLinkedTransformFeedbackVaryings().empty() &&
            context->getWorkarounds().disableProgramCachingForTransformFeedback)
        {
            WARN() << "Saving program binary with transform feedback, which is not supported on this "
                      "driver.";
        }
    
        stream.writeInt(state.getLinkedTransformFeedbackVaryings().size());
        for (const auto &var : state.getLinkedTransformFeedbackVaryings())
        {
            stream.writeIntVector(var.arraySizes);
            stream.writeInt(var.type);
            stream.writeString(var.name);
    
            stream.writeIntOrNegOne(var.arrayIndex);
        }
    
        stream.writeInt(state.getTransformFeedbackBufferMode());
    
        stream.writeInt(state.getOutputVariables().size());
        for (const sh::OutputVariable &output : state.getOutputVariables())
        {
            WriteShaderVar(&stream, output);
            stream.writeInt(output.location);
        }
    
        stream.writeInt(state.getOutputLocations().size());
        for (const auto &outputVar : state.getOutputLocations())
        {
            stream.writeInt(outputVar.arrayIndex);
            stream.writeIntOrNegOne(outputVar.index);
            stream.writeInt(outputVar.ignored);
        }
    
        stream.writeInt(state.mOutputVariableTypes.size());
        for (const auto &outputVariableType : state.mOutputVariableTypes)
        {
            stream.writeInt(outputVariableType);
        }
    
        static_assert(
            IMPLEMENTATION_MAX_DRAW_BUFFERS * 2 <= 8 * sizeof(uint32_t),
            "All bits of mDrawBufferTypeMask and mActiveOutputVariables can be contained in 32 bits");
        stream.writeInt(static_cast<int>(state.mDrawBufferTypeMask.to_ulong()));
        stream.writeInt(static_cast<int>(state.mActiveOutputVariables.to_ulong()));
    
        stream.writeInt(state.getSamplerUniformRange().low());
        stream.writeInt(state.getSamplerUniformRange().high());
    
        stream.writeInt(state.getSamplerBindings().size());
        for (const auto &samplerBinding : state.getSamplerBindings())
        {
            stream.writeEnum(samplerBinding.textureType);
            stream.writeInt(samplerBinding.boundTextureUnits.size());
            stream.writeInt(samplerBinding.unreferenced);
        }
    
        stream.writeInt(state.getImageUniformRange().low());
        stream.writeInt(state.getImageUniformRange().high());
    
        stream.writeInt(state.getImageBindings().size());
        for (const auto &imageBinding : state.getImageBindings())
        {
            stream.writeInt(imageBinding.boundImageUnits.size());
            for (size_t i = 0; i < imageBinding.boundImageUnits.size(); ++i)
            {
                stream.writeInt(imageBinding.boundImageUnits[i]);
            }
        }
    
        stream.writeInt(state.getAtomicCounterUniformRange().low());
        stream.writeInt(state.getAtomicCounterUniformRange().high());
    
        stream.writeInt(state.getLinkedShaderStages().to_ulong());
    
        program->getImplementation()->save(context, &stream);
    
        ASSERT(binaryOut);
        binaryOut->resize(stream.length());
        memcpy(binaryOut->data(), stream.data(), stream.length());
    }
    
    // static
    void MemoryProgramCache::ComputeHash(const Context *context,
                                         const Program *program,
                                         ProgramHash *hashOut)
    {
        const Shader *vertexShader   = program->getAttachedVertexShader();
        const Shader *fragmentShader = program->getAttachedFragmentShader();
        const Shader *computeShader  = program->getAttachedComputeShader();
        const Shader *geometryShader = program->getAttachedGeometryShader();
    
        // Compute the program hash. Start with the shader hashes and resource strings.
        HashStream hashStream;
        hashStream << vertexShader << fragmentShader << computeShader << geometryShader;
    
        // Add some ANGLE metadata and Context properties, such as version and back-end.
        hashStream << ANGLE_COMMIT_HASH << context->getClientMajorVersion()
                   << context->getClientMinorVersion() << context->getString(GL_RENDERER);
    
        // Hash pre-link program properties.
        hashStream << program->getAttributeBindings() << program->getUniformLocationBindings()
                   << program->getFragmentInputBindings()
                   << program->getState().getTransformFeedbackVaryingNames()
                   << program->getState().getTransformFeedbackBufferMode();
    
        // Call the secure SHA hashing function.
        const std::string &programKey = hashStream.str();
        angle::base::SHA1HashBytes(reinterpret_cast<const unsigned char *>(programKey.c_str()),
                                   programKey.length(), hashOut->data());
    }
    
    LinkResult MemoryProgramCache::getProgram(const Context *context,
                                              const Program *program,
                                              ProgramState *state,
                                              ProgramHash *hashOut)
    {
        ComputeHash(context, program, hashOut);
        const angle::MemoryBuffer *binaryProgram = nullptr;
        LinkResult result(false);
        if (get(*hashOut, &binaryProgram))
        {
            InfoLog infoLog;
            ANGLE_TRY_RESULT(Deserialize(context, program, state, binaryProgram->data(),
                                         binaryProgram->size(), infoLog),
                             result);
            ANGLE_HISTOGRAM_BOOLEAN("GPU.ANGLE.ProgramCache.LoadBinarySuccess", result.getResult());
            if (!result.getResult())
            {
                // Cache load failed, evict.
                if (mIssuedWarnings++ < kWarningLimit)
                {
                    WARN() << "Failed to load binary from cache: " << infoLog.str();
    
                    if (mIssuedWarnings == kWarningLimit)
                    {
                        WARN() << "Reaching warning limit for cache load failures, silencing "
                                  "subsequent warnings.";
                    }
                }
                remove(*hashOut);
            }
        }
        return result;
    }
    
    bool MemoryProgramCache::get(const ProgramHash &programHash, const angle::MemoryBuffer **programOut)
    {
        const CacheEntry *entry = nullptr;
        if (!mProgramBinaryCache.get(programHash, &entry))
        {
            ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.ProgramCache.CacheResult", kCacheMiss,
                                        kCacheResultMax);
            return false;
        }
    
        if (entry->second == CacheSource::PutProgram)
        {
            ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.ProgramCache.CacheResult", kCacheHitMemory,
                                        kCacheResultMax);
        }
        else
        {
            ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.ProgramCache.CacheResult", kCacheHitDisk,
                                        kCacheResultMax);
        }
    
        *programOut = &entry->first;
        return true;
    }
    
    bool MemoryProgramCache::getAt(size_t index,
                                   ProgramHash *hashOut,
                                   const angle::MemoryBuffer **programOut)
    {
        const CacheEntry *entry = nullptr;
        if (!mProgramBinaryCache.getAt(index, hashOut, &entry))
        {
            return false;
        }
    
        *programOut = &entry->first;
        return true;
    }
    
    void MemoryProgramCache::remove(const ProgramHash &programHash)
    {
        bool result = mProgramBinaryCache.eraseByKey(programHash);
        ASSERT(result);
    }
    
    void MemoryProgramCache::putProgram(const ProgramHash &programHash,
                                        const Context *context,
                                        const Program *program)
    {
        CacheEntry newEntry;
        Serialize(context, program, &newEntry.first);
        newEntry.second = CacheSource::PutProgram;
    
        ANGLE_HISTOGRAM_COUNTS("GPU.ANGLE.ProgramCache.ProgramBinarySizeBytes",
                               static_cast<int>(newEntry.first.size()));
    
        const CacheEntry *result =
            mProgramBinaryCache.put(programHash, std::move(newEntry), newEntry.first.size());
        if (!result)
        {
            ERR() << "Failed to store binary program in memory cache, program is too large.";
        }
        else
        {
            auto *platform = ANGLEPlatformCurrent();
            platform->cacheProgram(platform, programHash, result->first.size(), result->first.data());
        }
    }
    
    void MemoryProgramCache::updateProgram(const Context *context, const Program *program)
    {
        gl::ProgramHash programHash;
        ComputeHash(context, program, &programHash);
        putProgram(programHash, context, program);
    }
    
    void MemoryProgramCache::putBinary(const ProgramHash &programHash,
                                       const uint8_t *binary,
                                       size_t length)
    {
        // Copy the binary.
        CacheEntry newEntry;
        newEntry.first.resize(length);
        memcpy(newEntry.first.data(), binary, length);
        newEntry.second = CacheSource::PutBinary;
    
        // Store the binary.
        const CacheEntry *result = mProgramBinaryCache.put(programHash, std::move(newEntry), length);
        if (!result)
        {
            ERR() << "Failed to store binary program in memory cache, program is too large.";
        }
    }
    
    void MemoryProgramCache::clear()
    {
        mProgramBinaryCache.clear();
        mIssuedWarnings = 0;
    }
    
    void MemoryProgramCache::resize(size_t maxCacheSizeBytes)
    {
        mProgramBinaryCache.resize(maxCacheSizeBytes);
    }
    
    size_t MemoryProgramCache::entryCount() const
    {
        return mProgramBinaryCache.entryCount();
    }
    
    size_t MemoryProgramCache::trim(size_t limit)
    {
        return mProgramBinaryCache.shrinkToSize(limit);
    }
    
    size_t MemoryProgramCache::size() const
    {
        return mProgramBinaryCache.size();
    }
    
    size_t MemoryProgramCache::maxSize() const
    {
        return mProgramBinaryCache.maxSize();
    }
    
    }  // namespace gl