xref: /aosp_15_r20/external/angle/src/libANGLE/MemoryProgramCache.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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/BinaryStream.h"
20 #include "common/angle_version_info.h"
21 #include "common/utilities.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 
36 // Limit decompressed programs to 10MB. If they're larger then this there is a good chance the data
37 // is not what we expect. This limits the amount of memory we will allocate based on a binary blob
38 // we believe is compressed data.
39 static constexpr size_t kMaxUncompressedProgramSize = 10 * 1024 * 1024;
40 
WriteProgramBindings(BinaryOutputStream * stream,const ProgramBindings & bindings)41 void WriteProgramBindings(BinaryOutputStream *stream, const ProgramBindings &bindings)
42 {
43     for (const auto &binding : bindings.getStableIterationMap())
44     {
45         stream->writeString(binding.first);
46         stream->writeInt(binding.second);
47     }
48 }
49 
WriteProgramAliasedBindings(BinaryOutputStream * stream,const ProgramAliasedBindings & bindings)50 void WriteProgramAliasedBindings(BinaryOutputStream *stream, const ProgramAliasedBindings &bindings)
51 {
52     for (const auto &binding : bindings.getStableIterationMap())
53     {
54         stream->writeString(binding.first);
55         stream->writeInt(binding.second.location);
56     }
57 }
58 
59 }  // anonymous namespace
60 
MemoryProgramCache(egl::BlobCache & blobCache)61 MemoryProgramCache::MemoryProgramCache(egl::BlobCache &blobCache) : mBlobCache(blobCache) {}
62 
~MemoryProgramCache()63 MemoryProgramCache::~MemoryProgramCache() {}
64 
ComputeHash(const Context * context,const Program * program,egl::BlobCache::Key * hashOut)65 void MemoryProgramCache::ComputeHash(const Context *context,
66                                      const Program *program,
67                                      egl::BlobCache::Key *hashOut)
68 {
69     // Compute the program hash. Start with the shader hashes.
70     BinaryOutputStream hashStream;
71     ShaderBitSet shaders;
72     for (ShaderType shaderType : AllShaderTypes())
73     {
74         Shader *shader = program->getAttachedShader(shaderType);
75         if (shader)
76         {
77             shaders.set(shaderType);
78             shader->writeShaderKey(&hashStream);
79         }
80     }
81 
82     hashStream.writeInt(shaders.bits());
83 
84     // Add some ANGLE metadata and Context properties, such as version and back-end.
85     hashStream.writeString(angle::GetANGLEShaderProgramVersion());
86     hashStream.writeInt(angle::GetANGLESHVersion());
87     hashStream.writeInt(context->getClientMajorVersion());
88     hashStream.writeInt(context->getClientMinorVersion());
89     hashStream.writeString(reinterpret_cast<const char *>(context->getString(GL_RENDERER)));
90 
91     // Hash pre-link program properties.
92     WriteProgramBindings(&hashStream, program->getAttributeBindings());
93     WriteProgramAliasedBindings(&hashStream, program->getUniformLocationBindings());
94     WriteProgramAliasedBindings(&hashStream, program->getFragmentOutputLocations());
95     WriteProgramAliasedBindings(&hashStream, program->getFragmentOutputIndexes());
96     for (const std::string &transformFeedbackVaryingName :
97          program->getState().getTransformFeedbackVaryingNames())
98     {
99         hashStream.writeString(transformFeedbackVaryingName);
100     }
101     hashStream.writeInt(program->getTransformFeedbackBufferMode());
102 
103     // Include the status of FrameCapture, which adds source strings to the binary
104     hashStream.writeBool(context->getShareGroup()->getFrameCaptureShared()->enabled());
105 
106     // Call the secure SHA hashing function.
107     const std::vector<uint8_t> &programKey = hashStream.getData();
108     angle::base::SHA1HashBytes(programKey.data(), programKey.size(), hashOut->data());
109 }
110 
getProgram(const Context * context,Program * program,egl::BlobCache::Key * hashOut,egl::CacheGetResult * resultOut)111 angle::Result MemoryProgramCache::getProgram(const Context *context,
112                                              Program *program,
113                                              egl::BlobCache::Key *hashOut,
114                                              egl::CacheGetResult *resultOut)
115 {
116     *resultOut = egl::CacheGetResult::NotFound;
117 
118     // If caching is effectively disabled, don't bother calculating the hash.
119     if (!mBlobCache.isCachingEnabled(context))
120     {
121         return angle::Result::Continue;
122     }
123 
124     ComputeHash(context, program, hashOut);
125 
126     angle::MemoryBuffer uncompressedData;
127     switch (mBlobCache.getAndDecompress(context, context->getScratchBuffer(), *hashOut,
128                                         kMaxUncompressedProgramSize, &uncompressedData))
129     {
130         case egl::BlobCache::GetAndDecompressResult::NotFound:
131             return angle::Result::Continue;
132 
133         case egl::BlobCache::GetAndDecompressResult::DecompressFailure:
134             ANGLE_PERF_WARNING(context->getState().getDebug(), GL_DEBUG_SEVERITY_LOW,
135                                "Error decompressing program binary data fetched from cache.");
136             remove(*hashOut);
137             // Consider this blob "not found".  As far as the rest of the code is considered,
138             // corrupted cache might as well not have existed.
139             return angle::Result::Continue;
140 
141         case egl::BlobCache::GetAndDecompressResult::Success:
142             ANGLE_TRY(program->loadBinary(context, uncompressedData.data(),
143                                           static_cast<int>(uncompressedData.size()), resultOut));
144 
145             // Result is either Success or Rejected
146             ASSERT(*resultOut != egl::CacheGetResult::NotFound);
147 
148             // If cache load failed, evict the entry
149             if (*resultOut == egl::CacheGetResult::Rejected)
150             {
151                 ANGLE_PERF_WARNING(context->getState().getDebug(), GL_DEBUG_SEVERITY_LOW,
152                                    "Failed to load program binary from cache.");
153                 remove(*hashOut);
154             }
155 
156             return angle::Result::Continue;
157     }
158 
159     UNREACHABLE();
160     return angle::Result::Continue;
161 }
162 
getAt(size_t index,const egl::BlobCache::Key ** hashOut,egl::BlobCache::Value * programOut)163 bool MemoryProgramCache::getAt(size_t index,
164                                const egl::BlobCache::Key **hashOut,
165                                egl::BlobCache::Value *programOut)
166 {
167     return mBlobCache.getAt(index, hashOut, programOut);
168 }
169 
remove(const egl::BlobCache::Key & programHash)170 void MemoryProgramCache::remove(const egl::BlobCache::Key &programHash)
171 {
172     mBlobCache.remove(programHash);
173 }
174 
putProgram(const egl::BlobCache::Key & programHash,const Context * context,Program * program)175 angle::Result MemoryProgramCache::putProgram(const egl::BlobCache::Key &programHash,
176                                              const Context *context,
177                                              Program *program)
178 {
179     // If caching is effectively disabled, don't bother serializing the program.
180     if (!mBlobCache.isCachingEnabled(context))
181     {
182         return angle::Result::Continue;
183     }
184 
185     ANGLE_TRY(program->serialize(context));
186     const angle::MemoryBuffer &serializedProgram = program->getSerializedBinary();
187 
188     angle::MemoryBuffer compressedData;
189     if (!angle::CompressBlob(serializedProgram.size(), serializedProgram.data(), &compressedData))
190     {
191         ANGLE_PERF_WARNING(context->getState().getDebug(), GL_DEBUG_SEVERITY_LOW,
192                            "Error compressing binary data.");
193         return angle::Result::Continue;
194     }
195 
196     {
197         std::scoped_lock<angle::SimpleMutex> lock(mBlobCache.getMutex());
198         // TODO: http://anglebug.com/42266037
199         // This was a workaround for Chrome until it added support for EGL_ANDROID_blob_cache,
200         // tracked by http://anglebug.com/42261225. This issue has since been closed, but removing
201         // this still causes a test failure.
202         auto *platform = ANGLEPlatformCurrent();
203         platform->cacheProgram(platform, programHash, compressedData.size(), compressedData.data());
204     }
205 
206     mBlobCache.put(context, programHash, std::move(compressedData));
207     return angle::Result::Continue;
208 }
209 
updateProgram(const Context * context,Program * program)210 angle::Result MemoryProgramCache::updateProgram(const Context *context, Program *program)
211 {
212     egl::BlobCache::Key programHash;
213     ComputeHash(context, program, &programHash);
214     return putProgram(programHash, context, program);
215 }
216 
putBinary(const egl::BlobCache::Key & programHash,const uint8_t * binary,size_t length)217 bool MemoryProgramCache::putBinary(const egl::BlobCache::Key &programHash,
218                                    const uint8_t *binary,
219                                    size_t length)
220 {
221     // Copy the binary.
222     angle::MemoryBuffer newEntry;
223     if (!newEntry.resize(length))
224     {
225         return false;
226     }
227     memcpy(newEntry.data(), binary, length);
228 
229     // Store the binary.
230     mBlobCache.populate(programHash, std::move(newEntry));
231 
232     return true;
233 }
234 
clear()235 void MemoryProgramCache::clear()
236 {
237     mBlobCache.clear();
238 }
239 
resize(size_t maxCacheSizeBytes)240 void MemoryProgramCache::resize(size_t maxCacheSizeBytes)
241 {
242     mBlobCache.resize(maxCacheSizeBytes);
243 }
244 
entryCount() const245 size_t MemoryProgramCache::entryCount() const
246 {
247     return mBlobCache.entryCount();
248 }
249 
trim(size_t limit)250 size_t MemoryProgramCache::trim(size_t limit)
251 {
252     return mBlobCache.trim(limit);
253 }
254 
size() const255 size_t MemoryProgramCache::size() const
256 {
257     return mBlobCache.size();
258 }
259 
maxSize() const260 size_t MemoryProgramCache::maxSize() const
261 {
262     return mBlobCache.maxSize();
263 }
264 
265 }  // namespace gl
266