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 enum CacheResult
30 {
31     kCacheMiss,
32     kCacheHitMemory,
33     kCacheHitDisk,
34     kCacheResultMax,
35 };
36 
37 constexpr unsigned int kWarningLimit = 3;
38 
WriteShaderVar(BinaryOutputStream * stream,const sh::ShaderVariable & var)39 void WriteShaderVar(BinaryOutputStream *stream, const sh::ShaderVariable &var)
40 {
41     stream->writeInt(var.type);
42     stream->writeInt(var.precision);
43     stream->writeString(var.name);
44     stream->writeString(var.mappedName);
45     stream->writeIntVector(var.arraySizes);
46     stream->writeInt(var.staticUse);
47     stream->writeString(var.structName);
48     ASSERT(var.fields.empty());
49 }
50 
LoadShaderVar(BinaryInputStream * stream,sh::ShaderVariable * var)51 void LoadShaderVar(BinaryInputStream *stream, sh::ShaderVariable *var)
52 {
53     var->type       = stream->readInt<GLenum>();
54     var->precision  = stream->readInt<GLenum>();
55     var->name       = stream->readString();
56     var->mappedName = stream->readString();
57     stream->readIntVector<unsigned int>(&var->arraySizes);
58     var->staticUse  = stream->readBool();
59     var->structName = stream->readString();
60 }
61 
WriteShaderVariableBuffer(BinaryOutputStream * stream,const ShaderVariableBuffer & var)62 void WriteShaderVariableBuffer(BinaryOutputStream *stream, const ShaderVariableBuffer &var)
63 {
64     stream->writeInt(var.binding);
65     stream->writeInt(var.dataSize);
66 
67     stream->writeInt(var.vertexStaticUse);
68     stream->writeInt(var.fragmentStaticUse);
69     stream->writeInt(var.computeStaticUse);
70 
71     stream->writeInt(var.memberIndexes.size());
72     for (unsigned int memberCounterIndex : var.memberIndexes)
73     {
74         stream->writeInt(memberCounterIndex);
75     }
76 }
77 
LoadShaderVariableBuffer(BinaryInputStream * stream,ShaderVariableBuffer * var)78 void LoadShaderVariableBuffer(BinaryInputStream *stream, ShaderVariableBuffer *var)
79 {
80     var->binding           = stream->readInt<int>();
81     var->dataSize          = stream->readInt<unsigned int>();
82     var->vertexStaticUse   = stream->readBool();
83     var->fragmentStaticUse = stream->readBool();
84     var->computeStaticUse  = stream->readBool();
85 
86     unsigned int numMembers = stream->readInt<unsigned int>();
87     for (unsigned int blockMemberIndex = 0; blockMemberIndex < numMembers; blockMemberIndex++)
88     {
89         var->memberIndexes.push_back(stream->readInt<unsigned int>());
90     }
91 }
92 
WriteBufferVariable(BinaryOutputStream * stream,const BufferVariable & var)93 void WriteBufferVariable(BinaryOutputStream *stream, const BufferVariable &var)
94 {
95     WriteShaderVar(stream, var);
96 
97     stream->writeInt(var.bufferIndex);
98     stream->writeInt(var.blockInfo.offset);
99     stream->writeInt(var.blockInfo.arrayStride);
100     stream->writeInt(var.blockInfo.matrixStride);
101     stream->writeInt(var.blockInfo.isRowMajorMatrix);
102     stream->writeInt(var.blockInfo.topLevelArrayStride);
103     stream->writeInt(var.topLevelArraySize);
104     stream->writeInt(var.vertexStaticUse);
105     stream->writeInt(var.fragmentStaticUse);
106     stream->writeInt(var.computeStaticUse);
107 }
108 
LoadBufferVariable(BinaryInputStream * stream,BufferVariable * var)109 void LoadBufferVariable(BinaryInputStream *stream, BufferVariable *var)
110 {
111     LoadShaderVar(stream, var);
112 
113     var->bufferIndex                   = stream->readInt<int>();
114     var->blockInfo.offset              = stream->readInt<int>();
115     var->blockInfo.arrayStride         = stream->readInt<int>();
116     var->blockInfo.matrixStride        = stream->readInt<int>();
117     var->blockInfo.isRowMajorMatrix    = stream->readBool();
118     var->blockInfo.topLevelArrayStride = stream->readInt<int>();
119     var->topLevelArraySize             = stream->readInt<int>();
120     var->vertexStaticUse               = stream->readBool();
121     var->fragmentStaticUse             = stream->readBool();
122     var->computeStaticUse              = stream->readBool();
123 }
124 
WriteInterfaceBlock(BinaryOutputStream * stream,const InterfaceBlock & block)125 void WriteInterfaceBlock(BinaryOutputStream *stream, const InterfaceBlock &block)
126 {
127     stream->writeString(block.name);
128     stream->writeString(block.mappedName);
129     stream->writeInt(block.isArray);
130     stream->writeInt(block.arrayElement);
131 
132     WriteShaderVariableBuffer(stream, block);
133 }
134 
LoadInterfaceBlock(BinaryInputStream * stream,InterfaceBlock * block)135 void LoadInterfaceBlock(BinaryInputStream *stream, InterfaceBlock *block)
136 {
137     block->name         = stream->readString();
138     block->mappedName   = stream->readString();
139     block->isArray      = stream->readBool();
140     block->arrayElement = stream->readInt<unsigned int>();
141 
142     LoadShaderVariableBuffer(stream, block);
143 }
144 
145 class HashStream final : angle::NonCopyable
146 {
147   public:
str()148     std::string str() { return mStringStream.str(); }
149 
150     template <typename T>
operator <<(T value)151     HashStream &operator<<(T value)
152     {
153         mStringStream << value << kSeparator;
154         return *this;
155     }
156 
157   private:
158     static constexpr char kSeparator = ':';
159     std::ostringstream mStringStream;
160 };
161 
operator <<(HashStream & stream,const Shader * shader)162 HashStream &operator<<(HashStream &stream, const Shader *shader)
163 {
164     if (shader)
165     {
166         stream << shader->getSourceString().c_str() << shader->getSourceString().length()
167                << shader->getCompilerResourcesString().c_str();
168     }
169     return stream;
170 }
171 
operator <<(HashStream & stream,const Program::Bindings & bindings)172 HashStream &operator<<(HashStream &stream, const Program::Bindings &bindings)
173 {
174     for (const auto &binding : bindings)
175     {
176         stream << binding.first << binding.second;
177     }
178     return stream;
179 }
180 
operator <<(HashStream & stream,const std::vector<std::string> & strings)181 HashStream &operator<<(HashStream &stream, const std::vector<std::string> &strings)
182 {
183     for (const auto &str : strings)
184     {
185         stream << str;
186     }
187     return stream;
188 }
189 
190 }  // anonymous namespace
191 
MemoryProgramCache(size_t maxCacheSizeBytes)192 MemoryProgramCache::MemoryProgramCache(size_t maxCacheSizeBytes)
193     : mProgramBinaryCache(maxCacheSizeBytes), mIssuedWarnings(0)
194 {
195 }
196 
~MemoryProgramCache()197 MemoryProgramCache::~MemoryProgramCache()
198 {
199 }
200 
201 // static
Deserialize(const Context * context,const Program * program,ProgramState * state,const uint8_t * binary,size_t length,InfoLog & infoLog)202 LinkResult MemoryProgramCache::Deserialize(const Context *context,
203                                            const Program *program,
204                                            ProgramState *state,
205                                            const uint8_t *binary,
206                                            size_t length,
207                                            InfoLog &infoLog)
208 {
209     BinaryInputStream stream(binary, length);
210 
211     unsigned char commitString[ANGLE_COMMIT_HASH_SIZE];
212     stream.readBytes(commitString, ANGLE_COMMIT_HASH_SIZE);
213     if (memcmp(commitString, ANGLE_COMMIT_HASH, sizeof(unsigned char) * ANGLE_COMMIT_HASH_SIZE) !=
214         0)
215     {
216         infoLog << "Invalid program binary version.";
217         return false;
218     }
219 
220     int majorVersion = stream.readInt<int>();
221     int minorVersion = stream.readInt<int>();
222     if (majorVersion != context->getClientMajorVersion() ||
223         minorVersion != context->getClientMinorVersion())
224     {
225         infoLog << "Cannot load program binaries across different ES context versions.";
226         return false;
227     }
228 
229     state->mComputeShaderLocalSize[0] = stream.readInt<int>();
230     state->mComputeShaderLocalSize[1] = stream.readInt<int>();
231     state->mComputeShaderLocalSize[2] = stream.readInt<int>();
232 
233     state->mNumViews = stream.readInt<int>();
234 
235     static_assert(MAX_VERTEX_ATTRIBS <= sizeof(unsigned long) * 8,
236                   "Too many vertex attribs for mask");
237     state->mActiveAttribLocationsMask = stream.readInt<unsigned long>();
238 
239     unsigned int attribCount = stream.readInt<unsigned int>();
240     ASSERT(state->mAttributes.empty());
241     for (unsigned int attribIndex = 0; attribIndex < attribCount; ++attribIndex)
242     {
243         sh::Attribute attrib;
244         LoadShaderVar(&stream, &attrib);
245         attrib.location = stream.readInt<int>();
246         state->mAttributes.push_back(attrib);
247     }
248 
249     unsigned int uniformCount = stream.readInt<unsigned int>();
250     ASSERT(state->mUniforms.empty());
251     for (unsigned int uniformIndex = 0; uniformIndex < uniformCount; ++uniformIndex)
252     {
253         LinkedUniform uniform;
254         LoadShaderVar(&stream, &uniform);
255 
256         uniform.bufferIndex                = stream.readInt<int>();
257         uniform.blockInfo.offset           = stream.readInt<int>();
258         uniform.blockInfo.arrayStride      = stream.readInt<int>();
259         uniform.blockInfo.matrixStride     = stream.readInt<int>();
260         uniform.blockInfo.isRowMajorMatrix = stream.readBool();
261 
262         uniform.typeInfo = &GetUniformTypeInfo(uniform.type);
263 
264         state->mUniforms.push_back(uniform);
265     }
266 
267     const unsigned int uniformIndexCount = stream.readInt<unsigned int>();
268     ASSERT(state->mUniformLocations.empty());
269     for (unsigned int uniformIndexIndex = 0; uniformIndexIndex < uniformIndexCount;
270          uniformIndexIndex++)
271     {
272         VariableLocation variable;
273         stream.readInt(&variable.arrayIndex);
274         stream.readInt(&variable.index);
275         stream.readBool(&variable.ignored);
276 
277         state->mUniformLocations.push_back(variable);
278     }
279 
280     unsigned int uniformBlockCount = stream.readInt<unsigned int>();
281     ASSERT(state->mUniformBlocks.empty());
282     for (unsigned int uniformBlockIndex = 0; uniformBlockIndex < uniformBlockCount;
283          ++uniformBlockIndex)
284     {
285         InterfaceBlock uniformBlock;
286         LoadInterfaceBlock(&stream, &uniformBlock);
287         state->mUniformBlocks.push_back(uniformBlock);
288 
289         state->mActiveUniformBlockBindings.set(uniformBlockIndex, uniformBlock.binding != 0);
290     }
291 
292     unsigned int bufferVariableCount = stream.readInt<unsigned int>();
293     ASSERT(state->mBufferVariables.empty());
294     for (unsigned int index = 0; index < bufferVariableCount; ++index)
295     {
296         BufferVariable bufferVariable;
297         LoadBufferVariable(&stream, &bufferVariable);
298         state->mBufferVariables.push_back(bufferVariable);
299     }
300 
301     unsigned int shaderStorageBlockCount = stream.readInt<unsigned int>();
302     ASSERT(state->mShaderStorageBlocks.empty());
303     for (unsigned int shaderStorageBlockIndex = 0;
304          shaderStorageBlockIndex < shaderStorageBlockCount; ++shaderStorageBlockIndex)
305     {
306         InterfaceBlock shaderStorageBlock;
307         LoadInterfaceBlock(&stream, &shaderStorageBlock);
308         state->mShaderStorageBlocks.push_back(shaderStorageBlock);
309     }
310 
311     unsigned int atomicCounterBufferCount = stream.readInt<unsigned int>();
312     ASSERT(state->mAtomicCounterBuffers.empty());
313     for (unsigned int bufferIndex = 0; bufferIndex < atomicCounterBufferCount; ++bufferIndex)
314     {
315         AtomicCounterBuffer atomicCounterBuffer;
316         LoadShaderVariableBuffer(&stream, &atomicCounterBuffer);
317 
318         state->mAtomicCounterBuffers.push_back(atomicCounterBuffer);
319     }
320 
321     unsigned int transformFeedbackVaryingCount = stream.readInt<unsigned int>();
322 
323     // Reject programs that use transform feedback varyings if the hardware cannot support them.
324     if (transformFeedbackVaryingCount > 0 &&
325         context->getWorkarounds().disableProgramCachingForTransformFeedback)
326     {
327         infoLog << "Current driver does not support transform feedback in binary programs.";
328         return false;
329     }
330 
331     ASSERT(state->mLinkedTransformFeedbackVaryings.empty());
332     for (unsigned int transformFeedbackVaryingIndex = 0;
333          transformFeedbackVaryingIndex < transformFeedbackVaryingCount;
334          ++transformFeedbackVaryingIndex)
335     {
336         sh::Varying varying;
337         stream.readIntVector<unsigned int>(&varying.arraySizes);
338         stream.readInt(&varying.type);
339         stream.readString(&varying.name);
340 
341         GLuint arrayIndex = stream.readInt<GLuint>();
342 
343         state->mLinkedTransformFeedbackVaryings.emplace_back(varying, arrayIndex);
344     }
345 
346     stream.readInt(&state->mTransformFeedbackBufferMode);
347 
348     unsigned int outputCount = stream.readInt<unsigned int>();
349     ASSERT(state->mOutputVariables.empty());
350     for (unsigned int outputIndex = 0; outputIndex < outputCount; ++outputIndex)
351     {
352         sh::OutputVariable output;
353         LoadShaderVar(&stream, &output);
354         output.location = stream.readInt<int>();
355         state->mOutputVariables.push_back(output);
356     }
357 
358     unsigned int outputVarCount = stream.readInt<unsigned int>();
359     ASSERT(state->mOutputLocations.empty());
360     for (unsigned int outputIndex = 0; outputIndex < outputVarCount; ++outputIndex)
361     {
362         VariableLocation locationData;
363         stream.readInt(&locationData.arrayIndex);
364         stream.readInt(&locationData.index);
365         stream.readBool(&locationData.ignored);
366         state->mOutputLocations.push_back(locationData);
367     }
368 
369     unsigned int outputTypeCount = stream.readInt<unsigned int>();
370     for (unsigned int outputIndex = 0; outputIndex < outputTypeCount; ++outputIndex)
371     {
372         state->mOutputVariableTypes.push_back(stream.readInt<GLenum>());
373     }
374     static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS < 8 * sizeof(uint32_t),
375                   "All bits of DrawBufferMask can be contained in an uint32_t");
376     state->mActiveOutputVariables = stream.readInt<uint32_t>();
377 
378     unsigned int samplerRangeLow  = stream.readInt<unsigned int>();
379     unsigned int samplerRangeHigh = stream.readInt<unsigned int>();
380     state->mSamplerUniformRange   = RangeUI(samplerRangeLow, samplerRangeHigh);
381     unsigned int samplerCount = stream.readInt<unsigned int>();
382     for (unsigned int samplerIndex = 0; samplerIndex < samplerCount; ++samplerIndex)
383     {
384         GLenum textureType  = stream.readInt<GLenum>();
385         size_t bindingCount = stream.readInt<size_t>();
386         bool unreferenced   = stream.readBool();
387         state->mSamplerBindings.emplace_back(
388             SamplerBinding(textureType, bindingCount, unreferenced));
389     }
390 
391     unsigned int imageRangeLow  = stream.readInt<unsigned int>();
392     unsigned int imageRangeHigh = stream.readInt<unsigned int>();
393     state->mImageUniformRange   = RangeUI(imageRangeLow, imageRangeHigh);
394     unsigned int imageBindingCount = stream.readInt<unsigned int>();
395     for (unsigned int imageIndex = 0; imageIndex < imageBindingCount; ++imageIndex)
396     {
397         unsigned int elementCount = stream.readInt<unsigned int>();
398         ImageBinding imageBinding(elementCount);
399         for (unsigned int i = 0; i < elementCount; ++i)
400         {
401             imageBinding.boundImageUnits[i] = stream.readInt<unsigned int>();
402         }
403         state->mImageBindings.emplace_back(imageBinding);
404     }
405 
406     unsigned int atomicCounterRangeLow  = stream.readInt<unsigned int>();
407     unsigned int atomicCounterRangeHigh = stream.readInt<unsigned int>();
408     state->mAtomicCounterUniformRange   = RangeUI(atomicCounterRangeLow, atomicCounterRangeHigh);
409 
410     static_assert(SHADER_TYPE_MAX <= sizeof(unsigned long) * 8, "Too many shader types");
411     state->mLinkedShaderStages = stream.readInt<unsigned long>();
412 
413     return program->getImplementation()->load(context, infoLog, &stream);
414 }
415 
416 // static
Serialize(const Context * context,const gl::Program * program,angle::MemoryBuffer * binaryOut)417 void MemoryProgramCache::Serialize(const Context *context,
418                                    const gl::Program *program,
419                                    angle::MemoryBuffer *binaryOut)
420 {
421     BinaryOutputStream stream;
422 
423     stream.writeBytes(reinterpret_cast<const unsigned char *>(ANGLE_COMMIT_HASH),
424                       ANGLE_COMMIT_HASH_SIZE);
425 
426     // nullptr context is supported when computing binary length.
427     if (context)
428     {
429         stream.writeInt(context->getClientVersion().major);
430         stream.writeInt(context->getClientVersion().minor);
431     }
432     else
433     {
434         stream.writeInt(2);
435         stream.writeInt(0);
436     }
437 
438     const auto &state = program->getState();
439 
440     const auto &computeLocalSize = state.getComputeShaderLocalSize();
441 
442     stream.writeInt(computeLocalSize[0]);
443     stream.writeInt(computeLocalSize[1]);
444     stream.writeInt(computeLocalSize[2]);
445 
446     stream.writeInt(state.mNumViews);
447 
448     stream.writeInt(state.getActiveAttribLocationsMask().to_ulong());
449 
450     stream.writeInt(state.getAttributes().size());
451     for (const sh::Attribute &attrib : state.getAttributes())
452     {
453         WriteShaderVar(&stream, attrib);
454         stream.writeInt(attrib.location);
455     }
456 
457     stream.writeInt(state.getUniforms().size());
458     for (const LinkedUniform &uniform : state.getUniforms())
459     {
460         WriteShaderVar(&stream, uniform);
461 
462         // FIXME: referenced
463 
464         stream.writeInt(uniform.bufferIndex);
465         stream.writeInt(uniform.blockInfo.offset);
466         stream.writeInt(uniform.blockInfo.arrayStride);
467         stream.writeInt(uniform.blockInfo.matrixStride);
468         stream.writeInt(uniform.blockInfo.isRowMajorMatrix);
469     }
470 
471     stream.writeInt(state.getUniformLocations().size());
472     for (const auto &variable : state.getUniformLocations())
473     {
474         stream.writeInt(variable.arrayIndex);
475         stream.writeIntOrNegOne(variable.index);
476         stream.writeInt(variable.ignored);
477     }
478 
479     stream.writeInt(state.getUniformBlocks().size());
480     for (const InterfaceBlock &uniformBlock : state.getUniformBlocks())
481     {
482         WriteInterfaceBlock(&stream, uniformBlock);
483     }
484 
485     stream.writeInt(state.getBufferVariables().size());
486     for (const BufferVariable &bufferVariable : state.getBufferVariables())
487     {
488         WriteBufferVariable(&stream, bufferVariable);
489     }
490 
491     stream.writeInt(state.getShaderStorageBlocks().size());
492     for (const InterfaceBlock &shaderStorageBlock : state.getShaderStorageBlocks())
493     {
494         WriteInterfaceBlock(&stream, shaderStorageBlock);
495     }
496 
497     stream.writeInt(state.mAtomicCounterBuffers.size());
498     for (const auto &atomicCounterBuffer : state.mAtomicCounterBuffers)
499     {
500         WriteShaderVariableBuffer(&stream, atomicCounterBuffer);
501     }
502 
503     // Warn the app layer if saving a binary with unsupported transform feedback.
504     if (!state.getLinkedTransformFeedbackVaryings().empty() &&
505         context->getWorkarounds().disableProgramCachingForTransformFeedback)
506     {
507         WARN() << "Saving program binary with transform feedback, which is not supported on this "
508                   "driver.";
509     }
510 
511     stream.writeInt(state.getLinkedTransformFeedbackVaryings().size());
512     for (const auto &var : state.getLinkedTransformFeedbackVaryings())
513     {
514         stream.writeIntVector(var.arraySizes);
515         stream.writeInt(var.type);
516         stream.writeString(var.name);
517 
518         stream.writeIntOrNegOne(var.arrayIndex);
519     }
520 
521     stream.writeInt(state.getTransformFeedbackBufferMode());
522 
523     stream.writeInt(state.getOutputVariables().size());
524     for (const sh::OutputVariable &output : state.getOutputVariables())
525     {
526         WriteShaderVar(&stream, output);
527         stream.writeInt(output.location);
528     }
529 
530     stream.writeInt(state.getOutputLocations().size());
531     for (const auto &outputVar : state.getOutputLocations())
532     {
533         stream.writeInt(outputVar.arrayIndex);
534         stream.writeIntOrNegOne(outputVar.index);
535         stream.writeInt(outputVar.ignored);
536     }
537 
538     stream.writeInt(state.mOutputVariableTypes.size());
539     for (const auto &outputVariableType : state.mOutputVariableTypes)
540     {
541         stream.writeInt(outputVariableType);
542     }
543 
544     static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS < 8 * sizeof(uint32_t),
545                   "All bits of DrawBufferMask can be contained in an uint32_t");
546     stream.writeInt(static_cast<uint32_t>(state.mActiveOutputVariables.to_ulong()));
547 
548     stream.writeInt(state.getSamplerUniformRange().low());
549     stream.writeInt(state.getSamplerUniformRange().high());
550 
551     stream.writeInt(state.getSamplerBindings().size());
552     for (const auto &samplerBinding : state.getSamplerBindings())
553     {
554         stream.writeInt(samplerBinding.textureType);
555         stream.writeInt(samplerBinding.boundTextureUnits.size());
556         stream.writeInt(samplerBinding.unreferenced);
557     }
558 
559     stream.writeInt(state.getImageUniformRange().low());
560     stream.writeInt(state.getImageUniformRange().high());
561 
562     stream.writeInt(state.getImageBindings().size());
563     for (const auto &imageBinding : state.getImageBindings())
564     {
565         stream.writeInt(imageBinding.boundImageUnits.size());
566         for (size_t i = 0; i < imageBinding.boundImageUnits.size(); ++i)
567         {
568             stream.writeInt(imageBinding.boundImageUnits[i]);
569         }
570     }
571 
572     stream.writeInt(state.getAtomicCounterUniformRange().low());
573     stream.writeInt(state.getAtomicCounterUniformRange().high());
574 
575     stream.writeInt(state.getLinkedShaderStages().to_ulong());
576 
577     program->getImplementation()->save(context, &stream);
578 
579     ASSERT(binaryOut);
580     binaryOut->resize(stream.length());
581     memcpy(binaryOut->data(), stream.data(), stream.length());
582 }
583 
584 // static
ComputeHash(const Context * context,const Program * program,ProgramHash * hashOut)585 void MemoryProgramCache::ComputeHash(const Context *context,
586                                      const Program *program,
587                                      ProgramHash *hashOut)
588 {
589     const Shader *vertexShader   = program->getAttachedVertexShader();
590     const Shader *fragmentShader = program->getAttachedFragmentShader();
591     const Shader *computeShader  = program->getAttachedComputeShader();
592 
593     // Compute the program hash. Start with the shader hashes and resource strings.
594     HashStream hashStream;
595     hashStream << vertexShader << fragmentShader << computeShader;
596 
597     // Add some ANGLE metadata and Context properties, such as version and back-end.
598     hashStream << ANGLE_COMMIT_HASH << context->getClientMajorVersion()
599                << context->getClientMinorVersion() << context->getString(GL_RENDERER);
600 
601     // Hash pre-link program properties.
602     hashStream << program->getAttributeBindings() << program->getUniformLocationBindings()
603                << program->getFragmentInputBindings()
604                << program->getState().getTransformFeedbackVaryingNames()
605                << program->getState().getTransformFeedbackBufferMode();
606 
607     // Call the secure SHA hashing function.
608     const std::string &programKey = hashStream.str();
609     angle::base::SHA1HashBytes(reinterpret_cast<const unsigned char *>(programKey.c_str()),
610                                programKey.length(), hashOut->data());
611 }
612 
getProgram(const Context * context,const Program * program,ProgramState * state,ProgramHash * hashOut)613 LinkResult MemoryProgramCache::getProgram(const Context *context,
614                                           const Program *program,
615                                           ProgramState *state,
616                                           ProgramHash *hashOut)
617 {
618     ComputeHash(context, program, hashOut);
619     const angle::MemoryBuffer *binaryProgram = nullptr;
620     LinkResult result(false);
621     if (get(*hashOut, &binaryProgram))
622     {
623         InfoLog infoLog;
624         ANGLE_TRY_RESULT(Deserialize(context, program, state, binaryProgram->data(),
625                                      binaryProgram->size(), infoLog),
626                          result);
627         ANGLE_HISTOGRAM_BOOLEAN("GPU.ANGLE.ProgramCache.LoadBinarySuccess", result.getResult());
628         if (!result.getResult())
629         {
630             // Cache load failed, evict.
631             if (mIssuedWarnings++ < kWarningLimit)
632             {
633                 WARN() << "Failed to load binary from cache: " << infoLog.str();
634 
635                 if (mIssuedWarnings == kWarningLimit)
636                 {
637                     WARN() << "Reaching warning limit for cache load failures, silencing "
638                               "subsequent warnings.";
639                 }
640             }
641             remove(*hashOut);
642         }
643     }
644     return result;
645 }
646 
get(const ProgramHash & programHash,const angle::MemoryBuffer ** programOut)647 bool MemoryProgramCache::get(const ProgramHash &programHash, const angle::MemoryBuffer **programOut)
648 {
649     const CacheEntry *entry = nullptr;
650     if (!mProgramBinaryCache.get(programHash, &entry))
651     {
652         ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.ProgramCache.CacheResult", kCacheMiss,
653                                     kCacheResultMax);
654         return false;
655     }
656 
657     if (entry->second == CacheSource::PutProgram)
658     {
659         ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.ProgramCache.CacheResult", kCacheHitMemory,
660                                     kCacheResultMax);
661     }
662     else
663     {
664         ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.ProgramCache.CacheResult", kCacheHitDisk,
665                                     kCacheResultMax);
666     }
667 
668     *programOut = &entry->first;
669     return true;
670 }
671 
getAt(size_t index,ProgramHash * hashOut,const angle::MemoryBuffer ** programOut)672 bool MemoryProgramCache::getAt(size_t index,
673                                ProgramHash *hashOut,
674                                const angle::MemoryBuffer **programOut)
675 {
676     const CacheEntry *entry = nullptr;
677     if (!mProgramBinaryCache.getAt(index, hashOut, &entry))
678     {
679         return false;
680     }
681 
682     *programOut = &entry->first;
683     return true;
684 }
685 
remove(const ProgramHash & programHash)686 void MemoryProgramCache::remove(const ProgramHash &programHash)
687 {
688     bool result = mProgramBinaryCache.eraseByKey(programHash);
689     ASSERT(result);
690 }
691 
putProgram(const ProgramHash & programHash,const Context * context,const Program * program)692 void MemoryProgramCache::putProgram(const ProgramHash &programHash,
693                                     const Context *context,
694                                     const Program *program)
695 {
696     CacheEntry newEntry;
697     Serialize(context, program, &newEntry.first);
698     newEntry.second = CacheSource::PutProgram;
699 
700     ANGLE_HISTOGRAM_COUNTS("GPU.ANGLE.ProgramCache.ProgramBinarySizeBytes",
701                            static_cast<int>(newEntry.first.size()));
702 
703     const CacheEntry *result =
704         mProgramBinaryCache.put(programHash, std::move(newEntry), newEntry.first.size());
705     if (!result)
706     {
707         ERR() << "Failed to store binary program in memory cache, program is too large.";
708     }
709     else
710     {
711         auto *platform = ANGLEPlatformCurrent();
712         platform->cacheProgram(platform, programHash, result->first.size(), result->first.data());
713     }
714 }
715 
updateProgram(const Context * context,const Program * program)716 void MemoryProgramCache::updateProgram(const Context *context, const Program *program)
717 {
718     gl::ProgramHash programHash;
719     ComputeHash(context, program, &programHash);
720     putProgram(programHash, context, program);
721 }
722 
putBinary(const ProgramHash & programHash,const uint8_t * binary,size_t length)723 void MemoryProgramCache::putBinary(const ProgramHash &programHash,
724                                    const uint8_t *binary,
725                                    size_t length)
726 {
727     // Copy the binary.
728     CacheEntry newEntry;
729     newEntry.first.resize(length);
730     memcpy(newEntry.first.data(), binary, length);
731     newEntry.second = CacheSource::PutBinary;
732 
733     // Store the binary.
734     const CacheEntry *result = mProgramBinaryCache.put(programHash, std::move(newEntry), length);
735     if (!result)
736     {
737         ERR() << "Failed to store binary program in memory cache, program is too large.";
738     }
739 }
740 
clear()741 void MemoryProgramCache::clear()
742 {
743     mProgramBinaryCache.clear();
744     mIssuedWarnings = 0;
745 }
746 
resize(size_t maxCacheSizeBytes)747 void MemoryProgramCache::resize(size_t maxCacheSizeBytes)
748 {
749     mProgramBinaryCache.resize(maxCacheSizeBytes);
750 }
751 
entryCount() const752 size_t MemoryProgramCache::entryCount() const
753 {
754     return mProgramBinaryCache.entryCount();
755 }
756 
trim(size_t limit)757 size_t MemoryProgramCache::trim(size_t limit)
758 {
759     return mProgramBinaryCache.shrinkToSize(limit);
760 }
761 
size() const762 size_t MemoryProgramCache::size() const
763 {
764     return mProgramBinaryCache.size();
765 }
766 
maxSize() const767 size_t MemoryProgramCache::maxSize() const
768 {
769     return mProgramBinaryCache.maxSize();
770 }
771 
772 }  // namespace gl
773