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