blob: 4f01c6283a612b2fcebfacdbe93fb5324632e90c [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);
Olli Etuaho465835d2017-09-26 13:34:10 +030045 stream->writeIntVector(var.arraySizes);
Jamie Madill4f86d052017-06-05 12:59:26 -040046 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();
Olli Etuaho465835d2017-09-26 13:34:10 +030057 stream->readIntVector<unsigned int>(&var->arraySizes);
Jamie Madill4f86d052017-06-05 12:59:26 -040058 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 Qin3a9090f2017-09-27 14:37:04 +080093void WriteBufferVariable(BinaryOutputStream *stream, const BufferVariable &var)
94{
95 WriteShaderVar(stream, var);
96
97 stream->writeInt(var.bufferIndex);
98 stream->writeInt(var.blockInfo.offset);
99 stream->writeInt(var.blockInfo.arrayStride);
100 stream->writeInt(var.blockInfo.matrixStride);
101 stream->writeInt(var.blockInfo.isRowMajorMatrix);
102 stream->writeInt(var.blockInfo.topLevelArrayStride);
103 stream->writeInt(var.topLevelArraySize);
104 stream->writeInt(var.vertexStaticUse);
105 stream->writeInt(var.fragmentStaticUse);
106 stream->writeInt(var.computeStaticUse);
107}
108
109void LoadBufferVariable(BinaryInputStream *stream, BufferVariable *var)
110{
111 LoadShaderVar(stream, var);
112
113 var->bufferIndex = stream->readInt<int>();
114 var->blockInfo.offset = stream->readInt<int>();
115 var->blockInfo.arrayStride = stream->readInt<int>();
116 var->blockInfo.matrixStride = stream->readInt<int>();
117 var->blockInfo.isRowMajorMatrix = stream->readBool();
118 var->blockInfo.topLevelArrayStride = stream->readInt<int>();
119 var->topLevelArraySize = stream->readInt<int>();
120 var->vertexStaticUse = stream->readBool();
121 var->fragmentStaticUse = stream->readBool();
122 var->computeStaticUse = stream->readBool();
123}
124
Jiajia Qin729b2c62017-08-14 09:36:11 +0800125void WriteInterfaceBlock(BinaryOutputStream *stream, const InterfaceBlock &block)
126{
127 stream->writeString(block.name);
128 stream->writeString(block.mappedName);
129 stream->writeInt(block.isArray);
130 stream->writeInt(block.arrayElement);
131
132 WriteShaderVariableBuffer(stream, block);
133}
134
135void LoadInterfaceBlock(BinaryInputStream *stream, InterfaceBlock *block)
136{
137 block->name = stream->readString();
138 block->mappedName = stream->readString();
139 block->isArray = stream->readBool();
140 block->arrayElement = stream->readInt<unsigned int>();
141
142 LoadShaderVariableBuffer(stream, block);
143}
144
Jamie Madill32447362017-06-28 14:53:52 -0400145class HashStream final : angle::NonCopyable
146{
147 public:
148 std::string str() { return mStringStream.str(); }
149
150 template <typename T>
151 HashStream &operator<<(T value)
152 {
153 mStringStream << value << kSeparator;
154 return *this;
155 }
156
157 private:
158 static constexpr char kSeparator = ':';
159 std::ostringstream mStringStream;
160};
161
162HashStream &operator<<(HashStream &stream, const Shader *shader)
163{
164 if (shader)
165 {
166 stream << shader->getSourceString().c_str() << shader->getSourceString().length()
167 << shader->getCompilerResourcesString().c_str();
168 }
169 return stream;
170}
171
Jamie Madill3c1da042017-11-27 18:33:40 -0500172HashStream &operator<<(HashStream &stream, const ProgramBindings &bindings)
Jamie Madill32447362017-06-28 14:53:52 -0400173{
174 for (const auto &binding : bindings)
175 {
176 stream << binding.first << binding.second;
177 }
178 return stream;
179}
180
181HashStream &operator<<(HashStream &stream, const std::vector<std::string> &strings)
182{
183 for (const auto &str : strings)
184 {
185 stream << str;
186 }
187 return stream;
188}
189
Jamie Madill4f86d052017-06-05 12:59:26 -0400190} // anonymous namespace
191
Jamie Madill32447362017-06-28 14:53:52 -0400192MemoryProgramCache::MemoryProgramCache(size_t maxCacheSizeBytes)
193 : mProgramBinaryCache(maxCacheSizeBytes), mIssuedWarnings(0)
194{
195}
196
197MemoryProgramCache::~MemoryProgramCache()
198{
199}
200
Jamie Madill4f86d052017-06-05 12:59:26 -0400201// static
202LinkResult MemoryProgramCache::Deserialize(const Context *context,
203 const Program *program,
204 ProgramState *state,
205 const uint8_t *binary,
206 size_t length,
207 InfoLog &infoLog)
208{
209 BinaryInputStream stream(binary, length);
210
211 unsigned char commitString[ANGLE_COMMIT_HASH_SIZE];
212 stream.readBytes(commitString, ANGLE_COMMIT_HASH_SIZE);
213 if (memcmp(commitString, ANGLE_COMMIT_HASH, sizeof(unsigned char) * ANGLE_COMMIT_HASH_SIZE) !=
214 0)
215 {
216 infoLog << "Invalid program binary version.";
217 return false;
218 }
219
220 int majorVersion = stream.readInt<int>();
221 int minorVersion = stream.readInt<int>();
222 if (majorVersion != context->getClientMajorVersion() ||
223 minorVersion != context->getClientMinorVersion())
224 {
225 infoLog << "Cannot load program binaries across different ES context versions.";
226 return false;
227 }
228
229 state->mComputeShaderLocalSize[0] = stream.readInt<int>();
230 state->mComputeShaderLocalSize[1] = stream.readInt<int>();
231 state->mComputeShaderLocalSize[2] = stream.readInt<int>();
232
Martin Radev7cf61662017-07-26 17:10:53 +0300233 state->mNumViews = stream.readInt<int>();
234
Brandon Jonesc405ae72017-12-06 14:15:03 -0800235 static_assert(MAX_VERTEX_ATTRIBS * 2 <= sizeof(uint32_t) * 8,
236 "All bits of mAttributesTypeMask types and mask fit into 32 bits each");
237 state->mAttributesTypeMask.from_ulong(stream.readInt<uint32_t>());
238 state->mAttributesMask = stream.readInt<uint32_t>();
239
Jamie Madill4f86d052017-06-05 12:59:26 -0400240 static_assert(MAX_VERTEX_ATTRIBS <= sizeof(unsigned long) * 8,
241 "Too many vertex attribs for mask");
242 state->mActiveAttribLocationsMask = stream.readInt<unsigned long>();
243
244 unsigned int attribCount = stream.readInt<unsigned int>();
245 ASSERT(state->mAttributes.empty());
246 for (unsigned int attribIndex = 0; attribIndex < attribCount; ++attribIndex)
247 {
248 sh::Attribute attrib;
249 LoadShaderVar(&stream, &attrib);
250 attrib.location = stream.readInt<int>();
251 state->mAttributes.push_back(attrib);
252 }
253
254 unsigned int uniformCount = stream.readInt<unsigned int>();
255 ASSERT(state->mUniforms.empty());
256 for (unsigned int uniformIndex = 0; uniformIndex < uniformCount; ++uniformIndex)
257 {
258 LinkedUniform uniform;
259 LoadShaderVar(&stream, &uniform);
260
jchen10eaef1e52017-06-13 10:44:11 +0800261 uniform.bufferIndex = stream.readInt<int>();
Jamie Madill4f86d052017-06-05 12:59:26 -0400262 uniform.blockInfo.offset = stream.readInt<int>();
263 uniform.blockInfo.arrayStride = stream.readInt<int>();
264 uniform.blockInfo.matrixStride = stream.readInt<int>();
265 uniform.blockInfo.isRowMajorMatrix = stream.readBool();
266
Jamie Madillf00f7ff2017-08-31 14:39:15 -0400267 uniform.typeInfo = &GetUniformTypeInfo(uniform.type);
268
Jamie Madill4f86d052017-06-05 12:59:26 -0400269 state->mUniforms.push_back(uniform);
270 }
271
272 const unsigned int uniformIndexCount = stream.readInt<unsigned int>();
273 ASSERT(state->mUniformLocations.empty());
274 for (unsigned int uniformIndexIndex = 0; uniformIndexIndex < uniformIndexCount;
275 uniformIndexIndex++)
276 {
277 VariableLocation variable;
Olli Etuaho1734e172017-10-27 15:30:27 +0300278 stream.readInt(&variable.arrayIndex);
Jamie Madill4f86d052017-06-05 12:59:26 -0400279 stream.readInt(&variable.index);
Jamie Madill4f86d052017-06-05 12:59:26 -0400280 stream.readBool(&variable.ignored);
281
282 state->mUniformLocations.push_back(variable);
283 }
284
285 unsigned int uniformBlockCount = stream.readInt<unsigned int>();
286 ASSERT(state->mUniformBlocks.empty());
287 for (unsigned int uniformBlockIndex = 0; uniformBlockIndex < uniformBlockCount;
288 ++uniformBlockIndex)
289 {
Jiajia Qin729b2c62017-08-14 09:36:11 +0800290 InterfaceBlock uniformBlock;
291 LoadInterfaceBlock(&stream, &uniformBlock);
Jamie Madill4f86d052017-06-05 12:59:26 -0400292 state->mUniformBlocks.push_back(uniformBlock);
Jamie Madill4f86d052017-06-05 12:59:26 -0400293
jchen107a20b972017-06-13 14:25:26 +0800294 state->mActiveUniformBlockBindings.set(uniformBlockIndex, uniformBlock.binding != 0);
Jamie Madill4f86d052017-06-05 12:59:26 -0400295 }
Jiajia Qin729b2c62017-08-14 09:36:11 +0800296
Jiajia Qin3a9090f2017-09-27 14:37:04 +0800297 unsigned int bufferVariableCount = stream.readInt<unsigned int>();
298 ASSERT(state->mBufferVariables.empty());
299 for (unsigned int index = 0; index < bufferVariableCount; ++index)
300 {
301 BufferVariable bufferVariable;
302 LoadBufferVariable(&stream, &bufferVariable);
303 state->mBufferVariables.push_back(bufferVariable);
304 }
305
Jiajia Qin729b2c62017-08-14 09:36:11 +0800306 unsigned int shaderStorageBlockCount = stream.readInt<unsigned int>();
307 ASSERT(state->mShaderStorageBlocks.empty());
308 for (unsigned int shaderStorageBlockIndex = 0;
309 shaderStorageBlockIndex < shaderStorageBlockCount; ++shaderStorageBlockIndex)
310 {
311 InterfaceBlock shaderStorageBlock;
312 LoadInterfaceBlock(&stream, &shaderStorageBlock);
313 state->mShaderStorageBlocks.push_back(shaderStorageBlock);
314 }
315
jchen10eaef1e52017-06-13 10:44:11 +0800316 unsigned int atomicCounterBufferCount = stream.readInt<unsigned int>();
317 ASSERT(state->mAtomicCounterBuffers.empty());
318 for (unsigned int bufferIndex = 0; bufferIndex < atomicCounterBufferCount; ++bufferIndex)
319 {
320 AtomicCounterBuffer atomicCounterBuffer;
321 LoadShaderVariableBuffer(&stream, &atomicCounterBuffer);
322
323 state->mAtomicCounterBuffers.push_back(atomicCounterBuffer);
324 }
Jamie Madill4f86d052017-06-05 12:59:26 -0400325
326 unsigned int transformFeedbackVaryingCount = stream.readInt<unsigned int>();
Jamie Madillffe00c02017-06-27 16:26:55 -0400327
328 // Reject programs that use transform feedback varyings if the hardware cannot support them.
329 if (transformFeedbackVaryingCount > 0 &&
330 context->getWorkarounds().disableProgramCachingForTransformFeedback)
331 {
332 infoLog << "Current driver does not support transform feedback in binary programs.";
333 return false;
334 }
335
Jamie Madill4f86d052017-06-05 12:59:26 -0400336 ASSERT(state->mLinkedTransformFeedbackVaryings.empty());
337 for (unsigned int transformFeedbackVaryingIndex = 0;
338 transformFeedbackVaryingIndex < transformFeedbackVaryingCount;
339 ++transformFeedbackVaryingIndex)
340 {
341 sh::Varying varying;
Olli Etuaho465835d2017-09-26 13:34:10 +0300342 stream.readIntVector<unsigned int>(&varying.arraySizes);
Jamie Madill4f86d052017-06-05 12:59:26 -0400343 stream.readInt(&varying.type);
344 stream.readString(&varying.name);
345
346 GLuint arrayIndex = stream.readInt<GLuint>();
347
348 state->mLinkedTransformFeedbackVaryings.emplace_back(varying, arrayIndex);
349 }
350
351 stream.readInt(&state->mTransformFeedbackBufferMode);
352
353 unsigned int outputCount = stream.readInt<unsigned int>();
354 ASSERT(state->mOutputVariables.empty());
355 for (unsigned int outputIndex = 0; outputIndex < outputCount; ++outputIndex)
356 {
357 sh::OutputVariable output;
358 LoadShaderVar(&stream, &output);
359 output.location = stream.readInt<int>();
360 state->mOutputVariables.push_back(output);
361 }
362
363 unsigned int outputVarCount = stream.readInt<unsigned int>();
Olli Etuahod2551232017-10-26 20:03:33 +0300364 ASSERT(state->mOutputLocations.empty());
Jamie Madill4f86d052017-06-05 12:59:26 -0400365 for (unsigned int outputIndex = 0; outputIndex < outputVarCount; ++outputIndex)
366 {
Jamie Madill4f86d052017-06-05 12:59:26 -0400367 VariableLocation locationData;
Olli Etuaho1734e172017-10-27 15:30:27 +0300368 stream.readInt(&locationData.arrayIndex);
Jamie Madill4f86d052017-06-05 12:59:26 -0400369 stream.readInt(&locationData.index);
Jamie Madillfb997ec2017-09-20 15:44:27 -0400370 stream.readBool(&locationData.ignored);
Olli Etuahod2551232017-10-26 20:03:33 +0300371 state->mOutputLocations.push_back(locationData);
Jamie Madill4f86d052017-06-05 12:59:26 -0400372 }
373
374 unsigned int outputTypeCount = stream.readInt<unsigned int>();
375 for (unsigned int outputIndex = 0; outputIndex < outputTypeCount; ++outputIndex)
376 {
377 state->mOutputVariableTypes.push_back(stream.readInt<GLenum>());
378 }
Brandon Jones76746f92017-11-22 11:44:41 -0800379
Brandon Jonesc405ae72017-12-06 14:15:03 -0800380 static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS * 2 <= 8 * sizeof(uint32_t),
381 "All bits of mDrawBufferTypeMask and mActiveOutputVariables types and mask fit "
382 "into 32 bits each");
383 state->mDrawBufferTypeMask.from_ulong(stream.readInt<uint32_t>());
Jamie Madill4f86d052017-06-05 12:59:26 -0400384 state->mActiveOutputVariables = stream.readInt<uint32_t>();
385
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800386 unsigned int samplerRangeLow = stream.readInt<unsigned int>();
387 unsigned int samplerRangeHigh = stream.readInt<unsigned int>();
388 state->mSamplerUniformRange = RangeUI(samplerRangeLow, samplerRangeHigh);
Jamie Madill4f86d052017-06-05 12:59:26 -0400389 unsigned int samplerCount = stream.readInt<unsigned int>();
390 for (unsigned int samplerIndex = 0; samplerIndex < samplerCount; ++samplerIndex)
391 {
392 GLenum textureType = stream.readInt<GLenum>();
393 size_t bindingCount = stream.readInt<size_t>();
Jamie Madill54164b02017-08-28 15:17:37 -0400394 bool unreferenced = stream.readBool();
395 state->mSamplerBindings.emplace_back(
396 SamplerBinding(textureType, bindingCount, unreferenced));
Jamie Madill4f86d052017-06-05 12:59:26 -0400397 }
398
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800399 unsigned int imageRangeLow = stream.readInt<unsigned int>();
400 unsigned int imageRangeHigh = stream.readInt<unsigned int>();
401 state->mImageUniformRange = RangeUI(imageRangeLow, imageRangeHigh);
Xinghua Cao0328b572017-06-26 15:51:36 +0800402 unsigned int imageBindingCount = stream.readInt<unsigned int>();
403 for (unsigned int imageIndex = 0; imageIndex < imageBindingCount; ++imageIndex)
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800404 {
Xinghua Cao0328b572017-06-26 15:51:36 +0800405 unsigned int elementCount = stream.readInt<unsigned int>();
406 ImageBinding imageBinding(elementCount);
407 for (unsigned int i = 0; i < elementCount; ++i)
408 {
409 imageBinding.boundImageUnits[i] = stream.readInt<unsigned int>();
410 }
411 state->mImageBindings.emplace_back(imageBinding);
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800412 }
413
jchen10eaef1e52017-06-13 10:44:11 +0800414 unsigned int atomicCounterRangeLow = stream.readInt<unsigned int>();
415 unsigned int atomicCounterRangeHigh = stream.readInt<unsigned int>();
416 state->mAtomicCounterUniformRange = RangeUI(atomicCounterRangeLow, atomicCounterRangeHigh);
417
Yunchao He85072e82017-11-14 15:43:28 +0800418 static_assert(SHADER_TYPE_MAX <= sizeof(unsigned long) * 8, "Too many shader types");
419 state->mLinkedShaderStages = stream.readInt<unsigned long>();
420
Jamie Madill4f86d052017-06-05 12:59:26 -0400421 return program->getImplementation()->load(context, infoLog, &stream);
422}
423
424// static
425void MemoryProgramCache::Serialize(const Context *context,
426 const gl::Program *program,
427 angle::MemoryBuffer *binaryOut)
428{
429 BinaryOutputStream stream;
430
431 stream.writeBytes(reinterpret_cast<const unsigned char *>(ANGLE_COMMIT_HASH),
432 ANGLE_COMMIT_HASH_SIZE);
433
434 // nullptr context is supported when computing binary length.
435 if (context)
436 {
437 stream.writeInt(context->getClientVersion().major);
438 stream.writeInt(context->getClientVersion().minor);
439 }
440 else
441 {
442 stream.writeInt(2);
443 stream.writeInt(0);
444 }
445
446 const auto &state = program->getState();
447
448 const auto &computeLocalSize = state.getComputeShaderLocalSize();
449
450 stream.writeInt(computeLocalSize[0]);
451 stream.writeInt(computeLocalSize[1]);
452 stream.writeInt(computeLocalSize[2]);
453
Martin Radev7cf61662017-07-26 17:10:53 +0300454 stream.writeInt(state.mNumViews);
455
Brandon Jonesc405ae72017-12-06 14:15:03 -0800456 static_assert(MAX_VERTEX_ATTRIBS * 2 <= sizeof(uint32_t) * 8,
457 "All bits of mAttributesTypeMask types and mask fit into 32 bits each");
458 stream.writeInt(static_cast<int>(state.mAttributesTypeMask.to_ulong()));
459 stream.writeInt(static_cast<int>(state.mAttributesMask.to_ulong()));
460
Jamie Madill4f86d052017-06-05 12:59:26 -0400461 stream.writeInt(state.getActiveAttribLocationsMask().to_ulong());
462
463 stream.writeInt(state.getAttributes().size());
464 for (const sh::Attribute &attrib : state.getAttributes())
465 {
466 WriteShaderVar(&stream, attrib);
467 stream.writeInt(attrib.location);
468 }
469
470 stream.writeInt(state.getUniforms().size());
471 for (const LinkedUniform &uniform : state.getUniforms())
472 {
473 WriteShaderVar(&stream, uniform);
474
475 // FIXME: referenced
476
jchen10eaef1e52017-06-13 10:44:11 +0800477 stream.writeInt(uniform.bufferIndex);
Jamie Madill4f86d052017-06-05 12:59:26 -0400478 stream.writeInt(uniform.blockInfo.offset);
479 stream.writeInt(uniform.blockInfo.arrayStride);
480 stream.writeInt(uniform.blockInfo.matrixStride);
481 stream.writeInt(uniform.blockInfo.isRowMajorMatrix);
482 }
483
484 stream.writeInt(state.getUniformLocations().size());
485 for (const auto &variable : state.getUniformLocations())
486 {
Olli Etuaho1734e172017-10-27 15:30:27 +0300487 stream.writeInt(variable.arrayIndex);
Jamie Madillfb997ec2017-09-20 15:44:27 -0400488 stream.writeIntOrNegOne(variable.index);
Jamie Madill4f86d052017-06-05 12:59:26 -0400489 stream.writeInt(variable.ignored);
490 }
491
492 stream.writeInt(state.getUniformBlocks().size());
Jiajia Qin729b2c62017-08-14 09:36:11 +0800493 for (const InterfaceBlock &uniformBlock : state.getUniformBlocks())
Jamie Madill4f86d052017-06-05 12:59:26 -0400494 {
Jiajia Qin729b2c62017-08-14 09:36:11 +0800495 WriteInterfaceBlock(&stream, uniformBlock);
496 }
Jamie Madill4f86d052017-06-05 12:59:26 -0400497
Jiajia Qin3a9090f2017-09-27 14:37:04 +0800498 stream.writeInt(state.getBufferVariables().size());
499 for (const BufferVariable &bufferVariable : state.getBufferVariables())
500 {
501 WriteBufferVariable(&stream, bufferVariable);
502 }
503
Jiajia Qin729b2c62017-08-14 09:36:11 +0800504 stream.writeInt(state.getShaderStorageBlocks().size());
505 for (const InterfaceBlock &shaderStorageBlock : state.getShaderStorageBlocks())
506 {
507 WriteInterfaceBlock(&stream, shaderStorageBlock);
jchen10eaef1e52017-06-13 10:44:11 +0800508 }
Jamie Madill4f86d052017-06-05 12:59:26 -0400509
jchen10eaef1e52017-06-13 10:44:11 +0800510 stream.writeInt(state.mAtomicCounterBuffers.size());
511 for (const auto &atomicCounterBuffer : state.mAtomicCounterBuffers)
512 {
513 WriteShaderVariableBuffer(&stream, atomicCounterBuffer);
Jamie Madill4f86d052017-06-05 12:59:26 -0400514 }
515
Jamie Madillffe00c02017-06-27 16:26:55 -0400516 // Warn the app layer if saving a binary with unsupported transform feedback.
517 if (!state.getLinkedTransformFeedbackVaryings().empty() &&
518 context->getWorkarounds().disableProgramCachingForTransformFeedback)
519 {
520 WARN() << "Saving program binary with transform feedback, which is not supported on this "
521 "driver.";
522 }
523
Jamie Madill4f86d052017-06-05 12:59:26 -0400524 stream.writeInt(state.getLinkedTransformFeedbackVaryings().size());
525 for (const auto &var : state.getLinkedTransformFeedbackVaryings())
526 {
Olli Etuaho465835d2017-09-26 13:34:10 +0300527 stream.writeIntVector(var.arraySizes);
Jamie Madill4f86d052017-06-05 12:59:26 -0400528 stream.writeInt(var.type);
529 stream.writeString(var.name);
530
531 stream.writeIntOrNegOne(var.arrayIndex);
532 }
533
534 stream.writeInt(state.getTransformFeedbackBufferMode());
535
536 stream.writeInt(state.getOutputVariables().size());
537 for (const sh::OutputVariable &output : state.getOutputVariables())
538 {
539 WriteShaderVar(&stream, output);
540 stream.writeInt(output.location);
541 }
542
543 stream.writeInt(state.getOutputLocations().size());
Olli Etuahod2551232017-10-26 20:03:33 +0300544 for (const auto &outputVar : state.getOutputLocations())
Jamie Madill4f86d052017-06-05 12:59:26 -0400545 {
Olli Etuaho1734e172017-10-27 15:30:27 +0300546 stream.writeInt(outputVar.arrayIndex);
Olli Etuahod2551232017-10-26 20:03:33 +0300547 stream.writeIntOrNegOne(outputVar.index);
Olli Etuahod2551232017-10-26 20:03:33 +0300548 stream.writeInt(outputVar.ignored);
Jamie Madill4f86d052017-06-05 12:59:26 -0400549 }
550
551 stream.writeInt(state.mOutputVariableTypes.size());
552 for (const auto &outputVariableType : state.mOutputVariableTypes)
553 {
554 stream.writeInt(outputVariableType);
555 }
556
Brandon Jonesc405ae72017-12-06 14:15:03 -0800557 static_assert(
558 IMPLEMENTATION_MAX_DRAW_BUFFERS * 2 <= 8 * sizeof(uint32_t),
559 "All bits of mDrawBufferTypeMask and mActiveOutputVariables can be contained in 32 bits");
560 stream.writeInt(static_cast<int>(state.mDrawBufferTypeMask.to_ulong()));
561 stream.writeInt(static_cast<int>(state.mActiveOutputVariables.to_ulong()));
Jamie Madill4f86d052017-06-05 12:59:26 -0400562
563 stream.writeInt(state.getSamplerUniformRange().low());
564 stream.writeInt(state.getSamplerUniformRange().high());
565
566 stream.writeInt(state.getSamplerBindings().size());
567 for (const auto &samplerBinding : state.getSamplerBindings())
568 {
569 stream.writeInt(samplerBinding.textureType);
570 stream.writeInt(samplerBinding.boundTextureUnits.size());
Jamie Madill54164b02017-08-28 15:17:37 -0400571 stream.writeInt(samplerBinding.unreferenced);
Jamie Madill4f86d052017-06-05 12:59:26 -0400572 }
573
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800574 stream.writeInt(state.getImageUniformRange().low());
575 stream.writeInt(state.getImageUniformRange().high());
576
577 stream.writeInt(state.getImageBindings().size());
578 for (const auto &imageBinding : state.getImageBindings())
579 {
Xinghua Cao0328b572017-06-26 15:51:36 +0800580 stream.writeInt(imageBinding.boundImageUnits.size());
581 for (size_t i = 0; i < imageBinding.boundImageUnits.size(); ++i)
582 {
583 stream.writeInt(imageBinding.boundImageUnits[i]);
584 }
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800585 }
586
jchen10eaef1e52017-06-13 10:44:11 +0800587 stream.writeInt(state.getAtomicCounterUniformRange().low());
588 stream.writeInt(state.getAtomicCounterUniformRange().high());
589
Yunchao He85072e82017-11-14 15:43:28 +0800590 stream.writeInt(state.getLinkedShaderStages().to_ulong());
591
Jamie Madill27a60632017-06-30 15:12:01 -0400592 program->getImplementation()->save(context, &stream);
Jamie Madill4f86d052017-06-05 12:59:26 -0400593
594 ASSERT(binaryOut);
595 binaryOut->resize(stream.length());
596 memcpy(binaryOut->data(), stream.data(), stream.length());
597}
598
Jamie Madill32447362017-06-28 14:53:52 -0400599// static
600void MemoryProgramCache::ComputeHash(const Context *context,
601 const Program *program,
602 ProgramHash *hashOut)
603{
Jamie Madillacf2f3a2017-11-21 19:22:44 -0500604 const Shader *vertexShader = program->getAttachedVertexShader();
605 const Shader *fragmentShader = program->getAttachedFragmentShader();
606 const Shader *computeShader = program->getAttachedComputeShader();
Jamie Madill32447362017-06-28 14:53:52 -0400607
608 // Compute the program hash. Start with the shader hashes and resource strings.
609 HashStream hashStream;
610 hashStream << vertexShader << fragmentShader << computeShader;
611
612 // Add some ANGLE metadata and Context properties, such as version and back-end.
613 hashStream << ANGLE_COMMIT_HASH << context->getClientMajorVersion()
614 << context->getClientMinorVersion() << context->getString(GL_RENDERER);
615
616 // Hash pre-link program properties.
617 hashStream << program->getAttributeBindings() << program->getUniformLocationBindings()
618 << program->getFragmentInputBindings()
619 << program->getState().getTransformFeedbackVaryingNames()
620 << program->getState().getTransformFeedbackBufferMode();
621
622 // Call the secure SHA hashing function.
623 const std::string &programKey = hashStream.str();
624 angle::base::SHA1HashBytes(reinterpret_cast<const unsigned char *>(programKey.c_str()),
625 programKey.length(), hashOut->data());
626}
627
628LinkResult MemoryProgramCache::getProgram(const Context *context,
629 const Program *program,
630 ProgramState *state,
631 ProgramHash *hashOut)
632{
633 ComputeHash(context, program, hashOut);
634 const angle::MemoryBuffer *binaryProgram = nullptr;
635 LinkResult result(false);
636 if (get(*hashOut, &binaryProgram))
637 {
638 InfoLog infoLog;
639 ANGLE_TRY_RESULT(Deserialize(context, program, state, binaryProgram->data(),
640 binaryProgram->size(), infoLog),
641 result);
Jamie Madill6c58b062017-08-01 13:44:25 -0400642 ANGLE_HISTOGRAM_BOOLEAN("GPU.ANGLE.ProgramCache.LoadBinarySuccess", result.getResult());
Jamie Madill32447362017-06-28 14:53:52 -0400643 if (!result.getResult())
644 {
645 // Cache load failed, evict.
646 if (mIssuedWarnings++ < kWarningLimit)
647 {
648 WARN() << "Failed to load binary from cache: " << infoLog.str();
649
650 if (mIssuedWarnings == kWarningLimit)
651 {
652 WARN() << "Reaching warning limit for cache load failures, silencing "
653 "subsequent warnings.";
654 }
655 }
656 remove(*hashOut);
657 }
658 }
659 return result;
660}
661
662bool MemoryProgramCache::get(const ProgramHash &programHash, const angle::MemoryBuffer **programOut)
663{
Jamie Madill6c58b062017-08-01 13:44:25 -0400664 const CacheEntry *entry = nullptr;
665 if (!mProgramBinaryCache.get(programHash, &entry))
666 {
667 ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.ProgramCache.CacheResult", kCacheMiss,
668 kCacheResultMax);
669 return false;
670 }
671
672 if (entry->second == CacheSource::PutProgram)
673 {
674 ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.ProgramCache.CacheResult", kCacheHitMemory,
675 kCacheResultMax);
676 }
677 else
678 {
679 ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.ProgramCache.CacheResult", kCacheHitDisk,
680 kCacheResultMax);
681 }
682
683 *programOut = &entry->first;
684 return true;
Jamie Madill32447362017-06-28 14:53:52 -0400685}
686
Jamie Madillc43be722017-07-13 16:22:14 -0400687bool MemoryProgramCache::getAt(size_t index,
688 ProgramHash *hashOut,
689 const angle::MemoryBuffer **programOut)
690{
Jamie Madill6c58b062017-08-01 13:44:25 -0400691 const CacheEntry *entry = nullptr;
692 if (!mProgramBinaryCache.getAt(index, hashOut, &entry))
693 {
694 return false;
695 }
696
697 *programOut = &entry->first;
698 return true;
Jamie Madillc43be722017-07-13 16:22:14 -0400699}
700
Jamie Madill32447362017-06-28 14:53:52 -0400701void MemoryProgramCache::remove(const ProgramHash &programHash)
702{
703 bool result = mProgramBinaryCache.eraseByKey(programHash);
704 ASSERT(result);
705}
706
Jamie Madill6c58b062017-08-01 13:44:25 -0400707void MemoryProgramCache::putProgram(const ProgramHash &programHash,
708 const Context *context,
709 const Program *program)
Jamie Madill32447362017-06-28 14:53:52 -0400710{
Jamie Madill6c58b062017-08-01 13:44:25 -0400711 CacheEntry newEntry;
712 Serialize(context, program, &newEntry.first);
713 newEntry.second = CacheSource::PutProgram;
714
715 ANGLE_HISTOGRAM_COUNTS("GPU.ANGLE.ProgramCache.ProgramBinarySizeBytes",
716 static_cast<int>(newEntry.first.size()));
717
718 const CacheEntry *result =
719 mProgramBinaryCache.put(programHash, std::move(newEntry), newEntry.first.size());
Jamie Madill360daee2017-06-29 10:36:19 -0400720 if (!result)
Jamie Madill32447362017-06-28 14:53:52 -0400721 {
722 ERR() << "Failed to store binary program in memory cache, program is too large.";
723 }
Jamie Madill360daee2017-06-29 10:36:19 -0400724 else
725 {
726 auto *platform = ANGLEPlatformCurrent();
Jamie Madill6c58b062017-08-01 13:44:25 -0400727 platform->cacheProgram(platform, programHash, result->first.size(), result->first.data());
Jamie Madill360daee2017-06-29 10:36:19 -0400728 }
Jamie Madill32447362017-06-28 14:53:52 -0400729}
730
Jamie Madill4c19a8a2017-07-24 11:46:06 -0400731void MemoryProgramCache::updateProgram(const Context *context, const Program *program)
732{
733 gl::ProgramHash programHash;
734 ComputeHash(context, program, &programHash);
735 putProgram(programHash, context, program);
736}
737
Jamie Madillc43be722017-07-13 16:22:14 -0400738void MemoryProgramCache::putBinary(const ProgramHash &programHash,
Jamie Madill32447362017-06-28 14:53:52 -0400739 const uint8_t *binary,
740 size_t length)
741{
742 // Copy the binary.
Jamie Madill6c58b062017-08-01 13:44:25 -0400743 CacheEntry newEntry;
744 newEntry.first.resize(length);
745 memcpy(newEntry.first.data(), binary, length);
746 newEntry.second = CacheSource::PutBinary;
Jamie Madill32447362017-06-28 14:53:52 -0400747
Jamie Madill32447362017-06-28 14:53:52 -0400748 // Store the binary.
Jamie Madill6c58b062017-08-01 13:44:25 -0400749 const CacheEntry *result = mProgramBinaryCache.put(programHash, std::move(newEntry), length);
Jamie Madillc43be722017-07-13 16:22:14 -0400750 if (!result)
751 {
752 ERR() << "Failed to store binary program in memory cache, program is too large.";
753 }
Jamie Madill32447362017-06-28 14:53:52 -0400754}
755
756void MemoryProgramCache::clear()
757{
758 mProgramBinaryCache.clear();
759 mIssuedWarnings = 0;
760}
761
Jamie Madillc43be722017-07-13 16:22:14 -0400762void MemoryProgramCache::resize(size_t maxCacheSizeBytes)
763{
764 mProgramBinaryCache.resize(maxCacheSizeBytes);
765}
766
767size_t MemoryProgramCache::entryCount() const
768{
769 return mProgramBinaryCache.entryCount();
770}
771
772size_t MemoryProgramCache::trim(size_t limit)
773{
774 return mProgramBinaryCache.shrinkToSize(limit);
775}
776
777size_t MemoryProgramCache::size() const
778{
779 return mProgramBinaryCache.size();
780}
781
782size_t MemoryProgramCache::maxSize() const
783{
784 return mProgramBinaryCache.maxSize();
785}
786
Jamie Madill4f86d052017-06-05 12:59:26 -0400787} // namespace gl