• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
WriteProgramBindings(BinaryOutputStream * stream,const ProgramBindings & bindings)36 void WriteProgramBindings(BinaryOutputStream *stream, const ProgramBindings &bindings)
37 {
38     for (const auto &binding : bindings.getStableIterationMap())
39     {
40         stream->writeString(binding.first);
41         stream->writeInt(binding.second);
42     }
43 }
44 
WriteProgramAliasedBindings(BinaryOutputStream * stream,const ProgramAliasedBindings & bindings)45 void WriteProgramAliasedBindings(BinaryOutputStream *stream, const ProgramAliasedBindings &bindings)
46 {
47     for (const auto &binding : bindings.getStableIterationMap())
48     {
49         stream->writeString(binding.first);
50         stream->writeInt(binding.second.location);
51     }
52 }
53 
WriteVariableLocations(BinaryOutputStream * stream,const std::vector<gl::VariableLocation> & locations)54 void WriteVariableLocations(BinaryOutputStream *stream,
55                             const std::vector<gl::VariableLocation> &locations)
56 {
57     for (const auto &loc : locations)
58     {
59         stream->writeInt(loc.index);
60         stream->writeInt(loc.arrayIndex);
61         stream->writeBool(loc.ignored);
62     }
63 }
64 
65 }  // anonymous namespace
66 
MemoryProgramCache(egl::BlobCache & blobCache)67 MemoryProgramCache::MemoryProgramCache(egl::BlobCache &blobCache) : mBlobCache(blobCache) {}
68 
~MemoryProgramCache()69 MemoryProgramCache::~MemoryProgramCache() {}
70 
ComputeHash(const Context * context,const Program * program,egl::BlobCache::Key * hashOut)71 void MemoryProgramCache::ComputeHash(const Context *context,
72                                      const Program *program,
73                                      egl::BlobCache::Key *hashOut)
74 {
75     // Compute the program hash. Start with the shader hashes.
76     BinaryOutputStream hashStream;
77     for (ShaderType shaderType : AllShaderTypes())
78     {
79         Shader *shader = program->getAttachedShader(shaderType);
80         if (shader)
81         {
82             shader->writeShaderKey(&hashStream);
83         }
84     }
85 
86     // Add some ANGLE metadata and Context properties, such as version and back-end.
87     hashStream.writeString(angle::GetANGLEShaderProgramVersion());
88     hashStream.writeInt(angle::GetANGLESHVersion());
89     hashStream.writeInt(context->getClientMajorVersion());
90     hashStream.writeInt(context->getClientMinorVersion());
91     hashStream.writeString(reinterpret_cast<const char *>(context->getString(GL_RENDERER)));
92 
93     // Hash pre-link program properties.
94     WriteProgramBindings(&hashStream, program->getAttributeBindings());
95     WriteProgramAliasedBindings(&hashStream, program->getUniformLocationBindings());
96     WriteProgramAliasedBindings(&hashStream, program->getFragmentOutputLocations());
97     WriteProgramAliasedBindings(&hashStream, program->getFragmentOutputIndexes());
98     for (const std::string &transformFeedbackVaryingName :
99          program->getState().getTransformFeedbackVaryingNames())
100     {
101         hashStream.writeString(transformFeedbackVaryingName);
102     }
103     hashStream.writeInt(program->getState().getTransformFeedbackBufferMode());
104     WriteVariableLocations(&hashStream, program->getState().getOutputLocations());
105     WriteVariableLocations(&hashStream, program->getState().getSecondaryOutputLocations());
106 
107     // Include the status of FrameCapture, which adds source strings to the binary
108     hashStream.writeBool(context->getShareGroup()->getFrameCaptureShared()->enabled());
109 
110     // Call the secure SHA hashing function.
111     const std::vector<uint8_t> &programKey = hashStream.getData();
112     angle::base::SHA1HashBytes(programKey.data(), programKey.size(), hashOut->data());
113 }
114 
getProgram(const Context * context,Program * program,egl::BlobCache::Key * hashOut)115 angle::Result MemoryProgramCache::getProgram(const Context *context,
116                                              Program *program,
117                                              egl::BlobCache::Key *hashOut)
118 {
119     // If caching is effectively disabled, don't bother calculating the hash.
120     if (!mBlobCache.isCachingEnabled())
121     {
122         return angle::Result::Incomplete;
123     }
124 
125     ComputeHash(context, program, hashOut);
126 
127     angle::MemoryBuffer uncompressedData;
128     switch (mBlobCache.getAndDecompress(context->getScratchBuffer(), *hashOut, &uncompressedData))
129     {
130         case egl::BlobCache::GetAndDecompressResult::NotFound:
131             return angle::Result::Incomplete;
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             return angle::Result::Incomplete;
137 
138         case egl::BlobCache::GetAndDecompressResult::GetSuccess:
139             angle::Result result =
140                 program->loadBinary(context, GL_PROGRAM_BINARY_ANGLE, uncompressedData.data(),
141                                     static_cast<int>(uncompressedData.size()));
142             ANGLE_TRY(result);
143 
144             if (result == angle::Result::Continue)
145                 return angle::Result::Continue;
146 
147             // Cache load failed, evict
148             ANGLE_PERF_WARNING(context->getState().getDebug(), GL_DEBUG_SEVERITY_LOW,
149                                "Failed to load program binary from cache.");
150             remove(*hashOut);
151 
152             return angle::Result::Incomplete;
153     }
154 
155     UNREACHABLE();
156     return angle::Result::Incomplete;
157 }
158 
getAt(size_t index,const egl::BlobCache::Key ** hashOut,egl::BlobCache::Value * programOut)159 bool MemoryProgramCache::getAt(size_t index,
160                                const egl::BlobCache::Key **hashOut,
161                                egl::BlobCache::Value *programOut)
162 {
163     return mBlobCache.getAt(index, hashOut, programOut);
164 }
165 
remove(const egl::BlobCache::Key & programHash)166 void MemoryProgramCache::remove(const egl::BlobCache::Key &programHash)
167 {
168     mBlobCache.remove(programHash);
169 }
170 
putProgram(const egl::BlobCache::Key & programHash,const Context * context,const Program * program)171 angle::Result MemoryProgramCache::putProgram(const egl::BlobCache::Key &programHash,
172                                              const Context *context,
173                                              const Program *program)
174 {
175     // If caching is effectively disabled, don't bother serializing the program.
176     if (!mBlobCache.isCachingEnabled())
177     {
178         return angle::Result::Incomplete;
179     }
180 
181     angle::MemoryBuffer serializedProgram;
182     ANGLE_TRY(program->serialize(context, &serializedProgram));
183 
184     angle::MemoryBuffer compressedData;
185     if (!egl::CompressBlobCacheData(serializedProgram.size(), serializedProgram.data(),
186                                     &compressedData))
187     {
188         ANGLE_PERF_WARNING(context->getState().getDebug(), GL_DEBUG_SEVERITY_LOW,
189                            "Error compressing binary data.");
190         return angle::Result::Incomplete;
191     }
192 
193     {
194         std::scoped_lock<std::mutex> lock(mBlobCache.getMutex());
195         // TODO: http://anglebug.com/7568
196         // This was a workaround for Chrome until it added support for EGL_ANDROID_blob_cache,
197         // tracked by http://anglebug.com/2516. This issue has since been closed, but removing this
198         // still causes a test failure.
199         auto *platform = ANGLEPlatformCurrent();
200         platform->cacheProgram(platform, programHash, compressedData.size(), compressedData.data());
201     }
202 
203     mBlobCache.put(programHash, std::move(compressedData));
204     return angle::Result::Continue;
205 }
206 
updateProgram(const Context * context,const Program * program)207 angle::Result MemoryProgramCache::updateProgram(const Context *context, const Program *program)
208 {
209     egl::BlobCache::Key programHash;
210     ComputeHash(context, program, &programHash);
211     return putProgram(programHash, context, program);
212 }
213 
putBinary(const egl::BlobCache::Key & programHash,const uint8_t * binary,size_t length)214 bool MemoryProgramCache::putBinary(const egl::BlobCache::Key &programHash,
215                                    const uint8_t *binary,
216                                    size_t length)
217 {
218     // Copy the binary.
219     angle::MemoryBuffer newEntry;
220     if (!newEntry.resize(length))
221     {
222         return false;
223     }
224     memcpy(newEntry.data(), binary, length);
225 
226     // Store the binary.
227     mBlobCache.populate(programHash, std::move(newEntry));
228 
229     return true;
230 }
231 
clear()232 void MemoryProgramCache::clear()
233 {
234     mBlobCache.clear();
235 }
236 
resize(size_t maxCacheSizeBytes)237 void MemoryProgramCache::resize(size_t maxCacheSizeBytes)
238 {
239     mBlobCache.resize(maxCacheSizeBytes);
240 }
241 
entryCount() const242 size_t MemoryProgramCache::entryCount() const
243 {
244     return mBlobCache.entryCount();
245 }
246 
trim(size_t limit)247 size_t MemoryProgramCache::trim(size_t limit)
248 {
249     return mBlobCache.trim(limit);
250 }
251 
size() const252 size_t MemoryProgramCache::size() const
253 {
254     return mBlobCache.size();
255 }
256 
maxSize() const257 size_t MemoryProgramCache::maxSize() const
258 {
259     return mBlobCache.maxSize();
260 }
261 
262 }  // namespace gl
263