blob: 1c0a4538a04db7a1303e562605f2addd43952754 [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;
Olli Etuahoc8538042017-09-27 11:20:15 +0300241 stream.readIntVector<unsigned int>(&variable.arrayIndices);
Jamie Madill4f86d052017-06-05 12:59:26 -0400242 stream.readInt(&variable.index);
Olli Etuahoc8538042017-09-27 11:20:15 +0300243 stream.readInt(&variable.flattenedArrayOffset);
Jamie Madill4f86d052017-06-05 12:59:26 -0400244 stream.readBool(&variable.ignored);
245
246 state->mUniformLocations.push_back(variable);
247 }
248
249 unsigned int uniformBlockCount = stream.readInt<unsigned int>();
250 ASSERT(state->mUniformBlocks.empty());
251 for (unsigned int uniformBlockIndex = 0; uniformBlockIndex < uniformBlockCount;
252 ++uniformBlockIndex)
253 {
Jiajia Qin729b2c62017-08-14 09:36:11 +0800254 InterfaceBlock uniformBlock;
255 LoadInterfaceBlock(&stream, &uniformBlock);
Jamie Madill4f86d052017-06-05 12:59:26 -0400256 state->mUniformBlocks.push_back(uniformBlock);
Jamie Madill4f86d052017-06-05 12:59:26 -0400257
jchen107a20b972017-06-13 14:25:26 +0800258 state->mActiveUniformBlockBindings.set(uniformBlockIndex, uniformBlock.binding != 0);
Jamie Madill4f86d052017-06-05 12:59:26 -0400259 }
Jiajia Qin729b2c62017-08-14 09:36:11 +0800260
261 unsigned int shaderStorageBlockCount = stream.readInt<unsigned int>();
262 ASSERT(state->mShaderStorageBlocks.empty());
263 for (unsigned int shaderStorageBlockIndex = 0;
264 shaderStorageBlockIndex < shaderStorageBlockCount; ++shaderStorageBlockIndex)
265 {
266 InterfaceBlock shaderStorageBlock;
267 LoadInterfaceBlock(&stream, &shaderStorageBlock);
268 state->mShaderStorageBlocks.push_back(shaderStorageBlock);
269 }
270
jchen10eaef1e52017-06-13 10:44:11 +0800271 unsigned int atomicCounterBufferCount = stream.readInt<unsigned int>();
272 ASSERT(state->mAtomicCounterBuffers.empty());
273 for (unsigned int bufferIndex = 0; bufferIndex < atomicCounterBufferCount; ++bufferIndex)
274 {
275 AtomicCounterBuffer atomicCounterBuffer;
276 LoadShaderVariableBuffer(&stream, &atomicCounterBuffer);
277
278 state->mAtomicCounterBuffers.push_back(atomicCounterBuffer);
279 }
Jamie Madill4f86d052017-06-05 12:59:26 -0400280
281 unsigned int transformFeedbackVaryingCount = stream.readInt<unsigned int>();
Jamie Madillffe00c02017-06-27 16:26:55 -0400282
283 // Reject programs that use transform feedback varyings if the hardware cannot support them.
284 if (transformFeedbackVaryingCount > 0 &&
285 context->getWorkarounds().disableProgramCachingForTransformFeedback)
286 {
287 infoLog << "Current driver does not support transform feedback in binary programs.";
288 return false;
289 }
290
Jamie Madill4f86d052017-06-05 12:59:26 -0400291 ASSERT(state->mLinkedTransformFeedbackVaryings.empty());
292 for (unsigned int transformFeedbackVaryingIndex = 0;
293 transformFeedbackVaryingIndex < transformFeedbackVaryingCount;
294 ++transformFeedbackVaryingIndex)
295 {
296 sh::Varying varying;
297 stream.readInt(&varying.arraySize);
298 stream.readInt(&varying.type);
299 stream.readString(&varying.name);
300
301 GLuint arrayIndex = stream.readInt<GLuint>();
302
303 state->mLinkedTransformFeedbackVaryings.emplace_back(varying, arrayIndex);
304 }
305
306 stream.readInt(&state->mTransformFeedbackBufferMode);
307
308 unsigned int outputCount = stream.readInt<unsigned int>();
309 ASSERT(state->mOutputVariables.empty());
310 for (unsigned int outputIndex = 0; outputIndex < outputCount; ++outputIndex)
311 {
312 sh::OutputVariable output;
313 LoadShaderVar(&stream, &output);
314 output.location = stream.readInt<int>();
315 state->mOutputVariables.push_back(output);
316 }
317
318 unsigned int outputVarCount = stream.readInt<unsigned int>();
Olli Etuahod2551232017-10-26 20:03:33 +0300319 ASSERT(state->mOutputLocations.empty());
Jamie Madill4f86d052017-06-05 12:59:26 -0400320 for (unsigned int outputIndex = 0; outputIndex < outputVarCount; ++outputIndex)
321 {
Jamie Madill4f86d052017-06-05 12:59:26 -0400322 VariableLocation locationData;
Olli Etuahoc8538042017-09-27 11:20:15 +0300323 stream.readIntVector<unsigned int>(&locationData.arrayIndices);
Jamie Madill4f86d052017-06-05 12:59:26 -0400324 stream.readInt(&locationData.index);
Olli Etuahoc8538042017-09-27 11:20:15 +0300325 stream.readInt(&locationData.flattenedArrayOffset);
Jamie Madillfb997ec2017-09-20 15:44:27 -0400326 stream.readBool(&locationData.ignored);
Olli Etuahod2551232017-10-26 20:03:33 +0300327 state->mOutputLocations.push_back(locationData);
Jamie Madill4f86d052017-06-05 12:59:26 -0400328 }
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 {
Olli Etuahoc8538042017-09-27 11:20:15 +0300432 stream.writeIntVector(variable.arrayIndices);
Jamie Madillfb997ec2017-09-20 15:44:27 -0400433 stream.writeIntOrNegOne(variable.index);
Olli Etuahoc8538042017-09-27 11:20:15 +0300434 stream.writeInt(variable.flattenedArrayOffset);
Jamie Madill4f86d052017-06-05 12:59:26 -0400435 stream.writeInt(variable.ignored);
436 }
437
438 stream.writeInt(state.getUniformBlocks().size());
Jiajia Qin729b2c62017-08-14 09:36:11 +0800439 for (const InterfaceBlock &uniformBlock : state.getUniformBlocks())
Jamie Madill4f86d052017-06-05 12:59:26 -0400440 {
Jiajia Qin729b2c62017-08-14 09:36:11 +0800441 WriteInterfaceBlock(&stream, uniformBlock);
442 }
Jamie Madill4f86d052017-06-05 12:59:26 -0400443
Jiajia Qin729b2c62017-08-14 09:36:11 +0800444 stream.writeInt(state.getShaderStorageBlocks().size());
445 for (const InterfaceBlock &shaderStorageBlock : state.getShaderStorageBlocks())
446 {
447 WriteInterfaceBlock(&stream, shaderStorageBlock);
jchen10eaef1e52017-06-13 10:44:11 +0800448 }
Jamie Madill4f86d052017-06-05 12:59:26 -0400449
jchen10eaef1e52017-06-13 10:44:11 +0800450 stream.writeInt(state.mAtomicCounterBuffers.size());
451 for (const auto &atomicCounterBuffer : state.mAtomicCounterBuffers)
452 {
453 WriteShaderVariableBuffer(&stream, atomicCounterBuffer);
Jamie Madill4f86d052017-06-05 12:59:26 -0400454 }
455
Jamie Madillffe00c02017-06-27 16:26:55 -0400456 // Warn the app layer if saving a binary with unsupported transform feedback.
457 if (!state.getLinkedTransformFeedbackVaryings().empty() &&
458 context->getWorkarounds().disableProgramCachingForTransformFeedback)
459 {
460 WARN() << "Saving program binary with transform feedback, which is not supported on this "
461 "driver.";
462 }
463
Jamie Madill4f86d052017-06-05 12:59:26 -0400464 stream.writeInt(state.getLinkedTransformFeedbackVaryings().size());
465 for (const auto &var : state.getLinkedTransformFeedbackVaryings())
466 {
467 stream.writeInt(var.arraySize);
468 stream.writeInt(var.type);
469 stream.writeString(var.name);
470
471 stream.writeIntOrNegOne(var.arrayIndex);
472 }
473
474 stream.writeInt(state.getTransformFeedbackBufferMode());
475
476 stream.writeInt(state.getOutputVariables().size());
477 for (const sh::OutputVariable &output : state.getOutputVariables())
478 {
479 WriteShaderVar(&stream, output);
480 stream.writeInt(output.location);
481 }
482
483 stream.writeInt(state.getOutputLocations().size());
Olli Etuahod2551232017-10-26 20:03:33 +0300484 for (const auto &outputVar : state.getOutputLocations())
Jamie Madill4f86d052017-06-05 12:59:26 -0400485 {
Olli Etuahod2551232017-10-26 20:03:33 +0300486 stream.writeIntVector(outputVar.arrayIndices);
487 stream.writeIntOrNegOne(outputVar.index);
488 stream.writeInt(outputVar.flattenedArrayOffset);
489 stream.writeInt(outputVar.ignored);
Jamie Madill4f86d052017-06-05 12:59:26 -0400490 }
491
492 stream.writeInt(state.mOutputVariableTypes.size());
493 for (const auto &outputVariableType : state.mOutputVariableTypes)
494 {
495 stream.writeInt(outputVariableType);
496 }
497
498 static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS < 8 * sizeof(uint32_t),
499 "All bits of DrawBufferMask can be contained in an uint32_t");
500 stream.writeInt(static_cast<uint32_t>(state.mActiveOutputVariables.to_ulong()));
501
502 stream.writeInt(state.getSamplerUniformRange().low());
503 stream.writeInt(state.getSamplerUniformRange().high());
504
505 stream.writeInt(state.getSamplerBindings().size());
506 for (const auto &samplerBinding : state.getSamplerBindings())
507 {
508 stream.writeInt(samplerBinding.textureType);
509 stream.writeInt(samplerBinding.boundTextureUnits.size());
Jamie Madill54164b02017-08-28 15:17:37 -0400510 stream.writeInt(samplerBinding.unreferenced);
Jamie Madill4f86d052017-06-05 12:59:26 -0400511 }
512
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800513 stream.writeInt(state.getImageUniformRange().low());
514 stream.writeInt(state.getImageUniformRange().high());
515
516 stream.writeInt(state.getImageBindings().size());
517 for (const auto &imageBinding : state.getImageBindings())
518 {
Xinghua Cao0328b572017-06-26 15:51:36 +0800519 stream.writeInt(imageBinding.boundImageUnits.size());
520 for (size_t i = 0; i < imageBinding.boundImageUnits.size(); ++i)
521 {
522 stream.writeInt(imageBinding.boundImageUnits[i]);
523 }
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800524 }
525
jchen10eaef1e52017-06-13 10:44:11 +0800526 stream.writeInt(state.getAtomicCounterUniformRange().low());
527 stream.writeInt(state.getAtomicCounterUniformRange().high());
528
Jamie Madill27a60632017-06-30 15:12:01 -0400529 program->getImplementation()->save(context, &stream);
Jamie Madill4f86d052017-06-05 12:59:26 -0400530
531 ASSERT(binaryOut);
532 binaryOut->resize(stream.length());
533 memcpy(binaryOut->data(), stream.data(), stream.length());
534}
535
Jamie Madill32447362017-06-28 14:53:52 -0400536// static
537void MemoryProgramCache::ComputeHash(const Context *context,
538 const Program *program,
539 ProgramHash *hashOut)
540{
541 auto vertexShader = program->getAttachedVertexShader();
542 auto fragmentShader = program->getAttachedFragmentShader();
543 auto computeShader = program->getAttachedComputeShader();
544
545 // Compute the program hash. Start with the shader hashes and resource strings.
546 HashStream hashStream;
547 hashStream << vertexShader << fragmentShader << computeShader;
548
549 // Add some ANGLE metadata and Context properties, such as version and back-end.
550 hashStream << ANGLE_COMMIT_HASH << context->getClientMajorVersion()
551 << context->getClientMinorVersion() << context->getString(GL_RENDERER);
552
553 // Hash pre-link program properties.
554 hashStream << program->getAttributeBindings() << program->getUniformLocationBindings()
555 << program->getFragmentInputBindings()
556 << program->getState().getTransformFeedbackVaryingNames()
557 << program->getState().getTransformFeedbackBufferMode();
558
559 // Call the secure SHA hashing function.
560 const std::string &programKey = hashStream.str();
561 angle::base::SHA1HashBytes(reinterpret_cast<const unsigned char *>(programKey.c_str()),
562 programKey.length(), hashOut->data());
563}
564
565LinkResult MemoryProgramCache::getProgram(const Context *context,
566 const Program *program,
567 ProgramState *state,
568 ProgramHash *hashOut)
569{
570 ComputeHash(context, program, hashOut);
571 const angle::MemoryBuffer *binaryProgram = nullptr;
572 LinkResult result(false);
573 if (get(*hashOut, &binaryProgram))
574 {
575 InfoLog infoLog;
576 ANGLE_TRY_RESULT(Deserialize(context, program, state, binaryProgram->data(),
577 binaryProgram->size(), infoLog),
578 result);
Jamie Madill6c58b062017-08-01 13:44:25 -0400579 ANGLE_HISTOGRAM_BOOLEAN("GPU.ANGLE.ProgramCache.LoadBinarySuccess", result.getResult());
Jamie Madill32447362017-06-28 14:53:52 -0400580 if (!result.getResult())
581 {
582 // Cache load failed, evict.
583 if (mIssuedWarnings++ < kWarningLimit)
584 {
585 WARN() << "Failed to load binary from cache: " << infoLog.str();
586
587 if (mIssuedWarnings == kWarningLimit)
588 {
589 WARN() << "Reaching warning limit for cache load failures, silencing "
590 "subsequent warnings.";
591 }
592 }
593 remove(*hashOut);
594 }
595 }
596 return result;
597}
598
599bool MemoryProgramCache::get(const ProgramHash &programHash, const angle::MemoryBuffer **programOut)
600{
Jamie Madill6c58b062017-08-01 13:44:25 -0400601 const CacheEntry *entry = nullptr;
602 if (!mProgramBinaryCache.get(programHash, &entry))
603 {
604 ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.ProgramCache.CacheResult", kCacheMiss,
605 kCacheResultMax);
606 return false;
607 }
608
609 if (entry->second == CacheSource::PutProgram)
610 {
611 ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.ProgramCache.CacheResult", kCacheHitMemory,
612 kCacheResultMax);
613 }
614 else
615 {
616 ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.ProgramCache.CacheResult", kCacheHitDisk,
617 kCacheResultMax);
618 }
619
620 *programOut = &entry->first;
621 return true;
Jamie Madill32447362017-06-28 14:53:52 -0400622}
623
Jamie Madillc43be722017-07-13 16:22:14 -0400624bool MemoryProgramCache::getAt(size_t index,
625 ProgramHash *hashOut,
626 const angle::MemoryBuffer **programOut)
627{
Jamie Madill6c58b062017-08-01 13:44:25 -0400628 const CacheEntry *entry = nullptr;
629 if (!mProgramBinaryCache.getAt(index, hashOut, &entry))
630 {
631 return false;
632 }
633
634 *programOut = &entry->first;
635 return true;
Jamie Madillc43be722017-07-13 16:22:14 -0400636}
637
Jamie Madill32447362017-06-28 14:53:52 -0400638void MemoryProgramCache::remove(const ProgramHash &programHash)
639{
640 bool result = mProgramBinaryCache.eraseByKey(programHash);
641 ASSERT(result);
642}
643
Jamie Madill6c58b062017-08-01 13:44:25 -0400644void MemoryProgramCache::putProgram(const ProgramHash &programHash,
645 const Context *context,
646 const Program *program)
Jamie Madill32447362017-06-28 14:53:52 -0400647{
Jamie Madill6c58b062017-08-01 13:44:25 -0400648 CacheEntry newEntry;
649 Serialize(context, program, &newEntry.first);
650 newEntry.second = CacheSource::PutProgram;
651
652 ANGLE_HISTOGRAM_COUNTS("GPU.ANGLE.ProgramCache.ProgramBinarySizeBytes",
653 static_cast<int>(newEntry.first.size()));
654
655 const CacheEntry *result =
656 mProgramBinaryCache.put(programHash, std::move(newEntry), newEntry.first.size());
Jamie Madill360daee2017-06-29 10:36:19 -0400657 if (!result)
Jamie Madill32447362017-06-28 14:53:52 -0400658 {
659 ERR() << "Failed to store binary program in memory cache, program is too large.";
660 }
Jamie Madill360daee2017-06-29 10:36:19 -0400661 else
662 {
663 auto *platform = ANGLEPlatformCurrent();
Jamie Madill6c58b062017-08-01 13:44:25 -0400664 platform->cacheProgram(platform, programHash, result->first.size(), result->first.data());
Jamie Madill360daee2017-06-29 10:36:19 -0400665 }
Jamie Madill32447362017-06-28 14:53:52 -0400666}
667
Jamie Madill4c19a8a2017-07-24 11:46:06 -0400668void MemoryProgramCache::updateProgram(const Context *context, const Program *program)
669{
670 gl::ProgramHash programHash;
671 ComputeHash(context, program, &programHash);
672 putProgram(programHash, context, program);
673}
674
Jamie Madillc43be722017-07-13 16:22:14 -0400675void MemoryProgramCache::putBinary(const ProgramHash &programHash,
Jamie Madill32447362017-06-28 14:53:52 -0400676 const uint8_t *binary,
677 size_t length)
678{
679 // Copy the binary.
Jamie Madill6c58b062017-08-01 13:44:25 -0400680 CacheEntry newEntry;
681 newEntry.first.resize(length);
682 memcpy(newEntry.first.data(), binary, length);
683 newEntry.second = CacheSource::PutBinary;
Jamie Madill32447362017-06-28 14:53:52 -0400684
Jamie Madill32447362017-06-28 14:53:52 -0400685 // Store the binary.
Jamie Madill6c58b062017-08-01 13:44:25 -0400686 const CacheEntry *result = mProgramBinaryCache.put(programHash, std::move(newEntry), length);
Jamie Madillc43be722017-07-13 16:22:14 -0400687 if (!result)
688 {
689 ERR() << "Failed to store binary program in memory cache, program is too large.";
690 }
Jamie Madill32447362017-06-28 14:53:52 -0400691}
692
693void MemoryProgramCache::clear()
694{
695 mProgramBinaryCache.clear();
696 mIssuedWarnings = 0;
697}
698
Jamie Madillc43be722017-07-13 16:22:14 -0400699void MemoryProgramCache::resize(size_t maxCacheSizeBytes)
700{
701 mProgramBinaryCache.resize(maxCacheSizeBytes);
702}
703
704size_t MemoryProgramCache::entryCount() const
705{
706 return mProgramBinaryCache.entryCount();
707}
708
709size_t MemoryProgramCache::trim(size_t limit)
710{
711 return mProgramBinaryCache.shrinkToSize(limit);
712}
713
714size_t MemoryProgramCache::size() const
715{
716 return mProgramBinaryCache.size();
717}
718
719size_t MemoryProgramCache::maxSize() const
720{
721 return mProgramBinaryCache.maxSize();
722}
723
Jamie Madill4f86d052017-06-05 12:59:26 -0400724} // namespace gl