blob: b6c2d73e16d9afce40da70ed3d5f335c306f1996 [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
15#include "common/version.h"
16#include "libANGLE/BinaryStream.h"
17#include "libANGLE/Context.h"
18#include "libANGLE/Uniform.h"
Jamie Madill6c58b062017-08-01 13:44:25 -040019#include "libANGLE/histogram_macros.h"
Jamie Madill4f86d052017-06-05 12:59:26 -040020#include "libANGLE/renderer/ProgramImpl.h"
Jamie Madill360daee2017-06-29 10:36:19 -040021#include "platform/Platform.h"
Jamie Madill4f86d052017-06-05 12:59:26 -040022
23namespace gl
24{
25
26namespace
27{
Jamie Madill6c58b062017-08-01 13:44:25 -040028enum CacheResult
29{
30 kCacheMiss,
31 kCacheHitMemory,
32 kCacheHitDisk,
33 kCacheResultMax,
34};
35
Jamie Madill32447362017-06-28 14:53:52 -040036constexpr unsigned int kWarningLimit = 3;
Jamie Madill4f86d052017-06-05 12:59:26 -040037
38void WriteShaderVar(BinaryOutputStream *stream, const sh::ShaderVariable &var)
39{
40 stream->writeInt(var.type);
41 stream->writeInt(var.precision);
42 stream->writeString(var.name);
43 stream->writeString(var.mappedName);
44 stream->writeInt(var.arraySize);
45 stream->writeInt(var.staticUse);
46 stream->writeString(var.structName);
47 ASSERT(var.fields.empty());
48}
49
50void LoadShaderVar(BinaryInputStream *stream, sh::ShaderVariable *var)
51{
52 var->type = stream->readInt<GLenum>();
53 var->precision = stream->readInt<GLenum>();
54 var->name = stream->readString();
55 var->mappedName = stream->readString();
56 var->arraySize = stream->readInt<unsigned int>();
57 var->staticUse = stream->readBool();
58 var->structName = stream->readString();
59}
60
jchen10eaef1e52017-06-13 10:44:11 +080061void WriteShaderVariableBuffer(BinaryOutputStream *stream, const ShaderVariableBuffer &var)
62{
63 stream->writeInt(var.binding);
64 stream->writeInt(var.dataSize);
65
66 stream->writeInt(var.vertexStaticUse);
67 stream->writeInt(var.fragmentStaticUse);
68 stream->writeInt(var.computeStaticUse);
69
70 stream->writeInt(var.memberIndexes.size());
71 for (unsigned int memberCounterIndex : var.memberIndexes)
72 {
73 stream->writeInt(memberCounterIndex);
74 }
75}
76
77void LoadShaderVariableBuffer(BinaryInputStream *stream, ShaderVariableBuffer *var)
78{
79 var->binding = stream->readInt<int>();
80 var->dataSize = stream->readInt<unsigned int>();
81 var->vertexStaticUse = stream->readBool();
82 var->fragmentStaticUse = stream->readBool();
83 var->computeStaticUse = stream->readBool();
84
85 unsigned int numMembers = stream->readInt<unsigned int>();
86 for (unsigned int blockMemberIndex = 0; blockMemberIndex < numMembers; blockMemberIndex++)
87 {
88 var->memberIndexes.push_back(stream->readInt<unsigned int>());
89 }
90}
91
Jamie Madill32447362017-06-28 14:53:52 -040092class HashStream final : angle::NonCopyable
93{
94 public:
95 std::string str() { return mStringStream.str(); }
96
97 template <typename T>
98 HashStream &operator<<(T value)
99 {
100 mStringStream << value << kSeparator;
101 return *this;
102 }
103
104 private:
105 static constexpr char kSeparator = ':';
106 std::ostringstream mStringStream;
107};
108
109HashStream &operator<<(HashStream &stream, const Shader *shader)
110{
111 if (shader)
112 {
113 stream << shader->getSourceString().c_str() << shader->getSourceString().length()
114 << shader->getCompilerResourcesString().c_str();
115 }
116 return stream;
117}
118
119HashStream &operator<<(HashStream &stream, const Program::Bindings &bindings)
120{
121 for (const auto &binding : bindings)
122 {
123 stream << binding.first << binding.second;
124 }
125 return stream;
126}
127
128HashStream &operator<<(HashStream &stream, const std::vector<std::string> &strings)
129{
130 for (const auto &str : strings)
131 {
132 stream << str;
133 }
134 return stream;
135}
136
Jamie Madill4f86d052017-06-05 12:59:26 -0400137} // anonymous namespace
138
Jamie Madill32447362017-06-28 14:53:52 -0400139MemoryProgramCache::MemoryProgramCache(size_t maxCacheSizeBytes)
140 : mProgramBinaryCache(maxCacheSizeBytes), mIssuedWarnings(0)
141{
142}
143
144MemoryProgramCache::~MemoryProgramCache()
145{
146}
147
Jamie Madill4f86d052017-06-05 12:59:26 -0400148// static
149LinkResult MemoryProgramCache::Deserialize(const Context *context,
150 const Program *program,
151 ProgramState *state,
152 const uint8_t *binary,
153 size_t length,
154 InfoLog &infoLog)
155{
156 BinaryInputStream stream(binary, length);
157
158 unsigned char commitString[ANGLE_COMMIT_HASH_SIZE];
159 stream.readBytes(commitString, ANGLE_COMMIT_HASH_SIZE);
160 if (memcmp(commitString, ANGLE_COMMIT_HASH, sizeof(unsigned char) * ANGLE_COMMIT_HASH_SIZE) !=
161 0)
162 {
163 infoLog << "Invalid program binary version.";
164 return false;
165 }
166
167 int majorVersion = stream.readInt<int>();
168 int minorVersion = stream.readInt<int>();
169 if (majorVersion != context->getClientMajorVersion() ||
170 minorVersion != context->getClientMinorVersion())
171 {
172 infoLog << "Cannot load program binaries across different ES context versions.";
173 return false;
174 }
175
176 state->mComputeShaderLocalSize[0] = stream.readInt<int>();
177 state->mComputeShaderLocalSize[1] = stream.readInt<int>();
178 state->mComputeShaderLocalSize[2] = stream.readInt<int>();
179
Martin Radev7cf61662017-07-26 17:10:53 +0300180 state->mNumViews = stream.readInt<int>();
181
Jamie Madill4f86d052017-06-05 12:59:26 -0400182 static_assert(MAX_VERTEX_ATTRIBS <= sizeof(unsigned long) * 8,
183 "Too many vertex attribs for mask");
184 state->mActiveAttribLocationsMask = stream.readInt<unsigned long>();
185
186 unsigned int attribCount = stream.readInt<unsigned int>();
187 ASSERT(state->mAttributes.empty());
188 for (unsigned int attribIndex = 0; attribIndex < attribCount; ++attribIndex)
189 {
190 sh::Attribute attrib;
191 LoadShaderVar(&stream, &attrib);
192 attrib.location = stream.readInt<int>();
193 state->mAttributes.push_back(attrib);
194 }
195
196 unsigned int uniformCount = stream.readInt<unsigned int>();
197 ASSERT(state->mUniforms.empty());
198 for (unsigned int uniformIndex = 0; uniformIndex < uniformCount; ++uniformIndex)
199 {
200 LinkedUniform uniform;
201 LoadShaderVar(&stream, &uniform);
202
jchen10eaef1e52017-06-13 10:44:11 +0800203 uniform.bufferIndex = stream.readInt<int>();
Jamie Madill4f86d052017-06-05 12:59:26 -0400204 uniform.blockInfo.offset = stream.readInt<int>();
205 uniform.blockInfo.arrayStride = stream.readInt<int>();
206 uniform.blockInfo.matrixStride = stream.readInt<int>();
207 uniform.blockInfo.isRowMajorMatrix = stream.readBool();
208
209 state->mUniforms.push_back(uniform);
210 }
211
212 const unsigned int uniformIndexCount = stream.readInt<unsigned int>();
213 ASSERT(state->mUniformLocations.empty());
214 for (unsigned int uniformIndexIndex = 0; uniformIndexIndex < uniformIndexCount;
215 uniformIndexIndex++)
216 {
217 VariableLocation variable;
218 stream.readString(&variable.name);
219 stream.readInt(&variable.element);
220 stream.readInt(&variable.index);
221 stream.readBool(&variable.used);
222 stream.readBool(&variable.ignored);
223
224 state->mUniformLocations.push_back(variable);
225 }
226
227 unsigned int uniformBlockCount = stream.readInt<unsigned int>();
228 ASSERT(state->mUniformBlocks.empty());
229 for (unsigned int uniformBlockIndex = 0; uniformBlockIndex < uniformBlockCount;
230 ++uniformBlockIndex)
231 {
232 UniformBlock uniformBlock;
233 stream.readString(&uniformBlock.name);
234 stream.readBool(&uniformBlock.isArray);
235 stream.readInt(&uniformBlock.arrayElement);
Jamie Madill4f86d052017-06-05 12:59:26 -0400236
jchen10eaef1e52017-06-13 10:44:11 +0800237 LoadShaderVariableBuffer(&stream, &uniformBlock);
Jamie Madill4f86d052017-06-05 12:59:26 -0400238
239 state->mUniformBlocks.push_back(uniformBlock);
Jamie Madill4f86d052017-06-05 12:59:26 -0400240
jchen107a20b972017-06-13 14:25:26 +0800241 state->mActiveUniformBlockBindings.set(uniformBlockIndex, uniformBlock.binding != 0);
Jamie Madill4f86d052017-06-05 12:59:26 -0400242 }
jchen10eaef1e52017-06-13 10:44:11 +0800243 unsigned int atomicCounterBufferCount = stream.readInt<unsigned int>();
244 ASSERT(state->mAtomicCounterBuffers.empty());
245 for (unsigned int bufferIndex = 0; bufferIndex < atomicCounterBufferCount; ++bufferIndex)
246 {
247 AtomicCounterBuffer atomicCounterBuffer;
248 LoadShaderVariableBuffer(&stream, &atomicCounterBuffer);
249
250 state->mAtomicCounterBuffers.push_back(atomicCounterBuffer);
251 }
Jamie Madill4f86d052017-06-05 12:59:26 -0400252
253 unsigned int transformFeedbackVaryingCount = stream.readInt<unsigned int>();
Jamie Madillffe00c02017-06-27 16:26:55 -0400254
255 // Reject programs that use transform feedback varyings if the hardware cannot support them.
256 if (transformFeedbackVaryingCount > 0 &&
257 context->getWorkarounds().disableProgramCachingForTransformFeedback)
258 {
259 infoLog << "Current driver does not support transform feedback in binary programs.";
260 return false;
261 }
262
Jamie Madill4f86d052017-06-05 12:59:26 -0400263 ASSERT(state->mLinkedTransformFeedbackVaryings.empty());
264 for (unsigned int transformFeedbackVaryingIndex = 0;
265 transformFeedbackVaryingIndex < transformFeedbackVaryingCount;
266 ++transformFeedbackVaryingIndex)
267 {
268 sh::Varying varying;
269 stream.readInt(&varying.arraySize);
270 stream.readInt(&varying.type);
271 stream.readString(&varying.name);
272
273 GLuint arrayIndex = stream.readInt<GLuint>();
274
275 state->mLinkedTransformFeedbackVaryings.emplace_back(varying, arrayIndex);
276 }
277
278 stream.readInt(&state->mTransformFeedbackBufferMode);
279
280 unsigned int outputCount = stream.readInt<unsigned int>();
281 ASSERT(state->mOutputVariables.empty());
282 for (unsigned int outputIndex = 0; outputIndex < outputCount; ++outputIndex)
283 {
284 sh::OutputVariable output;
285 LoadShaderVar(&stream, &output);
286 output.location = stream.readInt<int>();
287 state->mOutputVariables.push_back(output);
288 }
289
290 unsigned int outputVarCount = stream.readInt<unsigned int>();
291 for (unsigned int outputIndex = 0; outputIndex < outputVarCount; ++outputIndex)
292 {
293 int locationIndex = stream.readInt<int>();
294 VariableLocation locationData;
295 stream.readInt(&locationData.element);
296 stream.readInt(&locationData.index);
297 stream.readString(&locationData.name);
298 state->mOutputLocations[locationIndex] = locationData;
299 }
300
301 unsigned int outputTypeCount = stream.readInt<unsigned int>();
302 for (unsigned int outputIndex = 0; outputIndex < outputTypeCount; ++outputIndex)
303 {
304 state->mOutputVariableTypes.push_back(stream.readInt<GLenum>());
305 }
306 static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS < 8 * sizeof(uint32_t),
307 "All bits of DrawBufferMask can be contained in an uint32_t");
308 state->mActiveOutputVariables = stream.readInt<uint32_t>();
309
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800310 unsigned int samplerRangeLow = stream.readInt<unsigned int>();
311 unsigned int samplerRangeHigh = stream.readInt<unsigned int>();
312 state->mSamplerUniformRange = RangeUI(samplerRangeLow, samplerRangeHigh);
Jamie Madill4f86d052017-06-05 12:59:26 -0400313 unsigned int samplerCount = stream.readInt<unsigned int>();
314 for (unsigned int samplerIndex = 0; samplerIndex < samplerCount; ++samplerIndex)
315 {
316 GLenum textureType = stream.readInt<GLenum>();
317 size_t bindingCount = stream.readInt<size_t>();
Jamie Madill54164b02017-08-28 15:17:37 -0400318 bool unreferenced = stream.readBool();
319 state->mSamplerBindings.emplace_back(
320 SamplerBinding(textureType, bindingCount, unreferenced));
Jamie Madill4f86d052017-06-05 12:59:26 -0400321 }
322
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800323 unsigned int imageRangeLow = stream.readInt<unsigned int>();
324 unsigned int imageRangeHigh = stream.readInt<unsigned int>();
325 state->mImageUniformRange = RangeUI(imageRangeLow, imageRangeHigh);
Xinghua Cao0328b572017-06-26 15:51:36 +0800326 unsigned int imageBindingCount = stream.readInt<unsigned int>();
327 for (unsigned int imageIndex = 0; imageIndex < imageBindingCount; ++imageIndex)
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800328 {
Xinghua Cao0328b572017-06-26 15:51:36 +0800329 unsigned int elementCount = stream.readInt<unsigned int>();
330 ImageBinding imageBinding(elementCount);
331 for (unsigned int i = 0; i < elementCount; ++i)
332 {
333 imageBinding.boundImageUnits[i] = stream.readInt<unsigned int>();
334 }
335 state->mImageBindings.emplace_back(imageBinding);
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800336 }
337
jchen10eaef1e52017-06-13 10:44:11 +0800338 unsigned int atomicCounterRangeLow = stream.readInt<unsigned int>();
339 unsigned int atomicCounterRangeHigh = stream.readInt<unsigned int>();
340 state->mAtomicCounterUniformRange = RangeUI(atomicCounterRangeLow, atomicCounterRangeHigh);
341
Jamie Madill4f86d052017-06-05 12:59:26 -0400342 return program->getImplementation()->load(context, infoLog, &stream);
343}
344
345// static
346void MemoryProgramCache::Serialize(const Context *context,
347 const gl::Program *program,
348 angle::MemoryBuffer *binaryOut)
349{
350 BinaryOutputStream stream;
351
352 stream.writeBytes(reinterpret_cast<const unsigned char *>(ANGLE_COMMIT_HASH),
353 ANGLE_COMMIT_HASH_SIZE);
354
355 // nullptr context is supported when computing binary length.
356 if (context)
357 {
358 stream.writeInt(context->getClientVersion().major);
359 stream.writeInt(context->getClientVersion().minor);
360 }
361 else
362 {
363 stream.writeInt(2);
364 stream.writeInt(0);
365 }
366
367 const auto &state = program->getState();
368
369 const auto &computeLocalSize = state.getComputeShaderLocalSize();
370
371 stream.writeInt(computeLocalSize[0]);
372 stream.writeInt(computeLocalSize[1]);
373 stream.writeInt(computeLocalSize[2]);
374
Martin Radev7cf61662017-07-26 17:10:53 +0300375 stream.writeInt(state.mNumViews);
376
Jamie Madill4f86d052017-06-05 12:59:26 -0400377 stream.writeInt(state.getActiveAttribLocationsMask().to_ulong());
378
379 stream.writeInt(state.getAttributes().size());
380 for (const sh::Attribute &attrib : state.getAttributes())
381 {
382 WriteShaderVar(&stream, attrib);
383 stream.writeInt(attrib.location);
384 }
385
386 stream.writeInt(state.getUniforms().size());
387 for (const LinkedUniform &uniform : state.getUniforms())
388 {
389 WriteShaderVar(&stream, uniform);
390
391 // FIXME: referenced
392
jchen10eaef1e52017-06-13 10:44:11 +0800393 stream.writeInt(uniform.bufferIndex);
Jamie Madill4f86d052017-06-05 12:59:26 -0400394 stream.writeInt(uniform.blockInfo.offset);
395 stream.writeInt(uniform.blockInfo.arrayStride);
396 stream.writeInt(uniform.blockInfo.matrixStride);
397 stream.writeInt(uniform.blockInfo.isRowMajorMatrix);
398 }
399
400 stream.writeInt(state.getUniformLocations().size());
401 for (const auto &variable : state.getUniformLocations())
402 {
403 stream.writeString(variable.name);
404 stream.writeInt(variable.element);
405 stream.writeInt(variable.index);
406 stream.writeInt(variable.used);
407 stream.writeInt(variable.ignored);
408 }
409
410 stream.writeInt(state.getUniformBlocks().size());
411 for (const UniformBlock &uniformBlock : state.getUniformBlocks())
412 {
413 stream.writeString(uniformBlock.name);
414 stream.writeInt(uniformBlock.isArray);
415 stream.writeInt(uniformBlock.arrayElement);
Jamie Madill4f86d052017-06-05 12:59:26 -0400416
jchen10eaef1e52017-06-13 10:44:11 +0800417 WriteShaderVariableBuffer(&stream, uniformBlock);
418 }
Jamie Madill4f86d052017-06-05 12:59:26 -0400419
jchen10eaef1e52017-06-13 10:44:11 +0800420 stream.writeInt(state.mAtomicCounterBuffers.size());
421 for (const auto &atomicCounterBuffer : state.mAtomicCounterBuffers)
422 {
423 WriteShaderVariableBuffer(&stream, atomicCounterBuffer);
Jamie Madill4f86d052017-06-05 12:59:26 -0400424 }
425
Jamie Madillffe00c02017-06-27 16:26:55 -0400426 // Warn the app layer if saving a binary with unsupported transform feedback.
427 if (!state.getLinkedTransformFeedbackVaryings().empty() &&
428 context->getWorkarounds().disableProgramCachingForTransformFeedback)
429 {
430 WARN() << "Saving program binary with transform feedback, which is not supported on this "
431 "driver.";
432 }
433
Jamie Madill4f86d052017-06-05 12:59:26 -0400434 stream.writeInt(state.getLinkedTransformFeedbackVaryings().size());
435 for (const auto &var : state.getLinkedTransformFeedbackVaryings())
436 {
437 stream.writeInt(var.arraySize);
438 stream.writeInt(var.type);
439 stream.writeString(var.name);
440
441 stream.writeIntOrNegOne(var.arrayIndex);
442 }
443
444 stream.writeInt(state.getTransformFeedbackBufferMode());
445
446 stream.writeInt(state.getOutputVariables().size());
447 for (const sh::OutputVariable &output : state.getOutputVariables())
448 {
449 WriteShaderVar(&stream, output);
450 stream.writeInt(output.location);
451 }
452
453 stream.writeInt(state.getOutputLocations().size());
454 for (const auto &outputPair : state.getOutputLocations())
455 {
456 stream.writeInt(outputPair.first);
457 stream.writeIntOrNegOne(outputPair.second.element);
458 stream.writeInt(outputPair.second.index);
459 stream.writeString(outputPair.second.name);
460 }
461
462 stream.writeInt(state.mOutputVariableTypes.size());
463 for (const auto &outputVariableType : state.mOutputVariableTypes)
464 {
465 stream.writeInt(outputVariableType);
466 }
467
468 static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS < 8 * sizeof(uint32_t),
469 "All bits of DrawBufferMask can be contained in an uint32_t");
470 stream.writeInt(static_cast<uint32_t>(state.mActiveOutputVariables.to_ulong()));
471
472 stream.writeInt(state.getSamplerUniformRange().low());
473 stream.writeInt(state.getSamplerUniformRange().high());
474
475 stream.writeInt(state.getSamplerBindings().size());
476 for (const auto &samplerBinding : state.getSamplerBindings())
477 {
478 stream.writeInt(samplerBinding.textureType);
479 stream.writeInt(samplerBinding.boundTextureUnits.size());
Jamie Madill54164b02017-08-28 15:17:37 -0400480 stream.writeInt(samplerBinding.unreferenced);
Jamie Madill4f86d052017-06-05 12:59:26 -0400481 }
482
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800483 stream.writeInt(state.getImageUniformRange().low());
484 stream.writeInt(state.getImageUniformRange().high());
485
486 stream.writeInt(state.getImageBindings().size());
487 for (const auto &imageBinding : state.getImageBindings())
488 {
Xinghua Cao0328b572017-06-26 15:51:36 +0800489 stream.writeInt(imageBinding.boundImageUnits.size());
490 for (size_t i = 0; i < imageBinding.boundImageUnits.size(); ++i)
491 {
492 stream.writeInt(imageBinding.boundImageUnits[i]);
493 }
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800494 }
495
jchen10eaef1e52017-06-13 10:44:11 +0800496 stream.writeInt(state.getAtomicCounterUniformRange().low());
497 stream.writeInt(state.getAtomicCounterUniformRange().high());
498
Jamie Madill27a60632017-06-30 15:12:01 -0400499 program->getImplementation()->save(context, &stream);
Jamie Madill4f86d052017-06-05 12:59:26 -0400500
501 ASSERT(binaryOut);
502 binaryOut->resize(stream.length());
503 memcpy(binaryOut->data(), stream.data(), stream.length());
504}
505
Jamie Madill32447362017-06-28 14:53:52 -0400506// static
507void MemoryProgramCache::ComputeHash(const Context *context,
508 const Program *program,
509 ProgramHash *hashOut)
510{
511 auto vertexShader = program->getAttachedVertexShader();
512 auto fragmentShader = program->getAttachedFragmentShader();
513 auto computeShader = program->getAttachedComputeShader();
514
515 // Compute the program hash. Start with the shader hashes and resource strings.
516 HashStream hashStream;
517 hashStream << vertexShader << fragmentShader << computeShader;
518
519 // Add some ANGLE metadata and Context properties, such as version and back-end.
520 hashStream << ANGLE_COMMIT_HASH << context->getClientMajorVersion()
521 << context->getClientMinorVersion() << context->getString(GL_RENDERER);
522
523 // Hash pre-link program properties.
524 hashStream << program->getAttributeBindings() << program->getUniformLocationBindings()
525 << program->getFragmentInputBindings()
526 << program->getState().getTransformFeedbackVaryingNames()
527 << program->getState().getTransformFeedbackBufferMode();
528
529 // Call the secure SHA hashing function.
530 const std::string &programKey = hashStream.str();
531 angle::base::SHA1HashBytes(reinterpret_cast<const unsigned char *>(programKey.c_str()),
532 programKey.length(), hashOut->data());
533}
534
535LinkResult MemoryProgramCache::getProgram(const Context *context,
536 const Program *program,
537 ProgramState *state,
538 ProgramHash *hashOut)
539{
540 ComputeHash(context, program, hashOut);
541 const angle::MemoryBuffer *binaryProgram = nullptr;
542 LinkResult result(false);
543 if (get(*hashOut, &binaryProgram))
544 {
545 InfoLog infoLog;
546 ANGLE_TRY_RESULT(Deserialize(context, program, state, binaryProgram->data(),
547 binaryProgram->size(), infoLog),
548 result);
Jamie Madill6c58b062017-08-01 13:44:25 -0400549 ANGLE_HISTOGRAM_BOOLEAN("GPU.ANGLE.ProgramCache.LoadBinarySuccess", result.getResult());
Jamie Madill32447362017-06-28 14:53:52 -0400550 if (!result.getResult())
551 {
552 // Cache load failed, evict.
553 if (mIssuedWarnings++ < kWarningLimit)
554 {
555 WARN() << "Failed to load binary from cache: " << infoLog.str();
556
557 if (mIssuedWarnings == kWarningLimit)
558 {
559 WARN() << "Reaching warning limit for cache load failures, silencing "
560 "subsequent warnings.";
561 }
562 }
563 remove(*hashOut);
564 }
565 }
566 return result;
567}
568
569bool MemoryProgramCache::get(const ProgramHash &programHash, const angle::MemoryBuffer **programOut)
570{
Jamie Madill6c58b062017-08-01 13:44:25 -0400571 const CacheEntry *entry = nullptr;
572 if (!mProgramBinaryCache.get(programHash, &entry))
573 {
574 ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.ProgramCache.CacheResult", kCacheMiss,
575 kCacheResultMax);
576 return false;
577 }
578
579 if (entry->second == CacheSource::PutProgram)
580 {
581 ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.ProgramCache.CacheResult", kCacheHitMemory,
582 kCacheResultMax);
583 }
584 else
585 {
586 ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.ProgramCache.CacheResult", kCacheHitDisk,
587 kCacheResultMax);
588 }
589
590 *programOut = &entry->first;
591 return true;
Jamie Madill32447362017-06-28 14:53:52 -0400592}
593
Jamie Madillc43be722017-07-13 16:22:14 -0400594bool MemoryProgramCache::getAt(size_t index,
595 ProgramHash *hashOut,
596 const angle::MemoryBuffer **programOut)
597{
Jamie Madill6c58b062017-08-01 13:44:25 -0400598 const CacheEntry *entry = nullptr;
599 if (!mProgramBinaryCache.getAt(index, hashOut, &entry))
600 {
601 return false;
602 }
603
604 *programOut = &entry->first;
605 return true;
Jamie Madillc43be722017-07-13 16:22:14 -0400606}
607
Jamie Madill32447362017-06-28 14:53:52 -0400608void MemoryProgramCache::remove(const ProgramHash &programHash)
609{
610 bool result = mProgramBinaryCache.eraseByKey(programHash);
611 ASSERT(result);
612}
613
Jamie Madill6c58b062017-08-01 13:44:25 -0400614void MemoryProgramCache::putProgram(const ProgramHash &programHash,
615 const Context *context,
616 const Program *program)
Jamie Madill32447362017-06-28 14:53:52 -0400617{
Jamie Madill6c58b062017-08-01 13:44:25 -0400618 CacheEntry newEntry;
619 Serialize(context, program, &newEntry.first);
620 newEntry.second = CacheSource::PutProgram;
621
622 ANGLE_HISTOGRAM_COUNTS("GPU.ANGLE.ProgramCache.ProgramBinarySizeBytes",
623 static_cast<int>(newEntry.first.size()));
624
625 const CacheEntry *result =
626 mProgramBinaryCache.put(programHash, std::move(newEntry), newEntry.first.size());
Jamie Madill360daee2017-06-29 10:36:19 -0400627 if (!result)
Jamie Madill32447362017-06-28 14:53:52 -0400628 {
629 ERR() << "Failed to store binary program in memory cache, program is too large.";
630 }
Jamie Madill360daee2017-06-29 10:36:19 -0400631 else
632 {
633 auto *platform = ANGLEPlatformCurrent();
Jamie Madill6c58b062017-08-01 13:44:25 -0400634 platform->cacheProgram(platform, programHash, result->first.size(), result->first.data());
Jamie Madill360daee2017-06-29 10:36:19 -0400635 }
Jamie Madill32447362017-06-28 14:53:52 -0400636}
637
Jamie Madill4c19a8a2017-07-24 11:46:06 -0400638void MemoryProgramCache::updateProgram(const Context *context, const Program *program)
639{
640 gl::ProgramHash programHash;
641 ComputeHash(context, program, &programHash);
642 putProgram(programHash, context, program);
643}
644
Jamie Madillc43be722017-07-13 16:22:14 -0400645void MemoryProgramCache::putBinary(const ProgramHash &programHash,
Jamie Madill32447362017-06-28 14:53:52 -0400646 const uint8_t *binary,
647 size_t length)
648{
649 // Copy the binary.
Jamie Madill6c58b062017-08-01 13:44:25 -0400650 CacheEntry newEntry;
651 newEntry.first.resize(length);
652 memcpy(newEntry.first.data(), binary, length);
653 newEntry.second = CacheSource::PutBinary;
Jamie Madill32447362017-06-28 14:53:52 -0400654
Jamie Madill32447362017-06-28 14:53:52 -0400655 // Store the binary.
Jamie Madill6c58b062017-08-01 13:44:25 -0400656 const CacheEntry *result = mProgramBinaryCache.put(programHash, std::move(newEntry), length);
Jamie Madillc43be722017-07-13 16:22:14 -0400657 if (!result)
658 {
659 ERR() << "Failed to store binary program in memory cache, program is too large.";
660 }
Jamie Madill32447362017-06-28 14:53:52 -0400661}
662
663void MemoryProgramCache::clear()
664{
665 mProgramBinaryCache.clear();
666 mIssuedWarnings = 0;
667}
668
Jamie Madillc43be722017-07-13 16:22:14 -0400669void MemoryProgramCache::resize(size_t maxCacheSizeBytes)
670{
671 mProgramBinaryCache.resize(maxCacheSizeBytes);
672}
673
674size_t MemoryProgramCache::entryCount() const
675{
676 return mProgramBinaryCache.entryCount();
677}
678
679size_t MemoryProgramCache::trim(size_t limit)
680{
681 return mProgramBinaryCache.shrinkToSize(limit);
682}
683
684size_t MemoryProgramCache::size() const
685{
686 return mProgramBinaryCache.size();
687}
688
689size_t MemoryProgramCache::maxSize() const
690{
691 return mProgramBinaryCache.maxSize();
692}
693
Jamie Madill4f86d052017-06-05 12:59:26 -0400694} // namespace gl