blob: 8586a92ac810d2de0720157c24391c831ff0aa81 [file] [log] [blame]
Jamie Madill4f86d052017-06-05 12:59:26 -04001//
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>
Jamie Madill32447362017-06-28 14:53:52 -040013#include <anglebase/sha1.h>
Jamie Madill4f86d052017-06-05 12:59:26 -040014
Jamie Madillf00f7ff2017-08-31 14:39:15 -040015#include "common/utilities.h"
Jamie Madill4f86d052017-06-05 12:59:26 -040016#include "common/version.h"
17#include "libANGLE/BinaryStream.h"
18#include "libANGLE/Context.h"
19#include "libANGLE/Uniform.h"
Jamie Madill6c58b062017-08-01 13:44:25 -040020#include "libANGLE/histogram_macros.h"
Jamie Madill4f86d052017-06-05 12:59:26 -040021#include "libANGLE/renderer/ProgramImpl.h"
Jamie Madill360daee2017-06-29 10:36:19 -040022#include "platform/Platform.h"
Jamie Madill4f86d052017-06-05 12:59:26 -040023
24namespace gl
25{
26
27namespace
28{
Jamie Madill6c58b062017-08-01 13:44:25 -040029enum CacheResult
30{
31 kCacheMiss,
32 kCacheHitMemory,
33 kCacheHitDisk,
34 kCacheResultMax,
35};
36
Jamie Madill32447362017-06-28 14:53:52 -040037constexpr unsigned int kWarningLimit = 3;
Jamie Madill4f86d052017-06-05 12:59:26 -040038
39void 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->writeInt(var.arraySize);
46 stream->writeInt(var.staticUse);
47 stream->writeString(var.structName);
48 ASSERT(var.fields.empty());
49}
50
51void 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 var->arraySize = stream->readInt<unsigned int>();
58 var->staticUse = stream->readBool();
59 var->structName = stream->readString();
60}
61
jchen10eaef1e52017-06-13 10:44:11 +080062void 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
78void 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
Jamie Madill32447362017-06-28 14:53:52 -040093class HashStream final : angle::NonCopyable
94{
95 public:
96 std::string str() { return mStringStream.str(); }
97
98 template <typename T>
99 HashStream &operator<<(T value)
100 {
101 mStringStream << value << kSeparator;
102 return *this;
103 }
104
105 private:
106 static constexpr char kSeparator = ':';
107 std::ostringstream mStringStream;
108};
109
110HashStream &operator<<(HashStream &stream, const Shader *shader)
111{
112 if (shader)
113 {
114 stream << shader->getSourceString().c_str() << shader->getSourceString().length()
115 << shader->getCompilerResourcesString().c_str();
116 }
117 return stream;
118}
119
120HashStream &operator<<(HashStream &stream, const Program::Bindings &bindings)
121{
122 for (const auto &binding : bindings)
123 {
124 stream << binding.first << binding.second;
125 }
126 return stream;
127}
128
129HashStream &operator<<(HashStream &stream, const std::vector<std::string> &strings)
130{
131 for (const auto &str : strings)
132 {
133 stream << str;
134 }
135 return stream;
136}
137
Jamie Madill4f86d052017-06-05 12:59:26 -0400138} // anonymous namespace
139
Jamie Madill32447362017-06-28 14:53:52 -0400140MemoryProgramCache::MemoryProgramCache(size_t maxCacheSizeBytes)
141 : mProgramBinaryCache(maxCacheSizeBytes), mIssuedWarnings(0)
142{
143}
144
145MemoryProgramCache::~MemoryProgramCache()
146{
147}
148
Jamie Madill4f86d052017-06-05 12:59:26 -0400149// static
150LinkResult MemoryProgramCache::Deserialize(const Context *context,
151 const Program *program,
152 ProgramState *state,
153 const uint8_t *binary,
154 size_t length,
155 InfoLog &infoLog)
156{
157 BinaryInputStream stream(binary, length);
158
159 unsigned char commitString[ANGLE_COMMIT_HASH_SIZE];
160 stream.readBytes(commitString, ANGLE_COMMIT_HASH_SIZE);
161 if (memcmp(commitString, ANGLE_COMMIT_HASH, sizeof(unsigned char) * ANGLE_COMMIT_HASH_SIZE) !=
162 0)
163 {
164 infoLog << "Invalid program binary version.";
165 return false;
166 }
167
168 int majorVersion = stream.readInt<int>();
169 int minorVersion = stream.readInt<int>();
170 if (majorVersion != context->getClientMajorVersion() ||
171 minorVersion != context->getClientMinorVersion())
172 {
173 infoLog << "Cannot load program binaries across different ES context versions.";
174 return false;
175 }
176
177 state->mComputeShaderLocalSize[0] = stream.readInt<int>();
178 state->mComputeShaderLocalSize[1] = stream.readInt<int>();
179 state->mComputeShaderLocalSize[2] = stream.readInt<int>();
180
Martin Radev7cf61662017-07-26 17:10:53 +0300181 state->mNumViews = stream.readInt<int>();
182
Jamie Madill4f86d052017-06-05 12:59:26 -0400183 static_assert(MAX_VERTEX_ATTRIBS <= sizeof(unsigned long) * 8,
184 "Too many vertex attribs for mask");
185 state->mActiveAttribLocationsMask = stream.readInt<unsigned long>();
186
187 unsigned int attribCount = stream.readInt<unsigned int>();
188 ASSERT(state->mAttributes.empty());
189 for (unsigned int attribIndex = 0; attribIndex < attribCount; ++attribIndex)
190 {
191 sh::Attribute attrib;
192 LoadShaderVar(&stream, &attrib);
193 attrib.location = stream.readInt<int>();
194 state->mAttributes.push_back(attrib);
195 }
196
197 unsigned int uniformCount = stream.readInt<unsigned int>();
198 ASSERT(state->mUniforms.empty());
199 for (unsigned int uniformIndex = 0; uniformIndex < uniformCount; ++uniformIndex)
200 {
201 LinkedUniform uniform;
202 LoadShaderVar(&stream, &uniform);
203
jchen10eaef1e52017-06-13 10:44:11 +0800204 uniform.bufferIndex = stream.readInt<int>();
Jamie Madill4f86d052017-06-05 12:59:26 -0400205 uniform.blockInfo.offset = stream.readInt<int>();
206 uniform.blockInfo.arrayStride = stream.readInt<int>();
207 uniform.blockInfo.matrixStride = stream.readInt<int>();
208 uniform.blockInfo.isRowMajorMatrix = stream.readBool();
209
Jamie Madillf00f7ff2017-08-31 14:39:15 -0400210 uniform.typeInfo = &GetUniformTypeInfo(uniform.type);
211
Jamie Madill4f86d052017-06-05 12:59:26 -0400212 state->mUniforms.push_back(uniform);
213 }
214
215 const unsigned int uniformIndexCount = stream.readInt<unsigned int>();
216 ASSERT(state->mUniformLocations.empty());
217 for (unsigned int uniformIndexIndex = 0; uniformIndexIndex < uniformIndexCount;
218 uniformIndexIndex++)
219 {
220 VariableLocation variable;
221 stream.readString(&variable.name);
222 stream.readInt(&variable.element);
223 stream.readInt(&variable.index);
224 stream.readBool(&variable.used);
225 stream.readBool(&variable.ignored);
226
227 state->mUniformLocations.push_back(variable);
228 }
229
230 unsigned int uniformBlockCount = stream.readInt<unsigned int>();
231 ASSERT(state->mUniformBlocks.empty());
232 for (unsigned int uniformBlockIndex = 0; uniformBlockIndex < uniformBlockCount;
233 ++uniformBlockIndex)
234 {
235 UniformBlock uniformBlock;
236 stream.readString(&uniformBlock.name);
Olli Etuaho855d9642017-05-17 14:05:06 +0300237 stream.readString(&uniformBlock.mappedName);
Jamie Madill4f86d052017-06-05 12:59:26 -0400238 stream.readBool(&uniformBlock.isArray);
239 stream.readInt(&uniformBlock.arrayElement);
Jamie Madill4f86d052017-06-05 12:59:26 -0400240
jchen10eaef1e52017-06-13 10:44:11 +0800241 LoadShaderVariableBuffer(&stream, &uniformBlock);
Jamie Madill4f86d052017-06-05 12:59:26 -0400242
243 state->mUniformBlocks.push_back(uniformBlock);
Jamie Madill4f86d052017-06-05 12:59:26 -0400244
jchen107a20b972017-06-13 14:25:26 +0800245 state->mActiveUniformBlockBindings.set(uniformBlockIndex, uniformBlock.binding != 0);
Jamie Madill4f86d052017-06-05 12:59:26 -0400246 }
jchen10eaef1e52017-06-13 10:44:11 +0800247 unsigned int atomicCounterBufferCount = stream.readInt<unsigned int>();
248 ASSERT(state->mAtomicCounterBuffers.empty());
249 for (unsigned int bufferIndex = 0; bufferIndex < atomicCounterBufferCount; ++bufferIndex)
250 {
251 AtomicCounterBuffer atomicCounterBuffer;
252 LoadShaderVariableBuffer(&stream, &atomicCounterBuffer);
253
254 state->mAtomicCounterBuffers.push_back(atomicCounterBuffer);
255 }
Jamie Madill4f86d052017-06-05 12:59:26 -0400256
257 unsigned int transformFeedbackVaryingCount = stream.readInt<unsigned int>();
Jamie Madillffe00c02017-06-27 16:26:55 -0400258
259 // Reject programs that use transform feedback varyings if the hardware cannot support them.
260 if (transformFeedbackVaryingCount > 0 &&
261 context->getWorkarounds().disableProgramCachingForTransformFeedback)
262 {
263 infoLog << "Current driver does not support transform feedback in binary programs.";
264 return false;
265 }
266
Jamie Madill4f86d052017-06-05 12:59:26 -0400267 ASSERT(state->mLinkedTransformFeedbackVaryings.empty());
268 for (unsigned int transformFeedbackVaryingIndex = 0;
269 transformFeedbackVaryingIndex < transformFeedbackVaryingCount;
270 ++transformFeedbackVaryingIndex)
271 {
272 sh::Varying varying;
273 stream.readInt(&varying.arraySize);
274 stream.readInt(&varying.type);
275 stream.readString(&varying.name);
276
277 GLuint arrayIndex = stream.readInt<GLuint>();
278
279 state->mLinkedTransformFeedbackVaryings.emplace_back(varying, arrayIndex);
280 }
281
282 stream.readInt(&state->mTransformFeedbackBufferMode);
283
284 unsigned int outputCount = stream.readInt<unsigned int>();
285 ASSERT(state->mOutputVariables.empty());
286 for (unsigned int outputIndex = 0; outputIndex < outputCount; ++outputIndex)
287 {
288 sh::OutputVariable output;
289 LoadShaderVar(&stream, &output);
290 output.location = stream.readInt<int>();
291 state->mOutputVariables.push_back(output);
292 }
293
294 unsigned int outputVarCount = stream.readInt<unsigned int>();
295 for (unsigned int outputIndex = 0; outputIndex < outputVarCount; ++outputIndex)
296 {
297 int locationIndex = stream.readInt<int>();
298 VariableLocation locationData;
299 stream.readInt(&locationData.element);
300 stream.readInt(&locationData.index);
301 stream.readString(&locationData.name);
302 state->mOutputLocations[locationIndex] = locationData;
303 }
304
305 unsigned int outputTypeCount = stream.readInt<unsigned int>();
306 for (unsigned int outputIndex = 0; outputIndex < outputTypeCount; ++outputIndex)
307 {
308 state->mOutputVariableTypes.push_back(stream.readInt<GLenum>());
309 }
310 static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS < 8 * sizeof(uint32_t),
311 "All bits of DrawBufferMask can be contained in an uint32_t");
312 state->mActiveOutputVariables = stream.readInt<uint32_t>();
313
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800314 unsigned int samplerRangeLow = stream.readInt<unsigned int>();
315 unsigned int samplerRangeHigh = stream.readInt<unsigned int>();
316 state->mSamplerUniformRange = RangeUI(samplerRangeLow, samplerRangeHigh);
Jamie Madill4f86d052017-06-05 12:59:26 -0400317 unsigned int samplerCount = stream.readInt<unsigned int>();
318 for (unsigned int samplerIndex = 0; samplerIndex < samplerCount; ++samplerIndex)
319 {
320 GLenum textureType = stream.readInt<GLenum>();
321 size_t bindingCount = stream.readInt<size_t>();
Jamie Madill54164b02017-08-28 15:17:37 -0400322 bool unreferenced = stream.readBool();
323 state->mSamplerBindings.emplace_back(
324 SamplerBinding(textureType, bindingCount, unreferenced));
Jamie Madill4f86d052017-06-05 12:59:26 -0400325 }
326
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800327 unsigned int imageRangeLow = stream.readInt<unsigned int>();
328 unsigned int imageRangeHigh = stream.readInt<unsigned int>();
329 state->mImageUniformRange = RangeUI(imageRangeLow, imageRangeHigh);
Xinghua Cao0328b572017-06-26 15:51:36 +0800330 unsigned int imageBindingCount = stream.readInt<unsigned int>();
331 for (unsigned int imageIndex = 0; imageIndex < imageBindingCount; ++imageIndex)
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800332 {
Xinghua Cao0328b572017-06-26 15:51:36 +0800333 unsigned int elementCount = stream.readInt<unsigned int>();
334 ImageBinding imageBinding(elementCount);
335 for (unsigned int i = 0; i < elementCount; ++i)
336 {
337 imageBinding.boundImageUnits[i] = stream.readInt<unsigned int>();
338 }
339 state->mImageBindings.emplace_back(imageBinding);
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800340 }
341
jchen10eaef1e52017-06-13 10:44:11 +0800342 unsigned int atomicCounterRangeLow = stream.readInt<unsigned int>();
343 unsigned int atomicCounterRangeHigh = stream.readInt<unsigned int>();
344 state->mAtomicCounterUniformRange = RangeUI(atomicCounterRangeLow, atomicCounterRangeHigh);
345
Jamie Madill4f86d052017-06-05 12:59:26 -0400346 return program->getImplementation()->load(context, infoLog, &stream);
347}
348
349// static
350void MemoryProgramCache::Serialize(const Context *context,
351 const gl::Program *program,
352 angle::MemoryBuffer *binaryOut)
353{
354 BinaryOutputStream stream;
355
356 stream.writeBytes(reinterpret_cast<const unsigned char *>(ANGLE_COMMIT_HASH),
357 ANGLE_COMMIT_HASH_SIZE);
358
359 // nullptr context is supported when computing binary length.
360 if (context)
361 {
362 stream.writeInt(context->getClientVersion().major);
363 stream.writeInt(context->getClientVersion().minor);
364 }
365 else
366 {
367 stream.writeInt(2);
368 stream.writeInt(0);
369 }
370
371 const auto &state = program->getState();
372
373 const auto &computeLocalSize = state.getComputeShaderLocalSize();
374
375 stream.writeInt(computeLocalSize[0]);
376 stream.writeInt(computeLocalSize[1]);
377 stream.writeInt(computeLocalSize[2]);
378
Martin Radev7cf61662017-07-26 17:10:53 +0300379 stream.writeInt(state.mNumViews);
380
Jamie Madill4f86d052017-06-05 12:59:26 -0400381 stream.writeInt(state.getActiveAttribLocationsMask().to_ulong());
382
383 stream.writeInt(state.getAttributes().size());
384 for (const sh::Attribute &attrib : state.getAttributes())
385 {
386 WriteShaderVar(&stream, attrib);
387 stream.writeInt(attrib.location);
388 }
389
390 stream.writeInt(state.getUniforms().size());
391 for (const LinkedUniform &uniform : state.getUniforms())
392 {
393 WriteShaderVar(&stream, uniform);
394
395 // FIXME: referenced
396
jchen10eaef1e52017-06-13 10:44:11 +0800397 stream.writeInt(uniform.bufferIndex);
Jamie Madill4f86d052017-06-05 12:59:26 -0400398 stream.writeInt(uniform.blockInfo.offset);
399 stream.writeInt(uniform.blockInfo.arrayStride);
400 stream.writeInt(uniform.blockInfo.matrixStride);
401 stream.writeInt(uniform.blockInfo.isRowMajorMatrix);
402 }
403
404 stream.writeInt(state.getUniformLocations().size());
405 for (const auto &variable : state.getUniformLocations())
406 {
407 stream.writeString(variable.name);
408 stream.writeInt(variable.element);
409 stream.writeInt(variable.index);
410 stream.writeInt(variable.used);
411 stream.writeInt(variable.ignored);
412 }
413
414 stream.writeInt(state.getUniformBlocks().size());
415 for (const UniformBlock &uniformBlock : state.getUniformBlocks())
416 {
417 stream.writeString(uniformBlock.name);
Olli Etuaho855d9642017-05-17 14:05:06 +0300418 stream.writeString(uniformBlock.mappedName);
Jamie Madill4f86d052017-06-05 12:59:26 -0400419 stream.writeInt(uniformBlock.isArray);
420 stream.writeInt(uniformBlock.arrayElement);
Jamie Madill4f86d052017-06-05 12:59:26 -0400421
jchen10eaef1e52017-06-13 10:44:11 +0800422 WriteShaderVariableBuffer(&stream, uniformBlock);
423 }
Jamie Madill4f86d052017-06-05 12:59:26 -0400424
jchen10eaef1e52017-06-13 10:44:11 +0800425 stream.writeInt(state.mAtomicCounterBuffers.size());
426 for (const auto &atomicCounterBuffer : state.mAtomicCounterBuffers)
427 {
428 WriteShaderVariableBuffer(&stream, atomicCounterBuffer);
Jamie Madill4f86d052017-06-05 12:59:26 -0400429 }
430
Jamie Madillffe00c02017-06-27 16:26:55 -0400431 // Warn the app layer if saving a binary with unsupported transform feedback.
432 if (!state.getLinkedTransformFeedbackVaryings().empty() &&
433 context->getWorkarounds().disableProgramCachingForTransformFeedback)
434 {
435 WARN() << "Saving program binary with transform feedback, which is not supported on this "
436 "driver.";
437 }
438
Jamie Madill4f86d052017-06-05 12:59:26 -0400439 stream.writeInt(state.getLinkedTransformFeedbackVaryings().size());
440 for (const auto &var : state.getLinkedTransformFeedbackVaryings())
441 {
442 stream.writeInt(var.arraySize);
443 stream.writeInt(var.type);
444 stream.writeString(var.name);
445
446 stream.writeIntOrNegOne(var.arrayIndex);
447 }
448
449 stream.writeInt(state.getTransformFeedbackBufferMode());
450
451 stream.writeInt(state.getOutputVariables().size());
452 for (const sh::OutputVariable &output : state.getOutputVariables())
453 {
454 WriteShaderVar(&stream, output);
455 stream.writeInt(output.location);
456 }
457
458 stream.writeInt(state.getOutputLocations().size());
459 for (const auto &outputPair : state.getOutputLocations())
460 {
461 stream.writeInt(outputPair.first);
462 stream.writeIntOrNegOne(outputPair.second.element);
463 stream.writeInt(outputPair.second.index);
464 stream.writeString(outputPair.second.name);
465 }
466
467 stream.writeInt(state.mOutputVariableTypes.size());
468 for (const auto &outputVariableType : state.mOutputVariableTypes)
469 {
470 stream.writeInt(outputVariableType);
471 }
472
473 static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS < 8 * sizeof(uint32_t),
474 "All bits of DrawBufferMask can be contained in an uint32_t");
475 stream.writeInt(static_cast<uint32_t>(state.mActiveOutputVariables.to_ulong()));
476
477 stream.writeInt(state.getSamplerUniformRange().low());
478 stream.writeInt(state.getSamplerUniformRange().high());
479
480 stream.writeInt(state.getSamplerBindings().size());
481 for (const auto &samplerBinding : state.getSamplerBindings())
482 {
483 stream.writeInt(samplerBinding.textureType);
484 stream.writeInt(samplerBinding.boundTextureUnits.size());
Jamie Madill54164b02017-08-28 15:17:37 -0400485 stream.writeInt(samplerBinding.unreferenced);
Jamie Madill4f86d052017-06-05 12:59:26 -0400486 }
487
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800488 stream.writeInt(state.getImageUniformRange().low());
489 stream.writeInt(state.getImageUniformRange().high());
490
491 stream.writeInt(state.getImageBindings().size());
492 for (const auto &imageBinding : state.getImageBindings())
493 {
Xinghua Cao0328b572017-06-26 15:51:36 +0800494 stream.writeInt(imageBinding.boundImageUnits.size());
495 for (size_t i = 0; i < imageBinding.boundImageUnits.size(); ++i)
496 {
497 stream.writeInt(imageBinding.boundImageUnits[i]);
498 }
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800499 }
500
jchen10eaef1e52017-06-13 10:44:11 +0800501 stream.writeInt(state.getAtomicCounterUniformRange().low());
502 stream.writeInt(state.getAtomicCounterUniformRange().high());
503
Jamie Madill27a60632017-06-30 15:12:01 -0400504 program->getImplementation()->save(context, &stream);
Jamie Madill4f86d052017-06-05 12:59:26 -0400505
506 ASSERT(binaryOut);
507 binaryOut->resize(stream.length());
508 memcpy(binaryOut->data(), stream.data(), stream.length());
509}
510
Jamie Madill32447362017-06-28 14:53:52 -0400511// static
512void MemoryProgramCache::ComputeHash(const Context *context,
513 const Program *program,
514 ProgramHash *hashOut)
515{
516 auto vertexShader = program->getAttachedVertexShader();
517 auto fragmentShader = program->getAttachedFragmentShader();
518 auto computeShader = program->getAttachedComputeShader();
519
520 // Compute the program hash. Start with the shader hashes and resource strings.
521 HashStream hashStream;
522 hashStream << vertexShader << fragmentShader << computeShader;
523
524 // Add some ANGLE metadata and Context properties, such as version and back-end.
525 hashStream << ANGLE_COMMIT_HASH << context->getClientMajorVersion()
526 << context->getClientMinorVersion() << context->getString(GL_RENDERER);
527
528 // Hash pre-link program properties.
529 hashStream << program->getAttributeBindings() << program->getUniformLocationBindings()
530 << program->getFragmentInputBindings()
531 << program->getState().getTransformFeedbackVaryingNames()
532 << program->getState().getTransformFeedbackBufferMode();
533
534 // Call the secure SHA hashing function.
535 const std::string &programKey = hashStream.str();
536 angle::base::SHA1HashBytes(reinterpret_cast<const unsigned char *>(programKey.c_str()),
537 programKey.length(), hashOut->data());
538}
539
540LinkResult MemoryProgramCache::getProgram(const Context *context,
541 const Program *program,
542 ProgramState *state,
543 ProgramHash *hashOut)
544{
545 ComputeHash(context, program, hashOut);
546 const angle::MemoryBuffer *binaryProgram = nullptr;
547 LinkResult result(false);
548 if (get(*hashOut, &binaryProgram))
549 {
550 InfoLog infoLog;
551 ANGLE_TRY_RESULT(Deserialize(context, program, state, binaryProgram->data(),
552 binaryProgram->size(), infoLog),
553 result);
Jamie Madill6c58b062017-08-01 13:44:25 -0400554 ANGLE_HISTOGRAM_BOOLEAN("GPU.ANGLE.ProgramCache.LoadBinarySuccess", result.getResult());
Jamie Madill32447362017-06-28 14:53:52 -0400555 if (!result.getResult())
556 {
557 // Cache load failed, evict.
558 if (mIssuedWarnings++ < kWarningLimit)
559 {
560 WARN() << "Failed to load binary from cache: " << infoLog.str();
561
562 if (mIssuedWarnings == kWarningLimit)
563 {
564 WARN() << "Reaching warning limit for cache load failures, silencing "
565 "subsequent warnings.";
566 }
567 }
568 remove(*hashOut);
569 }
570 }
571 return result;
572}
573
574bool MemoryProgramCache::get(const ProgramHash &programHash, const angle::MemoryBuffer **programOut)
575{
Jamie Madill6c58b062017-08-01 13:44:25 -0400576 const CacheEntry *entry = nullptr;
577 if (!mProgramBinaryCache.get(programHash, &entry))
578 {
579 ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.ProgramCache.CacheResult", kCacheMiss,
580 kCacheResultMax);
581 return false;
582 }
583
584 if (entry->second == CacheSource::PutProgram)
585 {
586 ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.ProgramCache.CacheResult", kCacheHitMemory,
587 kCacheResultMax);
588 }
589 else
590 {
591 ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.ProgramCache.CacheResult", kCacheHitDisk,
592 kCacheResultMax);
593 }
594
595 *programOut = &entry->first;
596 return true;
Jamie Madill32447362017-06-28 14:53:52 -0400597}
598
Jamie Madillc43be722017-07-13 16:22:14 -0400599bool MemoryProgramCache::getAt(size_t index,
600 ProgramHash *hashOut,
601 const angle::MemoryBuffer **programOut)
602{
Jamie Madill6c58b062017-08-01 13:44:25 -0400603 const CacheEntry *entry = nullptr;
604 if (!mProgramBinaryCache.getAt(index, hashOut, &entry))
605 {
606 return false;
607 }
608
609 *programOut = &entry->first;
610 return true;
Jamie Madillc43be722017-07-13 16:22:14 -0400611}
612
Jamie Madill32447362017-06-28 14:53:52 -0400613void MemoryProgramCache::remove(const ProgramHash &programHash)
614{
615 bool result = mProgramBinaryCache.eraseByKey(programHash);
616 ASSERT(result);
617}
618
Jamie Madill6c58b062017-08-01 13:44:25 -0400619void MemoryProgramCache::putProgram(const ProgramHash &programHash,
620 const Context *context,
621 const Program *program)
Jamie Madill32447362017-06-28 14:53:52 -0400622{
Jamie Madill6c58b062017-08-01 13:44:25 -0400623 CacheEntry newEntry;
624 Serialize(context, program, &newEntry.first);
625 newEntry.second = CacheSource::PutProgram;
626
627 ANGLE_HISTOGRAM_COUNTS("GPU.ANGLE.ProgramCache.ProgramBinarySizeBytes",
628 static_cast<int>(newEntry.first.size()));
629
630 const CacheEntry *result =
631 mProgramBinaryCache.put(programHash, std::move(newEntry), newEntry.first.size());
Jamie Madill360daee2017-06-29 10:36:19 -0400632 if (!result)
Jamie Madill32447362017-06-28 14:53:52 -0400633 {
634 ERR() << "Failed to store binary program in memory cache, program is too large.";
635 }
Jamie Madill360daee2017-06-29 10:36:19 -0400636 else
637 {
638 auto *platform = ANGLEPlatformCurrent();
Jamie Madill6c58b062017-08-01 13:44:25 -0400639 platform->cacheProgram(platform, programHash, result->first.size(), result->first.data());
Jamie Madill360daee2017-06-29 10:36:19 -0400640 }
Jamie Madill32447362017-06-28 14:53:52 -0400641}
642
Jamie Madill4c19a8a2017-07-24 11:46:06 -0400643void MemoryProgramCache::updateProgram(const Context *context, const Program *program)
644{
645 gl::ProgramHash programHash;
646 ComputeHash(context, program, &programHash);
647 putProgram(programHash, context, program);
648}
649
Jamie Madillc43be722017-07-13 16:22:14 -0400650void MemoryProgramCache::putBinary(const ProgramHash &programHash,
Jamie Madill32447362017-06-28 14:53:52 -0400651 const uint8_t *binary,
652 size_t length)
653{
654 // Copy the binary.
Jamie Madill6c58b062017-08-01 13:44:25 -0400655 CacheEntry newEntry;
656 newEntry.first.resize(length);
657 memcpy(newEntry.first.data(), binary, length);
658 newEntry.second = CacheSource::PutBinary;
Jamie Madill32447362017-06-28 14:53:52 -0400659
Jamie Madill32447362017-06-28 14:53:52 -0400660 // Store the binary.
Jamie Madill6c58b062017-08-01 13:44:25 -0400661 const CacheEntry *result = mProgramBinaryCache.put(programHash, std::move(newEntry), length);
Jamie Madillc43be722017-07-13 16:22:14 -0400662 if (!result)
663 {
664 ERR() << "Failed to store binary program in memory cache, program is too large.";
665 }
Jamie Madill32447362017-06-28 14:53:52 -0400666}
667
668void MemoryProgramCache::clear()
669{
670 mProgramBinaryCache.clear();
671 mIssuedWarnings = 0;
672}
673
Jamie Madillc43be722017-07-13 16:22:14 -0400674void MemoryProgramCache::resize(size_t maxCacheSizeBytes)
675{
676 mProgramBinaryCache.resize(maxCacheSizeBytes);
677}
678
679size_t MemoryProgramCache::entryCount() const
680{
681 return mProgramBinaryCache.entryCount();
682}
683
684size_t MemoryProgramCache::trim(size_t limit)
685{
686 return mProgramBinaryCache.shrinkToSize(limit);
687}
688
689size_t MemoryProgramCache::size() const
690{
691 return mProgramBinaryCache.size();
692}
693
694size_t MemoryProgramCache::maxSize() const
695{
696 return mProgramBinaryCache.maxSize();
697}
698
Jamie Madill4f86d052017-06-05 12:59:26 -0400699} // namespace gl