blob: c86c58ba65f6786d52a07fc7a0b5a20ffe1e5c8f [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);
Olli Etuaho855d9642017-05-17 14:05:06 +0300234 stream.readString(&uniformBlock.mappedName);
Jamie Madill4f86d052017-06-05 12:59:26 -0400235 stream.readBool(&uniformBlock.isArray);
236 stream.readInt(&uniformBlock.arrayElement);
Jamie Madill4f86d052017-06-05 12:59:26 -0400237
jchen10eaef1e52017-06-13 10:44:11 +0800238 LoadShaderVariableBuffer(&stream, &uniformBlock);
Jamie Madill4f86d052017-06-05 12:59:26 -0400239
240 state->mUniformBlocks.push_back(uniformBlock);
Jamie Madill4f86d052017-06-05 12:59:26 -0400241
jchen107a20b972017-06-13 14:25:26 +0800242 state->mActiveUniformBlockBindings.set(uniformBlockIndex, uniformBlock.binding != 0);
Jamie Madill4f86d052017-06-05 12:59:26 -0400243 }
jchen10eaef1e52017-06-13 10:44:11 +0800244 unsigned int atomicCounterBufferCount = stream.readInt<unsigned int>();
245 ASSERT(state->mAtomicCounterBuffers.empty());
246 for (unsigned int bufferIndex = 0; bufferIndex < atomicCounterBufferCount; ++bufferIndex)
247 {
248 AtomicCounterBuffer atomicCounterBuffer;
249 LoadShaderVariableBuffer(&stream, &atomicCounterBuffer);
250
251 state->mAtomicCounterBuffers.push_back(atomicCounterBuffer);
252 }
Jamie Madill4f86d052017-06-05 12:59:26 -0400253
254 unsigned int transformFeedbackVaryingCount = stream.readInt<unsigned int>();
Jamie Madillffe00c02017-06-27 16:26:55 -0400255
256 // Reject programs that use transform feedback varyings if the hardware cannot support them.
257 if (transformFeedbackVaryingCount > 0 &&
258 context->getWorkarounds().disableProgramCachingForTransformFeedback)
259 {
260 infoLog << "Current driver does not support transform feedback in binary programs.";
261 return false;
262 }
263
Jamie Madill4f86d052017-06-05 12:59:26 -0400264 ASSERT(state->mLinkedTransformFeedbackVaryings.empty());
265 for (unsigned int transformFeedbackVaryingIndex = 0;
266 transformFeedbackVaryingIndex < transformFeedbackVaryingCount;
267 ++transformFeedbackVaryingIndex)
268 {
269 sh::Varying varying;
270 stream.readInt(&varying.arraySize);
271 stream.readInt(&varying.type);
272 stream.readString(&varying.name);
273
274 GLuint arrayIndex = stream.readInt<GLuint>();
275
276 state->mLinkedTransformFeedbackVaryings.emplace_back(varying, arrayIndex);
277 }
278
279 stream.readInt(&state->mTransformFeedbackBufferMode);
280
281 unsigned int outputCount = stream.readInt<unsigned int>();
282 ASSERT(state->mOutputVariables.empty());
283 for (unsigned int outputIndex = 0; outputIndex < outputCount; ++outputIndex)
284 {
285 sh::OutputVariable output;
286 LoadShaderVar(&stream, &output);
287 output.location = stream.readInt<int>();
288 state->mOutputVariables.push_back(output);
289 }
290
291 unsigned int outputVarCount = stream.readInt<unsigned int>();
292 for (unsigned int outputIndex = 0; outputIndex < outputVarCount; ++outputIndex)
293 {
294 int locationIndex = stream.readInt<int>();
295 VariableLocation locationData;
296 stream.readInt(&locationData.element);
297 stream.readInt(&locationData.index);
298 stream.readString(&locationData.name);
299 state->mOutputLocations[locationIndex] = locationData;
300 }
301
302 unsigned int outputTypeCount = stream.readInt<unsigned int>();
303 for (unsigned int outputIndex = 0; outputIndex < outputTypeCount; ++outputIndex)
304 {
305 state->mOutputVariableTypes.push_back(stream.readInt<GLenum>());
306 }
307 static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS < 8 * sizeof(uint32_t),
308 "All bits of DrawBufferMask can be contained in an uint32_t");
309 state->mActiveOutputVariables = stream.readInt<uint32_t>();
310
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800311 unsigned int samplerRangeLow = stream.readInt<unsigned int>();
312 unsigned int samplerRangeHigh = stream.readInt<unsigned int>();
313 state->mSamplerUniformRange = RangeUI(samplerRangeLow, samplerRangeHigh);
Jamie Madill4f86d052017-06-05 12:59:26 -0400314 unsigned int samplerCount = stream.readInt<unsigned int>();
315 for (unsigned int samplerIndex = 0; samplerIndex < samplerCount; ++samplerIndex)
316 {
317 GLenum textureType = stream.readInt<GLenum>();
318 size_t bindingCount = stream.readInt<size_t>();
Jamie Madill54164b02017-08-28 15:17:37 -0400319 bool unreferenced = stream.readBool();
320 state->mSamplerBindings.emplace_back(
321 SamplerBinding(textureType, bindingCount, unreferenced));
Jamie Madill4f86d052017-06-05 12:59:26 -0400322 }
323
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800324 unsigned int imageRangeLow = stream.readInt<unsigned int>();
325 unsigned int imageRangeHigh = stream.readInt<unsigned int>();
326 state->mImageUniformRange = RangeUI(imageRangeLow, imageRangeHigh);
Xinghua Cao0328b572017-06-26 15:51:36 +0800327 unsigned int imageBindingCount = stream.readInt<unsigned int>();
328 for (unsigned int imageIndex = 0; imageIndex < imageBindingCount; ++imageIndex)
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800329 {
Xinghua Cao0328b572017-06-26 15:51:36 +0800330 unsigned int elementCount = stream.readInt<unsigned int>();
331 ImageBinding imageBinding(elementCount);
332 for (unsigned int i = 0; i < elementCount; ++i)
333 {
334 imageBinding.boundImageUnits[i] = stream.readInt<unsigned int>();
335 }
336 state->mImageBindings.emplace_back(imageBinding);
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800337 }
338
jchen10eaef1e52017-06-13 10:44:11 +0800339 unsigned int atomicCounterRangeLow = stream.readInt<unsigned int>();
340 unsigned int atomicCounterRangeHigh = stream.readInt<unsigned int>();
341 state->mAtomicCounterUniformRange = RangeUI(atomicCounterRangeLow, atomicCounterRangeHigh);
342
Jamie Madill4f86d052017-06-05 12:59:26 -0400343 return program->getImplementation()->load(context, infoLog, &stream);
344}
345
346// static
347void MemoryProgramCache::Serialize(const Context *context,
348 const gl::Program *program,
349 angle::MemoryBuffer *binaryOut)
350{
351 BinaryOutputStream stream;
352
353 stream.writeBytes(reinterpret_cast<const unsigned char *>(ANGLE_COMMIT_HASH),
354 ANGLE_COMMIT_HASH_SIZE);
355
356 // nullptr context is supported when computing binary length.
357 if (context)
358 {
359 stream.writeInt(context->getClientVersion().major);
360 stream.writeInt(context->getClientVersion().minor);
361 }
362 else
363 {
364 stream.writeInt(2);
365 stream.writeInt(0);
366 }
367
368 const auto &state = program->getState();
369
370 const auto &computeLocalSize = state.getComputeShaderLocalSize();
371
372 stream.writeInt(computeLocalSize[0]);
373 stream.writeInt(computeLocalSize[1]);
374 stream.writeInt(computeLocalSize[2]);
375
Martin Radev7cf61662017-07-26 17:10:53 +0300376 stream.writeInt(state.mNumViews);
377
Jamie Madill4f86d052017-06-05 12:59:26 -0400378 stream.writeInt(state.getActiveAttribLocationsMask().to_ulong());
379
380 stream.writeInt(state.getAttributes().size());
381 for (const sh::Attribute &attrib : state.getAttributes())
382 {
383 WriteShaderVar(&stream, attrib);
384 stream.writeInt(attrib.location);
385 }
386
387 stream.writeInt(state.getUniforms().size());
388 for (const LinkedUniform &uniform : state.getUniforms())
389 {
390 WriteShaderVar(&stream, uniform);
391
392 // FIXME: referenced
393
jchen10eaef1e52017-06-13 10:44:11 +0800394 stream.writeInt(uniform.bufferIndex);
Jamie Madill4f86d052017-06-05 12:59:26 -0400395 stream.writeInt(uniform.blockInfo.offset);
396 stream.writeInt(uniform.blockInfo.arrayStride);
397 stream.writeInt(uniform.blockInfo.matrixStride);
398 stream.writeInt(uniform.blockInfo.isRowMajorMatrix);
399 }
400
401 stream.writeInt(state.getUniformLocations().size());
402 for (const auto &variable : state.getUniformLocations())
403 {
404 stream.writeString(variable.name);
405 stream.writeInt(variable.element);
406 stream.writeInt(variable.index);
407 stream.writeInt(variable.used);
408 stream.writeInt(variable.ignored);
409 }
410
411 stream.writeInt(state.getUniformBlocks().size());
412 for (const UniformBlock &uniformBlock : state.getUniformBlocks())
413 {
414 stream.writeString(uniformBlock.name);
Olli Etuaho855d9642017-05-17 14:05:06 +0300415 stream.writeString(uniformBlock.mappedName);
Jamie Madill4f86d052017-06-05 12:59:26 -0400416 stream.writeInt(uniformBlock.isArray);
417 stream.writeInt(uniformBlock.arrayElement);
Jamie Madill4f86d052017-06-05 12:59:26 -0400418
jchen10eaef1e52017-06-13 10:44:11 +0800419 WriteShaderVariableBuffer(&stream, uniformBlock);
420 }
Jamie Madill4f86d052017-06-05 12:59:26 -0400421
jchen10eaef1e52017-06-13 10:44:11 +0800422 stream.writeInt(state.mAtomicCounterBuffers.size());
423 for (const auto &atomicCounterBuffer : state.mAtomicCounterBuffers)
424 {
425 WriteShaderVariableBuffer(&stream, atomicCounterBuffer);
Jamie Madill4f86d052017-06-05 12:59:26 -0400426 }
427
Jamie Madillffe00c02017-06-27 16:26:55 -0400428 // Warn the app layer if saving a binary with unsupported transform feedback.
429 if (!state.getLinkedTransformFeedbackVaryings().empty() &&
430 context->getWorkarounds().disableProgramCachingForTransformFeedback)
431 {
432 WARN() << "Saving program binary with transform feedback, which is not supported on this "
433 "driver.";
434 }
435
Jamie Madill4f86d052017-06-05 12:59:26 -0400436 stream.writeInt(state.getLinkedTransformFeedbackVaryings().size());
437 for (const auto &var : state.getLinkedTransformFeedbackVaryings())
438 {
439 stream.writeInt(var.arraySize);
440 stream.writeInt(var.type);
441 stream.writeString(var.name);
442
443 stream.writeIntOrNegOne(var.arrayIndex);
444 }
445
446 stream.writeInt(state.getTransformFeedbackBufferMode());
447
448 stream.writeInt(state.getOutputVariables().size());
449 for (const sh::OutputVariable &output : state.getOutputVariables())
450 {
451 WriteShaderVar(&stream, output);
452 stream.writeInt(output.location);
453 }
454
455 stream.writeInt(state.getOutputLocations().size());
456 for (const auto &outputPair : state.getOutputLocations())
457 {
458 stream.writeInt(outputPair.first);
459 stream.writeIntOrNegOne(outputPair.second.element);
460 stream.writeInt(outputPair.second.index);
461 stream.writeString(outputPair.second.name);
462 }
463
464 stream.writeInt(state.mOutputVariableTypes.size());
465 for (const auto &outputVariableType : state.mOutputVariableTypes)
466 {
467 stream.writeInt(outputVariableType);
468 }
469
470 static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS < 8 * sizeof(uint32_t),
471 "All bits of DrawBufferMask can be contained in an uint32_t");
472 stream.writeInt(static_cast<uint32_t>(state.mActiveOutputVariables.to_ulong()));
473
474 stream.writeInt(state.getSamplerUniformRange().low());
475 stream.writeInt(state.getSamplerUniformRange().high());
476
477 stream.writeInt(state.getSamplerBindings().size());
478 for (const auto &samplerBinding : state.getSamplerBindings())
479 {
480 stream.writeInt(samplerBinding.textureType);
481 stream.writeInt(samplerBinding.boundTextureUnits.size());
Jamie Madill54164b02017-08-28 15:17:37 -0400482 stream.writeInt(samplerBinding.unreferenced);
Jamie Madill4f86d052017-06-05 12:59:26 -0400483 }
484
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800485 stream.writeInt(state.getImageUniformRange().low());
486 stream.writeInt(state.getImageUniformRange().high());
487
488 stream.writeInt(state.getImageBindings().size());
489 for (const auto &imageBinding : state.getImageBindings())
490 {
Xinghua Cao0328b572017-06-26 15:51:36 +0800491 stream.writeInt(imageBinding.boundImageUnits.size());
492 for (size_t i = 0; i < imageBinding.boundImageUnits.size(); ++i)
493 {
494 stream.writeInt(imageBinding.boundImageUnits[i]);
495 }
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800496 }
497
jchen10eaef1e52017-06-13 10:44:11 +0800498 stream.writeInt(state.getAtomicCounterUniformRange().low());
499 stream.writeInt(state.getAtomicCounterUniformRange().high());
500
Jamie Madill27a60632017-06-30 15:12:01 -0400501 program->getImplementation()->save(context, &stream);
Jamie Madill4f86d052017-06-05 12:59:26 -0400502
503 ASSERT(binaryOut);
504 binaryOut->resize(stream.length());
505 memcpy(binaryOut->data(), stream.data(), stream.length());
506}
507
Jamie Madill32447362017-06-28 14:53:52 -0400508// static
509void MemoryProgramCache::ComputeHash(const Context *context,
510 const Program *program,
511 ProgramHash *hashOut)
512{
513 auto vertexShader = program->getAttachedVertexShader();
514 auto fragmentShader = program->getAttachedFragmentShader();
515 auto computeShader = program->getAttachedComputeShader();
516
517 // Compute the program hash. Start with the shader hashes and resource strings.
518 HashStream hashStream;
519 hashStream << vertexShader << fragmentShader << computeShader;
520
521 // Add some ANGLE metadata and Context properties, such as version and back-end.
522 hashStream << ANGLE_COMMIT_HASH << context->getClientMajorVersion()
523 << context->getClientMinorVersion() << context->getString(GL_RENDERER);
524
525 // Hash pre-link program properties.
526 hashStream << program->getAttributeBindings() << program->getUniformLocationBindings()
527 << program->getFragmentInputBindings()
528 << program->getState().getTransformFeedbackVaryingNames()
529 << program->getState().getTransformFeedbackBufferMode();
530
531 // Call the secure SHA hashing function.
532 const std::string &programKey = hashStream.str();
533 angle::base::SHA1HashBytes(reinterpret_cast<const unsigned char *>(programKey.c_str()),
534 programKey.length(), hashOut->data());
535}
536
537LinkResult MemoryProgramCache::getProgram(const Context *context,
538 const Program *program,
539 ProgramState *state,
540 ProgramHash *hashOut)
541{
542 ComputeHash(context, program, hashOut);
543 const angle::MemoryBuffer *binaryProgram = nullptr;
544 LinkResult result(false);
545 if (get(*hashOut, &binaryProgram))
546 {
547 InfoLog infoLog;
548 ANGLE_TRY_RESULT(Deserialize(context, program, state, binaryProgram->data(),
549 binaryProgram->size(), infoLog),
550 result);
Jamie Madill6c58b062017-08-01 13:44:25 -0400551 ANGLE_HISTOGRAM_BOOLEAN("GPU.ANGLE.ProgramCache.LoadBinarySuccess", result.getResult());
Jamie Madill32447362017-06-28 14:53:52 -0400552 if (!result.getResult())
553 {
554 // Cache load failed, evict.
555 if (mIssuedWarnings++ < kWarningLimit)
556 {
557 WARN() << "Failed to load binary from cache: " << infoLog.str();
558
559 if (mIssuedWarnings == kWarningLimit)
560 {
561 WARN() << "Reaching warning limit for cache load failures, silencing "
562 "subsequent warnings.";
563 }
564 }
565 remove(*hashOut);
566 }
567 }
568 return result;
569}
570
571bool MemoryProgramCache::get(const ProgramHash &programHash, const angle::MemoryBuffer **programOut)
572{
Jamie Madill6c58b062017-08-01 13:44:25 -0400573 const CacheEntry *entry = nullptr;
574 if (!mProgramBinaryCache.get(programHash, &entry))
575 {
576 ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.ProgramCache.CacheResult", kCacheMiss,
577 kCacheResultMax);
578 return false;
579 }
580
581 if (entry->second == CacheSource::PutProgram)
582 {
583 ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.ProgramCache.CacheResult", kCacheHitMemory,
584 kCacheResultMax);
585 }
586 else
587 {
588 ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.ProgramCache.CacheResult", kCacheHitDisk,
589 kCacheResultMax);
590 }
591
592 *programOut = &entry->first;
593 return true;
Jamie Madill32447362017-06-28 14:53:52 -0400594}
595
Jamie Madillc43be722017-07-13 16:22:14 -0400596bool MemoryProgramCache::getAt(size_t index,
597 ProgramHash *hashOut,
598 const angle::MemoryBuffer **programOut)
599{
Jamie Madill6c58b062017-08-01 13:44:25 -0400600 const CacheEntry *entry = nullptr;
601 if (!mProgramBinaryCache.getAt(index, hashOut, &entry))
602 {
603 return false;
604 }
605
606 *programOut = &entry->first;
607 return true;
Jamie Madillc43be722017-07-13 16:22:14 -0400608}
609
Jamie Madill32447362017-06-28 14:53:52 -0400610void MemoryProgramCache::remove(const ProgramHash &programHash)
611{
612 bool result = mProgramBinaryCache.eraseByKey(programHash);
613 ASSERT(result);
614}
615
Jamie Madill6c58b062017-08-01 13:44:25 -0400616void MemoryProgramCache::putProgram(const ProgramHash &programHash,
617 const Context *context,
618 const Program *program)
Jamie Madill32447362017-06-28 14:53:52 -0400619{
Jamie Madill6c58b062017-08-01 13:44:25 -0400620 CacheEntry newEntry;
621 Serialize(context, program, &newEntry.first);
622 newEntry.second = CacheSource::PutProgram;
623
624 ANGLE_HISTOGRAM_COUNTS("GPU.ANGLE.ProgramCache.ProgramBinarySizeBytes",
625 static_cast<int>(newEntry.first.size()));
626
627 const CacheEntry *result =
628 mProgramBinaryCache.put(programHash, std::move(newEntry), newEntry.first.size());
Jamie Madill360daee2017-06-29 10:36:19 -0400629 if (!result)
Jamie Madill32447362017-06-28 14:53:52 -0400630 {
631 ERR() << "Failed to store binary program in memory cache, program is too large.";
632 }
Jamie Madill360daee2017-06-29 10:36:19 -0400633 else
634 {
635 auto *platform = ANGLEPlatformCurrent();
Jamie Madill6c58b062017-08-01 13:44:25 -0400636 platform->cacheProgram(platform, programHash, result->first.size(), result->first.data());
Jamie Madill360daee2017-06-29 10:36:19 -0400637 }
Jamie Madill32447362017-06-28 14:53:52 -0400638}
639
Jamie Madill4c19a8a2017-07-24 11:46:06 -0400640void MemoryProgramCache::updateProgram(const Context *context, const Program *program)
641{
642 gl::ProgramHash programHash;
643 ComputeHash(context, program, &programHash);
644 putProgram(programHash, context, program);
645}
646
Jamie Madillc43be722017-07-13 16:22:14 -0400647void MemoryProgramCache::putBinary(const ProgramHash &programHash,
Jamie Madill32447362017-06-28 14:53:52 -0400648 const uint8_t *binary,
649 size_t length)
650{
651 // Copy the binary.
Jamie Madill6c58b062017-08-01 13:44:25 -0400652 CacheEntry newEntry;
653 newEntry.first.resize(length);
654 memcpy(newEntry.first.data(), binary, length);
655 newEntry.second = CacheSource::PutBinary;
Jamie Madill32447362017-06-28 14:53:52 -0400656
Jamie Madill32447362017-06-28 14:53:52 -0400657 // Store the binary.
Jamie Madill6c58b062017-08-01 13:44:25 -0400658 const CacheEntry *result = mProgramBinaryCache.put(programHash, std::move(newEntry), length);
Jamie Madillc43be722017-07-13 16:22:14 -0400659 if (!result)
660 {
661 ERR() << "Failed to store binary program in memory cache, program is too large.";
662 }
Jamie Madill32447362017-06-28 14:53:52 -0400663}
664
665void MemoryProgramCache::clear()
666{
667 mProgramBinaryCache.clear();
668 mIssuedWarnings = 0;
669}
670
Jamie Madillc43be722017-07-13 16:22:14 -0400671void MemoryProgramCache::resize(size_t maxCacheSizeBytes)
672{
673 mProgramBinaryCache.resize(maxCacheSizeBytes);
674}
675
676size_t MemoryProgramCache::entryCount() const
677{
678 return mProgramBinaryCache.entryCount();
679}
680
681size_t MemoryProgramCache::trim(size_t limit)
682{
683 return mProgramBinaryCache.shrinkToSize(limit);
684}
685
686size_t MemoryProgramCache::size() const
687{
688 return mProgramBinaryCache.size();
689}
690
691size_t MemoryProgramCache::maxSize() const
692{
693 return mProgramBinaryCache.maxSize();
694}
695
Jamie Madill4f86d052017-06-05 12:59:26 -0400696} // namespace gl