• 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/angle_version_info.h"
20 #include "common/utilities.h"
21 #include "libANGLE/BinaryStream.h"
22 #include "libANGLE/Context.h"
23 #include "libANGLE/Uniform.h"
24 #include "libANGLE/capture/FrameCapture.h"
25 #include "libANGLE/histogram_macros.h"
26 #include "libANGLE/renderer/ProgramImpl.h"
27 #include "platform/PlatformMethods.h"
28 
29 namespace gl
30 {
31 
32 namespace
33 {
34 constexpr unsigned int kWarningLimit = 3;
35 
36 class HashStream final : angle::NonCopyable
37 {
38   public:
str()39     std::string str() { return mStringStream.str(); }
40 
41     template <typename T>
operator <<(T value)42     HashStream &operator<<(T value)
43     {
44         mStringStream << value << kSeparator;
45         return *this;
46     }
47 
48   private:
49     static constexpr char kSeparator = ':';
50     std::ostringstream mStringStream;
51 };
52 
operator <<(HashStream & stream,Shader * shader)53 HashStream &operator<<(HashStream &stream, Shader *shader)
54 {
55     if (shader)
56     {
57         stream << shader->getSourceString().c_str() << shader->getSourceString().length()
58                << shader->getCompilerResourcesString().c_str();
59     }
60     return stream;
61 }
62 
operator <<(HashStream & stream,const ProgramBindings & bindings)63 HashStream &operator<<(HashStream &stream, const ProgramBindings &bindings)
64 {
65     for (const auto &binding : bindings.getStableIterationMap())
66     {
67         stream << binding.first << binding.second;
68     }
69     return stream;
70 }
71 
operator <<(HashStream & stream,const ProgramAliasedBindings & bindings)72 HashStream &operator<<(HashStream &stream, const ProgramAliasedBindings &bindings)
73 {
74     for (const auto &binding : bindings.getStableIterationMap())
75     {
76         stream << binding.first << binding.second.location;
77     }
78     return stream;
79 }
80 
operator <<(HashStream & stream,const std::vector<std::string> & strings)81 HashStream &operator<<(HashStream &stream, const std::vector<std::string> &strings)
82 {
83     for (const auto &str : strings)
84     {
85         stream << str;
86     }
87     return stream;
88 }
89 
operator <<(HashStream & stream,const std::vector<gl::VariableLocation> & locations)90 HashStream &operator<<(HashStream &stream, const std::vector<gl::VariableLocation> &locations)
91 {
92     for (const auto &loc : locations)
93     {
94         stream << loc.index << loc.arrayIndex << loc.ignored;
95     }
96     return stream;
97 }
98 
99 }  // anonymous namespace
100 
MemoryProgramCache(egl::BlobCache & blobCache)101 MemoryProgramCache::MemoryProgramCache(egl::BlobCache &blobCache)
102     : mBlobCache(blobCache), mIssuedWarnings(0)
103 {}
104 
~MemoryProgramCache()105 MemoryProgramCache::~MemoryProgramCache() {}
106 
ComputeHash(const Context * context,const Program * program,egl::BlobCache::Key * hashOut)107 void MemoryProgramCache::ComputeHash(const Context *context,
108                                      const Program *program,
109                                      egl::BlobCache::Key *hashOut)
110 {
111     // Compute the program hash. Start with the shader hashes and resource strings.
112     HashStream hashStream;
113     for (ShaderType shaderType : AllShaderTypes())
114     {
115         hashStream << program->getAttachedShader(shaderType);
116     }
117 
118     // Add some ANGLE metadata and Context properties, such as version and back-end.
119     hashStream << angle::GetANGLECommitHash() << context->getClientMajorVersion()
120                << context->getClientMinorVersion() << context->getString(GL_RENDERER);
121 
122     // Hash pre-link program properties.
123     hashStream << program->getAttributeBindings() << program->getUniformLocationBindings()
124                << program->getFragmentOutputLocations() << program->getFragmentOutputIndexes()
125                << program->getState().getTransformFeedbackVaryingNames()
126                << program->getState().getTransformFeedbackBufferMode()
127                << program->getState().getOutputLocations()
128                << program->getState().getSecondaryOutputLocations();
129 
130     // Include the status of FrameCapture, which adds source strings to the binary
131     hashStream << context->getShareGroup()->getFrameCaptureShared()->enabled();
132 
133     // Call the secure SHA hashing function.
134     const std::string &programKey = hashStream.str();
135     angle::base::SHA1HashBytes(reinterpret_cast<const unsigned char *>(programKey.c_str()),
136                                programKey.length(), hashOut->data());
137 }
138 
getProgram(const Context * context,Program * program,egl::BlobCache::Key * hashOut)139 angle::Result MemoryProgramCache::getProgram(const Context *context,
140                                              Program *program,
141                                              egl::BlobCache::Key *hashOut)
142 {
143     // If caching is effectively disabled, don't bother calculating the hash.
144     if (!mBlobCache.isCachingEnabled())
145     {
146         return angle::Result::Incomplete;
147     }
148 
149     ComputeHash(context, program, hashOut);
150     egl::BlobCache::Value binaryProgram;
151     size_t programSize = 0;
152     if (get(context, *hashOut, &binaryProgram, &programSize))
153     {
154         angle::MemoryBuffer uncompressedData;
155         if (!egl::DecompressBlobCacheData(binaryProgram.data(), programSize, &uncompressedData))
156         {
157             ERR() << "Error decompressing binary data.";
158             return angle::Result::Incomplete;
159         }
160 
161         angle::Result result =
162             program->loadBinary(context, GL_PROGRAM_BINARY_ANGLE, uncompressedData.data(),
163                                 static_cast<int>(uncompressedData.size()));
164         ANGLE_HISTOGRAM_BOOLEAN("GPU.ANGLE.ProgramCache.LoadBinarySuccess",
165                                 result == angle::Result::Continue);
166         ANGLE_TRY(result);
167 
168         if (result == angle::Result::Continue)
169             return angle::Result::Continue;
170 
171         // Cache load failed, evict.
172         if (mIssuedWarnings++ < kWarningLimit)
173         {
174             WARN() << "Failed to load binary from cache.";
175 
176             if (mIssuedWarnings == kWarningLimit)
177             {
178                 WARN() << "Reaching warning limit for cache load failures, silencing "
179                           "subsequent warnings.";
180             }
181         }
182         remove(*hashOut);
183     }
184     return angle::Result::Incomplete;
185 }
186 
get(const Context * context,const egl::BlobCache::Key & programHash,egl::BlobCache::Value * programOut,size_t * programSizeOut)187 bool MemoryProgramCache::get(const Context *context,
188                              const egl::BlobCache::Key &programHash,
189                              egl::BlobCache::Value *programOut,
190                              size_t *programSizeOut)
191 {
192     return mBlobCache.get(context->getScratchBuffer(), programHash, programOut, programSizeOut);
193 }
194 
getAt(size_t index,const egl::BlobCache::Key ** hashOut,egl::BlobCache::Value * programOut)195 bool MemoryProgramCache::getAt(size_t index,
196                                const egl::BlobCache::Key **hashOut,
197                                egl::BlobCache::Value *programOut)
198 {
199     return mBlobCache.getAt(index, hashOut, programOut);
200 }
201 
remove(const egl::BlobCache::Key & programHash)202 void MemoryProgramCache::remove(const egl::BlobCache::Key &programHash)
203 {
204     mBlobCache.remove(programHash);
205 }
206 
putProgram(const egl::BlobCache::Key & programHash,const Context * context,const Program * program)207 angle::Result MemoryProgramCache::putProgram(const egl::BlobCache::Key &programHash,
208                                              const Context *context,
209                                              const Program *program)
210 {
211     // If caching is effectively disabled, don't bother serializing the program.
212     if (!mBlobCache.isCachingEnabled())
213     {
214         return angle::Result::Incomplete;
215     }
216 
217     angle::MemoryBuffer serializedProgram;
218     ANGLE_TRY(program->serialize(context, &serializedProgram));
219 
220     angle::MemoryBuffer compressedData;
221     if (!egl::CompressBlobCacheData(serializedProgram.size(), serializedProgram.data(),
222                                     &compressedData))
223     {
224         ERR() << "Error compressing binary data.";
225         return angle::Result::Incomplete;
226     }
227 
228     ANGLE_HISTOGRAM_COUNTS("GPU.ANGLE.ProgramCache.ProgramBinarySizeBytes",
229                            static_cast<int>(compressedData.size()));
230 
231     // TODO(syoussefi): to be removed.  Compatibility for Chrome until it supports
232     // EGL_ANDROID_blob_cache. http://anglebug.com/2516
233     auto *platform = ANGLEPlatformCurrent();
234     platform->cacheProgram(platform, programHash, compressedData.size(), compressedData.data());
235 
236     mBlobCache.put(programHash, std::move(compressedData));
237     return angle::Result::Continue;
238 }
239 
updateProgram(const Context * context,const Program * program)240 angle::Result MemoryProgramCache::updateProgram(const Context *context, const Program *program)
241 {
242     egl::BlobCache::Key programHash;
243     ComputeHash(context, program, &programHash);
244     return putProgram(programHash, context, program);
245 }
246 
putBinary(const egl::BlobCache::Key & programHash,const uint8_t * binary,size_t length)247 bool MemoryProgramCache::putBinary(const egl::BlobCache::Key &programHash,
248                                    const uint8_t *binary,
249                                    size_t length)
250 {
251     // Copy the binary.
252     angle::MemoryBuffer newEntry;
253     if (!newEntry.resize(length))
254     {
255         return false;
256     }
257     memcpy(newEntry.data(), binary, length);
258 
259     // Store the binary.
260     mBlobCache.populate(programHash, std::move(newEntry));
261 
262     return true;
263 }
264 
clear()265 void MemoryProgramCache::clear()
266 {
267     mBlobCache.clear();
268     mIssuedWarnings = 0;
269 }
270 
resize(size_t maxCacheSizeBytes)271 void MemoryProgramCache::resize(size_t maxCacheSizeBytes)
272 {
273     mBlobCache.resize(maxCacheSizeBytes);
274 }
275 
entryCount() const276 size_t MemoryProgramCache::entryCount() const
277 {
278     return mBlobCache.entryCount();
279 }
280 
trim(size_t limit)281 size_t MemoryProgramCache::trim(size_t limit)
282 {
283     return mBlobCache.trim(limit);
284 }
285 
size() const286 size_t MemoryProgramCache::size() const
287 {
288     return mBlobCache.size();
289 }
290 
maxSize() const291 size_t MemoryProgramCache::maxSize() const
292 {
293     return mBlobCache.maxSize();
294 }
295 
296 }  // namespace gl
297