• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2018 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 // BlobCache: Stores keyed blobs in memory to support EGL_ANDROID_blob_cache.
7 // Can be used in conjunction with the platform layer to warm up the cache from
8 // disk.  MemoryProgramCache uses this to handle caching of compiled programs.
9 
10 #include "libANGLE/BlobCache.h"
11 #include "common/utilities.h"
12 #include "libANGLE/Context.h"
13 #include "libANGLE/Display.h"
14 #include "libANGLE/histogram_macros.h"
15 #include "platform/PlatformMethods.h"
16 
17 namespace egl
18 {
BlobCache(size_t maxCacheSizeBytes)19 BlobCache::BlobCache(size_t maxCacheSizeBytes)
20     : mBlobCache(maxCacheSizeBytes), mSetBlobFunc(nullptr), mGetBlobFunc(nullptr)
21 {}
22 
~BlobCache()23 BlobCache::~BlobCache() {}
24 
put(const BlobCache::Key & key,angle::MemoryBuffer && value)25 void BlobCache::put(const BlobCache::Key &key, angle::MemoryBuffer &&value)
26 {
27     if (areBlobCacheFuncsSet())
28     {
29         std::scoped_lock<angle::SimpleMutex> lock(mBlobCacheMutex);
30         // Store the result in the application's cache
31         mSetBlobFunc(key.data(), key.size(), value.data(), value.size());
32     }
33     else
34     {
35         populate(key, std::move(value), CacheSource::Memory);
36     }
37 }
38 
compressAndPut(const BlobCache::Key & key,angle::MemoryBuffer && uncompressedValue,size_t * compressedSize)39 bool BlobCache::compressAndPut(const BlobCache::Key &key,
40                                angle::MemoryBuffer &&uncompressedValue,
41                                size_t *compressedSize)
42 {
43     angle::MemoryBuffer compressedValue;
44     if (!angle::CompressBlob(uncompressedValue.size(), uncompressedValue.data(), &compressedValue))
45     {
46         return false;
47     }
48     if (compressedSize != nullptr)
49         *compressedSize = compressedValue.size();
50     put(key, std::move(compressedValue));
51     return true;
52 }
53 
putApplication(const BlobCache::Key & key,const angle::MemoryBuffer & value)54 void BlobCache::putApplication(const BlobCache::Key &key, const angle::MemoryBuffer &value)
55 {
56     if (areBlobCacheFuncsSet())
57     {
58         std::scoped_lock<angle::SimpleMutex> lock(mBlobCacheMutex);
59         mSetBlobFunc(key.data(), key.size(), value.data(), value.size());
60     }
61 }
62 
populate(const BlobCache::Key & key,angle::MemoryBuffer && value,CacheSource source)63 void BlobCache::populate(const BlobCache::Key &key, angle::MemoryBuffer &&value, CacheSource source)
64 {
65     std::scoped_lock<angle::SimpleMutex> lock(mBlobCacheMutex);
66     CacheEntry newEntry;
67     newEntry.first  = std::move(value);
68     newEntry.second = source;
69 
70     // Cache it inside blob cache only if caching inside the application is not possible.
71     mBlobCache.put(key, std::move(newEntry), newEntry.first.size());
72 }
73 
get(angle::ScratchBuffer * scratchBuffer,const BlobCache::Key & key,BlobCache::Value * valueOut)74 bool BlobCache::get(angle::ScratchBuffer *scratchBuffer,
75                     const BlobCache::Key &key,
76                     BlobCache::Value *valueOut)
77 {
78     // Look into the application's cache, if there is such a cache
79     if (areBlobCacheFuncsSet())
80     {
81         std::scoped_lock<angle::SimpleMutex> lock(mBlobCacheMutex);
82         EGLsizeiANDROID valueSize = mGetBlobFunc(key.data(), key.size(), nullptr, 0);
83         if (valueSize <= 0)
84         {
85             return false;
86         }
87 
88         angle::MemoryBuffer *scratchMemory;
89         bool result = scratchBuffer->get(valueSize, &scratchMemory);
90         if (!result)
91         {
92             ERR() << "Failed to allocate memory for binary blob";
93             return false;
94         }
95 
96         EGLsizeiANDROID originalValueSize = valueSize;
97         valueSize = mGetBlobFunc(key.data(), key.size(), scratchMemory->data(), valueSize);
98 
99         // Make sure the key/value pair still exists/is unchanged after the second call
100         // (modifications to the application cache by another thread are a possibility)
101         if (valueSize != originalValueSize)
102         {
103             // This warning serves to find issues with the application cache, none of which are
104             // currently known to be thread-safe.  If such a use ever arises, this WARN can be
105             // removed.
106             WARN() << "Binary blob no longer available in cache (removed by a thread?)";
107             return false;
108         }
109 
110         *valueOut = BlobCache::Value(scratchMemory->data(), valueSize);
111         return true;
112     }
113 
114     std::scoped_lock<angle::SimpleMutex> lock(mBlobCacheMutex);
115     // Otherwise we are doing caching internally, so try to find it there
116     const CacheEntry *entry;
117     bool result = mBlobCache.get(key, &entry);
118 
119     if (result)
120     {
121         *valueOut = BlobCache::Value(entry->first.data(), entry->first.size());
122     }
123 
124     return result;
125 }
126 
getAt(size_t index,const BlobCache::Key ** keyOut,BlobCache::Value * valueOut)127 bool BlobCache::getAt(size_t index, const BlobCache::Key **keyOut, BlobCache::Value *valueOut)
128 {
129     std::scoped_lock<angle::SimpleMutex> lock(mBlobCacheMutex);
130     const CacheEntry *valueBuf;
131     bool result = mBlobCache.getAt(index, keyOut, &valueBuf);
132     if (result)
133     {
134         *valueOut = BlobCache::Value(valueBuf->first.data(), valueBuf->first.size());
135     }
136     return result;
137 }
138 
getAndDecompress(angle::ScratchBuffer * scratchBuffer,const BlobCache::Key & key,size_t maxUncompressedDataSize,angle::MemoryBuffer * uncompressedValueOut)139 BlobCache::GetAndDecompressResult BlobCache::getAndDecompress(
140     angle::ScratchBuffer *scratchBuffer,
141     const BlobCache::Key &key,
142     size_t maxUncompressedDataSize,
143     angle::MemoryBuffer *uncompressedValueOut)
144 {
145     ASSERT(uncompressedValueOut);
146 
147     Value compressedValue;
148     if (!get(scratchBuffer, key, &compressedValue))
149     {
150         return GetAndDecompressResult::NotFound;
151     }
152 
153     {
154         // This needs to be locked because `DecompressBlob` is reading shared memory from
155         // `compressedValue.data()`.
156         std::scoped_lock<angle::SimpleMutex> lock(mBlobCacheMutex);
157         if (!angle::DecompressBlob(compressedValue.data(), compressedValue.size(),
158                                    maxUncompressedDataSize, uncompressedValueOut))
159         {
160             return GetAndDecompressResult::DecompressFailure;
161         }
162     }
163 
164     return GetAndDecompressResult::Success;
165 }
166 
remove(const BlobCache::Key & key)167 void BlobCache::remove(const BlobCache::Key &key)
168 {
169     std::scoped_lock<angle::SimpleMutex> lock(mBlobCacheMutex);
170     mBlobCache.eraseByKey(key);
171 }
172 
setBlobCacheFuncs(EGLSetBlobFuncANDROID set,EGLGetBlobFuncANDROID get)173 void BlobCache::setBlobCacheFuncs(EGLSetBlobFuncANDROID set, EGLGetBlobFuncANDROID get)
174 {
175     std::scoped_lock<angle::SimpleMutex> lock(mBlobCacheMutex);
176     mSetBlobFunc = set;
177     mGetBlobFunc = get;
178 }
179 
areBlobCacheFuncsSet() const180 bool BlobCache::areBlobCacheFuncsSet() const
181 {
182     std::scoped_lock<angle::SimpleMutex> lock(mBlobCacheMutex);
183     // Either none or both of the callbacks should be set.
184     ASSERT((mSetBlobFunc != nullptr) == (mGetBlobFunc != nullptr));
185 
186     return mSetBlobFunc != nullptr && mGetBlobFunc != nullptr;
187 }
188 
189 }  // namespace egl
190