// // Copyright 2018 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. // // BlobCache: 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. #ifndef LIBANGLE_BLOB_CACHE_H_ #define LIBANGLE_BLOB_CACHE_H_ #include #include #include #include "common/MemoryBuffer.h" #include "common/hash_utils.h" #include "libANGLE/Error.h" #include "libANGLE/SizedMRUCache.h" namespace gl { class Context; } // namespace gl namespace egl { // 160-bit SHA-1 hash key used for hasing a program. BlobCache opts in using fixed keys for // simplicity and efficiency. static constexpr size_t kBlobCacheKeyLength = angle::base::kSHA1Length; using BlobCacheKey = std::array; } // namespace egl namespace std { template <> struct hash { // Simple routine to hash four ints. size_t operator()(const egl::BlobCacheKey &key) const { return angle::ComputeGenericHash(key.data(), key.size()); } }; } // namespace std namespace egl { bool CompressBlobCacheData(const size_t cacheSize, const uint8_t *cacheData, angle::MemoryBuffer *compressedData); bool DecompressBlobCacheData(const uint8_t *compressedData, const size_t compressedSize, angle::MemoryBuffer *uncompressedData); class BlobCache final : angle::NonCopyable { public: // 160-bit SHA-1 hash key used for hasing a program. BlobCache opts in using fixed keys for // simplicity and efficiency. static constexpr size_t kKeyLength = kBlobCacheKeyLength; using Key = BlobCacheKey; class Value { public: Value() : mPtr(nullptr), mSize(0) {} Value(const uint8_t *ptr, size_t sz) : mPtr(ptr), mSize(sz) {} // A very basic struct to hold the pointer and size together. The objects of this class // don't own the memory. const uint8_t *data() { return mPtr; } size_t size() { return mSize; } const uint8_t &operator[](size_t pos) const { ASSERT(pos < mSize); return mPtr[pos]; } private: const uint8_t *mPtr; size_t mSize; }; enum class CacheSource { Memory, Disk, }; explicit BlobCache(size_t maxCacheSizeBytes); ~BlobCache(); // Store a key-blob pair in the cache. If application callbacks are set, the application cache // will be used. Otherwise the value is cached in this object. void put(const BlobCache::Key &key, angle::MemoryBuffer &&value); // Store a key-blob pair in the cache, but compress the blob before insertion. Returns false if // compression fails, returns true otherwise. bool compressAndPut(const BlobCache::Key &key, angle::MemoryBuffer &&uncompressedValue, size_t *compressedSize); // Store a key-blob pair in the application cache, only if application callbacks are set. void putApplication(const BlobCache::Key &key, const angle::MemoryBuffer &value); // Store a key-blob pair in the cache without making callbacks to the application. This is used // to repopulate this object's cache on startup without generating callback calls. void populate(const BlobCache::Key &key, angle::MemoryBuffer &&value, CacheSource source = CacheSource::Disk); // Check if the cache contains the blob corresponding to this key. If application callbacks are // set, those will be used. Otherwise they key is looked up in this object's cache. [[nodiscard]] bool get(angle::ScratchBuffer *scratchBuffer, const BlobCache::Key &key, BlobCache::Value *valueOut, size_t *bufferSizeOut); // For querying the contents of the cache. [[nodiscard]] bool getAt(size_t index, const BlobCache::Key **keyOut, BlobCache::Value *valueOut); enum class GetAndDecompressResult { GetSuccess, NotFound, DecompressFailure, }; [[nodiscard]] GetAndDecompressResult getAndDecompress( angle::ScratchBuffer *scratchBuffer, const BlobCache::Key &key, angle::MemoryBuffer *uncompressedValueOut); // Evict a blob from the binary cache. void remove(const BlobCache::Key &key); // Empty the cache. void clear() { mBlobCache.clear(); } // Resize the cache. Discards current contents. void resize(size_t maxCacheSizeBytes) { mBlobCache.resize(maxCacheSizeBytes); } // Returns the number of entries in the cache. size_t entryCount() const { return mBlobCache.entryCount(); } // Reduces the current cache size and returns the number of bytes freed. size_t trim(size_t limit) { return mBlobCache.shrinkToSize(limit); } // Returns the current cache size in bytes. size_t size() const { return mBlobCache.size(); } // Returns whether the cache is empty bool empty() const { return mBlobCache.empty(); } // Returns the maximum cache size in bytes. size_t maxSize() const { return mBlobCache.maxSize(); } void setBlobCacheFuncs(EGLSetBlobFuncANDROID set, EGLGetBlobFuncANDROID get); bool areBlobCacheFuncsSet() const; bool isCachingEnabled() const { return areBlobCacheFuncsSet() || maxSize() > 0; } std::mutex &getMutex() { return mBlobCacheMutex; } private: // This internal cache is used only if the application is not providing caching callbacks using CacheEntry = std::pair; mutable std::mutex mBlobCacheMutex; angle::SizedMRUCache mBlobCache; EGLSetBlobFuncANDROID mSetBlobFunc; EGLGetBlobFuncANDROID mGetBlobFunc; }; } // namespace egl #endif // LIBANGLE_MEMORY_PROGRAM_CACHE_H_