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