Edit

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

Branch :

  • Show log

    Commit

  • Author : Jamie Madill
    Date : 2017-08-31 14:39:15
    Hash : f00f7ffe
    Message : Add a Uniform type info table. Currently most uniform type info is determined by switching on the uniform type. Some values are computed from other values, which can result in three or more switch statements plus some multiplies or other math. This patch attempts to improve the speed by pre computing necessary values into constant static tables. Improves performance by about 7% in a uniform stress test. BUG=angleproject:1390 Change-Id: I29bef259a17f6d6536171ade4950e2d712bfd39c Reviewed-on: https://chromium-review.googlesource.com/643791 Reviewed-by: Geoff Lang <geofflang@chromium.org> Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: 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->writeInt(var.arraySize);
        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();
        var->arraySize  = stream->readInt<unsigned int>();
        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.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();
    
        unsigned int numMembers = stream->readInt<unsigned int>();
        for (unsigned int blockMemberIndex = 0; blockMemberIndex < numMembers; blockMemberIndex++)
        {
            var->memberIndexes.push_back(stream->readInt<unsigned int>());
        }
    }
    
    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 Program::Bindings &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->mNumViews = stream.readInt<int>();
    
        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.readString(&variable.name);
            stream.readInt(&variable.element);
            stream.readInt(&variable.index);
            stream.readBool(&variable.used);
            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)
        {
            UniformBlock uniformBlock;
            stream.readString(&uniformBlock.name);
            stream.readString(&uniformBlock.mappedName);
            stream.readBool(&uniformBlock.isArray);
            stream.readInt(&uniformBlock.arrayElement);
    
            LoadShaderVariableBuffer(&stream, &uniformBlock);
    
            state->mUniformBlocks.push_back(uniformBlock);
    
            state->mActiveUniformBlockBindings.set(uniformBlockIndex, uniformBlock.binding != 0);
        }
        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.readInt(&varying.arraySize);
            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>();
        for (unsigned int outputIndex = 0; outputIndex < outputVarCount; ++outputIndex)
        {
            int locationIndex = stream.readInt<int>();
            VariableLocation locationData;
            stream.readInt(&locationData.element);
            stream.readInt(&locationData.index);
            stream.readString(&locationData.name);
            state->mOutputLocations[locationIndex] = 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 < 8 * sizeof(uint32_t),
                      "All bits of DrawBufferMask can be contained in an 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)
        {
            GLenum textureType  = stream.readInt<GLenum>();
            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);
    
        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]);
    
        stream.writeInt(state.mNumViews);
    
        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.writeString(variable.name);
            stream.writeInt(variable.element);
            stream.writeInt(variable.index);
            stream.writeInt(variable.used);
            stream.writeInt(variable.ignored);
        }
    
        stream.writeInt(state.getUniformBlocks().size());
        for (const UniformBlock &uniformBlock : state.getUniformBlocks())
        {
            stream.writeString(uniformBlock.name);
            stream.writeString(uniformBlock.mappedName);
            stream.writeInt(uniformBlock.isArray);
            stream.writeInt(uniformBlock.arrayElement);
    
            WriteShaderVariableBuffer(&stream, uniformBlock);
        }
    
        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.writeInt(var.arraySize);
            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 &outputPair : state.getOutputLocations())
        {
            stream.writeInt(outputPair.first);
            stream.writeIntOrNegOne(outputPair.second.element);
            stream.writeInt(outputPair.second.index);
            stream.writeString(outputPair.second.name);
        }
    
        stream.writeInt(state.mOutputVariableTypes.size());
        for (const auto &outputVariableType : state.mOutputVariableTypes)
        {
            stream.writeInt(outputVariableType);
        }
    
        static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS < 8 * sizeof(uint32_t),
                      "All bits of DrawBufferMask can be contained in an uint32_t");
        stream.writeInt(static_cast<uint32_t>(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.writeInt(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());
    
        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)
    {
        auto vertexShader   = program->getAttachedVertexShader();
        auto fragmentShader = program->getAttachedFragmentShader();
        auto computeShader  = program->getAttachedComputeShader();
    
        // Compute the program hash. Start with the shader hashes and resource strings.
        HashStream hashStream;
        hashStream << vertexShader << fragmentShader << computeShader;
    
        // 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