blob: a3e49a6ae5e136bcb64e933748565a5b8a19c931 [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);
Jiawei Shao0d88ec92018-02-27 16:25:31 +080070 stream->writeInt(var.geometryStaticUse);
jchen10eaef1e52017-06-13 10:44:11 +080071
72 stream->writeInt(var.memberIndexes.size());
73 for (unsigned int memberCounterIndex : var.memberIndexes)
74 {
75 stream->writeInt(memberCounterIndex);
76 }
77}
78
79void LoadShaderVariableBuffer(BinaryInputStream *stream, ShaderVariableBuffer *var)
80{
81 var->binding = stream->readInt<int>();
82 var->dataSize = stream->readInt<unsigned int>();
83 var->vertexStaticUse = stream->readBool();
84 var->fragmentStaticUse = stream->readBool();
85 var->computeStaticUse = stream->readBool();
Jiawei Shao0d88ec92018-02-27 16:25:31 +080086 var->geometryStaticUse = stream->readBool();
jchen10eaef1e52017-06-13 10:44:11 +080087
88 unsigned int numMembers = stream->readInt<unsigned int>();
89 for (unsigned int blockMemberIndex = 0; blockMemberIndex < numMembers; blockMemberIndex++)
90 {
91 var->memberIndexes.push_back(stream->readInt<unsigned int>());
92 }
93}
94
Jiajia Qin3a9090f2017-09-27 14:37:04 +080095void WriteBufferVariable(BinaryOutputStream *stream, const BufferVariable &var)
96{
97 WriteShaderVar(stream, var);
98
99 stream->writeInt(var.bufferIndex);
100 stream->writeInt(var.blockInfo.offset);
101 stream->writeInt(var.blockInfo.arrayStride);
102 stream->writeInt(var.blockInfo.matrixStride);
103 stream->writeInt(var.blockInfo.isRowMajorMatrix);
104 stream->writeInt(var.blockInfo.topLevelArrayStride);
105 stream->writeInt(var.topLevelArraySize);
106 stream->writeInt(var.vertexStaticUse);
107 stream->writeInt(var.fragmentStaticUse);
108 stream->writeInt(var.computeStaticUse);
109}
110
111void LoadBufferVariable(BinaryInputStream *stream, BufferVariable *var)
112{
113 LoadShaderVar(stream, var);
114
115 var->bufferIndex = stream->readInt<int>();
116 var->blockInfo.offset = stream->readInt<int>();
117 var->blockInfo.arrayStride = stream->readInt<int>();
118 var->blockInfo.matrixStride = stream->readInt<int>();
119 var->blockInfo.isRowMajorMatrix = stream->readBool();
120 var->blockInfo.topLevelArrayStride = stream->readInt<int>();
121 var->topLevelArraySize = stream->readInt<int>();
122 var->vertexStaticUse = stream->readBool();
123 var->fragmentStaticUse = stream->readBool();
124 var->computeStaticUse = stream->readBool();
125}
126
Jiajia Qin729b2c62017-08-14 09:36:11 +0800127void WriteInterfaceBlock(BinaryOutputStream *stream, const InterfaceBlock &block)
128{
129 stream->writeString(block.name);
130 stream->writeString(block.mappedName);
131 stream->writeInt(block.isArray);
132 stream->writeInt(block.arrayElement);
133
134 WriteShaderVariableBuffer(stream, block);
135}
136
137void LoadInterfaceBlock(BinaryInputStream *stream, InterfaceBlock *block)
138{
139 block->name = stream->readString();
140 block->mappedName = stream->readString();
141 block->isArray = stream->readBool();
142 block->arrayElement = stream->readInt<unsigned int>();
143
144 LoadShaderVariableBuffer(stream, block);
145}
146
Jamie Madill32447362017-06-28 14:53:52 -0400147class HashStream final : angle::NonCopyable
148{
149 public:
150 std::string str() { return mStringStream.str(); }
151
152 template <typename T>
153 HashStream &operator<<(T value)
154 {
155 mStringStream << value << kSeparator;
156 return *this;
157 }
158
159 private:
160 static constexpr char kSeparator = ':';
161 std::ostringstream mStringStream;
162};
163
164HashStream &operator<<(HashStream &stream, const Shader *shader)
165{
166 if (shader)
167 {
168 stream << shader->getSourceString().c_str() << shader->getSourceString().length()
169 << shader->getCompilerResourcesString().c_str();
170 }
171 return stream;
172}
173
Jamie Madill3c1da042017-11-27 18:33:40 -0500174HashStream &operator<<(HashStream &stream, const ProgramBindings &bindings)
Jamie Madill32447362017-06-28 14:53:52 -0400175{
176 for (const auto &binding : bindings)
177 {
178 stream << binding.first << binding.second;
179 }
180 return stream;
181}
182
183HashStream &operator<<(HashStream &stream, const std::vector<std::string> &strings)
184{
185 for (const auto &str : strings)
186 {
187 stream << str;
188 }
189 return stream;
190}
191
Jamie Madill4f86d052017-06-05 12:59:26 -0400192} // anonymous namespace
193
Jamie Madill32447362017-06-28 14:53:52 -0400194MemoryProgramCache::MemoryProgramCache(size_t maxCacheSizeBytes)
195 : mProgramBinaryCache(maxCacheSizeBytes), mIssuedWarnings(0)
196{
197}
198
199MemoryProgramCache::~MemoryProgramCache()
200{
201}
202
Jamie Madill4f86d052017-06-05 12:59:26 -0400203// static
204LinkResult MemoryProgramCache::Deserialize(const Context *context,
205 const Program *program,
206 ProgramState *state,
207 const uint8_t *binary,
208 size_t length,
209 InfoLog &infoLog)
210{
211 BinaryInputStream stream(binary, length);
212
213 unsigned char commitString[ANGLE_COMMIT_HASH_SIZE];
214 stream.readBytes(commitString, ANGLE_COMMIT_HASH_SIZE);
215 if (memcmp(commitString, ANGLE_COMMIT_HASH, sizeof(unsigned char) * ANGLE_COMMIT_HASH_SIZE) !=
216 0)
217 {
218 infoLog << "Invalid program binary version.";
219 return false;
220 }
221
222 int majorVersion = stream.readInt<int>();
223 int minorVersion = stream.readInt<int>();
224 if (majorVersion != context->getClientMajorVersion() ||
225 minorVersion != context->getClientMinorVersion())
226 {
227 infoLog << "Cannot load program binaries across different ES context versions.";
228 return false;
229 }
230
231 state->mComputeShaderLocalSize[0] = stream.readInt<int>();
232 state->mComputeShaderLocalSize[1] = stream.readInt<int>();
233 state->mComputeShaderLocalSize[2] = stream.readInt<int>();
234
Jiawei Shao4ed05da2018-02-02 14:26:15 +0800235 state->mGeometryShaderInputPrimitiveType = stream.readInt<GLenum>();
236 state->mGeometryShaderOutputPrimitiveType = stream.readInt<GLenum>();
237 state->mGeometryShaderInvocations = stream.readInt<int>();
238 state->mGeometryShaderMaxVertices = stream.readInt<int>();
239
Martin Radev7cf61662017-07-26 17:10:53 +0300240 state->mNumViews = stream.readInt<int>();
241
Brandon Jonesc405ae72017-12-06 14:15:03 -0800242 static_assert(MAX_VERTEX_ATTRIBS * 2 <= sizeof(uint32_t) * 8,
243 "All bits of mAttributesTypeMask types and mask fit into 32 bits each");
244 state->mAttributesTypeMask.from_ulong(stream.readInt<uint32_t>());
245 state->mAttributesMask = stream.readInt<uint32_t>();
246
Jamie Madill4f86d052017-06-05 12:59:26 -0400247 static_assert(MAX_VERTEX_ATTRIBS <= sizeof(unsigned long) * 8,
248 "Too many vertex attribs for mask");
249 state->mActiveAttribLocationsMask = stream.readInt<unsigned long>();
250
251 unsigned int attribCount = stream.readInt<unsigned int>();
252 ASSERT(state->mAttributes.empty());
253 for (unsigned int attribIndex = 0; attribIndex < attribCount; ++attribIndex)
254 {
255 sh::Attribute attrib;
256 LoadShaderVar(&stream, &attrib);
257 attrib.location = stream.readInt<int>();
258 state->mAttributes.push_back(attrib);
259 }
260
261 unsigned int uniformCount = stream.readInt<unsigned int>();
262 ASSERT(state->mUniforms.empty());
263 for (unsigned int uniformIndex = 0; uniformIndex < uniformCount; ++uniformIndex)
264 {
265 LinkedUniform uniform;
266 LoadShaderVar(&stream, &uniform);
267
jchen10eaef1e52017-06-13 10:44:11 +0800268 uniform.bufferIndex = stream.readInt<int>();
Jamie Madill4f86d052017-06-05 12:59:26 -0400269 uniform.blockInfo.offset = stream.readInt<int>();
270 uniform.blockInfo.arrayStride = stream.readInt<int>();
271 uniform.blockInfo.matrixStride = stream.readInt<int>();
272 uniform.blockInfo.isRowMajorMatrix = stream.readBool();
273
Jamie Madillf00f7ff2017-08-31 14:39:15 -0400274 uniform.typeInfo = &GetUniformTypeInfo(uniform.type);
275
Jamie Madill4f86d052017-06-05 12:59:26 -0400276 state->mUniforms.push_back(uniform);
277 }
278
279 const unsigned int uniformIndexCount = stream.readInt<unsigned int>();
280 ASSERT(state->mUniformLocations.empty());
281 for (unsigned int uniformIndexIndex = 0; uniformIndexIndex < uniformIndexCount;
282 uniformIndexIndex++)
283 {
284 VariableLocation variable;
Olli Etuaho1734e172017-10-27 15:30:27 +0300285 stream.readInt(&variable.arrayIndex);
Jamie Madill4f86d052017-06-05 12:59:26 -0400286 stream.readInt(&variable.index);
Jamie Madill4f86d052017-06-05 12:59:26 -0400287 stream.readBool(&variable.ignored);
288
289 state->mUniformLocations.push_back(variable);
290 }
291
292 unsigned int uniformBlockCount = stream.readInt<unsigned int>();
293 ASSERT(state->mUniformBlocks.empty());
294 for (unsigned int uniformBlockIndex = 0; uniformBlockIndex < uniformBlockCount;
295 ++uniformBlockIndex)
296 {
Jiajia Qin729b2c62017-08-14 09:36:11 +0800297 InterfaceBlock uniformBlock;
298 LoadInterfaceBlock(&stream, &uniformBlock);
Jamie Madill4f86d052017-06-05 12:59:26 -0400299 state->mUniformBlocks.push_back(uniformBlock);
Jamie Madill4f86d052017-06-05 12:59:26 -0400300
jchen107a20b972017-06-13 14:25:26 +0800301 state->mActiveUniformBlockBindings.set(uniformBlockIndex, uniformBlock.binding != 0);
Jamie Madill4f86d052017-06-05 12:59:26 -0400302 }
Jiajia Qin729b2c62017-08-14 09:36:11 +0800303
Jiajia Qin3a9090f2017-09-27 14:37:04 +0800304 unsigned int bufferVariableCount = stream.readInt<unsigned int>();
305 ASSERT(state->mBufferVariables.empty());
306 for (unsigned int index = 0; index < bufferVariableCount; ++index)
307 {
308 BufferVariable bufferVariable;
309 LoadBufferVariable(&stream, &bufferVariable);
310 state->mBufferVariables.push_back(bufferVariable);
311 }
312
Jiajia Qin729b2c62017-08-14 09:36:11 +0800313 unsigned int shaderStorageBlockCount = stream.readInt<unsigned int>();
314 ASSERT(state->mShaderStorageBlocks.empty());
315 for (unsigned int shaderStorageBlockIndex = 0;
316 shaderStorageBlockIndex < shaderStorageBlockCount; ++shaderStorageBlockIndex)
317 {
318 InterfaceBlock shaderStorageBlock;
319 LoadInterfaceBlock(&stream, &shaderStorageBlock);
320 state->mShaderStorageBlocks.push_back(shaderStorageBlock);
321 }
322
jchen10eaef1e52017-06-13 10:44:11 +0800323 unsigned int atomicCounterBufferCount = stream.readInt<unsigned int>();
324 ASSERT(state->mAtomicCounterBuffers.empty());
325 for (unsigned int bufferIndex = 0; bufferIndex < atomicCounterBufferCount; ++bufferIndex)
326 {
327 AtomicCounterBuffer atomicCounterBuffer;
328 LoadShaderVariableBuffer(&stream, &atomicCounterBuffer);
329
330 state->mAtomicCounterBuffers.push_back(atomicCounterBuffer);
331 }
Jamie Madill4f86d052017-06-05 12:59:26 -0400332
333 unsigned int transformFeedbackVaryingCount = stream.readInt<unsigned int>();
Jamie Madillffe00c02017-06-27 16:26:55 -0400334
335 // Reject programs that use transform feedback varyings if the hardware cannot support them.
336 if (transformFeedbackVaryingCount > 0 &&
337 context->getWorkarounds().disableProgramCachingForTransformFeedback)
338 {
339 infoLog << "Current driver does not support transform feedback in binary programs.";
340 return false;
341 }
342
Jamie Madill4f86d052017-06-05 12:59:26 -0400343 ASSERT(state->mLinkedTransformFeedbackVaryings.empty());
344 for (unsigned int transformFeedbackVaryingIndex = 0;
345 transformFeedbackVaryingIndex < transformFeedbackVaryingCount;
346 ++transformFeedbackVaryingIndex)
347 {
348 sh::Varying varying;
Olli Etuaho465835d2017-09-26 13:34:10 +0300349 stream.readIntVector<unsigned int>(&varying.arraySizes);
Jamie Madill4f86d052017-06-05 12:59:26 -0400350 stream.readInt(&varying.type);
351 stream.readString(&varying.name);
352
353 GLuint arrayIndex = stream.readInt<GLuint>();
354
355 state->mLinkedTransformFeedbackVaryings.emplace_back(varying, arrayIndex);
356 }
357
358 stream.readInt(&state->mTransformFeedbackBufferMode);
359
360 unsigned int outputCount = stream.readInt<unsigned int>();
361 ASSERT(state->mOutputVariables.empty());
362 for (unsigned int outputIndex = 0; outputIndex < outputCount; ++outputIndex)
363 {
364 sh::OutputVariable output;
365 LoadShaderVar(&stream, &output);
366 output.location = stream.readInt<int>();
367 state->mOutputVariables.push_back(output);
368 }
369
370 unsigned int outputVarCount = stream.readInt<unsigned int>();
Olli Etuahod2551232017-10-26 20:03:33 +0300371 ASSERT(state->mOutputLocations.empty());
Jamie Madill4f86d052017-06-05 12:59:26 -0400372 for (unsigned int outputIndex = 0; outputIndex < outputVarCount; ++outputIndex)
373 {
Jamie Madill4f86d052017-06-05 12:59:26 -0400374 VariableLocation locationData;
Olli Etuaho1734e172017-10-27 15:30:27 +0300375 stream.readInt(&locationData.arrayIndex);
Jamie Madill4f86d052017-06-05 12:59:26 -0400376 stream.readInt(&locationData.index);
Jamie Madillfb997ec2017-09-20 15:44:27 -0400377 stream.readBool(&locationData.ignored);
Olli Etuahod2551232017-10-26 20:03:33 +0300378 state->mOutputLocations.push_back(locationData);
Jamie Madill4f86d052017-06-05 12:59:26 -0400379 }
380
381 unsigned int outputTypeCount = stream.readInt<unsigned int>();
382 for (unsigned int outputIndex = 0; outputIndex < outputTypeCount; ++outputIndex)
383 {
384 state->mOutputVariableTypes.push_back(stream.readInt<GLenum>());
385 }
Brandon Jones76746f92017-11-22 11:44:41 -0800386
Brandon Jonesc405ae72017-12-06 14:15:03 -0800387 static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS * 2 <= 8 * sizeof(uint32_t),
388 "All bits of mDrawBufferTypeMask and mActiveOutputVariables types and mask fit "
389 "into 32 bits each");
390 state->mDrawBufferTypeMask.from_ulong(stream.readInt<uint32_t>());
Jamie Madill4f86d052017-06-05 12:59:26 -0400391 state->mActiveOutputVariables = stream.readInt<uint32_t>();
392
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800393 unsigned int samplerRangeLow = stream.readInt<unsigned int>();
394 unsigned int samplerRangeHigh = stream.readInt<unsigned int>();
395 state->mSamplerUniformRange = RangeUI(samplerRangeLow, samplerRangeHigh);
Jamie Madill4f86d052017-06-05 12:59:26 -0400396 unsigned int samplerCount = stream.readInt<unsigned int>();
397 for (unsigned int samplerIndex = 0; samplerIndex < samplerCount; ++samplerIndex)
398 {
Corentin Wallezf0e89be2017-11-08 14:00:32 -0800399 TextureType textureType = stream.readEnum<TextureType>();
Jamie Madill4f86d052017-06-05 12:59:26 -0400400 size_t bindingCount = stream.readInt<size_t>();
Jamie Madill54164b02017-08-28 15:17:37 -0400401 bool unreferenced = stream.readBool();
402 state->mSamplerBindings.emplace_back(
403 SamplerBinding(textureType, bindingCount, unreferenced));
Jamie Madill4f86d052017-06-05 12:59:26 -0400404 }
405
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800406 unsigned int imageRangeLow = stream.readInt<unsigned int>();
407 unsigned int imageRangeHigh = stream.readInt<unsigned int>();
408 state->mImageUniformRange = RangeUI(imageRangeLow, imageRangeHigh);
Xinghua Cao0328b572017-06-26 15:51:36 +0800409 unsigned int imageBindingCount = stream.readInt<unsigned int>();
410 for (unsigned int imageIndex = 0; imageIndex < imageBindingCount; ++imageIndex)
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800411 {
Xinghua Cao0328b572017-06-26 15:51:36 +0800412 unsigned int elementCount = stream.readInt<unsigned int>();
413 ImageBinding imageBinding(elementCount);
414 for (unsigned int i = 0; i < elementCount; ++i)
415 {
416 imageBinding.boundImageUnits[i] = stream.readInt<unsigned int>();
417 }
418 state->mImageBindings.emplace_back(imageBinding);
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800419 }
420
jchen10eaef1e52017-06-13 10:44:11 +0800421 unsigned int atomicCounterRangeLow = stream.readInt<unsigned int>();
422 unsigned int atomicCounterRangeHigh = stream.readInt<unsigned int>();
423 state->mAtomicCounterUniformRange = RangeUI(atomicCounterRangeLow, atomicCounterRangeHigh);
424
Yunchao He85072e82017-11-14 15:43:28 +0800425 static_assert(SHADER_TYPE_MAX <= sizeof(unsigned long) * 8, "Too many shader types");
426 state->mLinkedShaderStages = stream.readInt<unsigned long>();
427
Jamie Madill4f86d052017-06-05 12:59:26 -0400428 return program->getImplementation()->load(context, infoLog, &stream);
429}
430
431// static
432void MemoryProgramCache::Serialize(const Context *context,
433 const gl::Program *program,
434 angle::MemoryBuffer *binaryOut)
435{
436 BinaryOutputStream stream;
437
438 stream.writeBytes(reinterpret_cast<const unsigned char *>(ANGLE_COMMIT_HASH),
439 ANGLE_COMMIT_HASH_SIZE);
440
441 // nullptr context is supported when computing binary length.
442 if (context)
443 {
444 stream.writeInt(context->getClientVersion().major);
445 stream.writeInt(context->getClientVersion().minor);
446 }
447 else
448 {
449 stream.writeInt(2);
450 stream.writeInt(0);
451 }
452
453 const auto &state = program->getState();
454
455 const auto &computeLocalSize = state.getComputeShaderLocalSize();
456
457 stream.writeInt(computeLocalSize[0]);
458 stream.writeInt(computeLocalSize[1]);
459 stream.writeInt(computeLocalSize[2]);
460
Jiawei Shao4ed05da2018-02-02 14:26:15 +0800461 ASSERT(state.mGeometryShaderInvocations >= 1 && state.mGeometryShaderMaxVertices >= 0);
462 stream.writeInt(state.mGeometryShaderInputPrimitiveType);
463 stream.writeInt(state.mGeometryShaderOutputPrimitiveType);
464 stream.writeInt(state.mGeometryShaderInvocations);
465 stream.writeInt(state.mGeometryShaderMaxVertices);
466
Martin Radev7cf61662017-07-26 17:10:53 +0300467 stream.writeInt(state.mNumViews);
468
Brandon Jonesc405ae72017-12-06 14:15:03 -0800469 static_assert(MAX_VERTEX_ATTRIBS * 2 <= sizeof(uint32_t) * 8,
470 "All bits of mAttributesTypeMask types and mask fit into 32 bits each");
471 stream.writeInt(static_cast<int>(state.mAttributesTypeMask.to_ulong()));
472 stream.writeInt(static_cast<int>(state.mAttributesMask.to_ulong()));
473
Jamie Madill4f86d052017-06-05 12:59:26 -0400474 stream.writeInt(state.getActiveAttribLocationsMask().to_ulong());
475
476 stream.writeInt(state.getAttributes().size());
477 for (const sh::Attribute &attrib : state.getAttributes())
478 {
479 WriteShaderVar(&stream, attrib);
480 stream.writeInt(attrib.location);
481 }
482
483 stream.writeInt(state.getUniforms().size());
484 for (const LinkedUniform &uniform : state.getUniforms())
485 {
486 WriteShaderVar(&stream, uniform);
487
488 // FIXME: referenced
489
jchen10eaef1e52017-06-13 10:44:11 +0800490 stream.writeInt(uniform.bufferIndex);
Jamie Madill4f86d052017-06-05 12:59:26 -0400491 stream.writeInt(uniform.blockInfo.offset);
492 stream.writeInt(uniform.blockInfo.arrayStride);
493 stream.writeInt(uniform.blockInfo.matrixStride);
494 stream.writeInt(uniform.blockInfo.isRowMajorMatrix);
495 }
496
497 stream.writeInt(state.getUniformLocations().size());
498 for (const auto &variable : state.getUniformLocations())
499 {
Olli Etuaho1734e172017-10-27 15:30:27 +0300500 stream.writeInt(variable.arrayIndex);
Jamie Madillfb997ec2017-09-20 15:44:27 -0400501 stream.writeIntOrNegOne(variable.index);
Jamie Madill4f86d052017-06-05 12:59:26 -0400502 stream.writeInt(variable.ignored);
503 }
504
505 stream.writeInt(state.getUniformBlocks().size());
Jiajia Qin729b2c62017-08-14 09:36:11 +0800506 for (const InterfaceBlock &uniformBlock : state.getUniformBlocks())
Jamie Madill4f86d052017-06-05 12:59:26 -0400507 {
Jiajia Qin729b2c62017-08-14 09:36:11 +0800508 WriteInterfaceBlock(&stream, uniformBlock);
509 }
Jamie Madill4f86d052017-06-05 12:59:26 -0400510
Jiajia Qin3a9090f2017-09-27 14:37:04 +0800511 stream.writeInt(state.getBufferVariables().size());
512 for (const BufferVariable &bufferVariable : state.getBufferVariables())
513 {
514 WriteBufferVariable(&stream, bufferVariable);
515 }
516
Jiajia Qin729b2c62017-08-14 09:36:11 +0800517 stream.writeInt(state.getShaderStorageBlocks().size());
518 for (const InterfaceBlock &shaderStorageBlock : state.getShaderStorageBlocks())
519 {
520 WriteInterfaceBlock(&stream, shaderStorageBlock);
jchen10eaef1e52017-06-13 10:44:11 +0800521 }
Jamie Madill4f86d052017-06-05 12:59:26 -0400522
jchen10eaef1e52017-06-13 10:44:11 +0800523 stream.writeInt(state.mAtomicCounterBuffers.size());
524 for (const auto &atomicCounterBuffer : state.mAtomicCounterBuffers)
525 {
526 WriteShaderVariableBuffer(&stream, atomicCounterBuffer);
Jamie Madill4f86d052017-06-05 12:59:26 -0400527 }
528
Jamie Madillffe00c02017-06-27 16:26:55 -0400529 // Warn the app layer if saving a binary with unsupported transform feedback.
530 if (!state.getLinkedTransformFeedbackVaryings().empty() &&
531 context->getWorkarounds().disableProgramCachingForTransformFeedback)
532 {
533 WARN() << "Saving program binary with transform feedback, which is not supported on this "
534 "driver.";
535 }
536
Jamie Madill4f86d052017-06-05 12:59:26 -0400537 stream.writeInt(state.getLinkedTransformFeedbackVaryings().size());
538 for (const auto &var : state.getLinkedTransformFeedbackVaryings())
539 {
Olli Etuaho465835d2017-09-26 13:34:10 +0300540 stream.writeIntVector(var.arraySizes);
Jamie Madill4f86d052017-06-05 12:59:26 -0400541 stream.writeInt(var.type);
542 stream.writeString(var.name);
543
544 stream.writeIntOrNegOne(var.arrayIndex);
545 }
546
547 stream.writeInt(state.getTransformFeedbackBufferMode());
548
549 stream.writeInt(state.getOutputVariables().size());
550 for (const sh::OutputVariable &output : state.getOutputVariables())
551 {
552 WriteShaderVar(&stream, output);
553 stream.writeInt(output.location);
554 }
555
556 stream.writeInt(state.getOutputLocations().size());
Olli Etuahod2551232017-10-26 20:03:33 +0300557 for (const auto &outputVar : state.getOutputLocations())
Jamie Madill4f86d052017-06-05 12:59:26 -0400558 {
Olli Etuaho1734e172017-10-27 15:30:27 +0300559 stream.writeInt(outputVar.arrayIndex);
Olli Etuahod2551232017-10-26 20:03:33 +0300560 stream.writeIntOrNegOne(outputVar.index);
Olli Etuahod2551232017-10-26 20:03:33 +0300561 stream.writeInt(outputVar.ignored);
Jamie Madill4f86d052017-06-05 12:59:26 -0400562 }
563
564 stream.writeInt(state.mOutputVariableTypes.size());
565 for (const auto &outputVariableType : state.mOutputVariableTypes)
566 {
567 stream.writeInt(outputVariableType);
568 }
569
Brandon Jonesc405ae72017-12-06 14:15:03 -0800570 static_assert(
571 IMPLEMENTATION_MAX_DRAW_BUFFERS * 2 <= 8 * sizeof(uint32_t),
572 "All bits of mDrawBufferTypeMask and mActiveOutputVariables can be contained in 32 bits");
573 stream.writeInt(static_cast<int>(state.mDrawBufferTypeMask.to_ulong()));
574 stream.writeInt(static_cast<int>(state.mActiveOutputVariables.to_ulong()));
Jamie Madill4f86d052017-06-05 12:59:26 -0400575
576 stream.writeInt(state.getSamplerUniformRange().low());
577 stream.writeInt(state.getSamplerUniformRange().high());
578
579 stream.writeInt(state.getSamplerBindings().size());
580 for (const auto &samplerBinding : state.getSamplerBindings())
581 {
Corentin Wallezf0e89be2017-11-08 14:00:32 -0800582 stream.writeEnum(samplerBinding.textureType);
Jamie Madill4f86d052017-06-05 12:59:26 -0400583 stream.writeInt(samplerBinding.boundTextureUnits.size());
Jamie Madill54164b02017-08-28 15:17:37 -0400584 stream.writeInt(samplerBinding.unreferenced);
Jamie Madill4f86d052017-06-05 12:59:26 -0400585 }
586
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800587 stream.writeInt(state.getImageUniformRange().low());
588 stream.writeInt(state.getImageUniformRange().high());
589
590 stream.writeInt(state.getImageBindings().size());
591 for (const auto &imageBinding : state.getImageBindings())
592 {
Xinghua Cao0328b572017-06-26 15:51:36 +0800593 stream.writeInt(imageBinding.boundImageUnits.size());
594 for (size_t i = 0; i < imageBinding.boundImageUnits.size(); ++i)
595 {
596 stream.writeInt(imageBinding.boundImageUnits[i]);
597 }
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800598 }
599
jchen10eaef1e52017-06-13 10:44:11 +0800600 stream.writeInt(state.getAtomicCounterUniformRange().low());
601 stream.writeInt(state.getAtomicCounterUniformRange().high());
602
Yunchao He85072e82017-11-14 15:43:28 +0800603 stream.writeInt(state.getLinkedShaderStages().to_ulong());
604
Jamie Madill27a60632017-06-30 15:12:01 -0400605 program->getImplementation()->save(context, &stream);
Jamie Madill4f86d052017-06-05 12:59:26 -0400606
607 ASSERT(binaryOut);
608 binaryOut->resize(stream.length());
609 memcpy(binaryOut->data(), stream.data(), stream.length());
610}
611
Jamie Madill32447362017-06-28 14:53:52 -0400612// static
613void MemoryProgramCache::ComputeHash(const Context *context,
614 const Program *program,
615 ProgramHash *hashOut)
616{
Jamie Madillacf2f3a2017-11-21 19:22:44 -0500617 const Shader *vertexShader = program->getAttachedVertexShader();
618 const Shader *fragmentShader = program->getAttachedFragmentShader();
619 const Shader *computeShader = program->getAttachedComputeShader();
Jiawei Shao4ed05da2018-02-02 14:26:15 +0800620 const Shader *geometryShader = program->getAttachedGeometryShader();
Jamie Madill32447362017-06-28 14:53:52 -0400621
622 // Compute the program hash. Start with the shader hashes and resource strings.
623 HashStream hashStream;
Jiawei Shao4ed05da2018-02-02 14:26:15 +0800624 hashStream << vertexShader << fragmentShader << computeShader << geometryShader;
Jamie Madill32447362017-06-28 14:53:52 -0400625
626 // Add some ANGLE metadata and Context properties, such as version and back-end.
627 hashStream << ANGLE_COMMIT_HASH << context->getClientMajorVersion()
628 << context->getClientMinorVersion() << context->getString(GL_RENDERER);
629
630 // Hash pre-link program properties.
631 hashStream << program->getAttributeBindings() << program->getUniformLocationBindings()
632 << program->getFragmentInputBindings()
633 << program->getState().getTransformFeedbackVaryingNames()
634 << program->getState().getTransformFeedbackBufferMode();
635
636 // Call the secure SHA hashing function.
637 const std::string &programKey = hashStream.str();
638 angle::base::SHA1HashBytes(reinterpret_cast<const unsigned char *>(programKey.c_str()),
639 programKey.length(), hashOut->data());
640}
641
642LinkResult MemoryProgramCache::getProgram(const Context *context,
643 const Program *program,
644 ProgramState *state,
645 ProgramHash *hashOut)
646{
647 ComputeHash(context, program, hashOut);
648 const angle::MemoryBuffer *binaryProgram = nullptr;
649 LinkResult result(false);
650 if (get(*hashOut, &binaryProgram))
651 {
652 InfoLog infoLog;
653 ANGLE_TRY_RESULT(Deserialize(context, program, state, binaryProgram->data(),
654 binaryProgram->size(), infoLog),
655 result);
Jamie Madill6c58b062017-08-01 13:44:25 -0400656 ANGLE_HISTOGRAM_BOOLEAN("GPU.ANGLE.ProgramCache.LoadBinarySuccess", result.getResult());
Jamie Madill32447362017-06-28 14:53:52 -0400657 if (!result.getResult())
658 {
659 // Cache load failed, evict.
660 if (mIssuedWarnings++ < kWarningLimit)
661 {
662 WARN() << "Failed to load binary from cache: " << infoLog.str();
663
664 if (mIssuedWarnings == kWarningLimit)
665 {
666 WARN() << "Reaching warning limit for cache load failures, silencing "
667 "subsequent warnings.";
668 }
669 }
670 remove(*hashOut);
671 }
672 }
673 return result;
674}
675
676bool MemoryProgramCache::get(const ProgramHash &programHash, const angle::MemoryBuffer **programOut)
677{
Jamie Madill6c58b062017-08-01 13:44:25 -0400678 const CacheEntry *entry = nullptr;
679 if (!mProgramBinaryCache.get(programHash, &entry))
680 {
681 ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.ProgramCache.CacheResult", kCacheMiss,
682 kCacheResultMax);
683 return false;
684 }
685
686 if (entry->second == CacheSource::PutProgram)
687 {
688 ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.ProgramCache.CacheResult", kCacheHitMemory,
689 kCacheResultMax);
690 }
691 else
692 {
693 ANGLE_HISTOGRAM_ENUMERATION("GPU.ANGLE.ProgramCache.CacheResult", kCacheHitDisk,
694 kCacheResultMax);
695 }
696
697 *programOut = &entry->first;
698 return true;
Jamie Madill32447362017-06-28 14:53:52 -0400699}
700
Jamie Madillc43be722017-07-13 16:22:14 -0400701bool MemoryProgramCache::getAt(size_t index,
702 ProgramHash *hashOut,
703 const angle::MemoryBuffer **programOut)
704{
Jamie Madill6c58b062017-08-01 13:44:25 -0400705 const CacheEntry *entry = nullptr;
706 if (!mProgramBinaryCache.getAt(index, hashOut, &entry))
707 {
708 return false;
709 }
710
711 *programOut = &entry->first;
712 return true;
Jamie Madillc43be722017-07-13 16:22:14 -0400713}
714
Jamie Madill32447362017-06-28 14:53:52 -0400715void MemoryProgramCache::remove(const ProgramHash &programHash)
716{
717 bool result = mProgramBinaryCache.eraseByKey(programHash);
718 ASSERT(result);
719}
720
Jamie Madill6c58b062017-08-01 13:44:25 -0400721void MemoryProgramCache::putProgram(const ProgramHash &programHash,
722 const Context *context,
723 const Program *program)
Jamie Madill32447362017-06-28 14:53:52 -0400724{
Jamie Madill6c58b062017-08-01 13:44:25 -0400725 CacheEntry newEntry;
726 Serialize(context, program, &newEntry.first);
727 newEntry.second = CacheSource::PutProgram;
728
729 ANGLE_HISTOGRAM_COUNTS("GPU.ANGLE.ProgramCache.ProgramBinarySizeBytes",
730 static_cast<int>(newEntry.first.size()));
731
732 const CacheEntry *result =
733 mProgramBinaryCache.put(programHash, std::move(newEntry), newEntry.first.size());
Jamie Madill360daee2017-06-29 10:36:19 -0400734 if (!result)
Jamie Madill32447362017-06-28 14:53:52 -0400735 {
736 ERR() << "Failed to store binary program in memory cache, program is too large.";
737 }
Jamie Madill360daee2017-06-29 10:36:19 -0400738 else
739 {
740 auto *platform = ANGLEPlatformCurrent();
Jamie Madill6c58b062017-08-01 13:44:25 -0400741 platform->cacheProgram(platform, programHash, result->first.size(), result->first.data());
Jamie Madill360daee2017-06-29 10:36:19 -0400742 }
Jamie Madill32447362017-06-28 14:53:52 -0400743}
744
Jamie Madill4c19a8a2017-07-24 11:46:06 -0400745void MemoryProgramCache::updateProgram(const Context *context, const Program *program)
746{
747 gl::ProgramHash programHash;
748 ComputeHash(context, program, &programHash);
749 putProgram(programHash, context, program);
750}
751
Jamie Madillc43be722017-07-13 16:22:14 -0400752void MemoryProgramCache::putBinary(const ProgramHash &programHash,
Jamie Madill32447362017-06-28 14:53:52 -0400753 const uint8_t *binary,
754 size_t length)
755{
756 // Copy the binary.
Jamie Madill6c58b062017-08-01 13:44:25 -0400757 CacheEntry newEntry;
758 newEntry.first.resize(length);
759 memcpy(newEntry.first.data(), binary, length);
760 newEntry.second = CacheSource::PutBinary;
Jamie Madill32447362017-06-28 14:53:52 -0400761
Jamie Madill32447362017-06-28 14:53:52 -0400762 // Store the binary.
Jamie Madill6c58b062017-08-01 13:44:25 -0400763 const CacheEntry *result = mProgramBinaryCache.put(programHash, std::move(newEntry), length);
Jamie Madillc43be722017-07-13 16:22:14 -0400764 if (!result)
765 {
766 ERR() << "Failed to store binary program in memory cache, program is too large.";
767 }
Jamie Madill32447362017-06-28 14:53:52 -0400768}
769
770void MemoryProgramCache::clear()
771{
772 mProgramBinaryCache.clear();
773 mIssuedWarnings = 0;
774}
775
Jamie Madillc43be722017-07-13 16:22:14 -0400776void MemoryProgramCache::resize(size_t maxCacheSizeBytes)
777{
778 mProgramBinaryCache.resize(maxCacheSizeBytes);
779}
780
781size_t MemoryProgramCache::entryCount() const
782{
783 return mProgramBinaryCache.entryCount();
784}
785
786size_t MemoryProgramCache::trim(size_t limit)
787{
788 return mProgramBinaryCache.shrinkToSize(limit);
789}
790
791size_t MemoryProgramCache::size() const
792{
793 return mProgramBinaryCache.size();
794}
795
796size_t MemoryProgramCache::maxSize() const
797{
798 return mProgramBinaryCache.maxSize();
799}
800
Jamie Madill4f86d052017-06-05 12:59:26 -0400801} // namespace gl