blob: 9add9f51264fb569500a3c46085702f54b20085a [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 Etuaho1734e172017-10-27 15:30:27 +0300241 stream.readInt(&variable.arrayIndex);
Jamie Madill4f86d052017-06-05 12:59:26 -0400242 stream.readInt(&variable.index);
Jamie Madill4f86d052017-06-05 12:59:26 -0400243 stream.readBool(&variable.ignored);
244
245 state->mUniformLocations.push_back(variable);
246 }
247
248 unsigned int uniformBlockCount = stream.readInt<unsigned int>();
249 ASSERT(state->mUniformBlocks.empty());
250 for (unsigned int uniformBlockIndex = 0; uniformBlockIndex < uniformBlockCount;
251 ++uniformBlockIndex)
252 {
Jiajia Qin729b2c62017-08-14 09:36:11 +0800253 InterfaceBlock uniformBlock;
254 LoadInterfaceBlock(&stream, &uniformBlock);
Jamie Madill4f86d052017-06-05 12:59:26 -0400255 state->mUniformBlocks.push_back(uniformBlock);
Jamie Madill4f86d052017-06-05 12:59:26 -0400256
jchen107a20b972017-06-13 14:25:26 +0800257 state->mActiveUniformBlockBindings.set(uniformBlockIndex, uniformBlock.binding != 0);
Jamie Madill4f86d052017-06-05 12:59:26 -0400258 }
Jiajia Qin729b2c62017-08-14 09:36:11 +0800259
260 unsigned int shaderStorageBlockCount = stream.readInt<unsigned int>();
261 ASSERT(state->mShaderStorageBlocks.empty());
262 for (unsigned int shaderStorageBlockIndex = 0;
263 shaderStorageBlockIndex < shaderStorageBlockCount; ++shaderStorageBlockIndex)
264 {
265 InterfaceBlock shaderStorageBlock;
266 LoadInterfaceBlock(&stream, &shaderStorageBlock);
267 state->mShaderStorageBlocks.push_back(shaderStorageBlock);
268 }
269
jchen10eaef1e52017-06-13 10:44:11 +0800270 unsigned int atomicCounterBufferCount = stream.readInt<unsigned int>();
271 ASSERT(state->mAtomicCounterBuffers.empty());
272 for (unsigned int bufferIndex = 0; bufferIndex < atomicCounterBufferCount; ++bufferIndex)
273 {
274 AtomicCounterBuffer atomicCounterBuffer;
275 LoadShaderVariableBuffer(&stream, &atomicCounterBuffer);
276
277 state->mAtomicCounterBuffers.push_back(atomicCounterBuffer);
278 }
Jamie Madill4f86d052017-06-05 12:59:26 -0400279
280 unsigned int transformFeedbackVaryingCount = stream.readInt<unsigned int>();
Jamie Madillffe00c02017-06-27 16:26:55 -0400281
282 // Reject programs that use transform feedback varyings if the hardware cannot support them.
283 if (transformFeedbackVaryingCount > 0 &&
284 context->getWorkarounds().disableProgramCachingForTransformFeedback)
285 {
286 infoLog << "Current driver does not support transform feedback in binary programs.";
287 return false;
288 }
289
Jamie Madill4f86d052017-06-05 12:59:26 -0400290 ASSERT(state->mLinkedTransformFeedbackVaryings.empty());
291 for (unsigned int transformFeedbackVaryingIndex = 0;
292 transformFeedbackVaryingIndex < transformFeedbackVaryingCount;
293 ++transformFeedbackVaryingIndex)
294 {
295 sh::Varying varying;
296 stream.readInt(&varying.arraySize);
297 stream.readInt(&varying.type);
298 stream.readString(&varying.name);
299
300 GLuint arrayIndex = stream.readInt<GLuint>();
301
302 state->mLinkedTransformFeedbackVaryings.emplace_back(varying, arrayIndex);
303 }
304
305 stream.readInt(&state->mTransformFeedbackBufferMode);
306
307 unsigned int outputCount = stream.readInt<unsigned int>();
308 ASSERT(state->mOutputVariables.empty());
309 for (unsigned int outputIndex = 0; outputIndex < outputCount; ++outputIndex)
310 {
311 sh::OutputVariable output;
312 LoadShaderVar(&stream, &output);
313 output.location = stream.readInt<int>();
314 state->mOutputVariables.push_back(output);
315 }
316
317 unsigned int outputVarCount = stream.readInt<unsigned int>();
Olli Etuahod2551232017-10-26 20:03:33 +0300318 ASSERT(state->mOutputLocations.empty());
Jamie Madill4f86d052017-06-05 12:59:26 -0400319 for (unsigned int outputIndex = 0; outputIndex < outputVarCount; ++outputIndex)
320 {
Jamie Madill4f86d052017-06-05 12:59:26 -0400321 VariableLocation locationData;
Olli Etuaho1734e172017-10-27 15:30:27 +0300322 stream.readInt(&locationData.arrayIndex);
Jamie Madill4f86d052017-06-05 12:59:26 -0400323 stream.readInt(&locationData.index);
Jamie Madillfb997ec2017-09-20 15:44:27 -0400324 stream.readBool(&locationData.ignored);
Olli Etuahod2551232017-10-26 20:03:33 +0300325 state->mOutputLocations.push_back(locationData);
Jamie Madill4f86d052017-06-05 12:59:26 -0400326 }
327
328 unsigned int outputTypeCount = stream.readInt<unsigned int>();
329 for (unsigned int outputIndex = 0; outputIndex < outputTypeCount; ++outputIndex)
330 {
331 state->mOutputVariableTypes.push_back(stream.readInt<GLenum>());
332 }
333 static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS < 8 * sizeof(uint32_t),
334 "All bits of DrawBufferMask can be contained in an uint32_t");
335 state->mActiveOutputVariables = stream.readInt<uint32_t>();
336
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800337 unsigned int samplerRangeLow = stream.readInt<unsigned int>();
338 unsigned int samplerRangeHigh = stream.readInt<unsigned int>();
339 state->mSamplerUniformRange = RangeUI(samplerRangeLow, samplerRangeHigh);
Jamie Madill4f86d052017-06-05 12:59:26 -0400340 unsigned int samplerCount = stream.readInt<unsigned int>();
341 for (unsigned int samplerIndex = 0; samplerIndex < samplerCount; ++samplerIndex)
342 {
343 GLenum textureType = stream.readInt<GLenum>();
344 size_t bindingCount = stream.readInt<size_t>();
Jamie Madill54164b02017-08-28 15:17:37 -0400345 bool unreferenced = stream.readBool();
346 state->mSamplerBindings.emplace_back(
347 SamplerBinding(textureType, bindingCount, unreferenced));
Jamie Madill4f86d052017-06-05 12:59:26 -0400348 }
349
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800350 unsigned int imageRangeLow = stream.readInt<unsigned int>();
351 unsigned int imageRangeHigh = stream.readInt<unsigned int>();
352 state->mImageUniformRange = RangeUI(imageRangeLow, imageRangeHigh);
Xinghua Cao0328b572017-06-26 15:51:36 +0800353 unsigned int imageBindingCount = stream.readInt<unsigned int>();
354 for (unsigned int imageIndex = 0; imageIndex < imageBindingCount; ++imageIndex)
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800355 {
Xinghua Cao0328b572017-06-26 15:51:36 +0800356 unsigned int elementCount = stream.readInt<unsigned int>();
357 ImageBinding imageBinding(elementCount);
358 for (unsigned int i = 0; i < elementCount; ++i)
359 {
360 imageBinding.boundImageUnits[i] = stream.readInt<unsigned int>();
361 }
362 state->mImageBindings.emplace_back(imageBinding);
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800363 }
364
jchen10eaef1e52017-06-13 10:44:11 +0800365 unsigned int atomicCounterRangeLow = stream.readInt<unsigned int>();
366 unsigned int atomicCounterRangeHigh = stream.readInt<unsigned int>();
367 state->mAtomicCounterUniformRange = RangeUI(atomicCounterRangeLow, atomicCounterRangeHigh);
368
Jamie Madill4f86d052017-06-05 12:59:26 -0400369 return program->getImplementation()->load(context, infoLog, &stream);
370}
371
372// static
373void MemoryProgramCache::Serialize(const Context *context,
374 const gl::Program *program,
375 angle::MemoryBuffer *binaryOut)
376{
377 BinaryOutputStream stream;
378
379 stream.writeBytes(reinterpret_cast<const unsigned char *>(ANGLE_COMMIT_HASH),
380 ANGLE_COMMIT_HASH_SIZE);
381
382 // nullptr context is supported when computing binary length.
383 if (context)
384 {
385 stream.writeInt(context->getClientVersion().major);
386 stream.writeInt(context->getClientVersion().minor);
387 }
388 else
389 {
390 stream.writeInt(2);
391 stream.writeInt(0);
392 }
393
394 const auto &state = program->getState();
395
396 const auto &computeLocalSize = state.getComputeShaderLocalSize();
397
398 stream.writeInt(computeLocalSize[0]);
399 stream.writeInt(computeLocalSize[1]);
400 stream.writeInt(computeLocalSize[2]);
401
Martin Radev7cf61662017-07-26 17:10:53 +0300402 stream.writeInt(state.mNumViews);
403
Jamie Madill4f86d052017-06-05 12:59:26 -0400404 stream.writeInt(state.getActiveAttribLocationsMask().to_ulong());
405
406 stream.writeInt(state.getAttributes().size());
407 for (const sh::Attribute &attrib : state.getAttributes())
408 {
409 WriteShaderVar(&stream, attrib);
410 stream.writeInt(attrib.location);
411 }
412
413 stream.writeInt(state.getUniforms().size());
414 for (const LinkedUniform &uniform : state.getUniforms())
415 {
416 WriteShaderVar(&stream, uniform);
417
418 // FIXME: referenced
419
jchen10eaef1e52017-06-13 10:44:11 +0800420 stream.writeInt(uniform.bufferIndex);
Jamie Madill4f86d052017-06-05 12:59:26 -0400421 stream.writeInt(uniform.blockInfo.offset);
422 stream.writeInt(uniform.blockInfo.arrayStride);
423 stream.writeInt(uniform.blockInfo.matrixStride);
424 stream.writeInt(uniform.blockInfo.isRowMajorMatrix);
425 }
426
427 stream.writeInt(state.getUniformLocations().size());
428 for (const auto &variable : state.getUniformLocations())
429 {
Olli Etuaho1734e172017-10-27 15:30:27 +0300430 stream.writeInt(variable.arrayIndex);
Jamie Madillfb997ec2017-09-20 15:44:27 -0400431 stream.writeIntOrNegOne(variable.index);
Jamie Madill4f86d052017-06-05 12:59:26 -0400432 stream.writeInt(variable.ignored);
433 }
434
435 stream.writeInt(state.getUniformBlocks().size());
Jiajia Qin729b2c62017-08-14 09:36:11 +0800436 for (const InterfaceBlock &uniformBlock : state.getUniformBlocks())
Jamie Madill4f86d052017-06-05 12:59:26 -0400437 {
Jiajia Qin729b2c62017-08-14 09:36:11 +0800438 WriteInterfaceBlock(&stream, uniformBlock);
439 }
Jamie Madill4f86d052017-06-05 12:59:26 -0400440
Jiajia Qin729b2c62017-08-14 09:36:11 +0800441 stream.writeInt(state.getShaderStorageBlocks().size());
442 for (const InterfaceBlock &shaderStorageBlock : state.getShaderStorageBlocks())
443 {
444 WriteInterfaceBlock(&stream, shaderStorageBlock);
jchen10eaef1e52017-06-13 10:44:11 +0800445 }
Jamie Madill4f86d052017-06-05 12:59:26 -0400446
jchen10eaef1e52017-06-13 10:44:11 +0800447 stream.writeInt(state.mAtomicCounterBuffers.size());
448 for (const auto &atomicCounterBuffer : state.mAtomicCounterBuffers)
449 {
450 WriteShaderVariableBuffer(&stream, atomicCounterBuffer);
Jamie Madill4f86d052017-06-05 12:59:26 -0400451 }
452
Jamie Madillffe00c02017-06-27 16:26:55 -0400453 // Warn the app layer if saving a binary with unsupported transform feedback.
454 if (!state.getLinkedTransformFeedbackVaryings().empty() &&
455 context->getWorkarounds().disableProgramCachingForTransformFeedback)
456 {
457 WARN() << "Saving program binary with transform feedback, which is not supported on this "
458 "driver.";
459 }
460
Jamie Madill4f86d052017-06-05 12:59:26 -0400461 stream.writeInt(state.getLinkedTransformFeedbackVaryings().size());
462 for (const auto &var : state.getLinkedTransformFeedbackVaryings())
463 {
464 stream.writeInt(var.arraySize);
465 stream.writeInt(var.type);
466 stream.writeString(var.name);
467
468 stream.writeIntOrNegOne(var.arrayIndex);
469 }
470
471 stream.writeInt(state.getTransformFeedbackBufferMode());
472
473 stream.writeInt(state.getOutputVariables().size());
474 for (const sh::OutputVariable &output : state.getOutputVariables())
475 {
476 WriteShaderVar(&stream, output);
477 stream.writeInt(output.location);
478 }
479
480 stream.writeInt(state.getOutputLocations().size());
Olli Etuahod2551232017-10-26 20:03:33 +0300481 for (const auto &outputVar : state.getOutputLocations())
Jamie Madill4f86d052017-06-05 12:59:26 -0400482 {
Olli Etuaho1734e172017-10-27 15:30:27 +0300483 stream.writeInt(outputVar.arrayIndex);
Olli Etuahod2551232017-10-26 20:03:33 +0300484 stream.writeIntOrNegOne(outputVar.index);
Olli Etuahod2551232017-10-26 20:03:33 +0300485 stream.writeInt(outputVar.ignored);
Jamie Madill4f86d052017-06-05 12:59:26 -0400486 }
487
488 stream.writeInt(state.mOutputVariableTypes.size());
489 for (const auto &outputVariableType : state.mOutputVariableTypes)
490 {
491 stream.writeInt(outputVariableType);
492 }
493
494 static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS < 8 * sizeof(uint32_t),
495 "All bits of DrawBufferMask can be contained in an uint32_t");
496 stream.writeInt(static_cast<uint32_t>(state.mActiveOutputVariables.to_ulong()));
497
498 stream.writeInt(state.getSamplerUniformRange().low());
499 stream.writeInt(state.getSamplerUniformRange().high());
500
501 stream.writeInt(state.getSamplerBindings().size());
502 for (const auto &samplerBinding : state.getSamplerBindings())
503 {
504 stream.writeInt(samplerBinding.textureType);
505 stream.writeInt(samplerBinding.boundTextureUnits.size());
Jamie Madill54164b02017-08-28 15:17:37 -0400506 stream.writeInt(samplerBinding.unreferenced);
Jamie Madill4f86d052017-06-05 12:59:26 -0400507 }
508
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800509 stream.writeInt(state.getImageUniformRange().low());
510 stream.writeInt(state.getImageUniformRange().high());
511
512 stream.writeInt(state.getImageBindings().size());
513 for (const auto &imageBinding : state.getImageBindings())
514 {
Xinghua Cao0328b572017-06-26 15:51:36 +0800515 stream.writeInt(imageBinding.boundImageUnits.size());
516 for (size_t i = 0; i < imageBinding.boundImageUnits.size(); ++i)
517 {
518 stream.writeInt(imageBinding.boundImageUnits[i]);
519 }
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800520 }
521
jchen10eaef1e52017-06-13 10:44:11 +0800522 stream.writeInt(state.getAtomicCounterUniformRange().low());
523 stream.writeInt(state.getAtomicCounterUniformRange().high());
524
Jamie Madill27a60632017-06-30 15:12:01 -0400525 program->getImplementation()->save(context, &stream);
Jamie Madill4f86d052017-06-05 12:59:26 -0400526
527 ASSERT(binaryOut);
528 binaryOut->resize(stream.length());
529 memcpy(binaryOut->data(), stream.data(), stream.length());
530}
531
Jamie Madill32447362017-06-28 14:53:52 -0400532// static
533void MemoryProgramCache::ComputeHash(const Context *context,
534 const Program *program,
535 ProgramHash *hashOut)
536{
537 auto vertexShader = program->getAttachedVertexShader();
538 auto fragmentShader = program->getAttachedFragmentShader();
539 auto computeShader = program->getAttachedComputeShader();
540
541 // Compute the program hash. Start with the shader hashes and resource strings.
542 HashStream hashStream;
543 hashStream << vertexShader << fragmentShader << computeShader;
544
545 // Add some ANGLE metadata and Context properties, such as version and back-end.
546 hashStream << ANGLE_COMMIT_HASH << context->getClientMajorVersion()
547 << context->getClientMinorVersion() << context->getString(GL_RENDERER);
548
549 // Hash pre-link program properties.
550 hashStream << program->getAttributeBindings() << program->getUniformLocationBindings()
551 << program->getFragmentInputBindings()
552 << program->getState().getTransformFeedbackVaryingNames()
553 << program->getState().getTransformFeedbackBufferMode();
554
555 // Call the secure SHA hashing function.
556 const std::string &programKey = hashStream.str();
557 angle::base::SHA1HashBytes(reinterpret_cast<const unsigned char *>(programKey.c_str()),
558 programKey.length(), hashOut->data());
559}
560
561LinkResult MemoryProgramCache::getProgram(const Context *context,
562 const Program *program,
563 ProgramState *state,
564 ProgramHash *hashOut)
565{
566 ComputeHash(context, program, hashOut);
567 const angle::MemoryBuffer *binaryProgram = nullptr;
568 LinkResult result(false);
569 if (get(*hashOut, &binaryProgram))
570 {
571 InfoLog infoLog;
572 ANGLE_TRY_RESULT(Deserialize(context, program, state, binaryProgram->data(),
573 binaryProgram->size(), infoLog),
574 result);
Jamie Madill6c58b062017-08-01 13:44:25 -0400575 ANGLE_HISTOGRAM_BOOLEAN("GPU.ANGLE.ProgramCache.LoadBinarySuccess", result.getResult());
Jamie Madill32447362017-06-28 14:53:52 -0400576 if (!result.getResult())
577 {
578 // Cache load failed, evict.
579 if (mIssuedWarnings++ < kWarningLimit)
580 {
581 WARN() << "Failed to load binary from cache: " << infoLog.str();
582
583 if (mIssuedWarnings == kWarningLimit)
584 {
585 WARN() << "Reaching warning limit for cache load failures, silencing "
586 "subsequent warnings.";
587 }
588 }
589 remove(*hashOut);
590 }
591 }
592 return result;
593}
594
595bool MemoryProgramCache::get(const ProgramHash &programHash, const angle::MemoryBuffer **programOut)
596{
Jamie Madill6c58b062017-08-01 13:44:25 -0400597 const CacheEntry *entry = nullptr;
598 if (!mProgramBinaryCache.get(programHash, &entry))
599 {
600 ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.ProgramCache.CacheResult", kCacheMiss,
601 kCacheResultMax);
602 return false;
603 }
604
605 if (entry->second == CacheSource::PutProgram)
606 {
607 ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.ProgramCache.CacheResult", kCacheHitMemory,
608 kCacheResultMax);
609 }
610 else
611 {
612 ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.ProgramCache.CacheResult", kCacheHitDisk,
613 kCacheResultMax);
614 }
615
616 *programOut = &entry->first;
617 return true;
Jamie Madill32447362017-06-28 14:53:52 -0400618}
619
Jamie Madillc43be722017-07-13 16:22:14 -0400620bool MemoryProgramCache::getAt(size_t index,
621 ProgramHash *hashOut,
622 const angle::MemoryBuffer **programOut)
623{
Jamie Madill6c58b062017-08-01 13:44:25 -0400624 const CacheEntry *entry = nullptr;
625 if (!mProgramBinaryCache.getAt(index, hashOut, &entry))
626 {
627 return false;
628 }
629
630 *programOut = &entry->first;
631 return true;
Jamie Madillc43be722017-07-13 16:22:14 -0400632}
633
Jamie Madill32447362017-06-28 14:53:52 -0400634void MemoryProgramCache::remove(const ProgramHash &programHash)
635{
636 bool result = mProgramBinaryCache.eraseByKey(programHash);
637 ASSERT(result);
638}
639
Jamie Madill6c58b062017-08-01 13:44:25 -0400640void MemoryProgramCache::putProgram(const ProgramHash &programHash,
641 const Context *context,
642 const Program *program)
Jamie Madill32447362017-06-28 14:53:52 -0400643{
Jamie Madill6c58b062017-08-01 13:44:25 -0400644 CacheEntry newEntry;
645 Serialize(context, program, &newEntry.first);
646 newEntry.second = CacheSource::PutProgram;
647
648 ANGLE_HISTOGRAM_COUNTS("GPU.ANGLE.ProgramCache.ProgramBinarySizeBytes",
649 static_cast<int>(newEntry.first.size()));
650
651 const CacheEntry *result =
652 mProgramBinaryCache.put(programHash, std::move(newEntry), newEntry.first.size());
Jamie Madill360daee2017-06-29 10:36:19 -0400653 if (!result)
Jamie Madill32447362017-06-28 14:53:52 -0400654 {
655 ERR() << "Failed to store binary program in memory cache, program is too large.";
656 }
Jamie Madill360daee2017-06-29 10:36:19 -0400657 else
658 {
659 auto *platform = ANGLEPlatformCurrent();
Jamie Madill6c58b062017-08-01 13:44:25 -0400660 platform->cacheProgram(platform, programHash, result->first.size(), result->first.data());
Jamie Madill360daee2017-06-29 10:36:19 -0400661 }
Jamie Madill32447362017-06-28 14:53:52 -0400662}
663
Jamie Madill4c19a8a2017-07-24 11:46:06 -0400664void MemoryProgramCache::updateProgram(const Context *context, const Program *program)
665{
666 gl::ProgramHash programHash;
667 ComputeHash(context, program, &programHash);
668 putProgram(programHash, context, program);
669}
670
Jamie Madillc43be722017-07-13 16:22:14 -0400671void MemoryProgramCache::putBinary(const ProgramHash &programHash,
Jamie Madill32447362017-06-28 14:53:52 -0400672 const uint8_t *binary,
673 size_t length)
674{
675 // Copy the binary.
Jamie Madill6c58b062017-08-01 13:44:25 -0400676 CacheEntry newEntry;
677 newEntry.first.resize(length);
678 memcpy(newEntry.first.data(), binary, length);
679 newEntry.second = CacheSource::PutBinary;
Jamie Madill32447362017-06-28 14:53:52 -0400680
Jamie Madill32447362017-06-28 14:53:52 -0400681 // Store the binary.
Jamie Madill6c58b062017-08-01 13:44:25 -0400682 const CacheEntry *result = mProgramBinaryCache.put(programHash, std::move(newEntry), length);
Jamie Madillc43be722017-07-13 16:22:14 -0400683 if (!result)
684 {
685 ERR() << "Failed to store binary program in memory cache, program is too large.";
686 }
Jamie Madill32447362017-06-28 14:53:52 -0400687}
688
689void MemoryProgramCache::clear()
690{
691 mProgramBinaryCache.clear();
692 mIssuedWarnings = 0;
693}
694
Jamie Madillc43be722017-07-13 16:22:14 -0400695void MemoryProgramCache::resize(size_t maxCacheSizeBytes)
696{
697 mProgramBinaryCache.resize(maxCacheSizeBytes);
698}
699
700size_t MemoryProgramCache::entryCount() const
701{
702 return mProgramBinaryCache.entryCount();
703}
704
705size_t MemoryProgramCache::trim(size_t limit)
706{
707 return mProgramBinaryCache.shrinkToSize(limit);
708}
709
710size_t MemoryProgramCache::size() const
711{
712 return mProgramBinaryCache.size();
713}
714
715size_t MemoryProgramCache::maxSize() const
716{
717 return mProgramBinaryCache.maxSize();
718}
719
Jamie Madill4f86d052017-06-05 12:59:26 -0400720} // namespace gl