blob: 276ae367b8444d90f78e09288b24334d6cf34298 [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
Jiajia Qin729b2c62017-08-14 09:36:11 +080093void WriteInterfaceBlock(BinaryOutputStream *stream, const InterfaceBlock &block)
94{
95 stream->writeString(block.name);
96 stream->writeString(block.mappedName);
97 stream->writeInt(block.isArray);
98 stream->writeInt(block.arrayElement);
99
100 WriteShaderVariableBuffer(stream, block);
101}
102
103void LoadInterfaceBlock(BinaryInputStream *stream, InterfaceBlock *block)
104{
105 block->name = stream->readString();
106 block->mappedName = stream->readString();
107 block->isArray = stream->readBool();
108 block->arrayElement = stream->readInt<unsigned int>();
109
110 LoadShaderVariableBuffer(stream, block);
111}
112
Jamie Madill32447362017-06-28 14:53:52 -0400113class HashStream final : angle::NonCopyable
114{
115 public:
116 std::string str() { return mStringStream.str(); }
117
118 template <typename T>
119 HashStream &operator<<(T value)
120 {
121 mStringStream << value << kSeparator;
122 return *this;
123 }
124
125 private:
126 static constexpr char kSeparator = ':';
127 std::ostringstream mStringStream;
128};
129
130HashStream &operator<<(HashStream &stream, const Shader *shader)
131{
132 if (shader)
133 {
134 stream << shader->getSourceString().c_str() << shader->getSourceString().length()
135 << shader->getCompilerResourcesString().c_str();
136 }
137 return stream;
138}
139
140HashStream &operator<<(HashStream &stream, const Program::Bindings &bindings)
141{
142 for (const auto &binding : bindings)
143 {
144 stream << binding.first << binding.second;
145 }
146 return stream;
147}
148
149HashStream &operator<<(HashStream &stream, const std::vector<std::string> &strings)
150{
151 for (const auto &str : strings)
152 {
153 stream << str;
154 }
155 return stream;
156}
157
Jamie Madill4f86d052017-06-05 12:59:26 -0400158} // anonymous namespace
159
Jamie Madill32447362017-06-28 14:53:52 -0400160MemoryProgramCache::MemoryProgramCache(size_t maxCacheSizeBytes)
161 : mProgramBinaryCache(maxCacheSizeBytes), mIssuedWarnings(0)
162{
163}
164
165MemoryProgramCache::~MemoryProgramCache()
166{
167}
168
Jamie Madill4f86d052017-06-05 12:59:26 -0400169// static
170LinkResult MemoryProgramCache::Deserialize(const Context *context,
171 const Program *program,
172 ProgramState *state,
173 const uint8_t *binary,
174 size_t length,
175 InfoLog &infoLog)
176{
177 BinaryInputStream stream(binary, length);
178
179 unsigned char commitString[ANGLE_COMMIT_HASH_SIZE];
180 stream.readBytes(commitString, ANGLE_COMMIT_HASH_SIZE);
181 if (memcmp(commitString, ANGLE_COMMIT_HASH, sizeof(unsigned char) * ANGLE_COMMIT_HASH_SIZE) !=
182 0)
183 {
184 infoLog << "Invalid program binary version.";
185 return false;
186 }
187
188 int majorVersion = stream.readInt<int>();
189 int minorVersion = stream.readInt<int>();
190 if (majorVersion != context->getClientMajorVersion() ||
191 minorVersion != context->getClientMinorVersion())
192 {
193 infoLog << "Cannot load program binaries across different ES context versions.";
194 return false;
195 }
196
197 state->mComputeShaderLocalSize[0] = stream.readInt<int>();
198 state->mComputeShaderLocalSize[1] = stream.readInt<int>();
199 state->mComputeShaderLocalSize[2] = stream.readInt<int>();
200
Martin Radev7cf61662017-07-26 17:10:53 +0300201 state->mNumViews = stream.readInt<int>();
202
Jamie Madill4f86d052017-06-05 12:59:26 -0400203 static_assert(MAX_VERTEX_ATTRIBS <= sizeof(unsigned long) * 8,
204 "Too many vertex attribs for mask");
205 state->mActiveAttribLocationsMask = stream.readInt<unsigned long>();
206
207 unsigned int attribCount = stream.readInt<unsigned int>();
208 ASSERT(state->mAttributes.empty());
209 for (unsigned int attribIndex = 0; attribIndex < attribCount; ++attribIndex)
210 {
211 sh::Attribute attrib;
212 LoadShaderVar(&stream, &attrib);
213 attrib.location = stream.readInt<int>();
214 state->mAttributes.push_back(attrib);
215 }
216
217 unsigned int uniformCount = stream.readInt<unsigned int>();
218 ASSERT(state->mUniforms.empty());
219 for (unsigned int uniformIndex = 0; uniformIndex < uniformCount; ++uniformIndex)
220 {
221 LinkedUniform uniform;
222 LoadShaderVar(&stream, &uniform);
223
jchen10eaef1e52017-06-13 10:44:11 +0800224 uniform.bufferIndex = stream.readInt<int>();
Jamie Madill4f86d052017-06-05 12:59:26 -0400225 uniform.blockInfo.offset = stream.readInt<int>();
226 uniform.blockInfo.arrayStride = stream.readInt<int>();
227 uniform.blockInfo.matrixStride = stream.readInt<int>();
228 uniform.blockInfo.isRowMajorMatrix = stream.readBool();
229
Jamie Madillf00f7ff2017-08-31 14:39:15 -0400230 uniform.typeInfo = &GetUniformTypeInfo(uniform.type);
231
Jamie Madill4f86d052017-06-05 12:59:26 -0400232 state->mUniforms.push_back(uniform);
233 }
234
235 const unsigned int uniformIndexCount = stream.readInt<unsigned int>();
236 ASSERT(state->mUniformLocations.empty());
237 for (unsigned int uniformIndexIndex = 0; uniformIndexIndex < uniformIndexCount;
238 uniformIndexIndex++)
239 {
240 VariableLocation variable;
241 stream.readString(&variable.name);
242 stream.readInt(&variable.element);
243 stream.readInt(&variable.index);
244 stream.readBool(&variable.used);
245 stream.readBool(&variable.ignored);
246
247 state->mUniformLocations.push_back(variable);
248 }
249
250 unsigned int uniformBlockCount = stream.readInt<unsigned int>();
251 ASSERT(state->mUniformBlocks.empty());
252 for (unsigned int uniformBlockIndex = 0; uniformBlockIndex < uniformBlockCount;
253 ++uniformBlockIndex)
254 {
Jiajia Qin729b2c62017-08-14 09:36:11 +0800255 InterfaceBlock uniformBlock;
256 LoadInterfaceBlock(&stream, &uniformBlock);
Jamie Madill4f86d052017-06-05 12:59:26 -0400257 state->mUniformBlocks.push_back(uniformBlock);
Jamie Madill4f86d052017-06-05 12:59:26 -0400258
jchen107a20b972017-06-13 14:25:26 +0800259 state->mActiveUniformBlockBindings.set(uniformBlockIndex, uniformBlock.binding != 0);
Jamie Madill4f86d052017-06-05 12:59:26 -0400260 }
Jiajia Qin729b2c62017-08-14 09:36:11 +0800261
262 unsigned int shaderStorageBlockCount = stream.readInt<unsigned int>();
263 ASSERT(state->mShaderStorageBlocks.empty());
264 for (unsigned int shaderStorageBlockIndex = 0;
265 shaderStorageBlockIndex < shaderStorageBlockCount; ++shaderStorageBlockIndex)
266 {
267 InterfaceBlock shaderStorageBlock;
268 LoadInterfaceBlock(&stream, &shaderStorageBlock);
269 state->mShaderStorageBlocks.push_back(shaderStorageBlock);
270 }
271
jchen10eaef1e52017-06-13 10:44:11 +0800272 unsigned int atomicCounterBufferCount = stream.readInt<unsigned int>();
273 ASSERT(state->mAtomicCounterBuffers.empty());
274 for (unsigned int bufferIndex = 0; bufferIndex < atomicCounterBufferCount; ++bufferIndex)
275 {
276 AtomicCounterBuffer atomicCounterBuffer;
277 LoadShaderVariableBuffer(&stream, &atomicCounterBuffer);
278
279 state->mAtomicCounterBuffers.push_back(atomicCounterBuffer);
280 }
Jamie Madill4f86d052017-06-05 12:59:26 -0400281
282 unsigned int transformFeedbackVaryingCount = stream.readInt<unsigned int>();
Jamie Madillffe00c02017-06-27 16:26:55 -0400283
284 // Reject programs that use transform feedback varyings if the hardware cannot support them.
285 if (transformFeedbackVaryingCount > 0 &&
286 context->getWorkarounds().disableProgramCachingForTransformFeedback)
287 {
288 infoLog << "Current driver does not support transform feedback in binary programs.";
289 return false;
290 }
291
Jamie Madill4f86d052017-06-05 12:59:26 -0400292 ASSERT(state->mLinkedTransformFeedbackVaryings.empty());
293 for (unsigned int transformFeedbackVaryingIndex = 0;
294 transformFeedbackVaryingIndex < transformFeedbackVaryingCount;
295 ++transformFeedbackVaryingIndex)
296 {
297 sh::Varying varying;
298 stream.readInt(&varying.arraySize);
299 stream.readInt(&varying.type);
300 stream.readString(&varying.name);
301
302 GLuint arrayIndex = stream.readInt<GLuint>();
303
304 state->mLinkedTransformFeedbackVaryings.emplace_back(varying, arrayIndex);
305 }
306
307 stream.readInt(&state->mTransformFeedbackBufferMode);
308
309 unsigned int outputCount = stream.readInt<unsigned int>();
310 ASSERT(state->mOutputVariables.empty());
311 for (unsigned int outputIndex = 0; outputIndex < outputCount; ++outputIndex)
312 {
313 sh::OutputVariable output;
314 LoadShaderVar(&stream, &output);
315 output.location = stream.readInt<int>();
316 state->mOutputVariables.push_back(output);
317 }
318
319 unsigned int outputVarCount = stream.readInt<unsigned int>();
320 for (unsigned int outputIndex = 0; outputIndex < outputVarCount; ++outputIndex)
321 {
322 int locationIndex = stream.readInt<int>();
323 VariableLocation locationData;
324 stream.readInt(&locationData.element);
325 stream.readInt(&locationData.index);
326 stream.readString(&locationData.name);
327 state->mOutputLocations[locationIndex] = locationData;
328 }
329
330 unsigned int outputTypeCount = stream.readInt<unsigned int>();
331 for (unsigned int outputIndex = 0; outputIndex < outputTypeCount; ++outputIndex)
332 {
333 state->mOutputVariableTypes.push_back(stream.readInt<GLenum>());
334 }
335 static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS < 8 * sizeof(uint32_t),
336 "All bits of DrawBufferMask can be contained in an uint32_t");
337 state->mActiveOutputVariables = stream.readInt<uint32_t>();
338
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800339 unsigned int samplerRangeLow = stream.readInt<unsigned int>();
340 unsigned int samplerRangeHigh = stream.readInt<unsigned int>();
341 state->mSamplerUniformRange = RangeUI(samplerRangeLow, samplerRangeHigh);
Jamie Madill4f86d052017-06-05 12:59:26 -0400342 unsigned int samplerCount = stream.readInt<unsigned int>();
343 for (unsigned int samplerIndex = 0; samplerIndex < samplerCount; ++samplerIndex)
344 {
345 GLenum textureType = stream.readInt<GLenum>();
346 size_t bindingCount = stream.readInt<size_t>();
Jamie Madill54164b02017-08-28 15:17:37 -0400347 bool unreferenced = stream.readBool();
348 state->mSamplerBindings.emplace_back(
349 SamplerBinding(textureType, bindingCount, unreferenced));
Jamie Madill4f86d052017-06-05 12:59:26 -0400350 }
351
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800352 unsigned int imageRangeLow = stream.readInt<unsigned int>();
353 unsigned int imageRangeHigh = stream.readInt<unsigned int>();
354 state->mImageUniformRange = RangeUI(imageRangeLow, imageRangeHigh);
Xinghua Cao0328b572017-06-26 15:51:36 +0800355 unsigned int imageBindingCount = stream.readInt<unsigned int>();
356 for (unsigned int imageIndex = 0; imageIndex < imageBindingCount; ++imageIndex)
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800357 {
Xinghua Cao0328b572017-06-26 15:51:36 +0800358 unsigned int elementCount = stream.readInt<unsigned int>();
359 ImageBinding imageBinding(elementCount);
360 for (unsigned int i = 0; i < elementCount; ++i)
361 {
362 imageBinding.boundImageUnits[i] = stream.readInt<unsigned int>();
363 }
364 state->mImageBindings.emplace_back(imageBinding);
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800365 }
366
jchen10eaef1e52017-06-13 10:44:11 +0800367 unsigned int atomicCounterRangeLow = stream.readInt<unsigned int>();
368 unsigned int atomicCounterRangeHigh = stream.readInt<unsigned int>();
369 state->mAtomicCounterUniformRange = RangeUI(atomicCounterRangeLow, atomicCounterRangeHigh);
370
Jamie Madill4f86d052017-06-05 12:59:26 -0400371 return program->getImplementation()->load(context, infoLog, &stream);
372}
373
374// static
375void MemoryProgramCache::Serialize(const Context *context,
376 const gl::Program *program,
377 angle::MemoryBuffer *binaryOut)
378{
379 BinaryOutputStream stream;
380
381 stream.writeBytes(reinterpret_cast<const unsigned char *>(ANGLE_COMMIT_HASH),
382 ANGLE_COMMIT_HASH_SIZE);
383
384 // nullptr context is supported when computing binary length.
385 if (context)
386 {
387 stream.writeInt(context->getClientVersion().major);
388 stream.writeInt(context->getClientVersion().minor);
389 }
390 else
391 {
392 stream.writeInt(2);
393 stream.writeInt(0);
394 }
395
396 const auto &state = program->getState();
397
398 const auto &computeLocalSize = state.getComputeShaderLocalSize();
399
400 stream.writeInt(computeLocalSize[0]);
401 stream.writeInt(computeLocalSize[1]);
402 stream.writeInt(computeLocalSize[2]);
403
Martin Radev7cf61662017-07-26 17:10:53 +0300404 stream.writeInt(state.mNumViews);
405
Jamie Madill4f86d052017-06-05 12:59:26 -0400406 stream.writeInt(state.getActiveAttribLocationsMask().to_ulong());
407
408 stream.writeInt(state.getAttributes().size());
409 for (const sh::Attribute &attrib : state.getAttributes())
410 {
411 WriteShaderVar(&stream, attrib);
412 stream.writeInt(attrib.location);
413 }
414
415 stream.writeInt(state.getUniforms().size());
416 for (const LinkedUniform &uniform : state.getUniforms())
417 {
418 WriteShaderVar(&stream, uniform);
419
420 // FIXME: referenced
421
jchen10eaef1e52017-06-13 10:44:11 +0800422 stream.writeInt(uniform.bufferIndex);
Jamie Madill4f86d052017-06-05 12:59:26 -0400423 stream.writeInt(uniform.blockInfo.offset);
424 stream.writeInt(uniform.blockInfo.arrayStride);
425 stream.writeInt(uniform.blockInfo.matrixStride);
426 stream.writeInt(uniform.blockInfo.isRowMajorMatrix);
427 }
428
429 stream.writeInt(state.getUniformLocations().size());
430 for (const auto &variable : state.getUniformLocations())
431 {
432 stream.writeString(variable.name);
433 stream.writeInt(variable.element);
434 stream.writeInt(variable.index);
435 stream.writeInt(variable.used);
436 stream.writeInt(variable.ignored);
437 }
438
439 stream.writeInt(state.getUniformBlocks().size());
Jiajia Qin729b2c62017-08-14 09:36:11 +0800440 for (const InterfaceBlock &uniformBlock : state.getUniformBlocks())
Jamie Madill4f86d052017-06-05 12:59:26 -0400441 {
Jiajia Qin729b2c62017-08-14 09:36:11 +0800442 WriteInterfaceBlock(&stream, uniformBlock);
443 }
Jamie Madill4f86d052017-06-05 12:59:26 -0400444
Jiajia Qin729b2c62017-08-14 09:36:11 +0800445 stream.writeInt(state.getShaderStorageBlocks().size());
446 for (const InterfaceBlock &shaderStorageBlock : state.getShaderStorageBlocks())
447 {
448 WriteInterfaceBlock(&stream, shaderStorageBlock);
jchen10eaef1e52017-06-13 10:44:11 +0800449 }
Jamie Madill4f86d052017-06-05 12:59:26 -0400450
jchen10eaef1e52017-06-13 10:44:11 +0800451 stream.writeInt(state.mAtomicCounterBuffers.size());
452 for (const auto &atomicCounterBuffer : state.mAtomicCounterBuffers)
453 {
454 WriteShaderVariableBuffer(&stream, atomicCounterBuffer);
Jamie Madill4f86d052017-06-05 12:59:26 -0400455 }
456
Jamie Madillffe00c02017-06-27 16:26:55 -0400457 // Warn the app layer if saving a binary with unsupported transform feedback.
458 if (!state.getLinkedTransformFeedbackVaryings().empty() &&
459 context->getWorkarounds().disableProgramCachingForTransformFeedback)
460 {
461 WARN() << "Saving program binary with transform feedback, which is not supported on this "
462 "driver.";
463 }
464
Jamie Madill4f86d052017-06-05 12:59:26 -0400465 stream.writeInt(state.getLinkedTransformFeedbackVaryings().size());
466 for (const auto &var : state.getLinkedTransformFeedbackVaryings())
467 {
468 stream.writeInt(var.arraySize);
469 stream.writeInt(var.type);
470 stream.writeString(var.name);
471
472 stream.writeIntOrNegOne(var.arrayIndex);
473 }
474
475 stream.writeInt(state.getTransformFeedbackBufferMode());
476
477 stream.writeInt(state.getOutputVariables().size());
478 for (const sh::OutputVariable &output : state.getOutputVariables())
479 {
480 WriteShaderVar(&stream, output);
481 stream.writeInt(output.location);
482 }
483
484 stream.writeInt(state.getOutputLocations().size());
485 for (const auto &outputPair : state.getOutputLocations())
486 {
487 stream.writeInt(outputPair.first);
488 stream.writeIntOrNegOne(outputPair.second.element);
489 stream.writeInt(outputPair.second.index);
490 stream.writeString(outputPair.second.name);
491 }
492
493 stream.writeInt(state.mOutputVariableTypes.size());
494 for (const auto &outputVariableType : state.mOutputVariableTypes)
495 {
496 stream.writeInt(outputVariableType);
497 }
498
499 static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS < 8 * sizeof(uint32_t),
500 "All bits of DrawBufferMask can be contained in an uint32_t");
501 stream.writeInt(static_cast<uint32_t>(state.mActiveOutputVariables.to_ulong()));
502
503 stream.writeInt(state.getSamplerUniformRange().low());
504 stream.writeInt(state.getSamplerUniformRange().high());
505
506 stream.writeInt(state.getSamplerBindings().size());
507 for (const auto &samplerBinding : state.getSamplerBindings())
508 {
509 stream.writeInt(samplerBinding.textureType);
510 stream.writeInt(samplerBinding.boundTextureUnits.size());
Jamie Madill54164b02017-08-28 15:17:37 -0400511 stream.writeInt(samplerBinding.unreferenced);
Jamie Madill4f86d052017-06-05 12:59:26 -0400512 }
513
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800514 stream.writeInt(state.getImageUniformRange().low());
515 stream.writeInt(state.getImageUniformRange().high());
516
517 stream.writeInt(state.getImageBindings().size());
518 for (const auto &imageBinding : state.getImageBindings())
519 {
Xinghua Cao0328b572017-06-26 15:51:36 +0800520 stream.writeInt(imageBinding.boundImageUnits.size());
521 for (size_t i = 0; i < imageBinding.boundImageUnits.size(); ++i)
522 {
523 stream.writeInt(imageBinding.boundImageUnits[i]);
524 }
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800525 }
526
jchen10eaef1e52017-06-13 10:44:11 +0800527 stream.writeInt(state.getAtomicCounterUniformRange().low());
528 stream.writeInt(state.getAtomicCounterUniformRange().high());
529
Jamie Madill27a60632017-06-30 15:12:01 -0400530 program->getImplementation()->save(context, &stream);
Jamie Madill4f86d052017-06-05 12:59:26 -0400531
532 ASSERT(binaryOut);
533 binaryOut->resize(stream.length());
534 memcpy(binaryOut->data(), stream.data(), stream.length());
535}
536
Jamie Madill32447362017-06-28 14:53:52 -0400537// static
538void MemoryProgramCache::ComputeHash(const Context *context,
539 const Program *program,
540 ProgramHash *hashOut)
541{
542 auto vertexShader = program->getAttachedVertexShader();
543 auto fragmentShader = program->getAttachedFragmentShader();
544 auto computeShader = program->getAttachedComputeShader();
545
546 // Compute the program hash. Start with the shader hashes and resource strings.
547 HashStream hashStream;
548 hashStream << vertexShader << fragmentShader << computeShader;
549
550 // Add some ANGLE metadata and Context properties, such as version and back-end.
551 hashStream << ANGLE_COMMIT_HASH << context->getClientMajorVersion()
552 << context->getClientMinorVersion() << context->getString(GL_RENDERER);
553
554 // Hash pre-link program properties.
555 hashStream << program->getAttributeBindings() << program->getUniformLocationBindings()
556 << program->getFragmentInputBindings()
557 << program->getState().getTransformFeedbackVaryingNames()
558 << program->getState().getTransformFeedbackBufferMode();
559
560 // Call the secure SHA hashing function.
561 const std::string &programKey = hashStream.str();
562 angle::base::SHA1HashBytes(reinterpret_cast<const unsigned char *>(programKey.c_str()),
563 programKey.length(), hashOut->data());
564}
565
566LinkResult MemoryProgramCache::getProgram(const Context *context,
567 const Program *program,
568 ProgramState *state,
569 ProgramHash *hashOut)
570{
571 ComputeHash(context, program, hashOut);
572 const angle::MemoryBuffer *binaryProgram = nullptr;
573 LinkResult result(false);
574 if (get(*hashOut, &binaryProgram))
575 {
576 InfoLog infoLog;
577 ANGLE_TRY_RESULT(Deserialize(context, program, state, binaryProgram->data(),
578 binaryProgram->size(), infoLog),
579 result);
Jamie Madill6c58b062017-08-01 13:44:25 -0400580 ANGLE_HISTOGRAM_BOOLEAN("GPU.ANGLE.ProgramCache.LoadBinarySuccess", result.getResult());
Jamie Madill32447362017-06-28 14:53:52 -0400581 if (!result.getResult())
582 {
583 // Cache load failed, evict.
584 if (mIssuedWarnings++ < kWarningLimit)
585 {
586 WARN() << "Failed to load binary from cache: " << infoLog.str();
587
588 if (mIssuedWarnings == kWarningLimit)
589 {
590 WARN() << "Reaching warning limit for cache load failures, silencing "
591 "subsequent warnings.";
592 }
593 }
594 remove(*hashOut);
595 }
596 }
597 return result;
598}
599
600bool MemoryProgramCache::get(const ProgramHash &programHash, const angle::MemoryBuffer **programOut)
601{
Jamie Madill6c58b062017-08-01 13:44:25 -0400602 const CacheEntry *entry = nullptr;
603 if (!mProgramBinaryCache.get(programHash, &entry))
604 {
605 ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.ProgramCache.CacheResult", kCacheMiss,
606 kCacheResultMax);
607 return false;
608 }
609
610 if (entry->second == CacheSource::PutProgram)
611 {
612 ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.ProgramCache.CacheResult", kCacheHitMemory,
613 kCacheResultMax);
614 }
615 else
616 {
617 ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.ProgramCache.CacheResult", kCacheHitDisk,
618 kCacheResultMax);
619 }
620
621 *programOut = &entry->first;
622 return true;
Jamie Madill32447362017-06-28 14:53:52 -0400623}
624
Jamie Madillc43be722017-07-13 16:22:14 -0400625bool MemoryProgramCache::getAt(size_t index,
626 ProgramHash *hashOut,
627 const angle::MemoryBuffer **programOut)
628{
Jamie Madill6c58b062017-08-01 13:44:25 -0400629 const CacheEntry *entry = nullptr;
630 if (!mProgramBinaryCache.getAt(index, hashOut, &entry))
631 {
632 return false;
633 }
634
635 *programOut = &entry->first;
636 return true;
Jamie Madillc43be722017-07-13 16:22:14 -0400637}
638
Jamie Madill32447362017-06-28 14:53:52 -0400639void MemoryProgramCache::remove(const ProgramHash &programHash)
640{
641 bool result = mProgramBinaryCache.eraseByKey(programHash);
642 ASSERT(result);
643}
644
Jamie Madill6c58b062017-08-01 13:44:25 -0400645void MemoryProgramCache::putProgram(const ProgramHash &programHash,
646 const Context *context,
647 const Program *program)
Jamie Madill32447362017-06-28 14:53:52 -0400648{
Jamie Madill6c58b062017-08-01 13:44:25 -0400649 CacheEntry newEntry;
650 Serialize(context, program, &newEntry.first);
651 newEntry.second = CacheSource::PutProgram;
652
653 ANGLE_HISTOGRAM_COUNTS("GPU.ANGLE.ProgramCache.ProgramBinarySizeBytes",
654 static_cast<int>(newEntry.first.size()));
655
656 const CacheEntry *result =
657 mProgramBinaryCache.put(programHash, std::move(newEntry), newEntry.first.size());
Jamie Madill360daee2017-06-29 10:36:19 -0400658 if (!result)
Jamie Madill32447362017-06-28 14:53:52 -0400659 {
660 ERR() << "Failed to store binary program in memory cache, program is too large.";
661 }
Jamie Madill360daee2017-06-29 10:36:19 -0400662 else
663 {
664 auto *platform = ANGLEPlatformCurrent();
Jamie Madill6c58b062017-08-01 13:44:25 -0400665 platform->cacheProgram(platform, programHash, result->first.size(), result->first.data());
Jamie Madill360daee2017-06-29 10:36:19 -0400666 }
Jamie Madill32447362017-06-28 14:53:52 -0400667}
668
Jamie Madill4c19a8a2017-07-24 11:46:06 -0400669void MemoryProgramCache::updateProgram(const Context *context, const Program *program)
670{
671 gl::ProgramHash programHash;
672 ComputeHash(context, program, &programHash);
673 putProgram(programHash, context, program);
674}
675
Jamie Madillc43be722017-07-13 16:22:14 -0400676void MemoryProgramCache::putBinary(const ProgramHash &programHash,
Jamie Madill32447362017-06-28 14:53:52 -0400677 const uint8_t *binary,
678 size_t length)
679{
680 // Copy the binary.
Jamie Madill6c58b062017-08-01 13:44:25 -0400681 CacheEntry newEntry;
682 newEntry.first.resize(length);
683 memcpy(newEntry.first.data(), binary, length);
684 newEntry.second = CacheSource::PutBinary;
Jamie Madill32447362017-06-28 14:53:52 -0400685
Jamie Madill32447362017-06-28 14:53:52 -0400686 // Store the binary.
Jamie Madill6c58b062017-08-01 13:44:25 -0400687 const CacheEntry *result = mProgramBinaryCache.put(programHash, std::move(newEntry), length);
Jamie Madillc43be722017-07-13 16:22:14 -0400688 if (!result)
689 {
690 ERR() << "Failed to store binary program in memory cache, program is too large.";
691 }
Jamie Madill32447362017-06-28 14:53:52 -0400692}
693
694void MemoryProgramCache::clear()
695{
696 mProgramBinaryCache.clear();
697 mIssuedWarnings = 0;
698}
699
Jamie Madillc43be722017-07-13 16:22:14 -0400700void MemoryProgramCache::resize(size_t maxCacheSizeBytes)
701{
702 mProgramBinaryCache.resize(maxCacheSizeBytes);
703}
704
705size_t MemoryProgramCache::entryCount() const
706{
707 return mProgramBinaryCache.entryCount();
708}
709
710size_t MemoryProgramCache::trim(size_t limit)
711{
712 return mProgramBinaryCache.shrinkToSize(limit);
713}
714
715size_t MemoryProgramCache::size() const
716{
717 return mProgramBinaryCache.size();
718}
719
720size_t MemoryProgramCache::maxSize() const
721{
722 return mProgramBinaryCache.maxSize();
723}
724
Jamie Madill4f86d052017-06-05 12:59:26 -0400725} // namespace gl