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