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/Platform.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.location;
63 }
64 return stream;
65 }
66
operator <<(HashStream & stream,const std::vector<std::string> & strings)67 HashStream &operator<<(HashStream &stream, const std::vector<std::string> &strings)
68 {
69 for (const auto &str : strings)
70 {
71 stream << str;
72 }
73 return stream;
74 }
75
operator <<(HashStream & stream,const std::vector<gl::VariableLocation> & locations)76 HashStream &operator<<(HashStream &stream, const std::vector<gl::VariableLocation> &locations)
77 {
78 for (const auto &loc : locations)
79 {
80 stream << loc.index << loc.arrayIndex << loc.ignored;
81 }
82 return stream;
83 }
84
85 } // anonymous namespace
86
MemoryProgramCache(egl::BlobCache & blobCache)87 MemoryProgramCache::MemoryProgramCache(egl::BlobCache &blobCache)
88 : mBlobCache(blobCache), mIssuedWarnings(0)
89 {}
90
~MemoryProgramCache()91 MemoryProgramCache::~MemoryProgramCache() {}
92
ComputeHash(const Context * context,const Program * program,egl::BlobCache::Key * hashOut)93 void MemoryProgramCache::ComputeHash(const Context *context,
94 const Program *program,
95 egl::BlobCache::Key *hashOut)
96 {
97 // Compute the program hash. Start with the shader hashes and resource strings.
98 HashStream hashStream;
99 for (ShaderType shaderType : AllShaderTypes())
100 {
101 hashStream << program->getAttachedShader(shaderType);
102 }
103
104 // Add some ANGLE metadata and Context properties, such as version and back-end.
105 hashStream << ANGLE_COMMIT_HASH << context->getClientMajorVersion()
106 << context->getClientMinorVersion() << context->getString(GL_RENDERER);
107
108 // Hash pre-link program properties.
109 hashStream << program->getAttributeBindings() << program->getUniformLocationBindings()
110 << program->getFragmentInputBindings()
111 << program->getState().getTransformFeedbackVaryingNames()
112 << program->getState().getTransformFeedbackBufferMode()
113 << program->getState().getOutputLocations()
114 << program->getState().getSecondaryOutputLocations();
115
116 // Call the secure SHA hashing function.
117 const std::string &programKey = hashStream.str();
118 angle::base::SHA1HashBytes(reinterpret_cast<const unsigned char *>(programKey.c_str()),
119 programKey.length(), hashOut->data());
120 }
121
getProgram(const Context * context,Program * program,egl::BlobCache::Key * hashOut)122 angle::Result MemoryProgramCache::getProgram(const Context *context,
123 Program *program,
124 egl::BlobCache::Key *hashOut)
125 {
126 // If caching is effectively disabled, don't bother calculating the hash.
127 if (!mBlobCache.isCachingEnabled())
128 {
129 return angle::Result::Incomplete;
130 }
131
132 ComputeHash(context, program, hashOut);
133 egl::BlobCache::Value binaryProgram;
134 if (get(context, *hashOut, &binaryProgram))
135 {
136 angle::Result result =
137 program->loadBinary(context, GL_PROGRAM_BINARY_ANGLE, binaryProgram.data(),
138 static_cast<int>(binaryProgram.size()));
139 ANGLE_HISTOGRAM_BOOLEAN("GPU.ANGLE.ProgramCache.LoadBinarySuccess",
140 result == angle::Result::Continue);
141 ANGLE_TRY(result);
142
143 if (result == angle::Result::Continue)
144 return angle::Result::Continue;
145
146 // Cache load failed, evict.
147 if (mIssuedWarnings++ < kWarningLimit)
148 {
149 WARN() << "Failed to load binary from cache.";
150
151 if (mIssuedWarnings == kWarningLimit)
152 {
153 WARN() << "Reaching warning limit for cache load failures, silencing "
154 "subsequent warnings.";
155 }
156 }
157 remove(*hashOut);
158 }
159 return angle::Result::Incomplete;
160 }
161
get(const Context * context,const egl::BlobCache::Key & programHash,egl::BlobCache::Value * programOut)162 bool MemoryProgramCache::get(const Context *context,
163 const egl::BlobCache::Key &programHash,
164 egl::BlobCache::Value *programOut)
165 {
166 return mBlobCache.get(context->getScratchBuffer(), programHash, programOut);
167 }
168
getAt(size_t index,const egl::BlobCache::Key ** hashOut,egl::BlobCache::Value * programOut)169 bool MemoryProgramCache::getAt(size_t index,
170 const egl::BlobCache::Key **hashOut,
171 egl::BlobCache::Value *programOut)
172 {
173 return mBlobCache.getAt(index, hashOut, programOut);
174 }
175
remove(const egl::BlobCache::Key & programHash)176 void MemoryProgramCache::remove(const egl::BlobCache::Key &programHash)
177 {
178 mBlobCache.remove(programHash);
179 }
180
putProgram(const egl::BlobCache::Key & programHash,const Context * context,const Program * program)181 void MemoryProgramCache::putProgram(const egl::BlobCache::Key &programHash,
182 const Context *context,
183 const Program *program)
184 {
185 // If caching is effectively disabled, don't bother serializing the program.
186 if (!mBlobCache.isCachingEnabled())
187 {
188 return;
189 }
190
191 angle::MemoryBuffer serializedProgram;
192 program->serialize(context, &serializedProgram);
193
194 ANGLE_HISTOGRAM_COUNTS("GPU.ANGLE.ProgramCache.ProgramBinarySizeBytes",
195 static_cast<int>(serializedProgram.size()));
196
197 // TODO(syoussefi): to be removed. Compatibility for Chrome until it supports
198 // EGL_ANDROID_blob_cache. http://anglebug.com/2516
199 auto *platform = ANGLEPlatformCurrent();
200 platform->cacheProgram(platform, programHash, serializedProgram.size(),
201 serializedProgram.data());
202
203 mBlobCache.put(programHash, std::move(serializedProgram));
204 }
205
updateProgram(const Context * context,const Program * program)206 void MemoryProgramCache::updateProgram(const Context *context, const Program *program)
207 {
208 egl::BlobCache::Key programHash;
209 ComputeHash(context, program, &programHash);
210 putProgram(programHash, context, program);
211 }
212
putBinary(const egl::BlobCache::Key & programHash,const uint8_t * binary,size_t length)213 void MemoryProgramCache::putBinary(const egl::BlobCache::Key &programHash,
214 const uint8_t *binary,
215 size_t length)
216 {
217 // Copy the binary.
218 angle::MemoryBuffer newEntry;
219 newEntry.resize(length);
220 memcpy(newEntry.data(), binary, length);
221
222 // Store the binary.
223 mBlobCache.populate(programHash, std::move(newEntry));
224 }
225
clear()226 void MemoryProgramCache::clear()
227 {
228 mBlobCache.clear();
229 mIssuedWarnings = 0;
230 }
231
resize(size_t maxCacheSizeBytes)232 void MemoryProgramCache::resize(size_t maxCacheSizeBytes)
233 {
234 mBlobCache.resize(maxCacheSizeBytes);
235 }
236
entryCount() const237 size_t MemoryProgramCache::entryCount() const
238 {
239 return mBlobCache.entryCount();
240 }
241
trim(size_t limit)242 size_t MemoryProgramCache::trim(size_t limit)
243 {
244 return mBlobCache.trim(limit);
245 }
246
size() const247 size_t MemoryProgramCache::size() const
248 {
249 return mBlobCache.size();
250 }
251
maxSize() const252 size_t MemoryProgramCache::maxSize() const
253 {
254 return mBlobCache.maxSize();
255 }
256
257 } // namespace gl
258