MemoryProgramCache.cpp (9078B)
1 // 2 // Copyright 2017 The ANGLE Project Authors. All rights reserved. 3 // Use of this source code is governed by a BSD-style license that can be 4 // found in the LICENSE file. 5 // 6 // MemoryProgramCache: Stores compiled and linked programs in memory so they don't 7 // always have to be re-compiled. Can be used in conjunction with the platform 8 // layer to warm up the cache from disk. 9 10 // Include zlib first, otherwise FAR gets defined elsewhere. 11 #define USE_SYSTEM_ZLIB 12 #include "compression_utils_portable.h" 13 14 #include "libANGLE/MemoryProgramCache.h" 15 16 #include <GLSLANG/ShaderVars.h> 17 #include <anglebase/sha1.h> 18 19 #include "common/angle_version_info.h" 20 #include "common/utilities.h" 21 #include "libANGLE/BinaryStream.h" 22 #include "libANGLE/Context.h" 23 #include "libANGLE/Debug.h" 24 #include "libANGLE/Uniform.h" 25 #include "libANGLE/capture/FrameCapture.h" 26 #include "libANGLE/histogram_macros.h" 27 #include "libANGLE/renderer/ProgramImpl.h" 28 #include "platform/PlatformMethods.h" 29 30 namespace gl 31 { 32 33 namespace 34 { 35 class HashStream final : angle::NonCopyable 36 { 37 public: 38 std::string str() { return mStringStream.str(); } 39 40 template <typename T> 41 HashStream &operator<<(T value) 42 { 43 mStringStream << value << kSeparator; 44 return *this; 45 } 46 47 private: 48 static constexpr char kSeparator = ':'; 49 std::ostringstream mStringStream; 50 }; 51 52 HashStream &operator<<(HashStream &stream, Shader *shader) 53 { 54 if (shader) 55 { 56 stream << shader->getSourceString().c_str() << shader->getSourceString().length() 57 << shader->getCompilerResourcesString().c_str(); 58 } 59 return stream; 60 } 61 62 HashStream &operator<<(HashStream &stream, const ProgramBindings &bindings) 63 { 64 for (const auto &binding : bindings.getStableIterationMap()) 65 { 66 stream << binding.first << binding.second; 67 } 68 return stream; 69 } 70 71 HashStream &operator<<(HashStream &stream, const ProgramAliasedBindings &bindings) 72 { 73 for (const auto &binding : bindings.getStableIterationMap()) 74 { 75 stream << binding.first << binding.second.location; 76 } 77 return stream; 78 } 79 80 HashStream &operator<<(HashStream &stream, const std::vector<std::string> &strings) 81 { 82 for (const auto &str : strings) 83 { 84 stream << str; 85 } 86 return stream; 87 } 88 89 HashStream &operator<<(HashStream &stream, const std::vector<gl::VariableLocation> &locations) 90 { 91 for (const auto &loc : locations) 92 { 93 stream << loc.index << loc.arrayIndex << loc.ignored; 94 } 95 return stream; 96 } 97 98 } // anonymous namespace 99 100 MemoryProgramCache::MemoryProgramCache(egl::BlobCache &blobCache) : mBlobCache(blobCache) {} 101 102 MemoryProgramCache::~MemoryProgramCache() {} 103 104 void MemoryProgramCache::ComputeHash(const Context *context, 105 const Program *program, 106 egl::BlobCache::Key *hashOut) 107 { 108 // Compute the program hash. Start with the shader hashes and resource strings. 109 HashStream hashStream; 110 for (ShaderType shaderType : AllShaderTypes()) 111 { 112 hashStream << program->getAttachedShader(shaderType); 113 } 114 115 // Add some ANGLE metadata and Context properties, such as version and back-end. 116 hashStream << angle::GetANGLECommitHash() << context->getClientMajorVersion() 117 << context->getClientMinorVersion() << context->getString(GL_RENDERER); 118 119 // Hash pre-link program properties. 120 hashStream << program->getAttributeBindings() << program->getUniformLocationBindings() 121 << program->getFragmentOutputLocations() << program->getFragmentOutputIndexes() 122 << program->getState().getTransformFeedbackVaryingNames() 123 << program->getState().getTransformFeedbackBufferMode() 124 << program->getState().getOutputLocations() 125 << program->getState().getSecondaryOutputLocations(); 126 127 // Include the status of FrameCapture, which adds source strings to the binary 128 hashStream << context->getShareGroup()->getFrameCaptureShared()->enabled(); 129 130 // Call the secure SHA hashing function. 131 const std::string &programKey = hashStream.str(); 132 angle::base::SHA1HashBytes(reinterpret_cast<const unsigned char *>(programKey.c_str()), 133 programKey.length(), hashOut->data()); 134 } 135 136 angle::Result MemoryProgramCache::getProgram(const Context *context, 137 Program *program, 138 egl::BlobCache::Key *hashOut) 139 { 140 // If caching is effectively disabled, don't bother calculating the hash. 141 if (!mBlobCache.isCachingEnabled()) 142 { 143 return angle::Result::Incomplete; 144 } 145 146 ComputeHash(context, program, hashOut); 147 148 angle::MemoryBuffer uncompressedData; 149 switch (mBlobCache.getAndDecompress(context->getScratchBuffer(), *hashOut, &uncompressedData)) 150 { 151 case egl::BlobCache::GetAndDecompressResult::NotFound: 152 return angle::Result::Incomplete; 153 154 case egl::BlobCache::GetAndDecompressResult::DecompressFailure: 155 ANGLE_PERF_WARNING(context->getState().getDebug(), GL_DEBUG_SEVERITY_LOW, 156 "Error decompressing program binary data fetched from cache."); 157 return angle::Result::Incomplete; 158 159 case egl::BlobCache::GetAndDecompressResult::GetSuccess: 160 angle::Result result = 161 program->loadBinary(context, GL_PROGRAM_BINARY_ANGLE, uncompressedData.data(), 162 static_cast<int>(uncompressedData.size())); 163 ANGLE_TRY(result); 164 165 if (result == angle::Result::Continue) 166 return angle::Result::Continue; 167 168 // Cache load failed, evict 169 ANGLE_PERF_WARNING(context->getState().getDebug(), GL_DEBUG_SEVERITY_LOW, 170 "Failed to load program binary from cache."); 171 remove(*hashOut); 172 173 return angle::Result::Incomplete; 174 } 175 176 UNREACHABLE(); 177 return angle::Result::Incomplete; 178 } 179 180 bool MemoryProgramCache::getAt(size_t index, 181 const egl::BlobCache::Key **hashOut, 182 egl::BlobCache::Value *programOut) 183 { 184 return mBlobCache.getAt(index, hashOut, programOut); 185 } 186 187 void MemoryProgramCache::remove(const egl::BlobCache::Key &programHash) 188 { 189 mBlobCache.remove(programHash); 190 } 191 192 angle::Result MemoryProgramCache::putProgram(const egl::BlobCache::Key &programHash, 193 const Context *context, 194 const Program *program) 195 { 196 // If caching is effectively disabled, don't bother serializing the program. 197 if (!mBlobCache.isCachingEnabled()) 198 { 199 return angle::Result::Incomplete; 200 } 201 202 angle::MemoryBuffer serializedProgram; 203 ANGLE_TRY(program->serialize(context, &serializedProgram)); 204 205 angle::MemoryBuffer compressedData; 206 if (!egl::CompressBlobCacheData(serializedProgram.size(), serializedProgram.data(), 207 &compressedData)) 208 { 209 ANGLE_PERF_WARNING(context->getState().getDebug(), GL_DEBUG_SEVERITY_LOW, 210 "Error compressing binary data."); 211 return angle::Result::Incomplete; 212 } 213 214 { 215 std::scoped_lock<std::mutex> lock(mBlobCache.getMutex()); 216 // TODO: http://anglebug.com/7568 217 // This was a workaround for Chrome until it added support for EGL_ANDROID_blob_cache, 218 // tracked by http://anglebug.com/2516. This issue has since been closed, but removing this 219 // still causes a test failure. 220 auto *platform = ANGLEPlatformCurrent(); 221 platform->cacheProgram(platform, programHash, compressedData.size(), compressedData.data()); 222 } 223 224 mBlobCache.put(programHash, std::move(compressedData)); 225 return angle::Result::Continue; 226 } 227 228 angle::Result MemoryProgramCache::updateProgram(const Context *context, const Program *program) 229 { 230 egl::BlobCache::Key programHash; 231 ComputeHash(context, program, &programHash); 232 return putProgram(programHash, context, program); 233 } 234 235 bool MemoryProgramCache::putBinary(const egl::BlobCache::Key &programHash, 236 const uint8_t *binary, 237 size_t length) 238 { 239 // Copy the binary. 240 angle::MemoryBuffer newEntry; 241 if (!newEntry.resize(length)) 242 { 243 return false; 244 } 245 memcpy(newEntry.data(), binary, length); 246 247 // Store the binary. 248 mBlobCache.populate(programHash, std::move(newEntry)); 249 250 return true; 251 } 252 253 void MemoryProgramCache::clear() 254 { 255 mBlobCache.clear(); 256 } 257 258 void MemoryProgramCache::resize(size_t maxCacheSizeBytes) 259 { 260 mBlobCache.resize(maxCacheSizeBytes); 261 } 262 263 size_t MemoryProgramCache::entryCount() const 264 { 265 return mBlobCache.entryCount(); 266 } 267 268 size_t MemoryProgramCache::trim(size_t limit) 269 { 270 return mBlobCache.trim(limit); 271 } 272 273 size_t MemoryProgramCache::size() const 274 { 275 return mBlobCache.size(); 276 } 277 278 size_t MemoryProgramCache::maxSize() const 279 { 280 return mBlobCache.maxSize(); 281 } 282 283 } // namespace gl