// // 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 #include #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 { constexpr unsigned int kWarningLimit = 3; class HashStream final : angle::NonCopyable { public: std::string str() { return mStringStream.str(); } template 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.location; } return stream; } HashStream &operator<<(HashStream &stream, const std::vector &strings) { for (const auto &str : strings) { stream << str; } return stream; } HashStream &operator<<(HashStream &stream, const std::vector &locations) { for (const auto &loc : locations) { stream << loc.index << loc.arrayIndex << loc.ignored; } return stream; } } // anonymous namespace MemoryProgramCache::MemoryProgramCache(egl::BlobCache &blobCache) : mBlobCache(blobCache), mIssuedWarnings(0) {} MemoryProgramCache::~MemoryProgramCache() {} void MemoryProgramCache::ComputeHash(const Context *context, const Program *program, egl::BlobCache::Key *hashOut) { // Compute the program hash. Start with the shader hashes and resource strings. HashStream hashStream; for (ShaderType shaderType : AllShaderTypes()) { hashStream << program->getAttachedShader(shaderType); } // 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() << program->getState().getOutputLocations() << program->getState().getSecondaryOutputLocations(); // Call the secure SHA hashing function. const std::string &programKey = hashStream.str(); angle::base::SHA1HashBytes(reinterpret_cast(programKey.c_str()), programKey.length(), hashOut->data()); } angle::Result MemoryProgramCache::getProgram(const Context *context, Program *program, egl::BlobCache::Key *hashOut) { // If caching is effectively disabled, don't bother calculating the hash. if (!mBlobCache.isCachingEnabled()) { return angle::Result::Incomplete; } ComputeHash(context, program, hashOut); egl::BlobCache::Value binaryProgram; if (get(context, *hashOut, &binaryProgram)) { angle::Result result = program->loadBinary(context, GL_PROGRAM_BINARY_ANGLE, binaryProgram.data(), binaryProgram.size()); ANGLE_HISTOGRAM_BOOLEAN("GPU.ANGLE.ProgramCache.LoadBinarySuccess", result == angle::Result::Continue); ANGLE_TRY(result); if (result == angle::Result::Continue) return angle::Result::Continue; // Cache load failed, evict. if (mIssuedWarnings++ < kWarningLimit) { WARN() << "Failed to load binary from cache."; if (mIssuedWarnings == kWarningLimit) { WARN() << "Reaching warning limit for cache load failures, silencing " "subsequent warnings."; } } remove(*hashOut); } return angle::Result::Incomplete; } bool MemoryProgramCache::get(const Context *context, const egl::BlobCache::Key &programHash, egl::BlobCache::Value *programOut) { return mBlobCache.get(context->getScratchBuffer(), programHash, programOut); } bool MemoryProgramCache::getAt(size_t index, const egl::BlobCache::Key **hashOut, egl::BlobCache::Value *programOut) { return mBlobCache.getAt(index, hashOut, programOut); } void MemoryProgramCache::remove(const egl::BlobCache::Key &programHash) { mBlobCache.remove(programHash); } void MemoryProgramCache::putProgram(const egl::BlobCache::Key &programHash, const Context *context, const Program *program) { // If caching is effectively disabled, don't bother serializing the program. if (!mBlobCache.isCachingEnabled()) { return; } angle::MemoryBuffer serializedProgram; program->serialize(context, &serializedProgram); ANGLE_HISTOGRAM_COUNTS("GPU.ANGLE.ProgramCache.ProgramBinarySizeBytes", static_cast(serializedProgram.size())); // TODO(syoussefi): to be removed. Compatibility for Chrome until it supports // EGL_ANDROID_blob_cache. http://anglebug.com/2516 auto *platform = ANGLEPlatformCurrent(); platform->cacheProgram(platform, programHash, serializedProgram.size(), serializedProgram.data()); mBlobCache.put(programHash, std::move(serializedProgram)); } void MemoryProgramCache::updateProgram(const Context *context, const Program *program) { egl::BlobCache::Key programHash; ComputeHash(context, program, &programHash); putProgram(programHash, context, program); } void MemoryProgramCache::putBinary(const egl::BlobCache::Key &programHash, const uint8_t *binary, size_t length) { // Copy the binary. angle::MemoryBuffer newEntry; newEntry.resize(length); memcpy(newEntry.data(), binary, length); // Store the binary. mBlobCache.populate(programHash, std::move(newEntry)); } void MemoryProgramCache::clear() { mBlobCache.clear(); mIssuedWarnings = 0; } void MemoryProgramCache::resize(size_t maxCacheSizeBytes) { mBlobCache.resize(maxCacheSizeBytes); } size_t MemoryProgramCache::entryCount() const { return mBlobCache.entryCount(); } size_t MemoryProgramCache::trim(size_t limit) { return mBlobCache.trim(limit); } size_t MemoryProgramCache::size() const { return mBlobCache.size(); } size_t MemoryProgramCache::maxSize() const { return mBlobCache.maxSize(); } } // namespace gl