blob: c851762b0105e57c055eaa4d20f00b6b91394d81 [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>
13
14#include "common/version.h"
15#include "libANGLE/BinaryStream.h"
16#include "libANGLE/Context.h"
17#include "libANGLE/Uniform.h"
18#include "libANGLE/renderer/ProgramImpl.h"
19
20namespace gl
21{
22
23namespace
24{
25
26void WriteShaderVar(BinaryOutputStream *stream, const sh::ShaderVariable &var)
27{
28 stream->writeInt(var.type);
29 stream->writeInt(var.precision);
30 stream->writeString(var.name);
31 stream->writeString(var.mappedName);
32 stream->writeInt(var.arraySize);
33 stream->writeInt(var.staticUse);
34 stream->writeString(var.structName);
35 ASSERT(var.fields.empty());
36}
37
38void LoadShaderVar(BinaryInputStream *stream, sh::ShaderVariable *var)
39{
40 var->type = stream->readInt<GLenum>();
41 var->precision = stream->readInt<GLenum>();
42 var->name = stream->readString();
43 var->mappedName = stream->readString();
44 var->arraySize = stream->readInt<unsigned int>();
45 var->staticUse = stream->readBool();
46 var->structName = stream->readString();
47}
48
49} // anonymous namespace
50
51// static
52LinkResult MemoryProgramCache::Deserialize(const Context *context,
53 const Program *program,
54 ProgramState *state,
55 const uint8_t *binary,
56 size_t length,
57 InfoLog &infoLog)
58{
59 BinaryInputStream stream(binary, length);
60
61 unsigned char commitString[ANGLE_COMMIT_HASH_SIZE];
62 stream.readBytes(commitString, ANGLE_COMMIT_HASH_SIZE);
63 if (memcmp(commitString, ANGLE_COMMIT_HASH, sizeof(unsigned char) * ANGLE_COMMIT_HASH_SIZE) !=
64 0)
65 {
66 infoLog << "Invalid program binary version.";
67 return false;
68 }
69
70 int majorVersion = stream.readInt<int>();
71 int minorVersion = stream.readInt<int>();
72 if (majorVersion != context->getClientMajorVersion() ||
73 minorVersion != context->getClientMinorVersion())
74 {
75 infoLog << "Cannot load program binaries across different ES context versions.";
76 return false;
77 }
78
79 state->mComputeShaderLocalSize[0] = stream.readInt<int>();
80 state->mComputeShaderLocalSize[1] = stream.readInt<int>();
81 state->mComputeShaderLocalSize[2] = stream.readInt<int>();
82
83 static_assert(MAX_VERTEX_ATTRIBS <= sizeof(unsigned long) * 8,
84 "Too many vertex attribs for mask");
85 state->mActiveAttribLocationsMask = stream.readInt<unsigned long>();
86
87 unsigned int attribCount = stream.readInt<unsigned int>();
88 ASSERT(state->mAttributes.empty());
89 for (unsigned int attribIndex = 0; attribIndex < attribCount; ++attribIndex)
90 {
91 sh::Attribute attrib;
92 LoadShaderVar(&stream, &attrib);
93 attrib.location = stream.readInt<int>();
94 state->mAttributes.push_back(attrib);
95 }
96
97 unsigned int uniformCount = stream.readInt<unsigned int>();
98 ASSERT(state->mUniforms.empty());
99 for (unsigned int uniformIndex = 0; uniformIndex < uniformCount; ++uniformIndex)
100 {
101 LinkedUniform uniform;
102 LoadShaderVar(&stream, &uniform);
103
104 uniform.blockIndex = stream.readInt<int>();
105 uniform.blockInfo.offset = stream.readInt<int>();
106 uniform.blockInfo.arrayStride = stream.readInt<int>();
107 uniform.blockInfo.matrixStride = stream.readInt<int>();
108 uniform.blockInfo.isRowMajorMatrix = stream.readBool();
109
110 state->mUniforms.push_back(uniform);
111 }
112
113 const unsigned int uniformIndexCount = stream.readInt<unsigned int>();
114 ASSERT(state->mUniformLocations.empty());
115 for (unsigned int uniformIndexIndex = 0; uniformIndexIndex < uniformIndexCount;
116 uniformIndexIndex++)
117 {
118 VariableLocation variable;
119 stream.readString(&variable.name);
120 stream.readInt(&variable.element);
121 stream.readInt(&variable.index);
122 stream.readBool(&variable.used);
123 stream.readBool(&variable.ignored);
124
125 state->mUniformLocations.push_back(variable);
126 }
127
128 unsigned int uniformBlockCount = stream.readInt<unsigned int>();
129 ASSERT(state->mUniformBlocks.empty());
130 for (unsigned int uniformBlockIndex = 0; uniformBlockIndex < uniformBlockCount;
131 ++uniformBlockIndex)
132 {
133 UniformBlock uniformBlock;
134 stream.readString(&uniformBlock.name);
135 stream.readBool(&uniformBlock.isArray);
136 stream.readInt(&uniformBlock.arrayElement);
137 stream.readInt(&uniformBlock.binding);
138 stream.readInt(&uniformBlock.dataSize);
139 stream.readBool(&uniformBlock.vertexStaticUse);
140 stream.readBool(&uniformBlock.fragmentStaticUse);
141
142 unsigned int numMembers = stream.readInt<unsigned int>();
143 for (unsigned int blockMemberIndex = 0; blockMemberIndex < numMembers; blockMemberIndex++)
144 {
145 uniformBlock.memberUniformIndexes.push_back(stream.readInt<unsigned int>());
146 }
147
148 state->mUniformBlocks.push_back(uniformBlock);
Jamie Madill4f86d052017-06-05 12:59:26 -0400149
jchen107a20b972017-06-13 14:25:26 +0800150 state->mActiveUniformBlockBindings.set(uniformBlockIndex, uniformBlock.binding != 0);
Jamie Madill4f86d052017-06-05 12:59:26 -0400151 }
152
153 unsigned int transformFeedbackVaryingCount = stream.readInt<unsigned int>();
Jamie Madillffe00c02017-06-27 16:26:55 -0400154
155 // Reject programs that use transform feedback varyings if the hardware cannot support them.
156 if (transformFeedbackVaryingCount > 0 &&
157 context->getWorkarounds().disableProgramCachingForTransformFeedback)
158 {
159 infoLog << "Current driver does not support transform feedback in binary programs.";
160 return false;
161 }
162
Jamie Madill4f86d052017-06-05 12:59:26 -0400163 ASSERT(state->mLinkedTransformFeedbackVaryings.empty());
164 for (unsigned int transformFeedbackVaryingIndex = 0;
165 transformFeedbackVaryingIndex < transformFeedbackVaryingCount;
166 ++transformFeedbackVaryingIndex)
167 {
168 sh::Varying varying;
169 stream.readInt(&varying.arraySize);
170 stream.readInt(&varying.type);
171 stream.readString(&varying.name);
172
173 GLuint arrayIndex = stream.readInt<GLuint>();
174
175 state->mLinkedTransformFeedbackVaryings.emplace_back(varying, arrayIndex);
176 }
177
178 stream.readInt(&state->mTransformFeedbackBufferMode);
179
180 unsigned int outputCount = stream.readInt<unsigned int>();
181 ASSERT(state->mOutputVariables.empty());
182 for (unsigned int outputIndex = 0; outputIndex < outputCount; ++outputIndex)
183 {
184 sh::OutputVariable output;
185 LoadShaderVar(&stream, &output);
186 output.location = stream.readInt<int>();
187 state->mOutputVariables.push_back(output);
188 }
189
190 unsigned int outputVarCount = stream.readInt<unsigned int>();
191 for (unsigned int outputIndex = 0; outputIndex < outputVarCount; ++outputIndex)
192 {
193 int locationIndex = stream.readInt<int>();
194 VariableLocation locationData;
195 stream.readInt(&locationData.element);
196 stream.readInt(&locationData.index);
197 stream.readString(&locationData.name);
198 state->mOutputLocations[locationIndex] = locationData;
199 }
200
201 unsigned int outputTypeCount = stream.readInt<unsigned int>();
202 for (unsigned int outputIndex = 0; outputIndex < outputTypeCount; ++outputIndex)
203 {
204 state->mOutputVariableTypes.push_back(stream.readInt<GLenum>());
205 }
206 static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS < 8 * sizeof(uint32_t),
207 "All bits of DrawBufferMask can be contained in an uint32_t");
208 state->mActiveOutputVariables = stream.readInt<uint32_t>();
209
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800210 unsigned int samplerRangeLow = stream.readInt<unsigned int>();
211 unsigned int samplerRangeHigh = stream.readInt<unsigned int>();
212 state->mSamplerUniformRange = RangeUI(samplerRangeLow, samplerRangeHigh);
Jamie Madill4f86d052017-06-05 12:59:26 -0400213 unsigned int samplerCount = stream.readInt<unsigned int>();
214 for (unsigned int samplerIndex = 0; samplerIndex < samplerCount; ++samplerIndex)
215 {
216 GLenum textureType = stream.readInt<GLenum>();
217 size_t bindingCount = stream.readInt<size_t>();
218 state->mSamplerBindings.emplace_back(SamplerBinding(textureType, bindingCount));
219 }
220
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800221 unsigned int imageRangeLow = stream.readInt<unsigned int>();
222 unsigned int imageRangeHigh = stream.readInt<unsigned int>();
223 state->mImageUniformRange = RangeUI(imageRangeLow, imageRangeHigh);
224 unsigned int imageCount = stream.readInt<unsigned int>();
225 for (unsigned int imageIndex = 0; imageIndex < imageCount; ++imageIndex)
226 {
227 GLuint boundImageUnit = stream.readInt<unsigned int>();
228 size_t elementCount = stream.readInt<size_t>();
229 state->mImageBindings.emplace_back(ImageBinding(boundImageUnit, elementCount));
230 }
231
Jamie Madill4f86d052017-06-05 12:59:26 -0400232 return program->getImplementation()->load(context, infoLog, &stream);
233}
234
235// static
236void MemoryProgramCache::Serialize(const Context *context,
237 const gl::Program *program,
238 angle::MemoryBuffer *binaryOut)
239{
240 BinaryOutputStream stream;
241
242 stream.writeBytes(reinterpret_cast<const unsigned char *>(ANGLE_COMMIT_HASH),
243 ANGLE_COMMIT_HASH_SIZE);
244
245 // nullptr context is supported when computing binary length.
246 if (context)
247 {
248 stream.writeInt(context->getClientVersion().major);
249 stream.writeInt(context->getClientVersion().minor);
250 }
251 else
252 {
253 stream.writeInt(2);
254 stream.writeInt(0);
255 }
256
257 const auto &state = program->getState();
258
259 const auto &computeLocalSize = state.getComputeShaderLocalSize();
260
261 stream.writeInt(computeLocalSize[0]);
262 stream.writeInt(computeLocalSize[1]);
263 stream.writeInt(computeLocalSize[2]);
264
265 stream.writeInt(state.getActiveAttribLocationsMask().to_ulong());
266
267 stream.writeInt(state.getAttributes().size());
268 for (const sh::Attribute &attrib : state.getAttributes())
269 {
270 WriteShaderVar(&stream, attrib);
271 stream.writeInt(attrib.location);
272 }
273
274 stream.writeInt(state.getUniforms().size());
275 for (const LinkedUniform &uniform : state.getUniforms())
276 {
277 WriteShaderVar(&stream, uniform);
278
279 // FIXME: referenced
280
281 stream.writeInt(uniform.blockIndex);
282 stream.writeInt(uniform.blockInfo.offset);
283 stream.writeInt(uniform.blockInfo.arrayStride);
284 stream.writeInt(uniform.blockInfo.matrixStride);
285 stream.writeInt(uniform.blockInfo.isRowMajorMatrix);
286 }
287
288 stream.writeInt(state.getUniformLocations().size());
289 for (const auto &variable : state.getUniformLocations())
290 {
291 stream.writeString(variable.name);
292 stream.writeInt(variable.element);
293 stream.writeInt(variable.index);
294 stream.writeInt(variable.used);
295 stream.writeInt(variable.ignored);
296 }
297
298 stream.writeInt(state.getUniformBlocks().size());
299 for (const UniformBlock &uniformBlock : state.getUniformBlocks())
300 {
301 stream.writeString(uniformBlock.name);
302 stream.writeInt(uniformBlock.isArray);
303 stream.writeInt(uniformBlock.arrayElement);
304 stream.writeInt(uniformBlock.binding);
305 stream.writeInt(uniformBlock.dataSize);
306
307 stream.writeInt(uniformBlock.vertexStaticUse);
308 stream.writeInt(uniformBlock.fragmentStaticUse);
309
310 stream.writeInt(uniformBlock.memberUniformIndexes.size());
311 for (unsigned int memberUniformIndex : uniformBlock.memberUniformIndexes)
312 {
313 stream.writeInt(memberUniformIndex);
314 }
315 }
316
Jamie Madillffe00c02017-06-27 16:26:55 -0400317 // Warn the app layer if saving a binary with unsupported transform feedback.
318 if (!state.getLinkedTransformFeedbackVaryings().empty() &&
319 context->getWorkarounds().disableProgramCachingForTransformFeedback)
320 {
321 WARN() << "Saving program binary with transform feedback, which is not supported on this "
322 "driver.";
323 }
324
Jamie Madill4f86d052017-06-05 12:59:26 -0400325 stream.writeInt(state.getLinkedTransformFeedbackVaryings().size());
326 for (const auto &var : state.getLinkedTransformFeedbackVaryings())
327 {
328 stream.writeInt(var.arraySize);
329 stream.writeInt(var.type);
330 stream.writeString(var.name);
331
332 stream.writeIntOrNegOne(var.arrayIndex);
333 }
334
335 stream.writeInt(state.getTransformFeedbackBufferMode());
336
337 stream.writeInt(state.getOutputVariables().size());
338 for (const sh::OutputVariable &output : state.getOutputVariables())
339 {
340 WriteShaderVar(&stream, output);
341 stream.writeInt(output.location);
342 }
343
344 stream.writeInt(state.getOutputLocations().size());
345 for (const auto &outputPair : state.getOutputLocations())
346 {
347 stream.writeInt(outputPair.first);
348 stream.writeIntOrNegOne(outputPair.second.element);
349 stream.writeInt(outputPair.second.index);
350 stream.writeString(outputPair.second.name);
351 }
352
353 stream.writeInt(state.mOutputVariableTypes.size());
354 for (const auto &outputVariableType : state.mOutputVariableTypes)
355 {
356 stream.writeInt(outputVariableType);
357 }
358
359 static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS < 8 * sizeof(uint32_t),
360 "All bits of DrawBufferMask can be contained in an uint32_t");
361 stream.writeInt(static_cast<uint32_t>(state.mActiveOutputVariables.to_ulong()));
362
363 stream.writeInt(state.getSamplerUniformRange().low());
364 stream.writeInt(state.getSamplerUniformRange().high());
365
366 stream.writeInt(state.getSamplerBindings().size());
367 for (const auto &samplerBinding : state.getSamplerBindings())
368 {
369 stream.writeInt(samplerBinding.textureType);
370 stream.writeInt(samplerBinding.boundTextureUnits.size());
371 }
372
Xinghua Cao65ec0b22017-03-28 16:10:52 +0800373 stream.writeInt(state.getImageUniformRange().low());
374 stream.writeInt(state.getImageUniformRange().high());
375
376 stream.writeInt(state.getImageBindings().size());
377 for (const auto &imageBinding : state.getImageBindings())
378 {
379 stream.writeInt(imageBinding.boundImageUnit);
380 stream.writeInt(imageBinding.elementCount);
381 }
382
Jamie Madill4f86d052017-06-05 12:59:26 -0400383 program->getImplementation()->save(&stream);
384
385 ASSERT(binaryOut);
386 binaryOut->resize(stream.length());
387 memcpy(binaryOut->data(), stream.data(), stream.length());
388}
389
390} // namespace gl