1 //
2 // Copyright 2019 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 // FrameCapture.cpp:
7 // ANGLE Frame capture implementation.
8 //
9
10 #include "libANGLE/FrameCapture.h"
11
12 #include <cerrno>
13 #include <cstring>
14 #include <fstream>
15 #include <string>
16
17 #include "sys/stat.h"
18
19 #include "common/mathutil.h"
20 #include "common/system_utils.h"
21 #include "libANGLE/Context.h"
22 #include "libANGLE/Fence.h"
23 #include "libANGLE/Framebuffer.h"
24 #include "libANGLE/Query.h"
25 #include "libANGLE/ResourceMap.h"
26 #include "libANGLE/Shader.h"
27 #include "libANGLE/VertexArray.h"
28 #include "libANGLE/capture_gles_2_0_autogen.h"
29 #include "libANGLE/capture_gles_3_0_autogen.h"
30 #include "libANGLE/gl_enum_utils.h"
31 #include "libANGLE/queryconversions.h"
32 #include "libANGLE/queryutils.h"
33
34 #define USE_SYSTEM_ZLIB
35 #include "compression_utils_portable.h"
36
37 #if !ANGLE_CAPTURE_ENABLED
38 # error Frame capture must be enbled to include this file.
39 #endif // !ANGLE_CAPTURE_ENABLED
40
41 namespace angle
42 {
43 namespace
44 {
45
46 constexpr char kEnabledVarName[] = "ANGLE_CAPTURE_ENABLED";
47 constexpr char kOutDirectoryVarName[] = "ANGLE_CAPTURE_OUT_DIR";
48 constexpr char kFrameStartVarName[] = "ANGLE_CAPTURE_FRAME_START";
49 constexpr char kFrameEndVarName[] = "ANGLE_CAPTURE_FRAME_END";
50 constexpr char kCaptureLabel[] = "ANGLE_CAPTURE_LABEL";
51 constexpr char kCompression[] = "ANGLE_CAPTURE_COMPRESSION";
52
53 constexpr size_t kBinaryAlignment = 16;
54
55 #if defined(ANGLE_PLATFORM_ANDROID)
56
57 constexpr char kAndroidCaptureEnabled[] = "debug.angle.capture.enabled";
58 constexpr char kAndroidOutDir[] = "debug.angle.capture.out_dir";
59 constexpr char kAndroidFrameStart[] = "debug.angle.capture.frame_start";
60 constexpr char kAndroidFrameEnd[] = "debug.angle.capture.frame_end";
61 constexpr char kAndroidCaptureLabel[] = "debug.angle.capture.label";
62 constexpr char kAndroidCompression[] = "debug.angle.capture.compression";
63
64 constexpr int kStreamSize = 64;
65
66 constexpr char kAndroidOutputSubdir[] = "/angle_capture/";
67
68 // Call out to 'getprop' on a shell and return a string if the value was set
AndroidGetEnvFromProp(const char * key)69 std::string AndroidGetEnvFromProp(const char *key)
70 {
71 std::string command("getprop ");
72 command += key;
73
74 // Run the command and open a I/O stream to read results
75 char stream[kStreamSize] = {};
76 FILE *pipe = popen(command.c_str(), "r");
77 if (pipe != nullptr)
78 {
79 fgets(stream, kStreamSize, pipe);
80 pclose(pipe);
81 }
82
83 // Right strip white space
84 std::string result(stream);
85 result.erase(result.find_last_not_of(" \n\r\t") + 1);
86 return result;
87 }
88
PrimeAndroidEnvironmentVariables()89 void PrimeAndroidEnvironmentVariables()
90 {
91 std::string enabled = AndroidGetEnvFromProp(kAndroidCaptureEnabled);
92 if (!enabled.empty())
93 {
94 INFO() << "Frame capture read " << enabled << " from " << kAndroidCaptureEnabled;
95 setenv(kEnabledVarName, enabled.c_str(), 1);
96 }
97
98 std::string outDir = AndroidGetEnvFromProp(kAndroidOutDir);
99 if (!outDir.empty())
100 {
101 INFO() << "Frame capture read " << outDir << " from " << kAndroidOutDir;
102 setenv(kOutDirectoryVarName, outDir.c_str(), 1);
103 }
104
105 std::string frameStart = AndroidGetEnvFromProp(kAndroidFrameStart);
106 if (!frameStart.empty())
107 {
108 INFO() << "Frame capture read " << frameStart << " from " << kAndroidFrameStart;
109 setenv(kFrameStartVarName, frameStart.c_str(), 1);
110 }
111
112 std::string frameEnd = AndroidGetEnvFromProp(kAndroidFrameEnd);
113 if (!frameEnd.empty())
114 {
115 INFO() << "Frame capture read " << frameEnd << " from " << kAndroidFrameEnd;
116 setenv(kFrameEndVarName, frameEnd.c_str(), 1);
117 }
118
119 std::string captureLabel = AndroidGetEnvFromProp(kAndroidCaptureLabel);
120 if (!captureLabel.empty())
121 {
122 INFO() << "Frame capture read " << captureLabel << " from " << kAndroidCaptureLabel;
123 setenv(kCaptureLabel, captureLabel.c_str(), 1);
124 }
125
126 std::string compression = AndroidGetEnvFromProp(kAndroidCompression);
127 if (!compression.empty())
128 {
129 INFO() << "Frame capture read " << compression << " from " << kAndroidCompression;
130 setenv(kCompression, compression.c_str(), 1);
131 }
132 }
133 #endif
134
GetDefaultOutDirectory()135 std::string GetDefaultOutDirectory()
136 {
137 #if defined(ANGLE_PLATFORM_ANDROID)
138 std::string path = "/sdcard/Android/data/";
139
140 // Linux interface to get application id of the running process
141 FILE *cmdline = fopen("/proc/self/cmdline", "r");
142 char applicationId[512];
143 if (cmdline)
144 {
145 fread(applicationId, 1, sizeof(applicationId), cmdline);
146 fclose(cmdline);
147
148 // Some package may have application id as <app_name>:<cmd_name>
149 char *colonSep = strchr(applicationId, ':');
150 if (colonSep)
151 {
152 *colonSep = '\0';
153 }
154 }
155 else
156 {
157 ERR() << "not able to lookup application id";
158 }
159
160 path += std::string(applicationId) + kAndroidOutputSubdir;
161
162 // Check for existance of output path
163 struct stat dir_stat;
164 if (stat(path.c_str(), &dir_stat) == -1)
165 {
166 ERR() << "Output directory '" << path
167 << "' does not exist. Create it over adb using mkdir.";
168 }
169
170 return path;
171 #else
172 return std::string("./");
173 #endif // defined(ANGLE_PLATFORM_ANDROID)
174 }
175
176 struct FmtCapturePrefix
177 {
FmtCapturePrefixangle::__anon2fe2bea00111::FmtCapturePrefix178 FmtCapturePrefix(gl::ContextID contextIdIn, const std::string &captureLabelIn)
179 : contextId(contextIdIn), captureLabel(captureLabelIn)
180 {}
181 gl::ContextID contextId;
182 const std::string &captureLabel;
183 };
184
operator <<(std::ostream & os,const FmtCapturePrefix & fmt)185 std::ostream &operator<<(std::ostream &os, const FmtCapturePrefix &fmt)
186 {
187 if (fmt.captureLabel.empty())
188 {
189 os << "angle";
190 }
191 else
192 {
193 os << fmt.captureLabel;
194 }
195 os << "_capture_context" << static_cast<int>(fmt.contextId);
196 return os;
197 }
198
199 struct FmtReplayFunction
200 {
FmtReplayFunctionangle::__anon2fe2bea00111::FmtReplayFunction201 FmtReplayFunction(gl::ContextID contextIdIn, uint32_t frameIndexIn)
202 : contextId(contextIdIn), frameIndex(frameIndexIn)
203 {}
204 gl::ContextID contextId;
205 uint32_t frameIndex;
206 };
207
operator <<(std::ostream & os,const FmtReplayFunction & fmt)208 std::ostream &operator<<(std::ostream &os, const FmtReplayFunction &fmt)
209 {
210 os << "ReplayContext" << static_cast<int>(fmt.contextId) << "Frame" << fmt.frameIndex << "()";
211 return os;
212 }
213
GetCaptureFileName(gl::ContextID contextId,const std::string & captureLabel,uint32_t frameIndex,const char * suffix)214 std::string GetCaptureFileName(gl::ContextID contextId,
215 const std::string &captureLabel,
216 uint32_t frameIndex,
217 const char *suffix)
218 {
219 std::stringstream fnameStream;
220 fnameStream << FmtCapturePrefix(contextId, captureLabel) << "_frame" << std::setfill('0')
221 << std::setw(3) << frameIndex << suffix;
222 return fnameStream.str();
223 }
224
GetCaptureFilePath(const std::string & outDir,gl::ContextID contextId,const std::string & captureLabel,uint32_t frameIndex,const char * suffix)225 std::string GetCaptureFilePath(const std::string &outDir,
226 gl::ContextID contextId,
227 const std::string &captureLabel,
228 uint32_t frameIndex,
229 const char *suffix)
230 {
231 return outDir + GetCaptureFileName(contextId, captureLabel, frameIndex, suffix);
232 }
233
WriteParamStaticVarName(const CallCapture & call,const ParamCapture & param,int counter,std::ostream & out)234 void WriteParamStaticVarName(const CallCapture &call,
235 const ParamCapture ¶m,
236 int counter,
237 std::ostream &out)
238 {
239 out << call.name() << "_" << param.name << "_" << counter;
240 }
241
WriteGLFloatValue(std::ostream & out,GLfloat value)242 void WriteGLFloatValue(std::ostream &out, GLfloat value)
243 {
244 // Check for non-representable values
245 ASSERT(std::numeric_limits<float>::has_infinity);
246 ASSERT(std::numeric_limits<float>::has_quiet_NaN);
247
248 if (std::isinf(value))
249 {
250 float negativeInf = -std::numeric_limits<float>::infinity();
251 if (value == negativeInf)
252 {
253 out << "-";
254 }
255 out << "std::numeric_limits<float>::infinity()";
256 }
257 else if (std::isnan(value))
258 {
259 out << "std::numeric_limits<float>::quiet_NaN()";
260 }
261 else
262 {
263 out << value;
264 }
265 }
266
267 template <typename T, typename CastT = T>
WriteInlineData(const std::vector<uint8_t> & vec,std::ostream & out)268 void WriteInlineData(const std::vector<uint8_t> &vec, std::ostream &out)
269 {
270 const T *data = reinterpret_cast<const T *>(vec.data());
271 size_t count = vec.size() / sizeof(T);
272
273 if (data == nullptr)
274 {
275 return;
276 }
277
278 out << static_cast<CastT>(data[0]);
279
280 for (size_t dataIndex = 1; dataIndex < count; ++dataIndex)
281 {
282 out << ", " << static_cast<CastT>(data[dataIndex]);
283 }
284 }
285
286 template <>
WriteInlineData(const std::vector<uint8_t> & vec,std::ostream & out)287 void WriteInlineData<GLchar>(const std::vector<uint8_t> &vec, std::ostream &out)
288 {
289 const GLchar *data = reinterpret_cast<const GLchar *>(vec.data());
290 size_t count = vec.size() / sizeof(GLchar);
291
292 if (data == nullptr || data[0] == '\0')
293 {
294 return;
295 }
296
297 out << "\"";
298
299 for (size_t dataIndex = 0; dataIndex < count; ++dataIndex)
300 {
301 if (data[dataIndex] == '\0')
302 break;
303
304 out << static_cast<GLchar>(data[dataIndex]);
305 }
306
307 out << "\"";
308 }
309
WriteStringParamReplay(std::ostream & out,const ParamCapture & param)310 void WriteStringParamReplay(std::ostream &out, const ParamCapture ¶m)
311 {
312 const std::vector<uint8_t> &data = param.data[0];
313 // null terminate C style string
314 ASSERT(data.size() > 0 && data.back() == '\0');
315 std::string str(data.begin(), data.end() - 1);
316 out << "\"" << str << "\"";
317 }
318
WriteStringPointerParamReplay(DataCounters * counters,std::ostream & out,std::ostream & header,const CallCapture & call,const ParamCapture & param)319 void WriteStringPointerParamReplay(DataCounters *counters,
320 std::ostream &out,
321 std::ostream &header,
322 const CallCapture &call,
323 const ParamCapture ¶m)
324 {
325 int counter = counters->getAndIncrement(call.entryPoint, param.name);
326
327 header << "const char *";
328 WriteParamStaticVarName(call, param, counter, header);
329 header << "[] = { \n";
330
331 for (const std::vector<uint8_t> &data : param.data)
332 {
333 // null terminate C style string
334 ASSERT(data.size() > 0 && data.back() == '\0');
335 std::string str(data.begin(), data.end() - 1);
336 header << " R\"(" << str << ")\",\n";
337 }
338
339 header << " };\n";
340 WriteParamStaticVarName(call, param, counter, out);
341 }
342
343 template <typename ParamT>
WriteResourceIDPointerParamReplay(DataCounters * counters,std::ostream & out,std::ostream & header,const CallCapture & call,const ParamCapture & param)344 void WriteResourceIDPointerParamReplay(DataCounters *counters,
345 std::ostream &out,
346 std::ostream &header,
347 const CallCapture &call,
348 const ParamCapture ¶m)
349 {
350 int counter = counters->getAndIncrement(call.entryPoint, param.name);
351
352 header << "const GLuint ";
353 WriteParamStaticVarName(call, param, counter, header);
354 header << "[] = { ";
355
356 const ResourceIDType resourceIDType = GetResourceIDTypeFromParamType(param.type);
357 ASSERT(resourceIDType != ResourceIDType::InvalidEnum);
358 const char *name = GetResourceIDTypeName(resourceIDType);
359
360 GLsizei n = call.params.getParamFlexName("n", "count", ParamType::TGLsizei, 0).value.GLsizeiVal;
361 ASSERT(param.data.size() == 1);
362 const ParamT *returnedIDs = reinterpret_cast<const ParamT *>(param.data[0].data());
363 for (GLsizei resIndex = 0; resIndex < n; ++resIndex)
364 {
365 ParamT id = returnedIDs[resIndex];
366 if (resIndex > 0)
367 {
368 header << ", ";
369 }
370 header << "g" << name << "Map[" << id.value << "]";
371 }
372
373 header << " };\n ";
374
375 WriteParamStaticVarName(call, param, counter, out);
376 }
377
WriteBinaryParamReplay(DataCounters * counters,std::ostream & out,std::ostream & header,const CallCapture & call,const ParamCapture & param,std::vector<uint8_t> * binaryData)378 void WriteBinaryParamReplay(DataCounters *counters,
379 std::ostream &out,
380 std::ostream &header,
381 const CallCapture &call,
382 const ParamCapture ¶m,
383 std::vector<uint8_t> *binaryData)
384 {
385 int counter = counters->getAndIncrement(call.entryPoint, param.name);
386
387 ASSERT(param.data.size() == 1);
388 const std::vector<uint8_t> &data = param.data[0];
389
390 ParamType overrideType = param.type;
391 if (param.type == ParamType::TGLvoidConstPointer || param.type == ParamType::TvoidConstPointer)
392 {
393 overrideType = ParamType::TGLubyteConstPointer;
394 }
395 if (overrideType == ParamType::TGLenumConstPointer || overrideType == ParamType::TGLcharPointer)
396 {
397 // Inline if data are of type string or enum
398 std::string paramTypeString = ParamTypeToString(param.type);
399 header << paramTypeString.substr(0, paramTypeString.length() - 1);
400 WriteParamStaticVarName(call, param, counter, header);
401 header << "[] = { ";
402 if (overrideType == ParamType::TGLenumConstPointer)
403 {
404 WriteInlineData<GLuint>(data, header);
405 }
406 else
407 {
408 ASSERT(overrideType == ParamType::TGLcharPointer);
409 WriteInlineData<GLchar>(data, header);
410 }
411 header << " };\n";
412 WriteParamStaticVarName(call, param, counter, out);
413 }
414 else
415 {
416 // Store in binary file if data are not of type string or enum
417 // Round up to 16-byte boundary for cross ABI safety
418 size_t offset = rx::roundUp(binaryData->size(), kBinaryAlignment);
419 binaryData->resize(offset + data.size());
420 memcpy(binaryData->data() + offset, data.data(), data.size());
421 out << "reinterpret_cast<" << ParamTypeToString(overrideType) << ">(&gBinaryData[" << offset
422 << "])";
423 }
424 }
425
SyncIndexValue(GLsync sync)426 uintptr_t SyncIndexValue(GLsync sync)
427 {
428 return reinterpret_cast<uintptr_t>(sync);
429 }
430
WriteCppReplayForCall(const CallCapture & call,DataCounters * counters,std::ostream & out,std::ostream & header,std::vector<uint8_t> * binaryData)431 void WriteCppReplayForCall(const CallCapture &call,
432 DataCounters *counters,
433 std::ostream &out,
434 std::ostream &header,
435 std::vector<uint8_t> *binaryData)
436 {
437 std::ostringstream callOut;
438
439 if (call.entryPoint == gl::EntryPoint::CreateShader ||
440 call.entryPoint == gl::EntryPoint::CreateProgram)
441 {
442 GLuint id = call.params.getReturnValue().value.GLuintVal;
443 callOut << "gShaderProgramMap[" << id << "] = ";
444 }
445
446 if (call.entryPoint == gl::EntryPoint::FenceSync)
447 {
448 GLsync sync = call.params.getReturnValue().value.GLsyncVal;
449 callOut << "gSyncMap[" << SyncIndexValue(sync) << "] = ";
450 }
451
452 if (call.entryPoint == gl::EntryPoint::MapBufferRange ||
453 call.entryPoint == gl::EntryPoint::MapBufferRangeEXT)
454 {
455 GLbitfield access =
456 call.params.getParam("access", ParamType::TGLbitfield, 3).value.GLbitfieldVal;
457
458 if (access & GL_MAP_WRITE_BIT)
459 {
460 // Track the returned pointer so we update its data when unmapped
461 gl::BufferID bufferID = call.params.getMappedBufferID();
462 callOut << "gMappedBufferData[";
463 WriteParamValueReplay<ParamType::TBufferID>(callOut, call, bufferID);
464 callOut << "] = ";
465 }
466 }
467
468 callOut << call.name() << "(";
469
470 bool first = true;
471 for (const ParamCapture ¶m : call.params.getParamCaptures())
472 {
473 if (!first)
474 {
475 callOut << ", ";
476 }
477
478 if (param.arrayClientPointerIndex != -1)
479 {
480 callOut << "gClientArrays[" << param.arrayClientPointerIndex << "]";
481 }
482 else if (param.readBufferSizeBytes > 0)
483 {
484 callOut << "reinterpret_cast<" << ParamTypeToString(param.type) << ">(gReadBuffer)";
485 }
486 else if (param.data.empty())
487 {
488 if (param.type == ParamType::TGLenum)
489 {
490 OutputGLenumString(callOut, param.enumGroup, param.value.GLenumVal);
491 }
492 else if (param.type == ParamType::TGLbitfield)
493 {
494 OutputGLbitfieldString(callOut, param.enumGroup, param.value.GLbitfieldVal);
495 }
496 else if (param.type == ParamType::TGLfloat)
497 {
498 WriteGLFloatValue(callOut, param.value.GLfloatVal);
499 }
500 else if (param.type == ParamType::TGLsync)
501 {
502 callOut << "gSyncMap[" << SyncIndexValue(param.value.GLsyncVal) << "]";
503 }
504 else if (param.type == ParamType::TGLuint64 && param.name == "timeout")
505 {
506 if (param.value.GLuint64Val == GL_TIMEOUT_IGNORED)
507 {
508 callOut << "GL_TIMEOUT_IGNORED";
509 }
510 else
511 {
512 WriteParamCaptureReplay(callOut, call, param);
513 }
514 }
515 else
516 {
517 WriteParamCaptureReplay(callOut, call, param);
518 }
519 }
520 else
521 {
522 switch (param.type)
523 {
524 case ParamType::TGLcharConstPointer:
525 WriteStringParamReplay(callOut, param);
526 break;
527 case ParamType::TGLcharConstPointerPointer:
528 WriteStringPointerParamReplay(counters, callOut, header, call, param);
529 break;
530 case ParamType::TBufferIDConstPointer:
531 WriteResourceIDPointerParamReplay<gl::BufferID>(counters, callOut, out, call,
532 param);
533 break;
534 case ParamType::TFenceNVIDConstPointer:
535 WriteResourceIDPointerParamReplay<gl::FenceNVID>(counters, callOut, out, call,
536 param);
537 break;
538 case ParamType::TFramebufferIDConstPointer:
539 WriteResourceIDPointerParamReplay<gl::FramebufferID>(counters, callOut, out,
540 call, param);
541 break;
542 case ParamType::TMemoryObjectIDConstPointer:
543 WriteResourceIDPointerParamReplay<gl::MemoryObjectID>(counters, callOut, out,
544 call, param);
545 break;
546 case ParamType::TProgramPipelineIDConstPointer:
547 WriteResourceIDPointerParamReplay<gl::ProgramPipelineID>(counters, callOut, out,
548 call, param);
549 break;
550 case ParamType::TQueryIDConstPointer:
551 WriteResourceIDPointerParamReplay<gl::QueryID>(counters, callOut, out, call,
552 param);
553 break;
554 case ParamType::TRenderbufferIDConstPointer:
555 WriteResourceIDPointerParamReplay<gl::RenderbufferID>(counters, callOut, out,
556 call, param);
557 break;
558 case ParamType::TSamplerIDConstPointer:
559 WriteResourceIDPointerParamReplay<gl::SamplerID>(counters, callOut, out, call,
560 param);
561 break;
562 case ParamType::TSemaphoreIDConstPointer:
563 WriteResourceIDPointerParamReplay<gl::SemaphoreID>(counters, callOut, out, call,
564 param);
565 break;
566 case ParamType::TTextureIDConstPointer:
567 WriteResourceIDPointerParamReplay<gl::TextureID>(counters, callOut, out, call,
568 param);
569 break;
570 case ParamType::TTransformFeedbackIDConstPointer:
571 WriteResourceIDPointerParamReplay<gl::TransformFeedbackID>(counters, callOut,
572 out, call, param);
573 break;
574 case ParamType::TVertexArrayIDConstPointer:
575 WriteResourceIDPointerParamReplay<gl::VertexArrayID>(counters, callOut, out,
576 call, param);
577 break;
578 default:
579 WriteBinaryParamReplay(counters, callOut, header, call, param, binaryData);
580 break;
581 }
582 }
583
584 first = false;
585 }
586
587 callOut << ")";
588
589 out << callOut.str();
590 }
591
MaxClientArraySize(const gl::AttribArray<size_t> & clientArraySizes)592 size_t MaxClientArraySize(const gl::AttribArray<size_t> &clientArraySizes)
593 {
594 size_t found = 0;
595 for (size_t size : clientArraySizes)
596 {
597 if (size > found)
598 found = size;
599 }
600
601 return found;
602 }
603
604 struct SaveFileHelper
605 {
606 public:
607 // We always use ios::binary to avoid inconsistent line endings when captured on Linux vs Win.
SaveFileHelperangle::__anon2fe2bea00111::SaveFileHelper608 SaveFileHelper(const std::string &filePathIn)
609 : mOfs(filePathIn, std::ios::binary | std::ios::out), mFilePath(filePathIn)
610 {
611 if (!mOfs.is_open())
612 {
613 FATAL() << "Could not open " << filePathIn;
614 }
615 }
616
~SaveFileHelperangle::__anon2fe2bea00111::SaveFileHelper617 ~SaveFileHelper() { printf("Saved '%s'.\n", mFilePath.c_str()); }
618
619 template <typename T>
operator <<angle::__anon2fe2bea00111::SaveFileHelper620 SaveFileHelper &operator<<(const T &value)
621 {
622 mOfs << value;
623 if (mOfs.bad())
624 {
625 FATAL() << "Error writing to " << mFilePath;
626 }
627 return *this;
628 }
629
writeangle::__anon2fe2bea00111::SaveFileHelper630 void write(const uint8_t *data, size_t size)
631 {
632 mOfs.write(reinterpret_cast<const char *>(data), size);
633 }
634
635 private:
636 std::ofstream mOfs;
637 std::string mFilePath;
638 };
639
GetBinaryDataFilePath(bool compression,gl::ContextID contextId,const std::string & captureLabel)640 std::string GetBinaryDataFilePath(bool compression,
641 gl::ContextID contextId,
642 const std::string &captureLabel)
643 {
644 std::stringstream fnameStream;
645 fnameStream << FmtCapturePrefix(contextId, captureLabel) << ".angledata";
646 if (compression)
647 {
648 fnameStream << ".gz";
649 }
650 return fnameStream.str();
651 }
652
SaveBinaryData(bool compression,const std::string & outDir,gl::ContextID contextId,const std::string & captureLabel,const std::vector<uint8_t> & binaryData)653 void SaveBinaryData(bool compression,
654 const std::string &outDir,
655 gl::ContextID contextId,
656 const std::string &captureLabel,
657 const std::vector<uint8_t> &binaryData)
658 {
659 std::string binaryDataFileName = GetBinaryDataFilePath(compression, contextId, captureLabel);
660 std::string dataFilepath = outDir + binaryDataFileName;
661
662 SaveFileHelper saveData(dataFilepath);
663
664 if (compression)
665 {
666 // Save compressed data.
667 uLong uncompressedSize = static_cast<uLong>(binaryData.size());
668 uLong expectedCompressedSize = zlib_internal::GzipExpectedCompressedSize(uncompressedSize);
669
670 std::vector<uint8_t> compressedData(expectedCompressedSize, 0);
671
672 uLong compressedSize = expectedCompressedSize;
673 int zResult = zlib_internal::GzipCompressHelper(compressedData.data(), &compressedSize,
674 binaryData.data(), uncompressedSize,
675 nullptr, nullptr);
676
677 if (zResult != Z_OK)
678 {
679 FATAL() << "Error compressing binary data: " << zResult;
680 }
681
682 saveData.write(compressedData.data(), compressedSize);
683 }
684 else
685 {
686 saveData.write(binaryData.data(), binaryData.size());
687 }
688 }
689
WriteLoadBinaryDataCall(bool compression,std::ostream & out,gl::ContextID contextId,const std::string & captureLabel)690 void WriteLoadBinaryDataCall(bool compression,
691 std::ostream &out,
692 gl::ContextID contextId,
693 const std::string &captureLabel)
694 {
695 std::string binaryDataFileName = GetBinaryDataFilePath(compression, contextId, captureLabel);
696 out << " LoadBinaryData(\"" << binaryDataFileName << "\");\n";
697 }
698
MaybeResetResources(std::stringstream & out,ResourceIDType resourceIDType,DataCounters * counters,std::stringstream & header,ResourceTracker * resourceTracker,std::vector<uint8_t> * binaryData)699 void MaybeResetResources(std::stringstream &out,
700 ResourceIDType resourceIDType,
701 DataCounters *counters,
702 std::stringstream &header,
703 ResourceTracker *resourceTracker,
704 std::vector<uint8_t> *binaryData)
705 {
706 switch (resourceIDType)
707 {
708 case ResourceIDType::Buffer:
709 {
710 BufferSet &newBuffers = resourceTracker->getNewBuffers();
711 BufferCalls &bufferRegenCalls = resourceTracker->getBufferRegenCalls();
712 BufferCalls &bufferRestoreCalls = resourceTracker->getBufferRestoreCalls();
713 BufferCalls &bufferMapCalls = resourceTracker->getBufferMapCalls();
714 BufferCalls &bufferUnmapCalls = resourceTracker->getBufferUnmapCalls();
715
716 // If we have any new buffers generated and not deleted during the run, delete them now
717 if (!newBuffers.empty())
718 {
719 out << " const GLuint deleteBuffers[] = {";
720 BufferSet::iterator bufferIter = newBuffers.begin();
721 for (size_t i = 0; bufferIter != newBuffers.end(); ++i, ++bufferIter)
722 {
723 if (i > 0)
724 {
725 out << ", ";
726 }
727 if ((i % 4) == 0)
728 {
729 out << "\n ";
730 }
731 out << "gBufferMap[" << (*bufferIter).value << "]";
732 }
733 out << "};\n";
734 out << " glDeleteBuffers(" << newBuffers.size() << ", deleteBuffers);\n";
735 }
736
737 // If any of our starting buffers were deleted during the run, recreate them
738 BufferSet &buffersToRegen = resourceTracker->getBuffersToRegen();
739 for (const gl::BufferID id : buffersToRegen)
740 {
741 // Emit their regen calls
742 for (CallCapture &call : bufferRegenCalls[id])
743 {
744 out << " ";
745 WriteCppReplayForCall(call, counters, out, header, binaryData);
746 out << ";\n";
747 }
748 }
749
750 // If any of our starting buffers were modified during the run, restore their contents
751 BufferSet &buffersToRestore = resourceTracker->getBuffersToRestore();
752 for (const gl::BufferID id : buffersToRestore)
753 {
754 // Emit their restore calls
755 for (CallCapture &call : bufferRestoreCalls[id])
756 {
757 out << " ";
758 WriteCppReplayForCall(call, counters, out, header, binaryData);
759 out << ";\n";
760
761 // Also note that this buffer has been implicitly unmapped by this call
762 resourceTracker->setBufferUnmapped(id);
763 }
764 }
765
766 // Update the map/unmap of buffers to match the starting state
767 BufferSet startingBuffers = resourceTracker->getStartingBuffers();
768 for (const gl::BufferID id : startingBuffers)
769 {
770 // If the buffer was mapped at the start, but is not mapped now, we need to map
771 if (resourceTracker->getStartingBuffersMappedInitial(id) &&
772 !resourceTracker->getStartingBuffersMappedCurrent(id))
773 {
774 // Emit their map calls
775 for (CallCapture &call : bufferMapCalls[id])
776 {
777 out << " ";
778 WriteCppReplayForCall(call, counters, out, header, binaryData);
779 out << ";\n";
780 }
781 }
782 // If the buffer was unmapped at the start, but is mapped now, we need to unmap
783 if (!resourceTracker->getStartingBuffersMappedInitial(id) &&
784 resourceTracker->getStartingBuffersMappedCurrent(id))
785 {
786 // Emit their unmap calls
787 for (CallCapture &call : bufferUnmapCalls[id])
788 {
789 out << " ";
790 WriteCppReplayForCall(call, counters, out, header, binaryData);
791 out << ";\n";
792 }
793 }
794 }
795
796 break;
797 }
798 default:
799 // TODO (http://anglebug.com/4599): Reset more than just buffers
800 break;
801 }
802 }
803
WriteCppReplay(bool compression,const std::string & outDir,gl::ContextID contextId,const std::string & captureLabel,uint32_t frameIndex,uint32_t frameEnd,const std::vector<CallCapture> & frameCalls,const std::vector<CallCapture> & setupCalls,ResourceTracker * resourceTracker,std::vector<uint8_t> * binaryData)804 void WriteCppReplay(bool compression,
805 const std::string &outDir,
806 gl::ContextID contextId,
807 const std::string &captureLabel,
808 uint32_t frameIndex,
809 uint32_t frameEnd,
810 const std::vector<CallCapture> &frameCalls,
811 const std::vector<CallCapture> &setupCalls,
812 ResourceTracker *resourceTracker,
813 std::vector<uint8_t> *binaryData)
814 {
815 DataCounters counters;
816
817 std::stringstream out;
818 std::stringstream header;
819
820 header << "#include \"" << FmtCapturePrefix(contextId, captureLabel) << ".h\"\n";
821 header << "";
822 header << "\n";
823 header << "namespace\n";
824 header << "{\n";
825
826 if (!captureLabel.empty())
827 {
828 out << "namespace " << captureLabel << "\n";
829 out << "{\n";
830 }
831
832 if (frameIndex == 0 || !setupCalls.empty())
833 {
834 out << "void SetupContext" << Str(static_cast<int>(contextId)) << "Replay()\n";
835 out << "{\n";
836
837 std::stringstream setupCallStream;
838
839 WriteLoadBinaryDataCall(compression, setupCallStream, contextId, captureLabel);
840
841 for (const CallCapture &call : setupCalls)
842 {
843 setupCallStream << " ";
844 WriteCppReplayForCall(call, &counters, setupCallStream, header, binaryData);
845 setupCallStream << ";\n";
846 }
847
848 out << setupCallStream.str();
849
850 out << "}\n";
851 out << "\n";
852 }
853
854 if (frameIndex == frameEnd)
855 {
856 // Emit code to reset back to starting state
857 out << "void ResetContext" << Str(static_cast<int>(contextId)) << "Replay()\n";
858 out << "{\n";
859
860 std::stringstream restoreCallStream;
861
862 // For now, we only reset buffer states
863 // TODO (http://anglebug.com/4599): Reset more state on frame loop
864 for (ResourceIDType resourceType : AllEnums<ResourceIDType>())
865 {
866 MaybeResetResources(restoreCallStream, resourceType, &counters, header, resourceTracker,
867 binaryData);
868 }
869
870 out << restoreCallStream.str();
871
872 out << "}\n";
873 out << "\n";
874 }
875
876 out << "void " << FmtReplayFunction(contextId, frameIndex) << "\n";
877 out << "{\n";
878
879 std::stringstream callStream;
880
881 for (const CallCapture &call : frameCalls)
882 {
883 callStream << " ";
884 WriteCppReplayForCall(call, &counters, callStream, header, binaryData);
885 callStream << ";\n";
886 }
887
888 out << callStream.str();
889 out << "}\n";
890
891 if (!captureLabel.empty())
892 {
893 out << "} // namespace " << captureLabel << "\n";
894 }
895
896 header << "} // namespace\n";
897
898 {
899 std::string outString = out.str();
900 std::string headerString = header.str();
901
902 std::string cppFilePath =
903 GetCaptureFilePath(outDir, contextId, captureLabel, frameIndex, ".cpp");
904
905 SaveFileHelper saveCpp(cppFilePath);
906 saveCpp << headerString << "\n" << outString;
907 }
908 }
909
WriteCppReplayIndexFiles(bool compression,const std::string & outDir,gl::ContextID contextId,const std::string & captureLabel,uint32_t frameStart,uint32_t frameEnd,size_t readBufferSize,const gl::AttribArray<size_t> & clientArraySizes,const HasResourceTypeMap & hasResourceType)910 void WriteCppReplayIndexFiles(bool compression,
911 const std::string &outDir,
912 gl::ContextID contextId,
913 const std::string &captureLabel,
914 uint32_t frameStart,
915 uint32_t frameEnd,
916 size_t readBufferSize,
917 const gl::AttribArray<size_t> &clientArraySizes,
918 const HasResourceTypeMap &hasResourceType)
919 {
920 size_t maxClientArraySize = MaxClientArraySize(clientArraySizes);
921
922 std::stringstream header;
923 std::stringstream source;
924
925 header << "#pragma once\n";
926 header << "\n";
927 header << "#include \"util/gles_loader_autogen.h\"\n";
928 header << "\n";
929 header << "#include <cstdint>\n";
930 header << "#include <cstdio>\n";
931 header << "#include <cstring>\n";
932 header << "#include <limits>\n";
933 header << "#include <vector>\n";
934 header << "#include <unordered_map>\n";
935 header << "\n";
936
937 if (!captureLabel.empty())
938 {
939 header << "namespace " << captureLabel << "\n";
940 header << "{\n";
941 }
942 header << "// Replay functions\n";
943 header << "\n";
944 header << "// Maps from <captured Program ID, captured location> to run-time location.\n";
945 header
946 << "using LocationsMap = std::unordered_map<GLuint, std::unordered_map<GLint, GLint>>;\n";
947 header << "extern LocationsMap gUniformLocations;\n";
948 header << "extern GLuint gCurrentProgram;\n";
949 header << "void UpdateUniformLocation(GLuint program, const char *name, GLint location);\n";
950 header << "void DeleteUniformLocations(GLuint program);\n";
951 header << "void UpdateCurrentProgram(GLuint program);\n";
952 header << "\n";
953 header << "// Maps from captured Resource ID to run-time Resource ID.\n";
954 header << "using ResourceMap = std::unordered_map<GLuint, GLuint>;\n";
955 header << "\n";
956 header << "\n";
957 header << "constexpr uint32_t kReplayFrameStart = " << frameStart << ";\n";
958 header << "constexpr uint32_t kReplayFrameEnd = " << frameEnd << ";\n";
959 header << "\n";
960 header << "void SetupContext" << static_cast<int>(contextId) << "Replay();\n";
961 header << "void ReplayContext" << static_cast<int>(contextId)
962 << "Frame(uint32_t frameIndex);\n";
963 header << "void ResetContext" << static_cast<int>(contextId) << "Replay();\n";
964 header << "\n";
965 header << "using FramebufferChangeCallback = void(*)(void *userData, GLenum target, GLuint "
966 "framebuffer);\n";
967 header << "void SetFramebufferChangeCallback(void *userData, FramebufferChangeCallback "
968 "callback);\n";
969 header << "void OnFramebufferChange(GLenum target, GLuint framebuffer);\n";
970 header << "\n";
971 for (uint32_t frameIndex = frameStart; frameIndex <= frameEnd; ++frameIndex)
972 {
973 header << "void " << FmtReplayFunction(contextId, frameIndex) << ";\n";
974 }
975 header << "\n";
976 header << "constexpr bool kIsBinaryDataCompressed = " << (compression ? "true" : "false")
977 << ";\n";
978 header << "\n";
979 header << "using DecompressCallback = uint8_t *(*)(const std::vector<uint8_t> &);\n";
980 header << "void SetBinaryDataDecompressCallback(DecompressCallback callback);\n";
981 header << "void SetBinaryDataDir(const char *dataDir);\n";
982 header << "void LoadBinaryData(const char *fileName);\n";
983 header << "\n";
984 header << "// Global state\n";
985 header << "\n";
986 header << "extern uint8_t *gBinaryData;\n";
987
988 source << "#include \"" << FmtCapturePrefix(contextId, captureLabel) << ".h\"\n";
989 source << "\n";
990
991 if (!captureLabel.empty())
992 {
993 source << "namespace " << captureLabel << "\n";
994 source << "{\n";
995 }
996
997 source << "namespace\n";
998 source << "{\n";
999 source << "void UpdateResourceMap(ResourceMap *resourceMap, GLuint id, GLsizei "
1000 "readBufferOffset)\n";
1001 source << "{\n";
1002 if (readBufferSize > 0)
1003 {
1004 source << " GLuint returnedID;\n";
1005 std::string captureNamespace = !captureLabel.empty() ? captureLabel + "::" : "";
1006 source << " memcpy(&returnedID, &" << captureNamespace
1007 << "gReadBuffer[readBufferOffset], sizeof(GLuint));\n";
1008 source << " (*resourceMap)[id] = returnedID;\n";
1009 }
1010 source << "}\n";
1011 source << "\n";
1012 source << "DecompressCallback gDecompressCallback;\n";
1013 source << "const char *gBinaryDataDir = \".\";\n";
1014 source << "FramebufferChangeCallback gFramebufferChangeCallback;\n";
1015 source << "void *gFramebufferChangeCallbackUserData;\n";
1016 source << "} // namespace\n";
1017 source << "\n";
1018 source << "LocationsMap gUniformLocations;\n";
1019 source << "GLuint gCurrentProgram = 0;\n";
1020 source << "\n";
1021 source << "void UpdateUniformLocation(GLuint program, const char *name, GLint location)\n";
1022 source << "{\n";
1023 source << " gUniformLocations[program][location] = glGetUniformLocation(program, name);\n";
1024 source << "}\n";
1025 source << "void DeleteUniformLocations(GLuint program)\n";
1026 source << "{\n";
1027 source << " gUniformLocations.erase(program);\n";
1028 source << "}\n";
1029 source << "void UpdateCurrentProgram(GLuint program)\n";
1030 source << "{\n";
1031 source << " gCurrentProgram = program;\n";
1032 source << "}\n";
1033 source << "\n";
1034
1035 source << "uint8_t *gBinaryData = nullptr;\n";
1036
1037 if (readBufferSize > 0)
1038 {
1039 header << "extern uint8_t gReadBuffer[" << readBufferSize << "];\n";
1040 source << "uint8_t gReadBuffer[" << readBufferSize << "];\n";
1041 }
1042 if (maxClientArraySize > 0)
1043 {
1044 header << "extern uint8_t gClientArrays[" << gl::MAX_VERTEX_ATTRIBS << "]["
1045 << maxClientArraySize << "];\n";
1046 source << "uint8_t gClientArrays[" << gl::MAX_VERTEX_ATTRIBS << "][" << maxClientArraySize
1047 << "];\n";
1048 }
1049 for (ResourceIDType resourceType : AllEnums<ResourceIDType>())
1050 {
1051 // TODO: Only emit resources needed by the frames (anglebug.com/4223)
1052 const char *name = GetResourceIDTypeName(resourceType);
1053 header << "extern ResourceMap g" << name << "Map;\n";
1054 source << "ResourceMap g" << name << "Map;\n";
1055 }
1056
1057 header << "using SyncResourceMap = std::unordered_map<uintptr_t, GLsync>;\n";
1058 header << "extern SyncResourceMap gSyncMap;\n";
1059 source << "SyncResourceMap gSyncMap;\n";
1060
1061 header << "\n";
1062
1063 source << "\n";
1064 source << "void SetFramebufferChangeCallback(void *userData, FramebufferChangeCallback "
1065 "callback)\n";
1066 source << "{\n";
1067 source << " gFramebufferChangeCallbackUserData = userData;\n";
1068 source << " gFramebufferChangeCallback = callback;\n";
1069 source << "}\n";
1070 source << "\n";
1071 source << "void OnFramebufferChange(GLenum target, GLuint framebuffer)\n";
1072 source << "{\n";
1073 source << " if (gFramebufferChangeCallback)\n";
1074 source << " gFramebufferChangeCallback(gFramebufferChangeCallbackUserData, target, "
1075 "framebuffer);\n";
1076 source << "}\n";
1077
1078 source << "\n";
1079 source << "void ReplayContext" << static_cast<int>(contextId) << "Frame(uint32_t frameIndex)\n";
1080 source << "{\n";
1081 source << " switch (frameIndex)\n";
1082 source << " {\n";
1083 for (uint32_t frameIndex = frameStart; frameIndex <= frameEnd; ++frameIndex)
1084 {
1085 source << " case " << frameIndex << ":\n";
1086 source << " ReplayContext" << static_cast<int>(contextId) << "Frame"
1087 << frameIndex << "();\n";
1088 source << " break;\n";
1089 }
1090 source << " default:\n";
1091 source << " break;\n";
1092 source << " }\n";
1093 source << "}\n";
1094 source << "\n";
1095 source << "void SetBinaryDataDecompressCallback(DecompressCallback callback)\n";
1096 source << "{\n";
1097 source << " gDecompressCallback = callback;\n";
1098 source << "}\n";
1099 source << "\n";
1100 source << "void SetBinaryDataDir(const char *dataDir)\n";
1101 source << "{\n";
1102 source << " gBinaryDataDir = dataDir;\n";
1103 source << "}\n";
1104 source << "\n";
1105 source << "void LoadBinaryData(const char *fileName)\n";
1106 source << "{\n";
1107 source << " if (gBinaryData != nullptr)\n";
1108 source << " {\n";
1109 source << " delete [] gBinaryData;\n";
1110 source << " }\n";
1111 source << " char pathBuffer[1000] = {};\n";
1112 source << " sprintf(pathBuffer, \"%s/%s\", gBinaryDataDir, fileName);\n";
1113 source << " FILE *fp = fopen(pathBuffer, \"rb\");\n";
1114 source << " if (fp == 0)\n";
1115 source << " {\n";
1116 source << " fprintf(stderr, \"Error loading binary data file: %s\\n\", fileName);\n";
1117 source << " return;\n";
1118 source << " }\n";
1119 source << " fseek(fp, 0, SEEK_END);\n";
1120 source << " long size = ftell(fp);\n";
1121 source << " fseek(fp, 0, SEEK_SET);\n";
1122 source << " if (gDecompressCallback)\n";
1123 source << " {\n";
1124 source << " if (!strstr(fileName, \".gz\"))\n";
1125 source << " {\n";
1126 source << " fprintf(stderr, \"Filename does not end in .gz\");\n";
1127 source << " exit(1);\n";
1128 source << " }\n";
1129 source << " std::vector<uint8_t> compressedData(size);\n";
1130 source << " (void)fread(compressedData.data(), 1, size, fp);\n";
1131 source << " gBinaryData = gDecompressCallback(compressedData);\n";
1132 source << " }\n";
1133 source << " else\n";
1134 source << " {\n";
1135 source << " if (!strstr(fileName, \".angledata\"))\n";
1136 source << " {\n";
1137 source << " fprintf(stderr, \"Filename does not end in .angledata\");\n";
1138 source << " exit(1);\n";
1139 source << " }\n";
1140 source << " gBinaryData = new uint8_t[size];\n";
1141 source << " (void)fread(gBinaryData, 1, size, fp);\n";
1142 source << " }\n";
1143 source << " fclose(fp);\n";
1144 source << "}\n";
1145
1146 if (maxClientArraySize > 0)
1147 {
1148 header
1149 << "void UpdateClientArrayPointer(int arrayIndex, const void *data, uint64_t size);\n";
1150
1151 source << "\n";
1152 source << "void UpdateClientArrayPointer(int arrayIndex, const void *data, uint64_t size)"
1153 << "\n";
1154 source << "{\n";
1155 source << " memcpy(gClientArrays[arrayIndex], data, static_cast<size_t>(size));\n";
1156 source << "}\n";
1157 }
1158
1159 // Data types and functions for tracking contents of mapped buffers
1160 header << "using BufferHandleMap = std::unordered_map<GLuint, void*>;\n";
1161 header << "extern BufferHandleMap gMappedBufferData;\n";
1162 header << "void UpdateClientBufferData(GLuint bufferID, const void *source, GLsizei size);\n";
1163 source << "BufferHandleMap gMappedBufferData;\n";
1164 source << "\n";
1165 source << "void UpdateClientBufferData(GLuint bufferID, const void *source, GLsizei size)";
1166 source << "\n";
1167 source << "{\n";
1168 source << " memcpy(gMappedBufferData[gBufferMap[bufferID]], source, size);\n";
1169 source << "}\n";
1170
1171 for (ResourceIDType resourceType : AllEnums<ResourceIDType>())
1172 {
1173 // TODO: Only emit resources needed by the frames (anglebug.com/4223)
1174 const char *name = GetResourceIDTypeName(resourceType);
1175 header << "void Update" << name << "ID(GLuint id, GLsizei readBufferOffset);\n";
1176
1177 source << "\n";
1178 source << "void Update" << name << "ID(GLuint id, GLsizei readBufferOffset)\n";
1179 source << "{\n";
1180 source << " UpdateResourceMap(&g" << name << "Map, id, readBufferOffset);\n";
1181 source << "}\n";
1182 }
1183
1184 if (!captureLabel.empty())
1185 {
1186 header << "} // namespace " << captureLabel << "\n";
1187 source << "} // namespace " << captureLabel << "\n";
1188 }
1189
1190 {
1191 std::string headerContents = header.str();
1192
1193 std::stringstream headerPathStream;
1194 headerPathStream << outDir << FmtCapturePrefix(contextId, captureLabel) << ".h";
1195 std::string headerPath = headerPathStream.str();
1196
1197 SaveFileHelper saveHeader(headerPath);
1198 saveHeader << headerContents;
1199 }
1200
1201 {
1202 std::string sourceContents = source.str();
1203
1204 std::stringstream sourcePathStream;
1205 sourcePathStream << outDir << FmtCapturePrefix(contextId, captureLabel) << ".cpp";
1206 std::string sourcePath = sourcePathStream.str();
1207
1208 SaveFileHelper saveSource(sourcePath);
1209 saveSource << sourceContents;
1210 }
1211
1212 {
1213 std::stringstream indexPathStream;
1214 indexPathStream << outDir << FmtCapturePrefix(contextId, captureLabel) << "_files.txt";
1215 std::string indexPath = indexPathStream.str();
1216
1217 SaveFileHelper saveIndex(indexPath);
1218 for (uint32_t frameIndex = frameStart; frameIndex <= frameEnd; ++frameIndex)
1219 {
1220 saveIndex << GetCaptureFileName(contextId, captureLabel, frameIndex, ".cpp") << "\n";
1221 }
1222 }
1223 }
1224
GetAttachedProgramSources(const gl::Program * program)1225 ProgramSources GetAttachedProgramSources(const gl::Program *program)
1226 {
1227 ProgramSources sources;
1228 for (gl::ShaderType shaderType : gl::AllShaderTypes())
1229 {
1230 const gl::Shader *shader = program->getAttachedShader(shaderType);
1231 if (shader)
1232 {
1233 sources[shaderType] = shader->getSourceString();
1234 }
1235 }
1236 return sources;
1237 }
1238
1239 template <typename IDType>
CaptureUpdateResourceIDs(const CallCapture & call,const ParamCapture & param,std::vector<CallCapture> * callsOut)1240 void CaptureUpdateResourceIDs(const CallCapture &call,
1241 const ParamCapture ¶m,
1242 std::vector<CallCapture> *callsOut)
1243 {
1244 GLsizei n = call.params.getParamFlexName("n", "count", ParamType::TGLsizei, 0).value.GLsizeiVal;
1245 ASSERT(param.data.size() == 1);
1246 ResourceIDType resourceIDType = GetResourceIDTypeFromParamType(param.type);
1247 ASSERT(resourceIDType != ResourceIDType::InvalidEnum);
1248 const char *resourceName = GetResourceIDTypeName(resourceIDType);
1249
1250 std::stringstream updateFuncNameStr;
1251 updateFuncNameStr << "Update" << resourceName << "ID";
1252 std::string updateFuncName = updateFuncNameStr.str();
1253
1254 const IDType *returnedIDs = reinterpret_cast<const IDType *>(param.data[0].data());
1255
1256 for (GLsizei idIndex = 0; idIndex < n; ++idIndex)
1257 {
1258 IDType id = returnedIDs[idIndex];
1259 GLsizei readBufferOffset = idIndex * sizeof(gl::RenderbufferID);
1260 ParamBuffer params;
1261 params.addValueParam("id", ParamType::TGLuint, id.value);
1262 params.addValueParam("readBufferOffset", ParamType::TGLsizei, readBufferOffset);
1263 callsOut->emplace_back(updateFuncName, std::move(params));
1264 }
1265 }
1266
CaptureUpdateUniformLocations(const gl::Program * program,std::vector<CallCapture> * callsOut)1267 void CaptureUpdateUniformLocations(const gl::Program *program, std::vector<CallCapture> *callsOut)
1268 {
1269 const std::vector<gl::LinkedUniform> &uniforms = program->getState().getUniforms();
1270 const std::vector<gl::VariableLocation> &locations = program->getUniformLocations();
1271
1272 for (GLint location = 0; location < static_cast<GLint>(locations.size()); ++location)
1273 {
1274 const gl::VariableLocation &locationVar = locations[location];
1275 const gl::LinkedUniform &uniform = uniforms[locationVar.index];
1276
1277 ParamBuffer params;
1278 params.addValueParam("program", ParamType::TShaderProgramID, program->id());
1279
1280 std::string name = uniform.name;
1281
1282 if (uniform.isArray())
1283 {
1284 if (locationVar.arrayIndex > 0)
1285 {
1286 // Non-sequential array uniform locations are not currently handled.
1287 // In practice array locations shouldn't ever be non-sequential.
1288 ASSERT(uniform.location == -1 ||
1289 location == uniform.location + static_cast<int>(locationVar.arrayIndex));
1290 continue;
1291 }
1292
1293 if (uniform.isArrayOfArrays())
1294 {
1295 UNIMPLEMENTED();
1296 }
1297
1298 name = gl::StripLastArrayIndex(name);
1299 }
1300
1301 ParamCapture nameParam("name", ParamType::TGLcharConstPointer);
1302 CaptureString(name.c_str(), &nameParam);
1303 params.addParam(std::move(nameParam));
1304
1305 params.addValueParam("location", ParamType::TGLint, location);
1306 callsOut->emplace_back("UpdateUniformLocation", std::move(params));
1307 }
1308 }
1309
CaptureDeleteUniformLocations(gl::ShaderProgramID program,std::vector<CallCapture> * callsOut)1310 void CaptureDeleteUniformLocations(gl::ShaderProgramID program, std::vector<CallCapture> *callsOut)
1311 {
1312 ParamBuffer params;
1313 params.addValueParam("program", ParamType::TShaderProgramID, program);
1314 callsOut->emplace_back("DeleteUniformLocations", std::move(params));
1315 }
1316
CaptureOnFramebufferChange(GLenum target,gl::FramebufferID framebufferID,std::vector<CallCapture> * callsOut)1317 void CaptureOnFramebufferChange(GLenum target,
1318 gl::FramebufferID framebufferID,
1319 std::vector<CallCapture> *callsOut)
1320 {
1321 ParamBuffer params;
1322 params.addValueParam("target", ParamType::TGLenum, target);
1323 params.addValueParam("framebuffer", ParamType::TFramebufferID, framebufferID);
1324 callsOut->emplace_back("OnFramebufferChange", std::move(params));
1325 }
1326
MaybeCaptureUpdateResourceIDs(std::vector<CallCapture> * callsOut)1327 void MaybeCaptureUpdateResourceIDs(std::vector<CallCapture> *callsOut)
1328 {
1329 const CallCapture &call = callsOut->back();
1330
1331 switch (call.entryPoint)
1332 {
1333 case gl::EntryPoint::GenBuffers:
1334 {
1335 const ParamCapture &buffers =
1336 call.params.getParam("buffersPacked", ParamType::TBufferIDPointer, 1);
1337 CaptureUpdateResourceIDs<gl::BufferID>(call, buffers, callsOut);
1338 break;
1339 }
1340
1341 case gl::EntryPoint::GenFencesNV:
1342 {
1343 const ParamCapture &fences =
1344 call.params.getParam("fencesPacked", ParamType::TFenceNVIDPointer, 1);
1345 CaptureUpdateResourceIDs<gl::FenceNVID>(call, fences, callsOut);
1346 break;
1347 }
1348
1349 case gl::EntryPoint::GenFramebuffers:
1350 case gl::EntryPoint::GenFramebuffersOES:
1351 {
1352 const ParamCapture &framebuffers =
1353 call.params.getParam("framebuffersPacked", ParamType::TFramebufferIDPointer, 1);
1354 CaptureUpdateResourceIDs<gl::FramebufferID>(call, framebuffers, callsOut);
1355 break;
1356 }
1357
1358 case gl::EntryPoint::GenProgramPipelines:
1359 {
1360 const ParamCapture &pipelines =
1361 call.params.getParam("pipelinesPacked", ParamType::TProgramPipelineIDPointer, 1);
1362 CaptureUpdateResourceIDs<gl::ProgramPipelineID>(call, pipelines, callsOut);
1363 break;
1364 }
1365
1366 case gl::EntryPoint::GenQueries:
1367 case gl::EntryPoint::GenQueriesEXT:
1368 {
1369 const ParamCapture &queries =
1370 call.params.getParam("idsPacked", ParamType::TQueryIDPointer, 1);
1371 CaptureUpdateResourceIDs<gl::QueryID>(call, queries, callsOut);
1372 break;
1373 }
1374
1375 case gl::EntryPoint::GenRenderbuffers:
1376 case gl::EntryPoint::GenRenderbuffersOES:
1377 {
1378 const ParamCapture &renderbuffers =
1379 call.params.getParam("renderbuffersPacked", ParamType::TRenderbufferIDPointer, 1);
1380 CaptureUpdateResourceIDs<gl::RenderbufferID>(call, renderbuffers, callsOut);
1381 break;
1382 }
1383
1384 case gl::EntryPoint::GenSamplers:
1385 {
1386 const ParamCapture &samplers =
1387 call.params.getParam("samplersPacked", ParamType::TSamplerIDPointer, 1);
1388 CaptureUpdateResourceIDs<gl::SamplerID>(call, samplers, callsOut);
1389 break;
1390 }
1391
1392 case gl::EntryPoint::GenSemaphoresEXT:
1393 {
1394 const ParamCapture &semaphores =
1395 call.params.getParam("semaphoresPacked", ParamType::TSemaphoreIDPointer, 1);
1396 CaptureUpdateResourceIDs<gl::SemaphoreID>(call, semaphores, callsOut);
1397 break;
1398 }
1399
1400 case gl::EntryPoint::GenTextures:
1401 {
1402 const ParamCapture &textures =
1403 call.params.getParam("texturesPacked", ParamType::TTextureIDPointer, 1);
1404 CaptureUpdateResourceIDs<gl::TextureID>(call, textures, callsOut);
1405 break;
1406 }
1407
1408 case gl::EntryPoint::GenTransformFeedbacks:
1409 {
1410 const ParamCapture &xfbs =
1411 call.params.getParam("idsPacked", ParamType::TTransformFeedbackIDPointer, 1);
1412 CaptureUpdateResourceIDs<gl::TransformFeedbackID>(call, xfbs, callsOut);
1413 break;
1414 }
1415
1416 case gl::EntryPoint::GenVertexArrays:
1417 case gl::EntryPoint::GenVertexArraysOES:
1418 {
1419 const ParamCapture &vertexArrays =
1420 call.params.getParam("arraysPacked", ParamType::TVertexArrayIDPointer, 1);
1421 CaptureUpdateResourceIDs<gl::VertexArrayID>(call, vertexArrays, callsOut);
1422 break;
1423 }
1424
1425 default:
1426 break;
1427 }
1428 }
1429
CaptureUpdateCurrentProgram(const CallCapture & call,std::vector<CallCapture> * callsOut)1430 void CaptureUpdateCurrentProgram(const CallCapture &call, std::vector<CallCapture> *callsOut)
1431 {
1432 const ParamCapture ¶m =
1433 call.params.getParam("programPacked", ParamType::TShaderProgramID, 0);
1434 gl::ShaderProgramID programID = param.value.ShaderProgramIDVal;
1435
1436 ParamBuffer paramBuffer;
1437 paramBuffer.addValueParam("program", ParamType::TShaderProgramID, programID);
1438
1439 callsOut->emplace_back("UpdateCurrentProgram", std::move(paramBuffer));
1440 }
1441
IsDefaultCurrentValue(const gl::VertexAttribCurrentValueData & currentValue)1442 bool IsDefaultCurrentValue(const gl::VertexAttribCurrentValueData ¤tValue)
1443 {
1444 if (currentValue.Type != gl::VertexAttribType::Float)
1445 return false;
1446
1447 return currentValue.Values.FloatValues[0] == 0.0f &&
1448 currentValue.Values.FloatValues[1] == 0.0f &&
1449 currentValue.Values.FloatValues[2] == 0.0f && currentValue.Values.FloatValues[3] == 1.0f;
1450 }
1451
IsQueryActive(const gl::State & glState,gl::QueryID & queryID)1452 bool IsQueryActive(const gl::State &glState, gl::QueryID &queryID)
1453 {
1454 const gl::ActiveQueryMap &activeQueries = glState.getActiveQueriesForCapture();
1455 for (const auto &activeQueryIter : activeQueries)
1456 {
1457 const gl::Query *activeQuery = activeQueryIter.get();
1458 if (activeQuery && activeQuery->id() == queryID)
1459 {
1460 return true;
1461 }
1462 }
1463
1464 return false;
1465 }
1466
Capture(std::vector<CallCapture> * setupCalls,CallCapture && call)1467 void Capture(std::vector<CallCapture> *setupCalls, CallCapture &&call)
1468 {
1469 setupCalls->emplace_back(std::move(call));
1470 }
1471
CaptureFramebufferAttachment(std::vector<CallCapture> * setupCalls,const gl::State & replayState,const gl::FramebufferAttachment & attachment)1472 void CaptureFramebufferAttachment(std::vector<CallCapture> *setupCalls,
1473 const gl::State &replayState,
1474 const gl::FramebufferAttachment &attachment)
1475 {
1476 GLuint resourceID = attachment.getResource()->getId();
1477
1478 // TODO(jmadill): Layer attachments. http://anglebug.com/3662
1479 if (attachment.type() == GL_TEXTURE)
1480 {
1481 gl::ImageIndex index = attachment.getTextureImageIndex();
1482
1483 Capture(setupCalls, CaptureFramebufferTexture2D(replayState, true, GL_FRAMEBUFFER,
1484 attachment.getBinding(), index.getTarget(),
1485 {resourceID}, index.getLevelIndex()));
1486 }
1487 else
1488 {
1489 ASSERT(attachment.type() == GL_RENDERBUFFER);
1490 Capture(setupCalls, CaptureFramebufferRenderbuffer(replayState, true, GL_FRAMEBUFFER,
1491 attachment.getBinding(), GL_RENDERBUFFER,
1492 {resourceID}));
1493 }
1494 }
1495
CaptureUpdateUniformValues(const gl::State & replayState,const gl::Context * context,const gl::Program * program,std::vector<CallCapture> * callsOut)1496 void CaptureUpdateUniformValues(const gl::State &replayState,
1497 const gl::Context *context,
1498 const gl::Program *program,
1499 std::vector<CallCapture> *callsOut)
1500 {
1501 if (!program->isLinked())
1502 {
1503 // We can't populate uniforms if the program hasn't been linked
1504 return;
1505 }
1506
1507 // We need to bind the program and update its uniforms
1508 // TODO (http://anglebug.com/3662): Only bind if different from currently bound
1509 Capture(callsOut, CaptureUseProgram(replayState, true, program->id()));
1510 CaptureUpdateCurrentProgram(callsOut->back(), callsOut);
1511
1512 const std::vector<gl::LinkedUniform> &uniforms = program->getState().getUniforms();
1513
1514 for (size_t i = 0; i < uniforms.size(); i++)
1515 {
1516 const gl::LinkedUniform &uniform = uniforms[i];
1517 std::string uniformName = uniform.name;
1518
1519 int uniformCount = 1;
1520 if (uniform.isArray())
1521 {
1522 if (uniform.isArrayOfArrays())
1523 {
1524 UNIMPLEMENTED();
1525 continue;
1526 }
1527
1528 uniformCount = uniform.arraySizes[0];
1529 uniformName = gl::StripLastArrayIndex(uniformName);
1530 }
1531
1532 gl::UniformLocation uniformLoc = program->getUniformLocation(uniformName);
1533 const gl::UniformTypeInfo *typeInfo = uniform.typeInfo;
1534 int uniformSize = uniformCount * typeInfo->componentCount;
1535
1536 switch (typeInfo->componentType)
1537 {
1538 case GL_FLOAT:
1539 {
1540 std::vector<GLfloat> uniformBuffer(uniformSize);
1541 program->getUniformfv(context, uniformLoc, uniformBuffer.data());
1542 switch (typeInfo->type)
1543 {
1544 // Note: All matrix uniforms are populated without transpose
1545 case GL_FLOAT_MAT4x3:
1546 Capture(callsOut, CaptureUniformMatrix4x3fv(replayState, true, uniformLoc,
1547 uniformCount, false,
1548 uniformBuffer.data()));
1549 break;
1550 case GL_FLOAT_MAT4x2:
1551 Capture(callsOut, CaptureUniformMatrix4x2fv(replayState, true, uniformLoc,
1552 uniformCount, false,
1553 uniformBuffer.data()));
1554 break;
1555 case GL_FLOAT_MAT4:
1556 Capture(callsOut,
1557 CaptureUniformMatrix4fv(replayState, true, uniformLoc, uniformCount,
1558 false, uniformBuffer.data()));
1559 break;
1560 case GL_FLOAT_MAT3x4:
1561 Capture(callsOut, CaptureUniformMatrix3x4fv(replayState, true, uniformLoc,
1562 uniformCount, false,
1563 uniformBuffer.data()));
1564 break;
1565 case GL_FLOAT_MAT3x2:
1566 Capture(callsOut, CaptureUniformMatrix3x2fv(replayState, true, uniformLoc,
1567 uniformCount, false,
1568 uniformBuffer.data()));
1569 break;
1570 case GL_FLOAT_MAT3:
1571 Capture(callsOut,
1572 CaptureUniformMatrix3fv(replayState, true, uniformLoc, uniformCount,
1573 false, uniformBuffer.data()));
1574 break;
1575 case GL_FLOAT_MAT2x4:
1576 Capture(callsOut, CaptureUniformMatrix2x4fv(replayState, true, uniformLoc,
1577 uniformCount, false,
1578 uniformBuffer.data()));
1579 break;
1580 case GL_FLOAT_MAT2x3:
1581 Capture(callsOut, CaptureUniformMatrix2x3fv(replayState, true, uniformLoc,
1582 uniformCount, false,
1583 uniformBuffer.data()));
1584 break;
1585 case GL_FLOAT_MAT2:
1586 Capture(callsOut,
1587 CaptureUniformMatrix2fv(replayState, true, uniformLoc, uniformCount,
1588 false, uniformBuffer.data()));
1589 break;
1590 case GL_FLOAT_VEC4:
1591 Capture(callsOut, CaptureUniform4fv(replayState, true, uniformLoc,
1592 uniformCount, uniformBuffer.data()));
1593 break;
1594 case GL_FLOAT_VEC3:
1595 Capture(callsOut, CaptureUniform3fv(replayState, true, uniformLoc,
1596 uniformCount, uniformBuffer.data()));
1597 break;
1598 case GL_FLOAT_VEC2:
1599 Capture(callsOut, CaptureUniform2fv(replayState, true, uniformLoc,
1600 uniformCount, uniformBuffer.data()));
1601 break;
1602 case GL_FLOAT:
1603 Capture(callsOut, CaptureUniform1fv(replayState, true, uniformLoc,
1604 uniformCount, uniformBuffer.data()));
1605 break;
1606 default:
1607 UNIMPLEMENTED();
1608 break;
1609 }
1610 break;
1611 }
1612 case GL_INT:
1613 {
1614 std::vector<GLint> uniformBuffer(uniformSize);
1615 program->getUniformiv(context, uniformLoc, uniformBuffer.data());
1616 switch (typeInfo->componentCount)
1617 {
1618 case 4:
1619 Capture(callsOut, CaptureUniform4iv(replayState, true, uniformLoc,
1620 uniformCount, uniformBuffer.data()));
1621 break;
1622 case 3:
1623 Capture(callsOut, CaptureUniform3iv(replayState, true, uniformLoc,
1624 uniformCount, uniformBuffer.data()));
1625 break;
1626 case 2:
1627 Capture(callsOut, CaptureUniform2iv(replayState, true, uniformLoc,
1628 uniformCount, uniformBuffer.data()));
1629 break;
1630 case 1:
1631 Capture(callsOut, CaptureUniform1iv(replayState, true, uniformLoc,
1632 uniformCount, uniformBuffer.data()));
1633 break;
1634 default:
1635 UNIMPLEMENTED();
1636 break;
1637 }
1638 break;
1639 }
1640 case GL_BOOL:
1641 case GL_UNSIGNED_INT:
1642 {
1643 std::vector<GLuint> uniformBuffer(uniformSize);
1644 program->getUniformuiv(context, uniformLoc, uniformBuffer.data());
1645 switch (typeInfo->componentCount)
1646 {
1647 case 4:
1648 Capture(callsOut, CaptureUniform4uiv(replayState, true, uniformLoc,
1649 uniformCount, uniformBuffer.data()));
1650 break;
1651 case 3:
1652 Capture(callsOut, CaptureUniform3uiv(replayState, true, uniformLoc,
1653 uniformCount, uniformBuffer.data()));
1654 break;
1655 case 2:
1656 Capture(callsOut, CaptureUniform2uiv(replayState, true, uniformLoc,
1657 uniformCount, uniformBuffer.data()));
1658 break;
1659 case 1:
1660 Capture(callsOut, CaptureUniform1uiv(replayState, true, uniformLoc,
1661 uniformCount, uniformBuffer.data()));
1662 break;
1663 default:
1664 UNIMPLEMENTED();
1665 break;
1666 }
1667 break;
1668 }
1669 default:
1670 UNIMPLEMENTED();
1671 break;
1672 }
1673 }
1674 }
1675
CaptureVertexArrayData(std::vector<CallCapture> * setupCalls,const gl::Context * context,const gl::VertexArray * vertexArray,gl::State * replayState)1676 void CaptureVertexArrayData(std::vector<CallCapture> *setupCalls,
1677 const gl::Context *context,
1678 const gl::VertexArray *vertexArray,
1679 gl::State *replayState)
1680 {
1681 const std::vector<gl::VertexAttribute> &vertexAttribs = vertexArray->getVertexAttributes();
1682 const std::vector<gl::VertexBinding> &vertexBindings = vertexArray->getVertexBindings();
1683
1684 for (GLuint attribIndex = 0; attribIndex < gl::MAX_VERTEX_ATTRIBS; ++attribIndex)
1685 {
1686 const gl::VertexAttribute defaultAttrib(attribIndex);
1687 const gl::VertexBinding defaultBinding;
1688
1689 const gl::VertexAttribute &attrib = vertexAttribs[attribIndex];
1690 const gl::VertexBinding &binding = vertexBindings[attrib.bindingIndex];
1691
1692 if (attrib.enabled != defaultAttrib.enabled)
1693 {
1694 Capture(setupCalls, CaptureEnableVertexAttribArray(*replayState, false, attribIndex));
1695 }
1696
1697 if (attrib.format != defaultAttrib.format || attrib.pointer != defaultAttrib.pointer ||
1698 binding.getStride() != defaultBinding.getStride() ||
1699 binding.getBuffer().get() != nullptr)
1700 {
1701 gl::Buffer *buffer = binding.getBuffer().get();
1702
1703 if (buffer != replayState->getArrayBuffer())
1704 {
1705 replayState->setBufferBinding(context, gl::BufferBinding::Array, buffer);
1706 Capture(setupCalls, CaptureBindBuffer(*replayState, true, gl::BufferBinding::Array,
1707 buffer->id()));
1708 }
1709
1710 Capture(setupCalls, CaptureVertexAttribPointer(
1711 *replayState, true, attribIndex, attrib.format->channelCount,
1712 attrib.format->vertexAttribType, attrib.format->isNorm(),
1713 binding.getStride(), attrib.pointer));
1714 }
1715
1716 if (binding.getDivisor() != 0)
1717 {
1718 Capture(setupCalls, CaptureVertexAttribDivisor(*replayState, true, attribIndex,
1719 binding.getDivisor()));
1720 }
1721 }
1722 }
1723
CaptureTextureStorage(std::vector<CallCapture> * setupCalls,gl::State * replayState,const gl::Texture * texture)1724 void CaptureTextureStorage(std::vector<CallCapture> *setupCalls,
1725 gl::State *replayState,
1726 const gl::Texture *texture)
1727 {
1728 // Use mip-level 0 for the base dimensions
1729 gl::ImageIndex imageIndex = gl::ImageIndex::MakeFromType(texture->getType(), 0);
1730 const gl::ImageDesc &desc = texture->getTextureState().getImageDesc(imageIndex);
1731
1732 switch (texture->getType())
1733 {
1734 case gl::TextureType::_2D:
1735 case gl::TextureType::CubeMap:
1736 {
1737 Capture(setupCalls, CaptureTexStorage2D(*replayState, true, texture->getType(),
1738 texture->getImmutableLevels(),
1739 desc.format.info->internalFormat,
1740 desc.size.width, desc.size.height));
1741 break;
1742 }
1743 case gl::TextureType::_3D:
1744 case gl::TextureType::_2DArray:
1745 {
1746 Capture(setupCalls, CaptureTexStorage3D(
1747 *replayState, true, texture->getType(),
1748 texture->getImmutableLevels(), desc.format.info->internalFormat,
1749 desc.size.width, desc.size.height, desc.size.depth));
1750 break;
1751 }
1752 default:
1753 UNIMPLEMENTED();
1754 break;
1755 }
1756 }
1757
CaptureTextureContents(std::vector<CallCapture> * setupCalls,gl::State * replayState,const gl::Texture * texture,const gl::ImageIndex & index,const gl::ImageDesc & desc,GLuint size,const void * data)1758 void CaptureTextureContents(std::vector<CallCapture> *setupCalls,
1759 gl::State *replayState,
1760 const gl::Texture *texture,
1761 const gl::ImageIndex &index,
1762 const gl::ImageDesc &desc,
1763 GLuint size,
1764 const void *data)
1765 {
1766 const gl::InternalFormat &format = *desc.format.info;
1767
1768 bool is3D =
1769 (index.getType() == gl::TextureType::_3D || index.getType() == gl::TextureType::_2DArray);
1770
1771 if (format.compressed)
1772 {
1773 if (is3D)
1774 {
1775 if (texture->getImmutableFormat())
1776 {
1777 Capture(setupCalls,
1778 CaptureCompressedTexSubImage3D(
1779 *replayState, true, index.getTarget(), index.getLevelIndex(), 0, 0, 0,
1780 desc.size.width, desc.size.height, desc.size.depth,
1781 format.internalFormat, size, data));
1782 }
1783 else
1784 {
1785 Capture(setupCalls,
1786 CaptureCompressedTexImage3D(*replayState, true, index.getTarget(),
1787 index.getLevelIndex(), format.internalFormat,
1788 desc.size.width, desc.size.height,
1789 desc.size.depth, 0, size, data));
1790 }
1791 }
1792 else
1793 {
1794 if (texture->getImmutableFormat())
1795 {
1796 Capture(setupCalls,
1797 CaptureCompressedTexSubImage2D(
1798 *replayState, true, index.getTarget(), index.getLevelIndex(), 0, 0,
1799 desc.size.width, desc.size.height, format.internalFormat, size, data));
1800 }
1801 else
1802 {
1803 Capture(setupCalls, CaptureCompressedTexImage2D(
1804 *replayState, true, index.getTarget(),
1805 index.getLevelIndex(), format.internalFormat,
1806 desc.size.width, desc.size.height, 0, size, data));
1807 }
1808 }
1809 }
1810 else
1811 {
1812 if (is3D)
1813 {
1814 if (texture->getImmutableFormat())
1815 {
1816 Capture(setupCalls,
1817 CaptureTexSubImage3D(*replayState, true, index.getTarget(),
1818 index.getLevelIndex(), 0, 0, 0, desc.size.width,
1819 desc.size.height, desc.size.depth, format.format,
1820 format.type, data));
1821 }
1822 else
1823 {
1824 Capture(
1825 setupCalls,
1826 CaptureTexImage3D(*replayState, true, index.getTarget(), index.getLevelIndex(),
1827 format.internalFormat, desc.size.width, desc.size.height,
1828 desc.size.depth, 0, format.format, format.type, data));
1829 }
1830 }
1831 else
1832 {
1833 if (texture->getImmutableFormat())
1834 {
1835 Capture(setupCalls,
1836 CaptureTexSubImage2D(*replayState, true, index.getTarget(),
1837 index.getLevelIndex(), 0, 0, desc.size.width,
1838 desc.size.height, format.format, format.type, data));
1839 }
1840 else
1841 {
1842 Capture(setupCalls, CaptureTexImage2D(*replayState, true, index.getTarget(),
1843 index.getLevelIndex(), format.internalFormat,
1844 desc.size.width, desc.size.height, 0,
1845 format.format, format.type, data));
1846 }
1847 }
1848 }
1849 }
1850
1851 // TODO(http://anglebug.com/4599): Improve reset/restore call generation
1852 // There are multiple ways to track reset calls for individual resources. For now, we are tracking
1853 // separate lists of instructions that mirror the calls created during mid-execution setup. Other
1854 // methods could involve passing the original CallCaptures to this function, or tracking the
1855 // indices of original setup calls.
CaptureBufferResetCalls(const gl::State & replayState,ResourceTracker * resourceTracker,gl::BufferID * id,const gl::Buffer * buffer)1856 void CaptureBufferResetCalls(const gl::State &replayState,
1857 ResourceTracker *resourceTracker,
1858 gl::BufferID *id,
1859 const gl::Buffer *buffer)
1860 {
1861 // Track this as a starting resource that may need to be restored.
1862 BufferSet &startingBuffers = resourceTracker->getStartingBuffers();
1863 startingBuffers.insert(*id);
1864
1865 // Track calls to regenerate a given buffer
1866 BufferCalls &bufferRegenCalls = resourceTracker->getBufferRegenCalls();
1867 Capture(&bufferRegenCalls[*id], CaptureDeleteBuffers(replayState, true, 1, id));
1868 Capture(&bufferRegenCalls[*id], CaptureGenBuffers(replayState, true, 1, id));
1869 MaybeCaptureUpdateResourceIDs(&bufferRegenCalls[*id]);
1870
1871 // Track calls to restore a given buffer's contents
1872 BufferCalls &bufferRestoreCalls = resourceTracker->getBufferRestoreCalls();
1873 Capture(&bufferRestoreCalls[*id],
1874 CaptureBindBuffer(replayState, true, gl::BufferBinding::Array, *id));
1875 Capture(&bufferRestoreCalls[*id],
1876 CaptureBufferData(replayState, true, gl::BufferBinding::Array,
1877 static_cast<GLsizeiptr>(buffer->getSize()), buffer->getMapPointer(),
1878 buffer->getUsage()));
1879
1880 if (buffer->isMapped())
1881 {
1882 // Track calls to remap a buffer that started as mapped
1883 BufferCalls &bufferMapCalls = resourceTracker->getBufferMapCalls();
1884
1885 Capture(&bufferMapCalls[*id],
1886 CaptureBindBuffer(replayState, true, gl::BufferBinding::Array, *id));
1887
1888 void *dontCare = nullptr;
1889 Capture(&bufferMapCalls[*id],
1890 CaptureMapBufferRange(replayState, true, gl::BufferBinding::Array,
1891 static_cast<GLsizeiptr>(buffer->getMapOffset()),
1892 static_cast<GLsizeiptr>(buffer->getMapLength()),
1893 buffer->getAccessFlags(), dontCare));
1894
1895 // Track the bufferID that was just mapped
1896 bufferMapCalls[*id].back().params.setMappedBufferID(buffer->id());
1897 }
1898
1899 // Track calls unmap a buffer that started as unmapped
1900 BufferCalls &bufferUnmapCalls = resourceTracker->getBufferUnmapCalls();
1901 Capture(&bufferUnmapCalls[*id],
1902 CaptureBindBuffer(replayState, true, gl::BufferBinding::Array, *id));
1903 Capture(&bufferUnmapCalls[*id],
1904 CaptureUnmapBuffer(replayState, true, gl::BufferBinding::Array, GL_TRUE));
1905 }
1906
CaptureMidExecutionSetup(const gl::Context * context,std::vector<CallCapture> * setupCalls,ResourceTracker * resourceTracker,const ShaderSourceMap & cachedShaderSources,const ProgramSourceMap & cachedProgramSources,const TextureLevelDataMap & cachedTextureLevelData,FrameCapture * frameCapture)1907 void CaptureMidExecutionSetup(const gl::Context *context,
1908 std::vector<CallCapture> *setupCalls,
1909 ResourceTracker *resourceTracker,
1910 const ShaderSourceMap &cachedShaderSources,
1911 const ProgramSourceMap &cachedProgramSources,
1912 const TextureLevelDataMap &cachedTextureLevelData,
1913 FrameCapture *frameCapture)
1914 {
1915 const gl::State &apiState = context->getState();
1916 gl::State replayState(nullptr, nullptr, nullptr, nullptr, EGL_OPENGL_ES_API,
1917 apiState.getClientVersion(), false, true, true, true, false,
1918 EGL_CONTEXT_PRIORITY_MEDIUM_IMG);
1919
1920 // Small helper function to make the code more readable.
1921 auto cap = [setupCalls](CallCapture &&call) { setupCalls->emplace_back(std::move(call)); };
1922
1923 // Currently this code assumes we can use create-on-bind. It does not support 'Gen' usage.
1924 // TODO(jmadill): Use handle mapping for captured objects. http://anglebug.com/3662
1925
1926 // Capture Buffer data.
1927 const gl::BufferManager &buffers = apiState.getBufferManagerForCapture();
1928 const gl::BoundBufferMap &boundBuffers = apiState.getBoundBuffersForCapture();
1929
1930 for (const auto &bufferIter : buffers)
1931 {
1932 gl::BufferID id = {bufferIter.first};
1933 gl::Buffer *buffer = bufferIter.second;
1934
1935 if (id.value == 0)
1936 {
1937 continue;
1938 }
1939
1940 // glBufferData. Would possibly be better implemented using a getData impl method.
1941 // Saving buffers that are mapped during a swap is not yet handled.
1942 if (buffer->getSize() == 0)
1943 {
1944 continue;
1945 }
1946
1947 // Remember if the buffer was already mapped
1948 GLboolean bufferMapped = buffer->isMapped();
1949
1950 // If needed, map the buffer so we can capture its contents
1951 if (!bufferMapped)
1952 {
1953 (void)buffer->mapRange(context, 0, static_cast<GLsizeiptr>(buffer->getSize()),
1954 GL_MAP_READ_BIT);
1955 }
1956
1957 // Generate binding.
1958 cap(CaptureGenBuffers(replayState, true, 1, &id));
1959 MaybeCaptureUpdateResourceIDs(setupCalls);
1960
1961 // Always use the array buffer binding point to upload data to keep things simple.
1962 if (buffer != replayState.getArrayBuffer())
1963 {
1964 replayState.setBufferBinding(context, gl::BufferBinding::Array, buffer);
1965 cap(CaptureBindBuffer(replayState, true, gl::BufferBinding::Array, id));
1966 }
1967
1968 cap(CaptureBufferData(replayState, true, gl::BufferBinding::Array,
1969 static_cast<GLsizeiptr>(buffer->getSize()), buffer->getMapPointer(),
1970 buffer->getUsage()));
1971
1972 if (bufferMapped)
1973 {
1974 void *dontCare = nullptr;
1975 Capture(setupCalls,
1976 CaptureMapBufferRange(replayState, true, gl::BufferBinding::Array,
1977 static_cast<GLsizeiptr>(buffer->getMapOffset()),
1978 static_cast<GLsizeiptr>(buffer->getMapLength()),
1979 buffer->getAccessFlags(), dontCare));
1980
1981 frameCapture->getResouceTracker().setStartingBufferMapped(buffer->id(), true);
1982
1983 frameCapture->trackBufferMapping(
1984 &setupCalls->back(), buffer->id(), static_cast<GLsizeiptr>(buffer->getMapOffset()),
1985 static_cast<GLsizeiptr>(buffer->getMapLength()), buffer->getAccessFlags());
1986 }
1987 else
1988 {
1989 frameCapture->getResouceTracker().setStartingBufferMapped(buffer->id(), false);
1990 }
1991
1992 // Generate the calls needed to restore this buffer to original state for frame looping
1993 CaptureBufferResetCalls(replayState, resourceTracker, &id, buffer);
1994
1995 // Unmap the buffer if it wasn't already mapped
1996 if (!bufferMapped)
1997 {
1998 GLboolean dontCare;
1999 (void)buffer->unmap(context, &dontCare);
2000 }
2001 }
2002
2003 // Vertex input states. Only handles GLES 2.0 states right now.
2004 // Must happen after buffer data initialization.
2005 // TODO(http://anglebug.com/3662): Complete state capture.
2006
2007 // Capture default vertex attribs
2008 const std::vector<gl::VertexAttribCurrentValueData> ¤tValues =
2009 apiState.getVertexAttribCurrentValues();
2010
2011 for (GLuint attribIndex = 0; attribIndex < gl::MAX_VERTEX_ATTRIBS; ++attribIndex)
2012 {
2013 const gl::VertexAttribCurrentValueData &defaultValue = currentValues[attribIndex];
2014 if (!IsDefaultCurrentValue(defaultValue))
2015 {
2016 Capture(setupCalls, CaptureVertexAttrib4fv(replayState, true, attribIndex,
2017 defaultValue.Values.FloatValues));
2018 }
2019 }
2020
2021 // Capture vertex array objects
2022 const gl::VertexArrayMap &vertexArrayMap = context->getVertexArraysForCapture();
2023 gl::VertexArrayID boundVertexArrayID = {0};
2024 for (const auto &vertexArrayIter : vertexArrayMap)
2025 {
2026 gl::VertexArrayID vertexArrayID = {vertexArrayIter.first};
2027 cap(CaptureGenVertexArrays(replayState, true, 1, &vertexArrayID));
2028 MaybeCaptureUpdateResourceIDs(setupCalls);
2029
2030 if (vertexArrayIter.second)
2031 {
2032 const gl::VertexArray *vertexArray = vertexArrayIter.second;
2033
2034 // Bind the vertexArray (unless default) and populate it
2035 if (vertexArrayID.value != 0)
2036 {
2037 cap(CaptureBindVertexArray(replayState, true, vertexArrayID));
2038 boundVertexArrayID = vertexArrayID;
2039 }
2040 CaptureVertexArrayData(setupCalls, context, vertexArray, &replayState);
2041 }
2042 }
2043
2044 // Bind the current vertex array
2045 const gl::VertexArray *currentVertexArray = apiState.getVertexArray();
2046 if (currentVertexArray->id() != boundVertexArrayID)
2047 {
2048 cap(CaptureBindVertexArray(replayState, true, currentVertexArray->id()));
2049 }
2050
2051 // Capture Buffer bindings.
2052 for (gl::BufferBinding binding : angle::AllEnums<gl::BufferBinding>())
2053 {
2054 gl::BufferID bufferID = boundBuffers[binding].id();
2055
2056 // Filter out redundant buffer binding commands. Note that the code in the previous section
2057 // only binds to ARRAY_BUFFER. Therefore we only check the array binding against the binding
2058 // we set earlier.
2059 bool isArray = binding == gl::BufferBinding::Array;
2060 const gl::Buffer *arrayBuffer = replayState.getArrayBuffer();
2061 if ((isArray && arrayBuffer && arrayBuffer->id() != bufferID) ||
2062 (!isArray && bufferID.value != 0))
2063 {
2064 cap(CaptureBindBuffer(replayState, true, binding, bufferID));
2065 }
2066 }
2067
2068 // Set a unpack alignment of 1.
2069 gl::PixelUnpackState ¤tUnpackState = replayState.getUnpackState();
2070 if (currentUnpackState.alignment != 1)
2071 {
2072 cap(CapturePixelStorei(replayState, true, GL_UNPACK_ALIGNMENT, 1));
2073 currentUnpackState.alignment = 1;
2074 }
2075
2076 // Capture Texture setup and data.
2077 const gl::TextureManager &textures = apiState.getTextureManagerForCapture();
2078 const gl::TextureBindingMap &boundTextures = apiState.getBoundTexturesForCapture();
2079
2080 gl::TextureTypeMap<gl::TextureID> currentTextureBindings;
2081
2082 for (const auto &textureIter : textures)
2083 {
2084 gl::TextureID id = {textureIter.first};
2085 gl::Texture *texture = textureIter.second;
2086
2087 if (id.value == 0)
2088 {
2089 continue;
2090 }
2091
2092 // Gen the Texture.
2093 cap(CaptureGenTextures(replayState, true, 1, &id));
2094 MaybeCaptureUpdateResourceIDs(setupCalls);
2095 cap(CaptureBindTexture(replayState, true, texture->getType(), id));
2096
2097 currentTextureBindings[texture->getType()] = id;
2098
2099 // Capture sampler parameter states.
2100 // TODO(jmadill): More sampler / texture states. http://anglebug.com/3662
2101 gl::SamplerState defaultSamplerState =
2102 gl::SamplerState::CreateDefaultForTarget(texture->getType());
2103 const gl::SamplerState &textureSamplerState = texture->getSamplerState();
2104
2105 auto capTexParam = [cap, &replayState, texture](GLenum pname, GLint param) {
2106 cap(CaptureTexParameteri(replayState, true, texture->getType(), pname, param));
2107 };
2108
2109 auto capTexParamf = [cap, &replayState, texture](GLenum pname, GLfloat param) {
2110 cap(CaptureTexParameterf(replayState, true, texture->getType(), pname, param));
2111 };
2112
2113 if (textureSamplerState.getMinFilter() != defaultSamplerState.getMinFilter())
2114 {
2115 capTexParam(GL_TEXTURE_MIN_FILTER, textureSamplerState.getMinFilter());
2116 }
2117
2118 if (textureSamplerState.getMagFilter() != defaultSamplerState.getMagFilter())
2119 {
2120 capTexParam(GL_TEXTURE_MAG_FILTER, textureSamplerState.getMagFilter());
2121 }
2122
2123 if (textureSamplerState.getWrapR() != defaultSamplerState.getWrapR())
2124 {
2125 capTexParam(GL_TEXTURE_WRAP_R, textureSamplerState.getWrapR());
2126 }
2127
2128 if (textureSamplerState.getWrapS() != defaultSamplerState.getWrapS())
2129 {
2130 capTexParam(GL_TEXTURE_WRAP_S, textureSamplerState.getWrapS());
2131 }
2132
2133 if (textureSamplerState.getWrapT() != defaultSamplerState.getWrapT())
2134 {
2135 capTexParam(GL_TEXTURE_WRAP_T, textureSamplerState.getWrapT());
2136 }
2137
2138 if (textureSamplerState.getMinLod() != defaultSamplerState.getMinLod())
2139 {
2140 capTexParamf(GL_TEXTURE_MIN_LOD, textureSamplerState.getMinLod());
2141 }
2142
2143 if (textureSamplerState.getMaxLod() != defaultSamplerState.getMaxLod())
2144 {
2145 capTexParamf(GL_TEXTURE_MAX_LOD, textureSamplerState.getMaxLod());
2146 }
2147
2148 if (textureSamplerState.getCompareMode() != defaultSamplerState.getCompareMode())
2149 {
2150 capTexParam(GL_TEXTURE_COMPARE_MODE, textureSamplerState.getCompareMode());
2151 }
2152
2153 if (textureSamplerState.getCompareFunc() != defaultSamplerState.getCompareFunc())
2154 {
2155 capTexParam(GL_TEXTURE_COMPARE_FUNC, textureSamplerState.getCompareFunc());
2156 }
2157
2158 // Texture parameters
2159 if (texture->getSwizzleRed() != GL_RED)
2160 {
2161 capTexParam(GL_TEXTURE_SWIZZLE_R, texture->getSwizzleRed());
2162 }
2163
2164 if (texture->getSwizzleGreen() != GL_GREEN)
2165 {
2166 capTexParam(GL_TEXTURE_SWIZZLE_G, texture->getSwizzleGreen());
2167 }
2168
2169 if (texture->getSwizzleBlue() != GL_BLUE)
2170 {
2171 capTexParam(GL_TEXTURE_SWIZZLE_B, texture->getSwizzleBlue());
2172 }
2173
2174 if (texture->getSwizzleAlpha() != GL_ALPHA)
2175 {
2176 capTexParam(GL_TEXTURE_SWIZZLE_A, texture->getSwizzleAlpha());
2177 }
2178
2179 if (texture->getBaseLevel() != 0)
2180 {
2181 capTexParam(GL_TEXTURE_BASE_LEVEL, texture->getBaseLevel());
2182 }
2183
2184 if (texture->getMaxLevel() != 1000)
2185 {
2186 capTexParam(GL_TEXTURE_MAX_LEVEL, texture->getMaxLevel());
2187 }
2188
2189 // If the texture is immutable, initialize it with TexStorage
2190 if (texture->getImmutableFormat())
2191 {
2192 CaptureTextureStorage(setupCalls, &replayState, texture);
2193 }
2194
2195 // Iterate texture levels and layers.
2196 gl::ImageIndexIterator imageIter = gl::ImageIndexIterator::MakeGeneric(
2197 texture->getType(), 0, texture->getMipmapMaxLevel() + 1, gl::ImageIndex::kEntireLevel,
2198 gl::ImageIndex::kEntireLevel);
2199 while (imageIter.hasNext())
2200 {
2201 gl::ImageIndex index = imageIter.next();
2202
2203 const gl::ImageDesc &desc = texture->getTextureState().getImageDesc(index);
2204
2205 if (desc.size.empty())
2206 continue;
2207
2208 const gl::InternalFormat &format = *desc.format.info;
2209
2210 // Check for supported textures
2211 ASSERT(index.getType() == gl::TextureType::_2D ||
2212 index.getType() == gl::TextureType::_3D ||
2213 index.getType() == gl::TextureType::_2DArray ||
2214 index.getType() == gl::TextureType::CubeMap);
2215
2216 if (format.compressed)
2217 {
2218 // For compressed images, we've tracked a copy of the incoming data, so we can
2219 // use that rather than try to read data back that may have been converted.
2220
2221 // Look up the data for the requested texture
2222 const auto &foundTextureLevels = cachedTextureLevelData.find(texture->id());
2223 ASSERT(foundTextureLevels != cachedTextureLevelData.end());
2224
2225 // For that texture, look up the data for the given level
2226 GLint level = index.getLevelIndex();
2227 const auto &foundTextureLevel = foundTextureLevels->second.find(level);
2228 ASSERT(foundTextureLevel != foundTextureLevels->second.end());
2229 const std::vector<uint8_t> &capturedTextureLevel = foundTextureLevel->second;
2230
2231 // Use the shadow copy of the data to populate the call
2232 CaptureTextureContents(setupCalls, &replayState, texture, index, desc,
2233 static_cast<GLuint>(capturedTextureLevel.size()),
2234 capturedTextureLevel.data());
2235 }
2236 else
2237 {
2238 // Use ANGLE_get_image to read back pixel data.
2239 if (context->getExtensions().getImageANGLE)
2240 {
2241 GLenum getFormat = format.format;
2242 GLenum getType = format.type;
2243
2244 angle::MemoryBuffer data;
2245
2246 const gl::Extents size(desc.size.width, desc.size.height, desc.size.depth);
2247 const gl::PixelUnpackState &unpack = apiState.getUnpackState();
2248
2249 GLuint endByte = 0;
2250 bool unpackSize =
2251 format.computePackUnpackEndByte(getType, size, unpack, true, &endByte);
2252 ASSERT(unpackSize);
2253
2254 bool result = data.resize(endByte);
2255 ASSERT(result);
2256
2257 gl::PixelPackState packState;
2258 packState.alignment = 1;
2259
2260 (void)texture->getTexImage(context, packState, nullptr, index.getTarget(),
2261 index.getLevelIndex(), getFormat, getType,
2262 data.data());
2263
2264 CaptureTextureContents(setupCalls, &replayState, texture, index, desc,
2265 static_cast<GLuint>(data.size()), data.data());
2266 }
2267 else
2268 {
2269 CaptureTextureContents(setupCalls, &replayState, texture, index, desc, 0,
2270 nullptr);
2271 }
2272 }
2273 }
2274 }
2275
2276 // Set Texture bindings.
2277 size_t currentActiveTexture = 0;
2278 for (gl::TextureType textureType : angle::AllEnums<gl::TextureType>())
2279 {
2280 const gl::TextureBindingVector &bindings = boundTextures[textureType];
2281 for (size_t bindingIndex = 0; bindingIndex < bindings.size(); ++bindingIndex)
2282 {
2283 gl::TextureID textureID = bindings[bindingIndex].id();
2284
2285 if (textureID.value != 0)
2286 {
2287 if (currentActiveTexture != bindingIndex)
2288 {
2289 cap(CaptureActiveTexture(replayState, true,
2290 GL_TEXTURE0 + static_cast<GLenum>(bindingIndex)));
2291 currentActiveTexture = bindingIndex;
2292 }
2293
2294 if (currentTextureBindings[textureType] != textureID)
2295 {
2296 cap(CaptureBindTexture(replayState, true, textureType, textureID));
2297 currentTextureBindings[textureType] = textureID;
2298 }
2299 }
2300 }
2301 }
2302
2303 // Set active Texture.
2304 size_t stateActiveTexture = apiState.getActiveSampler();
2305 if (currentActiveTexture != stateActiveTexture)
2306 {
2307 cap(CaptureActiveTexture(replayState, true,
2308 GL_TEXTURE0 + static_cast<GLenum>(stateActiveTexture)));
2309 }
2310
2311 // Capture Renderbuffers.
2312 const gl::RenderbufferManager &renderbuffers = apiState.getRenderbufferManagerForCapture();
2313
2314 gl::RenderbufferID currentRenderbuffer = {0};
2315 for (const auto &renderbufIter : renderbuffers)
2316 {
2317 gl::RenderbufferID id = {renderbufIter.first};
2318 const gl::Renderbuffer *renderbuffer = renderbufIter.second;
2319
2320 // Generate renderbuffer id.
2321 cap(CaptureGenRenderbuffers(replayState, true, 1, &id));
2322 MaybeCaptureUpdateResourceIDs(setupCalls);
2323 cap(CaptureBindRenderbuffer(replayState, true, GL_RENDERBUFFER, id));
2324
2325 currentRenderbuffer = id;
2326
2327 GLenum internalformat = renderbuffer->getFormat().info->internalFormat;
2328
2329 if (renderbuffer->getSamples() > 0)
2330 {
2331 // Note: We could also use extensions if available.
2332 cap(CaptureRenderbufferStorageMultisample(
2333 replayState, true, GL_RENDERBUFFER, renderbuffer->getSamples(), internalformat,
2334 renderbuffer->getWidth(), renderbuffer->getHeight()));
2335 }
2336 else
2337 {
2338 cap(CaptureRenderbufferStorage(replayState, true, GL_RENDERBUFFER, internalformat,
2339 renderbuffer->getWidth(), renderbuffer->getHeight()));
2340 }
2341
2342 // TODO(jmadill): Capture renderbuffer contents. http://anglebug.com/3662
2343 }
2344
2345 // Set Renderbuffer binding.
2346 if (currentRenderbuffer != apiState.getRenderbufferId())
2347 {
2348 cap(CaptureBindRenderbuffer(replayState, true, GL_RENDERBUFFER,
2349 apiState.getRenderbufferId()));
2350 }
2351
2352 // Capture Framebuffers.
2353 const gl::FramebufferManager &framebuffers = apiState.getFramebufferManagerForCapture();
2354
2355 gl::FramebufferID currentDrawFramebuffer = {0};
2356 gl::FramebufferID currentReadFramebuffer = {0};
2357
2358 for (const auto &framebufferIter : framebuffers)
2359 {
2360 gl::FramebufferID id = {framebufferIter.first};
2361 const gl::Framebuffer *framebuffer = framebufferIter.second;
2362
2363 // The default Framebuffer exists (by default).
2364 if (framebuffer->isDefault())
2365 continue;
2366
2367 cap(CaptureGenFramebuffers(replayState, true, 1, &id));
2368 MaybeCaptureUpdateResourceIDs(setupCalls);
2369 cap(CaptureBindFramebuffer(replayState, true, GL_FRAMEBUFFER, id));
2370 currentDrawFramebuffer = currentReadFramebuffer = id;
2371
2372 // Color Attachments.
2373 for (const gl::FramebufferAttachment &colorAttachment : framebuffer->getColorAttachments())
2374 {
2375 if (!colorAttachment.isAttached())
2376 {
2377 continue;
2378 }
2379
2380 CaptureFramebufferAttachment(setupCalls, replayState, colorAttachment);
2381 }
2382
2383 const gl::FramebufferAttachment *depthAttachment = framebuffer->getDepthAttachment();
2384 if (depthAttachment)
2385 {
2386 ASSERT(depthAttachment->getBinding() == GL_DEPTH_ATTACHMENT);
2387 CaptureFramebufferAttachment(setupCalls, replayState, *depthAttachment);
2388 }
2389
2390 const gl::FramebufferAttachment *stencilAttachment = framebuffer->getStencilAttachment();
2391 if (stencilAttachment)
2392 {
2393 ASSERT(stencilAttachment->getBinding() == GL_STENCIL_ATTACHMENT);
2394 CaptureFramebufferAttachment(setupCalls, replayState, *stencilAttachment);
2395 }
2396
2397 // TODO(jmadill): Draw buffer states. http://anglebug.com/3662
2398 }
2399
2400 // Capture framebuffer bindings.
2401 gl::FramebufferID stateReadFramebuffer = apiState.getReadFramebuffer()->id();
2402 gl::FramebufferID stateDrawFramebuffer = apiState.getDrawFramebuffer()->id();
2403 if (stateDrawFramebuffer == stateReadFramebuffer)
2404 {
2405 if (currentDrawFramebuffer != stateDrawFramebuffer ||
2406 currentReadFramebuffer != stateReadFramebuffer)
2407 {
2408 cap(CaptureBindFramebuffer(replayState, true, GL_FRAMEBUFFER, stateDrawFramebuffer));
2409 currentDrawFramebuffer = currentReadFramebuffer = stateDrawFramebuffer;
2410 }
2411 }
2412 else
2413 {
2414 if (currentDrawFramebuffer != stateDrawFramebuffer)
2415 {
2416 cap(CaptureBindFramebuffer(replayState, true, GL_DRAW_FRAMEBUFFER,
2417 currentDrawFramebuffer));
2418 currentDrawFramebuffer = stateDrawFramebuffer;
2419 }
2420
2421 if (currentReadFramebuffer != stateReadFramebuffer)
2422 {
2423 cap(CaptureBindFramebuffer(replayState, true, GL_READ_FRAMEBUFFER,
2424 replayState.getReadFramebuffer()->id()));
2425 currentReadFramebuffer = stateReadFramebuffer;
2426 }
2427 }
2428
2429 // Capture Shaders and Programs.
2430 const gl::ShaderProgramManager &shadersAndPrograms =
2431 apiState.getShaderProgramManagerForCapture();
2432 const gl::ResourceMap<gl::Shader, gl::ShaderProgramID> &shaders =
2433 shadersAndPrograms.getShadersForCapture();
2434 const gl::ResourceMap<gl::Program, gl::ShaderProgramID> &programs =
2435 shadersAndPrograms.getProgramsForCapture();
2436
2437 // Capture Program binary state. Use shader ID 1 as a temporary shader ID.
2438 gl::ShaderProgramID tempShaderID = {1};
2439 for (const auto &programIter : programs)
2440 {
2441 gl::ShaderProgramID id = {programIter.first};
2442 const gl::Program *program = programIter.second;
2443
2444 // Get last compiled shader source.
2445 const auto &foundSources = cachedProgramSources.find(id);
2446 ASSERT(foundSources != cachedProgramSources.end());
2447 const ProgramSources &linkedSources = foundSources->second;
2448
2449 // Unlinked programs don't have an executable. Thus they don't need to be linked.
2450 if (!program->isLinked())
2451 {
2452 continue;
2453 }
2454
2455 cap(CaptureCreateProgram(replayState, true, id.value));
2456
2457 // Compile with last linked sources.
2458 for (gl::ShaderType shaderType : program->getExecutable().getLinkedShaderStages())
2459 {
2460 const std::string &sourceString = linkedSources[shaderType];
2461 const char *sourcePointer = sourceString.c_str();
2462
2463 // Compile and attach the temporary shader. Then free it immediately.
2464 cap(CaptureCreateShader(replayState, true, shaderType, tempShaderID.value));
2465 cap(CaptureShaderSource(replayState, true, tempShaderID, 1, &sourcePointer, nullptr));
2466 cap(CaptureCompileShader(replayState, true, tempShaderID));
2467 cap(CaptureAttachShader(replayState, true, id, tempShaderID));
2468 cap(CaptureDeleteShader(replayState, true, tempShaderID));
2469 }
2470
2471 // Gather XFB varyings
2472 std::vector<std::string> xfbVaryings;
2473 for (const gl::TransformFeedbackVarying &xfbVarying :
2474 program->getState().getLinkedTransformFeedbackVaryings())
2475 {
2476 xfbVaryings.push_back(xfbVarying.nameWithArrayIndex());
2477 }
2478
2479 if (!xfbVaryings.empty())
2480 {
2481 std::vector<const char *> varyingsStrings;
2482 for (const std::string &varyingString : xfbVaryings)
2483 {
2484 varyingsStrings.push_back(varyingString.data());
2485 }
2486
2487 GLenum xfbMode = program->getState().getTransformFeedbackBufferMode();
2488 cap(CaptureTransformFeedbackVaryings(replayState, true, id,
2489 static_cast<GLint>(xfbVaryings.size()),
2490 varyingsStrings.data(), xfbMode));
2491 }
2492
2493 // Force the attributes to be bound the same way as in the existing program.
2494 // This can affect attributes that are optimized out in some implementations.
2495 for (const sh::ShaderVariable &attrib : program->getState().getProgramInputs())
2496 {
2497 ASSERT(attrib.location != -1);
2498 cap(CaptureBindAttribLocation(
2499 replayState, true, id, static_cast<GLuint>(attrib.location), attrib.name.c_str()));
2500 }
2501
2502 cap(CaptureLinkProgram(replayState, true, id));
2503 CaptureUpdateUniformLocations(program, setupCalls);
2504 CaptureUpdateUniformValues(replayState, context, program, setupCalls);
2505 }
2506
2507 // Handle shaders.
2508 for (const auto &shaderIter : shaders)
2509 {
2510 gl::ShaderProgramID id = {shaderIter.first};
2511 gl::Shader *shader = shaderIter.second;
2512 cap(CaptureCreateShader(replayState, true, shader->getType(), id.value));
2513
2514 std::string shaderSource = shader->getSourceString();
2515 const char *sourcePointer = shaderSource.empty() ? nullptr : shaderSource.c_str();
2516
2517 // This does not handle some more tricky situations like attaching shaders to a non-linked
2518 // program. Or attaching uncompiled shaders. Or attaching and then deleting a shader.
2519 // TODO(jmadill): Handle trickier program uses. http://anglebug.com/3662
2520 if (shader->isCompiled())
2521 {
2522 const auto &foundSources = cachedShaderSources.find(id);
2523 ASSERT(foundSources != cachedShaderSources.end());
2524 const std::string &capturedSource = foundSources->second;
2525
2526 if (capturedSource != shaderSource)
2527 {
2528 ASSERT(!capturedSource.empty());
2529 sourcePointer = capturedSource.c_str();
2530 }
2531
2532 cap(CaptureShaderSource(replayState, true, id, 1, &sourcePointer, nullptr));
2533 cap(CaptureCompileShader(replayState, true, id));
2534 }
2535
2536 if (sourcePointer && (!shader->isCompiled() || sourcePointer != shaderSource.c_str()))
2537 {
2538 cap(CaptureShaderSource(replayState, true, id, 1, &sourcePointer, nullptr));
2539 }
2540 }
2541
2542 // For now we assume the installed program executable is the same as the current program.
2543 // TODO(jmadill): Handle installed program executable. http://anglebug.com/3662
2544 if (apiState.getProgram())
2545 {
2546 cap(CaptureUseProgram(replayState, true, apiState.getProgram()->id()));
2547 CaptureUpdateCurrentProgram(setupCalls->back(), setupCalls);
2548 }
2549
2550 // TODO(http://anglebug.com/3662): ES 3.x objects.
2551
2552 // Create existing queries. Note that queries may be genned and not yet started. In that
2553 // case the queries will exist in the query map as nullptr entries.
2554 const gl::QueryMap &queryMap = context->getQueriesForCapture();
2555 for (gl::QueryMap::Iterator queryIter = queryMap.beginWithNull();
2556 queryIter != queryMap.endWithNull(); ++queryIter)
2557 {
2558 ASSERT(queryIter->first);
2559 gl::QueryID queryID = {queryIter->first};
2560
2561 cap(CaptureGenQueries(replayState, true, 1, &queryID));
2562 MaybeCaptureUpdateResourceIDs(setupCalls);
2563
2564 gl::Query *query = queryIter->second;
2565 if (query)
2566 {
2567 gl::QueryType queryType = query->getType();
2568
2569 // Begin the query to generate the object
2570 cap(CaptureBeginQuery(replayState, true, queryType, queryID));
2571
2572 // End the query if it was not active
2573 if (!IsQueryActive(apiState, queryID))
2574 {
2575 cap(CaptureEndQuery(replayState, true, queryType));
2576 }
2577 }
2578 }
2579
2580 // Transform Feedback
2581 const gl::TransformFeedbackMap &xfbMap = context->getTransformFeedbacksForCapture();
2582 for (const auto &xfbIter : xfbMap)
2583 {
2584 gl::TransformFeedbackID xfbID = {xfbIter.first};
2585 cap(CaptureGenTransformFeedbacks(replayState, true, 1, &xfbID));
2586 MaybeCaptureUpdateResourceIDs(setupCalls);
2587
2588 gl::TransformFeedback *xfb = xfbIter.second;
2589 if (!xfb)
2590 {
2591 // The object was never created
2592 continue;
2593 }
2594
2595 // Bind XFB to create the object
2596 cap(CaptureBindTransformFeedback(replayState, true, GL_TRANSFORM_FEEDBACK, xfbID));
2597
2598 // Bind the buffers associated with this XFB object
2599 for (size_t i = 0; i < xfb->getIndexedBufferCount(); ++i)
2600 {
2601 const gl::OffsetBindingPointer<gl::Buffer> &xfbBuffer = xfb->getIndexedBuffer(i);
2602
2603 // Note: Buffers bound with BindBufferBase can be used with BindBuffer
2604 cap(CaptureBindBufferRange(replayState, true, gl::BufferBinding::TransformFeedback, 0,
2605 xfbBuffer.id(), xfbBuffer.getOffset(), xfbBuffer.getSize()));
2606 }
2607
2608 if (xfb->isActive() || xfb->isPaused())
2609 {
2610 // We don't support active XFB in MEC yet
2611 UNIMPLEMENTED();
2612 }
2613 }
2614
2615 // Bind the current XFB buffer after populating XFB objects
2616 gl::TransformFeedback *currentXFB = apiState.getCurrentTransformFeedback();
2617 cap(CaptureBindTransformFeedback(replayState, true, GL_TRANSFORM_FEEDBACK, currentXFB->id()));
2618
2619 // Capture Sampler Objects
2620 const gl::SamplerManager &samplers = apiState.getSamplerManagerForCapture();
2621 for (const auto &samplerIter : samplers)
2622 {
2623 gl::SamplerID samplerID = {samplerIter.first};
2624 cap(CaptureGenSamplers(replayState, true, 1, &samplerID));
2625 MaybeCaptureUpdateResourceIDs(setupCalls);
2626
2627 gl::Sampler *sampler = samplerIter.second;
2628 if (!sampler)
2629 {
2630 continue;
2631 }
2632
2633 gl::SamplerState defaultSamplerState;
2634 if (sampler->getMinFilter() != defaultSamplerState.getMinFilter())
2635 {
2636 cap(CaptureSamplerParameteri(replayState, true, samplerID, GL_TEXTURE_MIN_FILTER,
2637 sampler->getMinFilter()));
2638 }
2639 if (sampler->getMagFilter() != defaultSamplerState.getMagFilter())
2640 {
2641 cap(CaptureSamplerParameteri(replayState, true, samplerID, GL_TEXTURE_MAG_FILTER,
2642 sampler->getMagFilter()));
2643 }
2644 if (sampler->getWrapS() != defaultSamplerState.getWrapS())
2645 {
2646 cap(CaptureSamplerParameteri(replayState, true, samplerID, GL_TEXTURE_WRAP_S,
2647 sampler->getWrapS()));
2648 }
2649 if (sampler->getWrapR() != defaultSamplerState.getWrapR())
2650 {
2651 cap(CaptureSamplerParameteri(replayState, true, samplerID, GL_TEXTURE_WRAP_R,
2652 sampler->getWrapR()));
2653 }
2654 if (sampler->getWrapT() != defaultSamplerState.getWrapT())
2655 {
2656 cap(CaptureSamplerParameteri(replayState, true, samplerID, GL_TEXTURE_WRAP_T,
2657 sampler->getWrapT()));
2658 }
2659 if (sampler->getMinLod() != defaultSamplerState.getMinLod())
2660 {
2661 cap(CaptureSamplerParameterf(replayState, true, samplerID, GL_TEXTURE_MIN_LOD,
2662 sampler->getMinLod()));
2663 }
2664 if (sampler->getMaxLod() != defaultSamplerState.getMaxLod())
2665 {
2666 cap(CaptureSamplerParameterf(replayState, true, samplerID, GL_TEXTURE_MAX_LOD,
2667 sampler->getMaxLod()));
2668 }
2669 if (sampler->getCompareMode() != defaultSamplerState.getCompareMode())
2670 {
2671 cap(CaptureSamplerParameteri(replayState, true, samplerID, GL_TEXTURE_COMPARE_MODE,
2672 sampler->getCompareMode()));
2673 }
2674 if (sampler->getCompareFunc() != defaultSamplerState.getCompareFunc())
2675 {
2676 cap(CaptureSamplerParameteri(replayState, true, samplerID, GL_TEXTURE_COMPARE_FUNC,
2677 sampler->getCompareFunc()));
2678 }
2679 }
2680
2681 // Bind samplers
2682 const gl::SamplerBindingVector &samplerBindings = apiState.getSamplers();
2683 for (GLuint bindingIndex = 0; bindingIndex < static_cast<GLuint>(samplerBindings.size());
2684 ++bindingIndex)
2685 {
2686 gl::SamplerID samplerID = samplerBindings[bindingIndex].id();
2687 if (samplerID.value != 0)
2688 {
2689 cap(CaptureBindSampler(replayState, true, bindingIndex, samplerID));
2690 }
2691 }
2692
2693 // Capture Sync Objects
2694 const gl::SyncManager &syncs = apiState.getSyncManagerForCapture();
2695 for (const auto &syncIter : syncs)
2696 {
2697 GLsync syncID = reinterpret_cast<GLsync>(syncIter.first);
2698 gl::Sync *sync = syncIter.second;
2699
2700 if (!sync)
2701 {
2702 continue;
2703 }
2704 cap(CaptureFenceSync(replayState, true, sync->getCondition(), sync->getFlags(), syncID));
2705 }
2706
2707 // Capture GL Context states.
2708 // TODO(http://anglebug.com/3662): Complete state capture.
2709 auto capCap = [cap, &replayState](GLenum capEnum, bool capValue) {
2710 if (capValue)
2711 {
2712 cap(CaptureEnable(replayState, true, capEnum));
2713 }
2714 else
2715 {
2716 cap(CaptureDisable(replayState, true, capEnum));
2717 }
2718 };
2719
2720 // Rasterizer state. Missing ES 3.x features.
2721 // TODO(http://anglebug.com/3662): Complete state capture.
2722 const gl::RasterizerState &defaultRasterState = replayState.getRasterizerState();
2723 const gl::RasterizerState ¤tRasterState = apiState.getRasterizerState();
2724 if (currentRasterState.cullFace != defaultRasterState.cullFace)
2725 {
2726 capCap(GL_CULL_FACE, currentRasterState.cullFace);
2727 }
2728
2729 if (currentRasterState.cullMode != defaultRasterState.cullMode)
2730 {
2731 cap(CaptureCullFace(replayState, true, currentRasterState.cullMode));
2732 }
2733
2734 if (currentRasterState.frontFace != defaultRasterState.frontFace)
2735 {
2736 cap(CaptureFrontFace(replayState, true, currentRasterState.frontFace));
2737 }
2738
2739 // Depth/stencil state.
2740 const gl::DepthStencilState &defaultDSState = replayState.getDepthStencilState();
2741 const gl::DepthStencilState ¤tDSState = apiState.getDepthStencilState();
2742 if (defaultDSState.depthFunc != currentDSState.depthFunc)
2743 {
2744 cap(CaptureDepthFunc(replayState, true, currentDSState.depthFunc));
2745 }
2746
2747 if (defaultDSState.depthMask != currentDSState.depthMask)
2748 {
2749 cap(CaptureDepthMask(replayState, true, gl::ConvertToGLBoolean(currentDSState.depthMask)));
2750 }
2751
2752 if (defaultDSState.depthTest != currentDSState.depthTest)
2753 {
2754 capCap(GL_DEPTH_TEST, currentDSState.depthTest);
2755 }
2756
2757 if (defaultDSState.stencilTest != currentDSState.stencilTest)
2758 {
2759 capCap(GL_STENCIL_TEST, currentDSState.stencilTest);
2760 }
2761
2762 if (defaultDSState.stencilFunc != currentDSState.stencilFunc ||
2763 defaultDSState.stencilMask != currentDSState.stencilMask || apiState.getStencilRef() != 0)
2764 {
2765 cap(CaptureStencilFuncSeparate(replayState, true, GL_FRONT, currentDSState.stencilFunc,
2766 apiState.getStencilRef(), currentDSState.stencilMask));
2767 }
2768
2769 if (defaultDSState.stencilBackFunc != currentDSState.stencilBackFunc ||
2770 defaultDSState.stencilBackMask != currentDSState.stencilBackMask ||
2771 apiState.getStencilBackRef() != 0)
2772 {
2773 cap(CaptureStencilFuncSeparate(replayState, true, GL_BACK, currentDSState.stencilBackFunc,
2774 apiState.getStencilBackRef(),
2775 currentDSState.stencilBackMask));
2776 }
2777
2778 if (defaultDSState.stencilFail != currentDSState.stencilFail ||
2779 defaultDSState.stencilPassDepthFail != currentDSState.stencilPassDepthFail ||
2780 defaultDSState.stencilPassDepthPass != currentDSState.stencilPassDepthPass)
2781 {
2782 cap(CaptureStencilOpSeparate(replayState, true, GL_FRONT, currentDSState.stencilFail,
2783 currentDSState.stencilPassDepthFail,
2784 currentDSState.stencilPassDepthPass));
2785 }
2786
2787 if (defaultDSState.stencilBackFail != currentDSState.stencilBackFail ||
2788 defaultDSState.stencilBackPassDepthFail != currentDSState.stencilBackPassDepthFail ||
2789 defaultDSState.stencilBackPassDepthPass != currentDSState.stencilBackPassDepthPass)
2790 {
2791 cap(CaptureStencilOpSeparate(replayState, true, GL_BACK, currentDSState.stencilBackFail,
2792 currentDSState.stencilBackPassDepthFail,
2793 currentDSState.stencilBackPassDepthPass));
2794 }
2795
2796 if (defaultDSState.stencilWritemask != currentDSState.stencilWritemask)
2797 {
2798 cap(CaptureStencilMaskSeparate(replayState, true, GL_FRONT,
2799 currentDSState.stencilWritemask));
2800 }
2801
2802 if (defaultDSState.stencilBackWritemask != currentDSState.stencilBackWritemask)
2803 {
2804 cap(CaptureStencilMaskSeparate(replayState, true, GL_BACK,
2805 currentDSState.stencilBackWritemask));
2806 }
2807
2808 // Blend state.
2809 const gl::BlendState &defaultBlendState = replayState.getBlendState();
2810 const gl::BlendState ¤tBlendState = apiState.getBlendState();
2811
2812 if (currentBlendState.blend != defaultBlendState.blend)
2813 {
2814 capCap(GL_BLEND, currentBlendState.blend);
2815 }
2816
2817 if (currentBlendState.sourceBlendRGB != defaultBlendState.sourceBlendRGB ||
2818 currentBlendState.destBlendRGB != defaultBlendState.destBlendRGB ||
2819 currentBlendState.sourceBlendAlpha != defaultBlendState.sourceBlendAlpha ||
2820 currentBlendState.destBlendAlpha != defaultBlendState.destBlendAlpha)
2821 {
2822 cap(CaptureBlendFuncSeparate(
2823 replayState, true, currentBlendState.sourceBlendRGB, currentBlendState.destBlendRGB,
2824 currentBlendState.sourceBlendAlpha, currentBlendState.destBlendAlpha));
2825 }
2826
2827 if (currentBlendState.blendEquationRGB != defaultBlendState.blendEquationRGB ||
2828 currentBlendState.blendEquationAlpha != defaultBlendState.blendEquationAlpha)
2829 {
2830 cap(CaptureBlendEquationSeparate(replayState, true, currentBlendState.blendEquationRGB,
2831 currentBlendState.blendEquationAlpha));
2832 }
2833
2834 if (currentBlendState.colorMaskRed != defaultBlendState.colorMaskRed ||
2835 currentBlendState.colorMaskGreen != defaultBlendState.colorMaskGreen ||
2836 currentBlendState.colorMaskBlue != defaultBlendState.colorMaskBlue ||
2837 currentBlendState.colorMaskAlpha != defaultBlendState.colorMaskAlpha)
2838 {
2839 cap(CaptureColorMask(replayState, true,
2840 gl::ConvertToGLBoolean(currentBlendState.colorMaskRed),
2841 gl::ConvertToGLBoolean(currentBlendState.colorMaskGreen),
2842 gl::ConvertToGLBoolean(currentBlendState.colorMaskBlue),
2843 gl::ConvertToGLBoolean(currentBlendState.colorMaskAlpha)));
2844 }
2845
2846 const gl::ColorF ¤tBlendColor = apiState.getBlendColor();
2847 if (currentBlendColor != gl::ColorF())
2848 {
2849 cap(CaptureBlendColor(replayState, true, currentBlendColor.red, currentBlendColor.green,
2850 currentBlendColor.blue, currentBlendColor.alpha));
2851 }
2852
2853 // Pixel storage states.
2854 gl::PixelPackState ¤tPackState = replayState.getPackState();
2855 if (currentPackState.alignment != apiState.getPackAlignment())
2856 {
2857 cap(CapturePixelStorei(replayState, true, GL_PACK_ALIGNMENT, apiState.getPackAlignment()));
2858 currentPackState.alignment = apiState.getPackAlignment();
2859 }
2860
2861 if (currentPackState.rowLength != apiState.getPackRowLength())
2862 {
2863 cap(CapturePixelStorei(replayState, true, GL_PACK_ROW_LENGTH, apiState.getPackRowLength()));
2864 currentPackState.rowLength = apiState.getPackRowLength();
2865 }
2866
2867 if (currentPackState.skipRows != apiState.getPackSkipRows())
2868 {
2869 cap(CapturePixelStorei(replayState, true, GL_PACK_SKIP_ROWS, apiState.getPackSkipRows()));
2870 currentPackState.skipRows = apiState.getPackSkipRows();
2871 }
2872
2873 if (currentPackState.skipPixels != apiState.getPackSkipPixels())
2874 {
2875 cap(CapturePixelStorei(replayState, true, GL_PACK_SKIP_PIXELS,
2876 apiState.getPackSkipPixels()));
2877 currentPackState.skipPixels = apiState.getPackSkipPixels();
2878 }
2879
2880 // We set unpack alignment above, no need to change it here
2881 ASSERT(currentUnpackState.alignment == 1);
2882 if (currentUnpackState.rowLength != apiState.getUnpackRowLength())
2883 {
2884 cap(CapturePixelStorei(replayState, true, GL_UNPACK_ROW_LENGTH,
2885 apiState.getUnpackRowLength()));
2886 currentUnpackState.rowLength = apiState.getUnpackRowLength();
2887 }
2888
2889 if (currentUnpackState.skipRows != apiState.getUnpackSkipRows())
2890 {
2891 cap(CapturePixelStorei(replayState, true, GL_UNPACK_SKIP_ROWS,
2892 apiState.getUnpackSkipRows()));
2893 currentUnpackState.skipRows = apiState.getUnpackSkipRows();
2894 }
2895
2896 if (currentUnpackState.skipPixels != apiState.getUnpackSkipPixels())
2897 {
2898 cap(CapturePixelStorei(replayState, true, GL_UNPACK_SKIP_PIXELS,
2899 apiState.getUnpackSkipPixels()));
2900 currentUnpackState.skipPixels = apiState.getUnpackSkipPixels();
2901 }
2902
2903 if (currentUnpackState.imageHeight != apiState.getUnpackImageHeight())
2904 {
2905 cap(CapturePixelStorei(replayState, true, GL_UNPACK_IMAGE_HEIGHT,
2906 apiState.getUnpackImageHeight()));
2907 currentUnpackState.imageHeight = apiState.getUnpackImageHeight();
2908 }
2909
2910 if (currentUnpackState.skipImages != apiState.getUnpackSkipImages())
2911 {
2912 cap(CapturePixelStorei(replayState, true, GL_UNPACK_SKIP_IMAGES,
2913 apiState.getUnpackSkipImages()));
2914 currentUnpackState.skipImages = apiState.getUnpackSkipImages();
2915 }
2916
2917 // Clear state. Missing ES 3.x features.
2918 // TODO(http://anglebug.com/3662): Complete state capture.
2919 const gl::ColorF ¤tClearColor = apiState.getColorClearValue();
2920 if (currentClearColor != gl::ColorF())
2921 {
2922 cap(CaptureClearColor(replayState, true, currentClearColor.red, currentClearColor.green,
2923 currentClearColor.blue, currentClearColor.alpha));
2924 }
2925
2926 if (apiState.getDepthClearValue() != 1.0f)
2927 {
2928 cap(CaptureClearDepthf(replayState, true, apiState.getDepthClearValue()));
2929 }
2930
2931 if (apiState.getStencilClearValue() != 0)
2932 {
2933 cap(CaptureClearStencil(replayState, true, apiState.getStencilClearValue()));
2934 }
2935
2936 // Viewport / scissor / clipping planes.
2937 const gl::Rectangle ¤tViewport = apiState.getViewport();
2938 if (currentViewport != gl::Rectangle())
2939 {
2940 cap(CaptureViewport(replayState, true, currentViewport.x, currentViewport.y,
2941 currentViewport.width, currentViewport.height));
2942 }
2943
2944 if (apiState.getNearPlane() != 0.0f || apiState.getFarPlane() != 1.0f)
2945 {
2946 cap(CaptureDepthRangef(replayState, true, apiState.getNearPlane(), apiState.getFarPlane()));
2947 }
2948
2949 if (apiState.isScissorTestEnabled())
2950 {
2951 capCap(GL_SCISSOR_TEST, apiState.isScissorTestEnabled());
2952 }
2953
2954 const gl::Rectangle ¤tScissor = apiState.getScissor();
2955 if (currentScissor != gl::Rectangle())
2956 {
2957 cap(CaptureScissor(replayState, true, currentScissor.x, currentScissor.y,
2958 currentScissor.width, currentScissor.height));
2959 }
2960
2961 if (apiState.isDitherEnabled())
2962 {
2963 capCap(GL_DITHER, apiState.isDitherEnabled());
2964 }
2965
2966 // Allow the replayState object to be destroyed conveniently.
2967 replayState.setBufferBinding(context, gl::BufferBinding::Array, nullptr);
2968 }
2969 } // namespace
2970
ParamCapture()2971 ParamCapture::ParamCapture() : type(ParamType::TGLenum), enumGroup(gl::GLenumGroup::DefaultGroup) {}
2972
ParamCapture(const char * nameIn,ParamType typeIn)2973 ParamCapture::ParamCapture(const char *nameIn, ParamType typeIn)
2974 : name(nameIn), type(typeIn), enumGroup(gl::GLenumGroup::DefaultGroup)
2975 {}
2976
2977 ParamCapture::~ParamCapture() = default;
2978
ParamCapture(ParamCapture && other)2979 ParamCapture::ParamCapture(ParamCapture &&other)
2980 : type(ParamType::TGLenum), enumGroup(gl::GLenumGroup::DefaultGroup)
2981 {
2982 *this = std::move(other);
2983 }
2984
operator =(ParamCapture && other)2985 ParamCapture &ParamCapture::operator=(ParamCapture &&other)
2986 {
2987 std::swap(name, other.name);
2988 std::swap(type, other.type);
2989 std::swap(value, other.value);
2990 std::swap(enumGroup, other.enumGroup);
2991 std::swap(data, other.data);
2992 std::swap(arrayClientPointerIndex, other.arrayClientPointerIndex);
2993 std::swap(readBufferSizeBytes, other.readBufferSizeBytes);
2994 return *this;
2995 }
2996
ParamBuffer()2997 ParamBuffer::ParamBuffer() {}
2998
2999 ParamBuffer::~ParamBuffer() = default;
3000
ParamBuffer(ParamBuffer && other)3001 ParamBuffer::ParamBuffer(ParamBuffer &&other)
3002 {
3003 *this = std::move(other);
3004 }
3005
operator =(ParamBuffer && other)3006 ParamBuffer &ParamBuffer::operator=(ParamBuffer &&other)
3007 {
3008 std::swap(mParamCaptures, other.mParamCaptures);
3009 std::swap(mClientArrayDataParam, other.mClientArrayDataParam);
3010 std::swap(mReadBufferSize, other.mReadBufferSize);
3011 std::swap(mReturnValueCapture, other.mReturnValueCapture);
3012 std::swap(mMappedBufferID, other.mMappedBufferID);
3013 return *this;
3014 }
3015
getParam(const char * paramName,ParamType paramType,int index)3016 ParamCapture &ParamBuffer::getParam(const char *paramName, ParamType paramType, int index)
3017 {
3018 ParamCapture &capture = mParamCaptures[index];
3019 ASSERT(capture.name == paramName);
3020 ASSERT(capture.type == paramType);
3021 return capture;
3022 }
3023
getParam(const char * paramName,ParamType paramType,int index) const3024 const ParamCapture &ParamBuffer::getParam(const char *paramName,
3025 ParamType paramType,
3026 int index) const
3027 {
3028 return const_cast<ParamBuffer *>(this)->getParam(paramName, paramType, index);
3029 }
3030
getParamFlexName(const char * paramName1,const char * paramName2,ParamType paramType,int index)3031 ParamCapture &ParamBuffer::getParamFlexName(const char *paramName1,
3032 const char *paramName2,
3033 ParamType paramType,
3034 int index)
3035 {
3036 ParamCapture &capture = mParamCaptures[index];
3037 ASSERT(capture.name == paramName1 || capture.name == paramName2);
3038 ASSERT(capture.type == paramType);
3039 return capture;
3040 }
3041
getParamFlexName(const char * paramName1,const char * paramName2,ParamType paramType,int index) const3042 const ParamCapture &ParamBuffer::getParamFlexName(const char *paramName1,
3043 const char *paramName2,
3044 ParamType paramType,
3045 int index) const
3046 {
3047 return const_cast<ParamBuffer *>(this)->getParamFlexName(paramName1, paramName2, paramType,
3048 index);
3049 }
3050
addParam(ParamCapture && param)3051 void ParamBuffer::addParam(ParamCapture &¶m)
3052 {
3053 if (param.arrayClientPointerIndex != -1)
3054 {
3055 ASSERT(mClientArrayDataParam == -1);
3056 mClientArrayDataParam = static_cast<int>(mParamCaptures.size());
3057 }
3058
3059 mReadBufferSize = std::max(param.readBufferSizeBytes, mReadBufferSize);
3060 mParamCaptures.emplace_back(std::move(param));
3061 }
3062
addReturnValue(ParamCapture && returnValue)3063 void ParamBuffer::addReturnValue(ParamCapture &&returnValue)
3064 {
3065 mReturnValueCapture = std::move(returnValue);
3066 }
3067
getClientArrayPointerParameter()3068 ParamCapture &ParamBuffer::getClientArrayPointerParameter()
3069 {
3070 ASSERT(hasClientArrayData());
3071 return mParamCaptures[mClientArrayDataParam];
3072 }
3073
CallCapture(gl::EntryPoint entryPointIn,ParamBuffer && paramsIn)3074 CallCapture::CallCapture(gl::EntryPoint entryPointIn, ParamBuffer &¶msIn)
3075 : entryPoint(entryPointIn), params(std::move(paramsIn))
3076 {}
3077
CallCapture(const std::string & customFunctionNameIn,ParamBuffer && paramsIn)3078 CallCapture::CallCapture(const std::string &customFunctionNameIn, ParamBuffer &¶msIn)
3079 : entryPoint(gl::EntryPoint::Invalid),
3080 customFunctionName(customFunctionNameIn),
3081 params(std::move(paramsIn))
3082 {}
3083
3084 CallCapture::~CallCapture() = default;
3085
CallCapture(CallCapture && other)3086 CallCapture::CallCapture(CallCapture &&other)
3087 {
3088 *this = std::move(other);
3089 }
3090
operator =(CallCapture && other)3091 CallCapture &CallCapture::operator=(CallCapture &&other)
3092 {
3093 std::swap(entryPoint, other.entryPoint);
3094 std::swap(customFunctionName, other.customFunctionName);
3095 std::swap(params, other.params);
3096 return *this;
3097 }
3098
name() const3099 const char *CallCapture::name() const
3100 {
3101 if (entryPoint == gl::EntryPoint::Invalid)
3102 {
3103 ASSERT(!customFunctionName.empty());
3104 return customFunctionName.c_str();
3105 }
3106
3107 return gl::GetEntryPointName(entryPoint);
3108 }
3109
ReplayContext(size_t readBufferSizebytes,const gl::AttribArray<size_t> & clientArraysSizebytes)3110 ReplayContext::ReplayContext(size_t readBufferSizebytes,
3111 const gl::AttribArray<size_t> &clientArraysSizebytes)
3112 {
3113 mReadBuffer.resize(readBufferSizebytes);
3114
3115 for (uint32_t i = 0; i < clientArraysSizebytes.size(); i++)
3116 {
3117 mClientArraysBuffer[i].resize(clientArraysSizebytes[i]);
3118 }
3119 }
~ReplayContext()3120 ReplayContext::~ReplayContext() {}
3121
FrameCapture()3122 FrameCapture::FrameCapture()
3123 : mEnabled(true),
3124 mCompression(true),
3125 mClientVertexArrayMap{},
3126 mFrameIndex(0),
3127 mFrameStart(0),
3128 mFrameEnd(10),
3129 mClientArraySizes{},
3130 mReadBufferSize(0),
3131 mHasResourceType{}
3132 {
3133 reset();
3134
3135 #if defined(ANGLE_PLATFORM_ANDROID)
3136 PrimeAndroidEnvironmentVariables();
3137 #endif
3138
3139 std::string enabledFromEnv = angle::GetEnvironmentVar(kEnabledVarName);
3140 if (enabledFromEnv == "0")
3141 {
3142 mEnabled = false;
3143 }
3144
3145 std::string pathFromEnv = angle::GetEnvironmentVar(kOutDirectoryVarName);
3146 if (pathFromEnv.empty())
3147 {
3148 mOutDirectory = GetDefaultOutDirectory();
3149 }
3150 else
3151 {
3152 mOutDirectory = pathFromEnv;
3153 }
3154
3155 // Ensure the capture path ends with a slash.
3156 if (mOutDirectory.back() != '\\' && mOutDirectory.back() != '/')
3157 {
3158 mOutDirectory += '/';
3159 }
3160
3161 std::string startFromEnv = angle::GetEnvironmentVar(kFrameStartVarName);
3162 if (!startFromEnv.empty())
3163 {
3164 mFrameStart = atoi(startFromEnv.c_str());
3165 }
3166
3167 std::string endFromEnv = angle::GetEnvironmentVar(kFrameEndVarName);
3168 if (!endFromEnv.empty())
3169 {
3170 mFrameEnd = atoi(endFromEnv.c_str());
3171 }
3172
3173 std::string labelFromEnv = angle::GetEnvironmentVar(kCaptureLabel);
3174 if (!labelFromEnv.empty())
3175 {
3176 // Optional label to provide unique file names and namespaces
3177 mCaptureLabel = labelFromEnv;
3178 }
3179
3180 std::string compressionFromEnv = angle::GetEnvironmentVar(kCompression);
3181 if (compressionFromEnv == "0")
3182 {
3183 mCompression = false;
3184 }
3185 }
3186
3187 FrameCapture::~FrameCapture() = default;
3188
captureCompressedTextureData(const gl::Context * context,const CallCapture & call)3189 void FrameCapture::captureCompressedTextureData(const gl::Context *context, const CallCapture &call)
3190 {
3191 // For compressed textures, track a shadow copy of the data
3192 // for use during mid-execution capture, rather than reading it back
3193 // with ANGLE_get_image
3194
3195 // Storing the compressed data is handled the same for all entry points,
3196 // they just have slightly different parameter locations
3197 int dataParamOffset = -1;
3198 int xoffsetParamOffset = -1;
3199 int yoffsetParamOffset = -1;
3200 int zoffsetParamOffset = -1;
3201 int widthParamOffset = -1;
3202 int heightParamOffset = -1;
3203 int depthParamOffset = -1;
3204 switch (call.entryPoint)
3205 {
3206 case gl::EntryPoint::CompressedTexSubImage3D:
3207 xoffsetParamOffset = 2;
3208 yoffsetParamOffset = 3;
3209 zoffsetParamOffset = 4;
3210 widthParamOffset = 5;
3211 heightParamOffset = 6;
3212 depthParamOffset = 7;
3213 dataParamOffset = 10;
3214 break;
3215 case gl::EntryPoint::CompressedTexImage3D:
3216 widthParamOffset = 4;
3217 heightParamOffset = 5;
3218 depthParamOffset = 6;
3219 dataParamOffset = 9;
3220 break;
3221 case gl::EntryPoint::CompressedTexSubImage2D:
3222 xoffsetParamOffset = 2;
3223 yoffsetParamOffset = 3;
3224 widthParamOffset = 4;
3225 heightParamOffset = 5;
3226 dataParamOffset = 8;
3227 break;
3228 case gl::EntryPoint::CompressedTexImage2D:
3229 widthParamOffset = 3;
3230 heightParamOffset = 4;
3231 dataParamOffset = 7;
3232 break;
3233 default:
3234 // There should be no other callers of this function
3235 ASSERT(0);
3236 break;
3237 }
3238
3239 gl::Buffer *pixelUnpackBuffer =
3240 context->getState().getTargetBuffer(gl::BufferBinding::PixelUnpack);
3241
3242 const uint8_t *data = static_cast<const uint8_t *>(
3243 call.params.getParam("data", ParamType::TvoidConstPointer, dataParamOffset)
3244 .value.voidConstPointerVal);
3245
3246 GLsizei imageSize = call.params.getParam("imageSize", ParamType::TGLsizei, dataParamOffset - 1)
3247 .value.GLsizeiVal;
3248
3249 const uint8_t *pixelData = nullptr;
3250
3251 if (pixelUnpackBuffer)
3252 {
3253 // If using pixel unpack buffer, map the buffer and track its data
3254 ASSERT(!pixelUnpackBuffer->isMapped());
3255 (void)pixelUnpackBuffer->mapRange(context, reinterpret_cast<GLintptr>(data), imageSize,
3256 GL_MAP_READ_BIT);
3257
3258 pixelData = reinterpret_cast<const uint8_t *>(pixelUnpackBuffer->getMapPointer());
3259 }
3260 else
3261 {
3262 pixelData = data;
3263 }
3264
3265 if (!pixelData)
3266 {
3267 // If no pointer was provided and we weren't able to map the buffer, there is no data to
3268 // capture
3269 return;
3270 }
3271
3272 // Look up the texture type
3273 gl::TextureTarget targetPacked =
3274 call.params.getParam("targetPacked", ParamType::TTextureTarget, 0).value.TextureTargetVal;
3275 gl::TextureType textureType = gl::TextureTargetToType(targetPacked);
3276
3277 // Create a copy of the incoming data
3278 std::vector<uint8_t> compressedData;
3279 compressedData.assign(pixelData, pixelData + imageSize);
3280
3281 // Look up the currently bound texture
3282 gl::Texture *texture = context->getState().getTargetTexture(textureType);
3283 ASSERT(texture);
3284
3285 // Record the data, indexed by textureID and level
3286 GLint level = call.params.getParam("level", ParamType::TGLint, 1).value.GLintVal;
3287 auto foundTextureLevels = mCachedTextureLevelData.find(texture->id());
3288 if (foundTextureLevels == mCachedTextureLevelData.end())
3289 {
3290 // Initialize the texture ID data.
3291 auto emplaceResult = mCachedTextureLevelData.emplace(texture->id(), TextureLevels());
3292 ASSERT(emplaceResult.second);
3293 foundTextureLevels = emplaceResult.first;
3294 }
3295
3296 // Get the format of the texture for use with the compressed block size math.
3297 const gl::InternalFormat &format = *texture->getFormat(targetPacked, level).info;
3298
3299 TextureLevels &foundLevels = foundTextureLevels->second;
3300 auto foundLevel = foundLevels.find(level);
3301
3302 // Divide dimensions according to block size.
3303 const gl::Extents &levelExtents = texture->getExtents(targetPacked, level);
3304
3305 if (foundLevel == foundLevels.end())
3306 {
3307 // Initialize texture rectangle data. Default init to zero for stability.
3308 GLuint sizeInBytes;
3309 bool result = format.computeCompressedImageSize(levelExtents, &sizeInBytes);
3310 ASSERT(result);
3311
3312 std::vector<uint8_t> newPixelData(sizeInBytes, 0);
3313 auto emplaceResult = foundLevels.emplace(level, std::move(newPixelData));
3314 ASSERT(emplaceResult.second);
3315 foundLevel = emplaceResult.first;
3316 }
3317
3318 // Unpack the various pixel rectangle parameters.
3319 ASSERT(widthParamOffset != -1);
3320 ASSERT(heightParamOffset != -1);
3321 GLsizei pixelWidth =
3322 call.params.getParam("width", ParamType::TGLsizei, widthParamOffset).value.GLsizeiVal;
3323 GLsizei pixelHeight =
3324 call.params.getParam("height", ParamType::TGLsizei, heightParamOffset).value.GLsizeiVal;
3325 GLsizei pixelDepth = 1;
3326 if (depthParamOffset != -1)
3327 {
3328 pixelDepth =
3329 call.params.getParam("depth", ParamType::TGLsizei, depthParamOffset).value.GLsizeiVal;
3330 }
3331
3332 GLint xoffset = 0;
3333 GLint yoffset = 0;
3334 GLint zoffset = 0;
3335
3336 if (xoffsetParamOffset != -1)
3337 {
3338 xoffset =
3339 call.params.getParam("xoffset", ParamType::TGLint, xoffsetParamOffset).value.GLintVal;
3340 }
3341
3342 if (yoffsetParamOffset != -1)
3343 {
3344 yoffset =
3345 call.params.getParam("yoffset", ParamType::TGLint, yoffsetParamOffset).value.GLintVal;
3346 }
3347
3348 if (zoffsetParamOffset != -1)
3349 {
3350 zoffset =
3351 call.params.getParam("zoffset", ParamType::TGLint, zoffsetParamOffset).value.GLintVal;
3352 }
3353
3354 // Since we're dealing in 4x4 blocks, scale down the width/height pixel offsets.
3355 ASSERT(format.compressedBlockWidth == 4);
3356 ASSERT(format.compressedBlockHeight == 4);
3357 ASSERT(format.compressedBlockDepth == 1);
3358 pixelWidth >>= 2;
3359 pixelHeight >>= 2;
3360 xoffset >>= 2;
3361 yoffset >>= 2;
3362
3363 // Update pixel data.
3364 std::vector<uint8_t> &levelData = foundLevel->second;
3365
3366 GLint pixelBytes = static_cast<GLint>(format.pixelBytes);
3367
3368 GLint pixelRowPitch = pixelWidth * pixelBytes;
3369 GLint pixelDepthPitch = pixelRowPitch * pixelHeight;
3370 GLint levelRowPitch = (levelExtents.width >> 2) * pixelBytes;
3371 GLint levelDepthPitch = levelRowPitch * (levelExtents.height >> 2);
3372
3373 for (GLint zindex = 0; zindex < pixelDepth; ++zindex)
3374 {
3375 GLint z = zindex + zoffset;
3376 for (GLint yindex = 0; yindex < pixelHeight; ++yindex)
3377 {
3378 GLint y = yindex + yoffset;
3379 GLint pixelOffset = zindex * pixelDepthPitch + yindex * pixelRowPitch;
3380 GLint levelOffset = z * levelDepthPitch + y * levelRowPitch + xoffset * pixelBytes;
3381 memcpy(&levelData[levelOffset], &pixelData[pixelOffset], pixelRowPitch);
3382 }
3383 }
3384
3385 if (pixelUnpackBuffer)
3386 {
3387 GLboolean success;
3388 (void)pixelUnpackBuffer->unmap(context, &success);
3389 ASSERT(success);
3390 }
3391 }
3392
trackBufferMapping(CallCapture * call,gl::BufferID id,GLintptr offset,GLsizeiptr length,GLbitfield accessFlags)3393 void FrameCapture::trackBufferMapping(CallCapture *call,
3394 gl::BufferID id,
3395 GLintptr offset,
3396 GLsizeiptr length,
3397 GLbitfield accessFlags)
3398 {
3399 // Track that the buffer was mapped
3400 mResourceTracker.setBufferMapped(id);
3401
3402 if (accessFlags & GL_MAP_WRITE_BIT)
3403 {
3404 // If this buffer was mapped writable, we don't have any visibility into what
3405 // happens to it. Therefore, remember the details about it, and we'll read it back
3406 // on Unmap to repopulate it during replay.
3407 mBufferDataMap[id] = std::make_pair(offset, length);
3408
3409 // Track that this buffer was potentially modified
3410 mResourceTracker.setBufferModified(id);
3411
3412 // Track the bufferID that was just mapped for use when writing return value
3413 call->params.setMappedBufferID(id);
3414 }
3415 }
3416
maybeCaptureClientData(const gl::Context * context,CallCapture & call)3417 void FrameCapture::maybeCaptureClientData(const gl::Context *context, CallCapture &call)
3418 {
3419 switch (call.entryPoint)
3420 {
3421 case gl::EntryPoint::VertexAttribPointer:
3422 {
3423 // Get array location
3424 GLuint index = call.params.getParam("index", ParamType::TGLuint, 0).value.GLuintVal;
3425
3426 if (call.params.hasClientArrayData())
3427 {
3428 mClientVertexArrayMap[index] = static_cast<int>(mFrameCalls.size());
3429 }
3430 else
3431 {
3432 mClientVertexArrayMap[index] = -1;
3433 }
3434 break;
3435 }
3436
3437 case gl::EntryPoint::DeleteBuffers:
3438 {
3439 GLsizei count = call.params.getParam("n", ParamType::TGLsizei, 0).value.GLsizeiVal;
3440 const gl::BufferID *bufferIDs =
3441 call.params.getParam("buffersPacked", ParamType::TBufferIDConstPointer, 1)
3442 .value.BufferIDConstPointerVal;
3443 for (GLsizei i = 0; i < count; i++)
3444 {
3445 // For each buffer being deleted, check our backup of data and remove it
3446 const auto &bufferDataInfo = mBufferDataMap.find(bufferIDs[i]);
3447 if (bufferDataInfo != mBufferDataMap.end())
3448 {
3449 mBufferDataMap.erase(bufferDataInfo);
3450 }
3451 // If we're capturing, track what new buffers have been genned
3452 if (mFrameIndex >= mFrameStart)
3453 {
3454 mResourceTracker.setDeletedBuffer(bufferIDs[i]);
3455 }
3456 }
3457 break;
3458 }
3459
3460 case gl::EntryPoint::GenBuffers:
3461 {
3462 GLsizei count = call.params.getParam("n", ParamType::TGLsizei, 0).value.GLsizeiVal;
3463 const gl::BufferID *bufferIDs =
3464 call.params.getParam("buffersPacked", ParamType::TBufferIDPointer, 1)
3465 .value.BufferIDPointerVal;
3466 for (GLsizei i = 0; i < count; i++)
3467 {
3468 // If we're capturing, track what new buffers have been genned
3469 if (mFrameIndex >= mFrameStart)
3470 {
3471 mResourceTracker.setGennedBuffer(bufferIDs[i]);
3472 }
3473 }
3474 break;
3475 }
3476
3477 case gl::EntryPoint::DrawArrays:
3478 {
3479 if (context->getStateCache().hasAnyActiveClientAttrib())
3480 {
3481 // Get counts from paramBuffer.
3482 GLint firstVertex =
3483 call.params.getParam("first", ParamType::TGLint, 1).value.GLintVal;
3484 GLsizei drawCount =
3485 call.params.getParam("count", ParamType::TGLsizei, 2).value.GLsizeiVal;
3486 captureClientArraySnapshot(context, firstVertex + drawCount, 1);
3487 }
3488 break;
3489 }
3490
3491 case gl::EntryPoint::DrawElements:
3492 {
3493 if (context->getStateCache().hasAnyActiveClientAttrib())
3494 {
3495 GLsizei count =
3496 call.params.getParam("count", ParamType::TGLsizei, 1).value.GLsizeiVal;
3497 gl::DrawElementsType drawElementsType =
3498 call.params.getParam("typePacked", ParamType::TDrawElementsType, 2)
3499 .value.DrawElementsTypeVal;
3500 const void *indices =
3501 call.params.getParam("indices", ParamType::TvoidConstPointer, 3)
3502 .value.voidConstPointerVal;
3503
3504 gl::IndexRange indexRange;
3505
3506 bool restart = context->getState().isPrimitiveRestartEnabled();
3507
3508 gl::Buffer *elementArrayBuffer =
3509 context->getState().getVertexArray()->getElementArrayBuffer();
3510 if (elementArrayBuffer)
3511 {
3512 size_t offset = reinterpret_cast<size_t>(indices);
3513 (void)elementArrayBuffer->getIndexRange(context, drawElementsType, offset,
3514 count, restart, &indexRange);
3515 }
3516 else
3517 {
3518 indexRange = gl::ComputeIndexRange(drawElementsType, indices, count, restart);
3519 }
3520
3521 // index starts from 0
3522 captureClientArraySnapshot(context, indexRange.end + 1, 1);
3523 }
3524 break;
3525 }
3526
3527 case gl::EntryPoint::CompileShader:
3528 {
3529 // Refresh the cached shader sources.
3530 gl::ShaderProgramID shaderID =
3531 call.params.getParam("shaderPacked", ParamType::TShaderProgramID, 0)
3532 .value.ShaderProgramIDVal;
3533 const gl::Shader *shader = context->getShader(shaderID);
3534 mCachedShaderSources[shaderID] = shader->getSourceString();
3535 break;
3536 }
3537
3538 case gl::EntryPoint::LinkProgram:
3539 {
3540 // Refresh the cached program sources.
3541 gl::ShaderProgramID programID =
3542 call.params.getParam("programPacked", ParamType::TShaderProgramID, 0)
3543 .value.ShaderProgramIDVal;
3544 const gl::Program *program = context->getProgramResolveLink(programID);
3545 mCachedProgramSources[programID] = GetAttachedProgramSources(program);
3546 break;
3547 }
3548
3549 case gl::EntryPoint::CompressedTexImage1D:
3550 case gl::EntryPoint::CompressedTexSubImage1D:
3551 {
3552 UNIMPLEMENTED();
3553 break;
3554 }
3555
3556 case gl::EntryPoint::CompressedTexImage2D:
3557 case gl::EntryPoint::CompressedTexImage3D:
3558 case gl::EntryPoint::CompressedTexSubImage2D:
3559 case gl::EntryPoint::CompressedTexSubImage3D:
3560 {
3561 captureCompressedTextureData(context, call);
3562 break;
3563 }
3564
3565 case gl::EntryPoint::DeleteTextures:
3566 {
3567 // Free any TextureLevelDataMap entries being tracked for this texture
3568 // This is to cover the scenario where a texture has been created, its
3569 // levels cached, then texture deleted and recreated, receiving the same ID
3570
3571 // Look up how many textures are being deleted
3572 GLsizei n = call.params.getParam("n", ParamType::TGLsizei, 0).value.GLsizeiVal;
3573
3574 // Look up the pointer to list of textures
3575 const gl::TextureID *textureIDs =
3576 call.params.getParam("texturesPacked", ParamType::TTextureIDConstPointer, 1)
3577 .value.TextureIDConstPointerVal;
3578
3579 // For each texture listed for deletion
3580 for (int32_t i = 0; i < n; ++i)
3581 {
3582 // Look it up in the cache, and delete it if found
3583 const auto &foundTextureLevels = mCachedTextureLevelData.find(textureIDs[i]);
3584 if (foundTextureLevels != mCachedTextureLevelData.end())
3585 {
3586 // Delete all texture levels at once
3587 mCachedTextureLevelData.erase(foundTextureLevels);
3588 }
3589 }
3590 break;
3591 }
3592
3593 case gl::EntryPoint::MapBuffer:
3594 {
3595 UNIMPLEMENTED();
3596 break;
3597 }
3598 case gl::EntryPoint::MapBufferOES:
3599 {
3600 UNIMPLEMENTED();
3601 break;
3602 }
3603 case gl::EntryPoint::UnmapNamedBuffer:
3604 {
3605 UNIMPLEMENTED();
3606 break;
3607 }
3608
3609 case gl::EntryPoint::MapBufferRange:
3610 case gl::EntryPoint::MapBufferRangeEXT:
3611 {
3612 GLintptr offset =
3613 call.params.getParam("offset", ParamType::TGLintptr, 1).value.GLintptrVal;
3614 GLsizeiptr length =
3615 call.params.getParam("length", ParamType::TGLsizeiptr, 2).value.GLsizeiptrVal;
3616 GLbitfield access =
3617 call.params.getParam("access", ParamType::TGLbitfield, 3).value.GLbitfieldVal;
3618
3619 gl::BufferBinding target =
3620 call.params.getParam("targetPacked", ParamType::TBufferBinding, 0)
3621 .value.BufferBindingVal;
3622 gl::Buffer *buffer = context->getState().getTargetBuffer(target);
3623
3624 trackBufferMapping(&call, buffer->id(), offset, length, access);
3625 break;
3626 }
3627
3628 case gl::EntryPoint::UnmapBuffer:
3629 case gl::EntryPoint::UnmapBufferOES:
3630 {
3631 // See if we need to capture the buffer contents
3632 captureMappedBufferSnapshot(context, call);
3633
3634 // Track that the buffer was unmapped, for use during state reset
3635 gl::BufferBinding target =
3636 call.params.getParam("targetPacked", ParamType::TBufferBinding, 0)
3637 .value.BufferBindingVal;
3638 gl::Buffer *buffer = context->getState().getTargetBuffer(target);
3639 mResourceTracker.setBufferUnmapped(buffer->id());
3640 break;
3641 }
3642
3643 case gl::EntryPoint::BufferData:
3644 case gl::EntryPoint::BufferSubData:
3645 {
3646 gl::BufferBinding target =
3647 call.params.getParam("targetPacked", ParamType::TBufferBinding, 0)
3648 .value.BufferBindingVal;
3649
3650 gl::Buffer *buffer = context->getState().getTargetBuffer(target);
3651
3652 // Track that this buffer's contents have been modified
3653 mResourceTracker.setBufferModified(buffer->id());
3654
3655 // BufferData is equivalent to UnmapBuffer, for what we're tracking.
3656 // From the ES 3.1 spec in BufferData section:
3657 // If any portion of the buffer object is mapped in the current context or any
3658 // context current to another thread, it is as though UnmapBuffer (see section
3659 // 6.3.1) is executed in each such context prior to deleting the existing data
3660 // store.
3661 // Track that the buffer was unmapped, for use during state reset
3662 mResourceTracker.setBufferUnmapped(buffer->id());
3663
3664 break;
3665 }
3666 default:
3667 break;
3668 }
3669 }
3670
captureCall(const gl::Context * context,CallCapture && call)3671 void FrameCapture::captureCall(const gl::Context *context, CallCapture &&call)
3672 {
3673 // Process client data snapshots.
3674 maybeCaptureClientData(context, call);
3675
3676 mReadBufferSize = std::max(mReadBufferSize, call.params.getReadBufferSize());
3677 mFrameCalls.emplace_back(std::move(call));
3678
3679 maybeCapturePostCallUpdates(context);
3680 }
3681
maybeCapturePostCallUpdates(const gl::Context * context)3682 void FrameCapture::maybeCapturePostCallUpdates(const gl::Context *context)
3683 {
3684 // Process resource ID updates.
3685 MaybeCaptureUpdateResourceIDs(&mFrameCalls);
3686
3687 const CallCapture &lastCall = mFrameCalls.back();
3688 switch (lastCall.entryPoint)
3689 {
3690 case gl::EntryPoint::LinkProgram:
3691 {
3692 const ParamCapture ¶m =
3693 lastCall.params.getParam("programPacked", ParamType::TShaderProgramID, 0);
3694 const gl::Program *program =
3695 context->getProgramResolveLink(param.value.ShaderProgramIDVal);
3696 CaptureUpdateUniformLocations(program, &mFrameCalls);
3697 break;
3698 }
3699 case gl::EntryPoint::UseProgram:
3700 CaptureUpdateCurrentProgram(lastCall, &mFrameCalls);
3701 break;
3702 case gl::EntryPoint::DeleteProgram:
3703 {
3704 const ParamCapture ¶m =
3705 lastCall.params.getParam("programPacked", ParamType::TShaderProgramID, 0);
3706 CaptureDeleteUniformLocations(param.value.ShaderProgramIDVal, &mFrameCalls);
3707 break;
3708 }
3709 case gl::EntryPoint::BindFramebuffer:
3710 {
3711 const ParamCapture &target = lastCall.params.getParam("target", ParamType::TGLenum, 0);
3712 const ParamCapture &framebuffer =
3713 lastCall.params.getParam("framebufferPacked", ParamType::TFramebufferID, 1);
3714 CaptureOnFramebufferChange(target.value.GLenumVal, framebuffer.value.FramebufferIDVal,
3715 &mFrameCalls);
3716 break;
3717 }
3718 default:
3719 break;
3720 }
3721 }
3722
captureClientArraySnapshot(const gl::Context * context,size_t vertexCount,size_t instanceCount)3723 void FrameCapture::captureClientArraySnapshot(const gl::Context *context,
3724 size_t vertexCount,
3725 size_t instanceCount)
3726 {
3727 const gl::VertexArray *vao = context->getState().getVertexArray();
3728
3729 // Capture client array data.
3730 for (size_t attribIndex : context->getStateCache().getActiveClientAttribsMask())
3731 {
3732 const gl::VertexAttribute &attrib = vao->getVertexAttribute(attribIndex);
3733 const gl::VertexBinding &binding = vao->getVertexBinding(attrib.bindingIndex);
3734
3735 int callIndex = mClientVertexArrayMap[attribIndex];
3736
3737 if (callIndex != -1)
3738 {
3739 size_t count = vertexCount;
3740
3741 if (binding.getDivisor() > 0)
3742 {
3743 count = rx::UnsignedCeilDivide(static_cast<uint32_t>(instanceCount),
3744 binding.getDivisor());
3745 }
3746
3747 // The last capture element doesn't take up the full stride.
3748 size_t bytesToCapture = (count - 1) * binding.getStride() + attrib.format->pixelBytes;
3749
3750 CallCapture &call = mFrameCalls[callIndex];
3751 ParamCapture ¶m = call.params.getClientArrayPointerParameter();
3752 ASSERT(param.type == ParamType::TvoidConstPointer);
3753
3754 ParamBuffer updateParamBuffer;
3755 updateParamBuffer.addValueParam<GLint>("arrayIndex", ParamType::TGLint,
3756 static_cast<uint32_t>(attribIndex));
3757
3758 ParamCapture updateMemory("pointer", ParamType::TvoidConstPointer);
3759 CaptureMemory(param.value.voidConstPointerVal, bytesToCapture, &updateMemory);
3760 updateParamBuffer.addParam(std::move(updateMemory));
3761
3762 updateParamBuffer.addValueParam<GLuint64>("size", ParamType::TGLuint64, bytesToCapture);
3763
3764 mFrameCalls.emplace_back("UpdateClientArrayPointer", std::move(updateParamBuffer));
3765
3766 mClientArraySizes[attribIndex] =
3767 std::max(mClientArraySizes[attribIndex], bytesToCapture);
3768 }
3769 }
3770 }
3771
captureMappedBufferSnapshot(const gl::Context * context,const CallCapture & call)3772 void FrameCapture::captureMappedBufferSnapshot(const gl::Context *context, const CallCapture &call)
3773 {
3774 // If the buffer was mapped writable, we need to restore its data, since we have no visibility
3775 // into what the client did to the buffer while mapped
3776 // This sequence will result in replay calls like this:
3777 // ...
3778 // gMappedBufferData[gBufferMap[42]] = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, 65536,
3779 // GL_MAP_WRITE_BIT);
3780 // ...
3781 // UpdateClientBufferData(42, &gBinaryData[164631024], 65536);
3782 // glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
3783 // ...
3784
3785 // Re-map the buffer, using the info we tracked about the buffer
3786 gl::BufferBinding target =
3787 call.params.getParam("targetPacked", ParamType::TBufferBinding, 0).value.BufferBindingVal;
3788
3789 gl::Buffer *buffer = context->getState().getTargetBuffer(target);
3790 const auto &bufferDataInfo = mBufferDataMap.find(buffer->id());
3791 if (bufferDataInfo == mBufferDataMap.end())
3792 {
3793 // This buffer was not marked writable, so we did not back it up
3794 return;
3795 }
3796
3797 GLintptr offset = bufferDataInfo->second.first;
3798 GLsizeiptr length = bufferDataInfo->second.second;
3799
3800 // Map the buffer so we can copy its contents out
3801 ASSERT(!buffer->isMapped());
3802 angle::Result result = buffer->mapRange(context, offset, length, GL_MAP_READ_BIT);
3803 if (result != angle::Result::Continue)
3804 {
3805 ERR() << "Failed to mapRange of buffer" << std::endl;
3806 }
3807 const uint8_t *data = reinterpret_cast<const uint8_t *>(buffer->getMapPointer());
3808
3809 // Create the parameters to our helper for use during replay
3810 ParamBuffer dataParamBuffer;
3811
3812 // Pass in the target buffer ID
3813 dataParamBuffer.addValueParam("dest", ParamType::TGLuint, buffer->id().value);
3814
3815 // Capture the current buffer data with a binary param
3816 ParamCapture captureData("source", ParamType::TvoidConstPointer);
3817 CaptureMemory(data, length, &captureData);
3818 dataParamBuffer.addParam(std::move(captureData));
3819
3820 // Also track its size for use with memcpy
3821 dataParamBuffer.addValueParam<GLsizeiptr>("size", ParamType::TGLsizeiptr, length);
3822
3823 // Call the helper that populates the buffer with captured data
3824 mFrameCalls.emplace_back("UpdateClientBufferData", std::move(dataParamBuffer));
3825
3826 // Unmap the buffer and move on
3827 GLboolean dontCare;
3828 (void)buffer->unmap(context, &dontCare);
3829 }
3830
onEndFrame(const gl::Context * context)3831 void FrameCapture::onEndFrame(const gl::Context *context)
3832 {
3833 // Note that we currently capture before the start frame to collect shader and program sources.
3834 if (!mFrameCalls.empty() && mFrameIndex >= mFrameStart)
3835 {
3836 WriteCppReplay(mCompression, mOutDirectory, context->id(), mCaptureLabel, mFrameIndex,
3837 mFrameEnd, mFrameCalls, mSetupCalls, &mResourceTracker, &mBinaryData);
3838
3839 // Save the index files after the last frame.
3840 if (mFrameIndex == mFrameEnd)
3841 {
3842 WriteCppReplayIndexFiles(mCompression, mOutDirectory, context->id(), mCaptureLabel,
3843 mFrameStart, mFrameEnd, mReadBufferSize, mClientArraySizes,
3844 mHasResourceType);
3845
3846 if (!mBinaryData.empty())
3847 {
3848 SaveBinaryData(mCompression, mOutDirectory, context->id(), mCaptureLabel,
3849 mBinaryData);
3850 mBinaryData.clear();
3851 }
3852 }
3853 }
3854
3855 // Count resource IDs. This is also done on every frame. It could probably be done by checking
3856 // the GL state instead of the calls.
3857 for (const CallCapture &call : mFrameCalls)
3858 {
3859 for (const ParamCapture ¶m : call.params.getParamCaptures())
3860 {
3861 ResourceIDType idType = GetResourceIDTypeFromParamType(param.type);
3862 if (idType != ResourceIDType::InvalidEnum)
3863 {
3864 mHasResourceType.set(idType);
3865 }
3866 }
3867 }
3868
3869 reset();
3870 mFrameIndex++;
3871
3872 if (enabled() && mFrameIndex == mFrameStart)
3873 {
3874 mSetupCalls.clear();
3875 CaptureMidExecutionSetup(context, &mSetupCalls, &mResourceTracker, mCachedShaderSources,
3876 mCachedProgramSources, mCachedTextureLevelData, this);
3877 }
3878 }
3879
3880 DataCounters::DataCounters() = default;
3881
3882 DataCounters::~DataCounters() = default;
3883
getAndIncrement(gl::EntryPoint entryPoint,const std::string & paramName)3884 int DataCounters::getAndIncrement(gl::EntryPoint entryPoint, const std::string ¶mName)
3885 {
3886 Counter counterKey = {entryPoint, paramName};
3887 return mData[counterKey]++;
3888 }
3889
3890 ResourceTracker::ResourceTracker() = default;
3891
3892 ResourceTracker::~ResourceTracker() = default;
3893
setDeletedBuffer(gl::BufferID id)3894 void ResourceTracker::setDeletedBuffer(gl::BufferID id)
3895 {
3896 if (mNewBuffers.find(id) != mNewBuffers.end())
3897 {
3898 // This is a buffer genned after MEC was initialized, just clear it, since there will be no
3899 // actions required for it to return to starting state.
3900 mNewBuffers.erase(id);
3901 return;
3902 }
3903
3904 // Ensure this buffer was in our starting set
3905 // It's possible this could fire if the app deletes buffers that were never generated
3906 ASSERT(mStartingBuffers.find(id) != mStartingBuffers.end());
3907
3908 // In this case, the app is deleting a buffer we started with, we need to regen on loop
3909 mBuffersToRegen.insert(id);
3910 mBuffersToRestore.insert(id);
3911 }
3912
setGennedBuffer(gl::BufferID id)3913 void ResourceTracker::setGennedBuffer(gl::BufferID id)
3914 {
3915 if (mStartingBuffers.find(id) == mStartingBuffers.end())
3916 {
3917 // This is a buffer genned after MEC was initialized, track it
3918 mNewBuffers.insert(id);
3919 return;
3920 }
3921 }
3922
setBufferModified(gl::BufferID id)3923 void ResourceTracker::setBufferModified(gl::BufferID id)
3924 {
3925 // If this was a starting buffer, we need to track it for restore
3926 if (mStartingBuffers.find(id) != mStartingBuffers.end())
3927 {
3928 mBuffersToRestore.insert(id);
3929 }
3930 }
3931
setBufferMapped(gl::BufferID id)3932 void ResourceTracker::setBufferMapped(gl::BufferID id)
3933 {
3934 // If this was a starting buffer, we may need to restore it to original state during Reset
3935 if (mStartingBuffers.find(id) != mStartingBuffers.end())
3936 {
3937 // Track that its current state is mapped (true)
3938 mStartingBuffersMappedCurrent[id] = true;
3939 }
3940 }
3941
setBufferUnmapped(gl::BufferID id)3942 void ResourceTracker::setBufferUnmapped(gl::BufferID id)
3943 {
3944 // If this was a starting buffer, we may need to restore it to original state during Reset
3945 if (mStartingBuffers.find(id) != mStartingBuffers.end())
3946 {
3947 // Track that its current state is unmapped (false)
3948 mStartingBuffersMappedCurrent[id] = false;
3949 }
3950 }
3951
isCapturing() const3952 bool FrameCapture::isCapturing() const
3953 {
3954 // Currently we will always do a capture up until the last frame. In the future we could improve
3955 // mid execution capture by only capturing between the start and end frames. The only necessary
3956 // reason we need to capture before the start is for attached program and shader sources.
3957 return mEnabled && mFrameIndex <= mFrameEnd;
3958 }
3959
replay(gl::Context * context)3960 void FrameCapture::replay(gl::Context *context)
3961 {
3962 ReplayContext replayContext(mReadBufferSize, mClientArraySizes);
3963 for (const CallCapture &call : mFrameCalls)
3964 {
3965 INFO() << "frame index: " << mFrameIndex << " " << call.name();
3966
3967 if (call.entryPoint == gl::EntryPoint::Invalid)
3968 {
3969 if (call.customFunctionName == "UpdateClientArrayPointer")
3970 {
3971 GLint arrayIndex =
3972 call.params.getParam("arrayIndex", ParamType::TGLint, 0).value.GLintVal;
3973 ASSERT(arrayIndex < gl::MAX_VERTEX_ATTRIBS);
3974
3975 const ParamCapture &pointerParam =
3976 call.params.getParam("pointer", ParamType::TvoidConstPointer, 1);
3977 ASSERT(pointerParam.data.size() == 1);
3978 const void *pointer = pointerParam.data[0].data();
3979
3980 size_t size = static_cast<size_t>(
3981 call.params.getParam("size", ParamType::TGLuint64, 2).value.GLuint64Val);
3982
3983 std::vector<uint8_t> &curClientArrayBuffer =
3984 replayContext.getClientArraysBuffer()[arrayIndex];
3985 ASSERT(curClientArrayBuffer.size() >= size);
3986 memcpy(curClientArrayBuffer.data(), pointer, size);
3987 }
3988 continue;
3989 }
3990
3991 ReplayCall(context, &replayContext, call);
3992 }
3993 }
3994
reset()3995 void FrameCapture::reset()
3996 {
3997 mFrameCalls.clear();
3998 mSetupCalls.clear();
3999 mClientVertexArrayMap.fill(-1);
4000
4001 // Do not reset replay-specific settings like the maximum read buffer size, client array sizes,
4002 // or the 'has seen' type map. We could refine this into per-frame and per-capture maximums if
4003 // necessary.
4004 }
4005
CaptureMemory(const void * source,size_t size,ParamCapture * paramCapture)4006 void CaptureMemory(const void *source, size_t size, ParamCapture *paramCapture)
4007 {
4008 std::vector<uint8_t> data(size);
4009 memcpy(data.data(), source, size);
4010 paramCapture->data.emplace_back(std::move(data));
4011 }
4012
CaptureString(const GLchar * str,ParamCapture * paramCapture)4013 void CaptureString(const GLchar *str, ParamCapture *paramCapture)
4014 {
4015 // include the '\0' suffix
4016 CaptureMemory(str, strlen(str) + 1, paramCapture);
4017 }
4018
CaptureStringLimit(const GLchar * str,uint32_t limit,ParamCapture * paramCapture)4019 void CaptureStringLimit(const GLchar *str, uint32_t limit, ParamCapture *paramCapture)
4020 {
4021 // Write the incoming string up to limit, including null terminator
4022 size_t length = strlen(str) + 1;
4023
4024 if (length > limit)
4025 {
4026 // If too many characters, resize the string to fit in the limit
4027 std::string newStr = str;
4028 newStr.resize(limit - 1);
4029 CaptureString(newStr.c_str(), paramCapture);
4030 }
4031 else
4032 {
4033 CaptureMemory(str, length, paramCapture);
4034 }
4035 }
4036
GetProgramForCapture(const gl::State & glState,gl::ShaderProgramID handle)4037 gl::Program *GetProgramForCapture(const gl::State &glState, gl::ShaderProgramID handle)
4038 {
4039 gl::Program *program = glState.getShaderProgramManagerForCapture().getProgram(handle);
4040 return program;
4041 }
4042
CaptureGetParameter(const gl::State & glState,GLenum pname,size_t typeSize,ParamCapture * paramCapture)4043 void CaptureGetParameter(const gl::State &glState,
4044 GLenum pname,
4045 size_t typeSize,
4046 ParamCapture *paramCapture)
4047 {
4048 GLenum nativeType;
4049 unsigned int numParams;
4050 if (!gl::GetQueryParameterInfo(glState, pname, &nativeType, &numParams))
4051 {
4052 numParams = 1;
4053 }
4054
4055 paramCapture->readBufferSizeBytes = typeSize * numParams;
4056 }
4057
CaptureGenHandlesImpl(GLsizei n,GLuint * handles,ParamCapture * paramCapture)4058 void CaptureGenHandlesImpl(GLsizei n, GLuint *handles, ParamCapture *paramCapture)
4059 {
4060 paramCapture->readBufferSizeBytes = sizeof(GLuint) * n;
4061 CaptureMemory(handles, paramCapture->readBufferSizeBytes, paramCapture);
4062 }
4063
4064 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,GLboolean value)4065 void WriteParamValueReplay<ParamType::TGLboolean>(std::ostream &os,
4066 const CallCapture &call,
4067 GLboolean value)
4068 {
4069 switch (value)
4070 {
4071 case GL_TRUE:
4072 os << "GL_TRUE";
4073 break;
4074 case GL_FALSE:
4075 os << "GL_FALSE";
4076 break;
4077 default:
4078 os << "GL_INVALID_ENUM";
4079 }
4080 }
4081
4082 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,const void * value)4083 void WriteParamValueReplay<ParamType::TvoidConstPointer>(std::ostream &os,
4084 const CallCapture &call,
4085 const void *value)
4086 {
4087 if (value == 0)
4088 {
4089 os << "nullptr";
4090 }
4091 else
4092 {
4093 os << "reinterpret_cast<const void *>("
4094 << static_cast<int>(reinterpret_cast<uintptr_t>(value)) << ")";
4095 }
4096 }
4097
4098 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,GLDEBUGPROCKHR value)4099 void WriteParamValueReplay<ParamType::TGLDEBUGPROCKHR>(std::ostream &os,
4100 const CallCapture &call,
4101 GLDEBUGPROCKHR value)
4102 {}
4103
4104 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,GLDEBUGPROC value)4105 void WriteParamValueReplay<ParamType::TGLDEBUGPROC>(std::ostream &os,
4106 const CallCapture &call,
4107 GLDEBUGPROC value)
4108 {}
4109
4110 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,gl::BufferID value)4111 void WriteParamValueReplay<ParamType::TBufferID>(std::ostream &os,
4112 const CallCapture &call,
4113 gl::BufferID value)
4114 {
4115 os << "gBufferMap[" << value.value << "]";
4116 }
4117
4118 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,gl::FenceNVID value)4119 void WriteParamValueReplay<ParamType::TFenceNVID>(std::ostream &os,
4120 const CallCapture &call,
4121 gl::FenceNVID value)
4122 {
4123 os << "gFenceMap[" << value.value << "]";
4124 }
4125
4126 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,gl::FramebufferID value)4127 void WriteParamValueReplay<ParamType::TFramebufferID>(std::ostream &os,
4128 const CallCapture &call,
4129 gl::FramebufferID value)
4130 {
4131 os << "gFramebufferMap[" << value.value << "]";
4132 }
4133
4134 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,gl::MemoryObjectID value)4135 void WriteParamValueReplay<ParamType::TMemoryObjectID>(std::ostream &os,
4136 const CallCapture &call,
4137 gl::MemoryObjectID value)
4138 {
4139 os << "gMemoryObjectMap[" << value.value << "]";
4140 }
4141
4142 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,gl::ProgramPipelineID value)4143 void WriteParamValueReplay<ParamType::TProgramPipelineID>(std::ostream &os,
4144 const CallCapture &call,
4145 gl::ProgramPipelineID value)
4146 {
4147 os << "gProgramPipelineMap[" << value.value << "]";
4148 }
4149
4150 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,gl::QueryID value)4151 void WriteParamValueReplay<ParamType::TQueryID>(std::ostream &os,
4152 const CallCapture &call,
4153 gl::QueryID value)
4154 {
4155 os << "gQueryMap[" << value.value << "]";
4156 }
4157
4158 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,gl::RenderbufferID value)4159 void WriteParamValueReplay<ParamType::TRenderbufferID>(std::ostream &os,
4160 const CallCapture &call,
4161 gl::RenderbufferID value)
4162 {
4163 os << "gRenderbufferMap[" << value.value << "]";
4164 }
4165
4166 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,gl::SamplerID value)4167 void WriteParamValueReplay<ParamType::TSamplerID>(std::ostream &os,
4168 const CallCapture &call,
4169 gl::SamplerID value)
4170 {
4171 os << "gSamplerMap[" << value.value << "]";
4172 }
4173
4174 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,gl::SemaphoreID value)4175 void WriteParamValueReplay<ParamType::TSemaphoreID>(std::ostream &os,
4176 const CallCapture &call,
4177 gl::SemaphoreID value)
4178 {
4179 os << "gSempahoreMap[" << value.value << "]";
4180 }
4181
4182 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,gl::ShaderProgramID value)4183 void WriteParamValueReplay<ParamType::TShaderProgramID>(std::ostream &os,
4184 const CallCapture &call,
4185 gl::ShaderProgramID value)
4186 {
4187 os << "gShaderProgramMap[" << value.value << "]";
4188 }
4189
4190 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,GLsync value)4191 void WriteParamValueReplay<ParamType::TGLsync>(std::ostream &os,
4192 const CallCapture &call,
4193 GLsync value)
4194 {
4195 os << "gSyncMap[" << SyncIndexValue(value) << "]";
4196 }
4197
4198 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,gl::TextureID value)4199 void WriteParamValueReplay<ParamType::TTextureID>(std::ostream &os,
4200 const CallCapture &call,
4201 gl::TextureID value)
4202 {
4203 os << "gTextureMap[" << value.value << "]";
4204 }
4205
4206 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,gl::TransformFeedbackID value)4207 void WriteParamValueReplay<ParamType::TTransformFeedbackID>(std::ostream &os,
4208 const CallCapture &call,
4209 gl::TransformFeedbackID value)
4210 {
4211 os << "gTransformFeedbackMap[" << value.value << "]";
4212 }
4213
4214 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,gl::VertexArrayID value)4215 void WriteParamValueReplay<ParamType::TVertexArrayID>(std::ostream &os,
4216 const CallCapture &call,
4217 gl::VertexArrayID value)
4218 {
4219 os << "gVertexArrayMap[" << value.value << "]";
4220 }
4221
FindShaderProgramIDInCall(const CallCapture & call,gl::ShaderProgramID * idOut)4222 bool FindShaderProgramIDInCall(const CallCapture &call, gl::ShaderProgramID *idOut)
4223 {
4224 for (const ParamCapture ¶m : call.params.getParamCaptures())
4225 {
4226 if (param.type == ParamType::TShaderProgramID && param.name == "programPacked")
4227 {
4228 *idOut = param.value.ShaderProgramIDVal;
4229 return true;
4230 }
4231 }
4232
4233 return false;
4234 }
4235
4236 template <>
WriteParamValueReplay(std::ostream & os,const CallCapture & call,gl::UniformLocation value)4237 void WriteParamValueReplay<ParamType::TUniformLocation>(std::ostream &os,
4238 const CallCapture &call,
4239 gl::UniformLocation value)
4240 {
4241 if (value.value == -1)
4242 {
4243 os << "-1";
4244 return;
4245 }
4246
4247 os << "gUniformLocations[";
4248
4249 // Find the program from the call parameters.
4250 gl::ShaderProgramID programID;
4251 if (FindShaderProgramIDInCall(call, &programID))
4252 {
4253 os << "gShaderProgramMap[" << programID.value << "]";
4254 }
4255 else
4256 {
4257 os << "gCurrentProgram";
4258 }
4259
4260 os << "][" << value.value << "]";
4261 }
4262 } // namespace angle
4263