xref: /aosp_15_r20/external/angle/src/libANGLE/BlobCache.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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 gl::Context * context,const BlobCache::Key & key,angle::MemoryBuffer && value)25 void BlobCache::put(const gl::Context *context,
26                     const BlobCache::Key &key,
27                     angle::MemoryBuffer &&value)
28 {
29     if (areBlobCacheFuncsSet() || (context && context->areBlobCacheFuncsSet()))
30     {
31         putApplication(context, key, value);
32     }
33     else
34     {
35         populate(key, std::move(value), CacheSource::Memory);
36     }
37 }
38 
compressAndPut(const gl::Context * context,const BlobCache::Key & key,angle::MemoryBuffer && uncompressedValue,size_t * compressedSize)39 bool BlobCache::compressAndPut(const gl::Context *context,
40                                const BlobCache::Key &key,
41                                angle::MemoryBuffer &&uncompressedValue,
42                                size_t *compressedSize)
43 {
44     angle::MemoryBuffer compressedValue;
45     if (!angle::CompressBlob(uncompressedValue.size(), uncompressedValue.data(), &compressedValue))
46     {
47         return false;
48     }
49     if (compressedSize != nullptr)
50         *compressedSize = compressedValue.size();
51     put(context, key, std::move(compressedValue));
52     return true;
53 }
54 
putApplication(const gl::Context * context,const BlobCache::Key & key,const angle::MemoryBuffer & value)55 void BlobCache::putApplication(const gl::Context *context,
56                                const BlobCache::Key &key,
57                                const angle::MemoryBuffer &value)
58 {
59     if (context && context->areBlobCacheFuncsSet())
60     {
61         std::scoped_lock<angle::SimpleMutex> lock(mBlobCacheMutex);
62         const gl::BlobCacheCallbacks &contextCallbacks =
63             context->getState().getBlobCacheCallbacks();
64         contextCallbacks.setFunction(key.data(), key.size(), value.data(), value.size(),
65                                      contextCallbacks.userParam);
66     }
67     else if (areBlobCacheFuncsSet())
68     {
69         std::scoped_lock<angle::SimpleMutex> lock(mBlobCacheMutex);
70         mSetBlobFunc(key.data(), key.size(), value.data(), value.size());
71     }
72 }
73 
populate(const BlobCache::Key & key,angle::MemoryBuffer && value,CacheSource source)74 void BlobCache::populate(const BlobCache::Key &key, angle::MemoryBuffer &&value, CacheSource source)
75 {
76     std::scoped_lock<angle::SimpleMutex> lock(mBlobCacheMutex);
77     CacheEntry newEntry;
78     newEntry.first  = std::move(value);
79     newEntry.second = source;
80 
81     // Cache it inside blob cache only if caching inside the application is not possible.
82     mBlobCache.put(key, std::move(newEntry), newEntry.first.size());
83 }
84 
get(const gl::Context * context,angle::ScratchBuffer * scratchBuffer,const BlobCache::Key & key,BlobCache::Value * valueOut)85 bool BlobCache::get(const gl::Context *context,
86                     angle::ScratchBuffer *scratchBuffer,
87                     const BlobCache::Key &key,
88                     BlobCache::Value *valueOut)
89 {
90     // Look into the application's cache, if there is such a cache
91     if (areBlobCacheFuncsSet() || (context && context->areBlobCacheFuncsSet()))
92     {
93         std::scoped_lock<angle::SimpleMutex> lock(mBlobCacheMutex);
94         EGLsizeiANDROID valueSize =
95             callBlobGetCallback(context, key.data(), key.size(), nullptr, 0);
96         if (valueSize <= 0)
97         {
98             return false;
99         }
100 
101         angle::MemoryBuffer *scratchMemory;
102         bool result = scratchBuffer->get(valueSize, &scratchMemory);
103         if (!result)
104         {
105             ERR() << "Failed to allocate memory for binary blob";
106             return false;
107         }
108 
109         EGLsizeiANDROID originalValueSize = valueSize;
110         valueSize =
111             callBlobGetCallback(context, key.data(), key.size(), scratchMemory->data(), valueSize);
112 
113         // Make sure the key/value pair still exists/is unchanged after the second call
114         // (modifications to the application cache by another thread are a possibility)
115         if (valueSize != originalValueSize)
116         {
117             // This warning serves to find issues with the application cache, none of which are
118             // currently known to be thread-safe.  If such a use ever arises, this WARN can be
119             // removed.
120             WARN() << "Binary blob no longer available in cache (removed by a thread?)";
121             return false;
122         }
123 
124         *valueOut = BlobCache::Value(scratchMemory->data(), valueSize);
125         return true;
126     }
127 
128     std::scoped_lock<angle::SimpleMutex> lock(mBlobCacheMutex);
129     // Otherwise we are doing caching internally, so try to find it there
130     const CacheEntry *entry;
131     bool result = mBlobCache.get(key, &entry);
132 
133     if (result)
134     {
135         *valueOut = BlobCache::Value(entry->first.data(), entry->first.size());
136     }
137 
138     return result;
139 }
140 
getAt(size_t index,const BlobCache::Key ** keyOut,BlobCache::Value * valueOut)141 bool BlobCache::getAt(size_t index, const BlobCache::Key **keyOut, BlobCache::Value *valueOut)
142 {
143     std::scoped_lock<angle::SimpleMutex> lock(mBlobCacheMutex);
144     const CacheEntry *valueBuf;
145     bool result = mBlobCache.getAt(index, keyOut, &valueBuf);
146     if (result)
147     {
148         *valueOut = BlobCache::Value(valueBuf->first.data(), valueBuf->first.size());
149     }
150     return result;
151 }
152 
getAndDecompress(const gl::Context * context,angle::ScratchBuffer * scratchBuffer,const BlobCache::Key & key,size_t maxUncompressedDataSize,angle::MemoryBuffer * uncompressedValueOut)153 BlobCache::GetAndDecompressResult BlobCache::getAndDecompress(
154     const gl::Context *context,
155     angle::ScratchBuffer *scratchBuffer,
156     const BlobCache::Key &key,
157     size_t maxUncompressedDataSize,
158     angle::MemoryBuffer *uncompressedValueOut)
159 {
160     ASSERT(uncompressedValueOut);
161 
162     Value compressedValue;
163     if (!get(context, scratchBuffer, key, &compressedValue))
164     {
165         return GetAndDecompressResult::NotFound;
166     }
167 
168     {
169         // This needs to be locked because `DecompressBlob` is reading shared memory from
170         // `compressedValue.data()`.
171         std::scoped_lock<angle::SimpleMutex> lock(mBlobCacheMutex);
172         if (!angle::DecompressBlob(compressedValue.data(), compressedValue.size(),
173                                    maxUncompressedDataSize, uncompressedValueOut))
174         {
175             return GetAndDecompressResult::DecompressFailure;
176         }
177     }
178 
179     return GetAndDecompressResult::Success;
180 }
181 
remove(const BlobCache::Key & key)182 void BlobCache::remove(const BlobCache::Key &key)
183 {
184     std::scoped_lock<angle::SimpleMutex> lock(mBlobCacheMutex);
185     mBlobCache.eraseByKey(key);
186 }
187 
setBlobCacheFuncs(EGLSetBlobFuncANDROID set,EGLGetBlobFuncANDROID get)188 void BlobCache::setBlobCacheFuncs(EGLSetBlobFuncANDROID set, EGLGetBlobFuncANDROID get)
189 {
190     std::scoped_lock<angle::SimpleMutex> lock(mBlobCacheMutex);
191     mSetBlobFunc = set;
192     mGetBlobFunc = get;
193 }
194 
areBlobCacheFuncsSet() const195 bool BlobCache::areBlobCacheFuncsSet() const
196 {
197     std::scoped_lock<angle::SimpleMutex> lock(mBlobCacheMutex);
198     // Either none or both of the callbacks should be set.
199     ASSERT((mSetBlobFunc != nullptr) == (mGetBlobFunc != nullptr));
200 
201     return mSetBlobFunc != nullptr && mGetBlobFunc != nullptr;
202 }
203 
isCachingEnabled(const gl::Context * context) const204 bool BlobCache::isCachingEnabled(const gl::Context *context) const
205 {
206     return areBlobCacheFuncsSet() || (context && context->areBlobCacheFuncsSet()) || maxSize() > 0;
207 }
208 
callBlobGetCallback(const gl::Context * context,const void * key,size_t keySize,void * value,size_t valueSize)209 size_t BlobCache::callBlobGetCallback(const gl::Context *context,
210                                       const void *key,
211                                       size_t keySize,
212                                       void *value,
213                                       size_t valueSize)
214 {
215     if (context && context->areBlobCacheFuncsSet())
216     {
217         const gl::BlobCacheCallbacks &contextCallbacks =
218             context->getState().getBlobCacheCallbacks();
219         return contextCallbacks.getFunction(key, keySize, value, valueSize,
220                                             contextCallbacks.userParam);
221     }
222     else
223     {
224         ASSERT(mGetBlobFunc);
225         return mGetBlobFunc(key, keySize, value, valueSize);
226     }
227 }
228 
229 }  // namespace egl
230