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